"""
    XNU Triage commands
"""
from xnu import *
import sys, shlex
from utils import *
import xnudefines
import re
import os.path

# Macro: xi
def OutputAddress(cmd_args=None):
    """ Returns out address and symbol corresponding to it without newline
        Parameters: <address whose symbol is needed>
    """
    if cmd_args is None or len(cmd_args) == 0:
        raise ArgumentError()

    a = unsigned(cmd_args[0])
    cmd_str = "image lookup -a {:#x}".format(a)
    cmd_out = lldb_run_command(cmd_str)
    if len(cmd_out) != 0 and cmd_out != "ERROR:":
        cmd_out1 = cmd_out.split('\n')
        if len(cmd_out1) != 0:
            cmd_out2 = cmd_out1[1].split('`')
            if cmd_out2 != 0:
                cmd_out3 = cmd_out2[1].split(' at')
                if len(cmd_out3) != 0:
                    symbol_str = "{:#018x} <{:s}>".format(unsigned(a), cmd_out3[0])
                    return symbol_str
    return ""

@lldb_command('xi')
def SymbolicateWithInstruction(cmd_args=None):
    """ Prints out address and symbol similar to x/i
        Usage: xi <address whose symbol is needed>
    """
    if cmd_args is None or len(cmd_args) == 0:
        raise ArgumentError()

    a = ArgumentStringToInt(cmd_args[0])
    print(OutputAddress([a]))

# Macro: xi

# Macro: newbt
@lldb_command('newbt')
def NewBt(cmd_args=None):
    """ Prints all the instructions by walking the given stack pointer
    """
    if cmd_args is None or len(cmd_args) == 0:
        raise ArgumentError()

    a = ArgumentStringToInt(cmd_args[0])
    while a != 0:
        if kern.arch == "x86_64" or kern.arch.startswith("arm64"):
            offset = 8
        else:
            offset = 4
        link_register = dereference(kern.GetValueFromAddress(a + offset, 'uintptr_t *'))
        cmd_str = "di -s {:#x} -c 1".format(link_register)
        cmd_out = lldb_run_command(cmd_str)
        if len(cmd_out) != 0:
            cmd_out1 = list(filter(None, cmd_out.split('\n')))
            if len(cmd_out1) != 0:
                address = OutputAddress([unsigned(link_register)])
                if not address:
                    address = '{:#018x} <???>'.format(unsigned(link_register))
                print(address + ": " + cmd_out1[-1].split(':', 1)[1])
        a = dereference(kern.GetValueFromAddress(unsigned(a), 'uintptr_t *'))

# EndMacro: newbt

paniclog_data = ""

# Macro: parseLR
@lldb_command('parseLR')
def parseLR(cmd_args=None):
    """ Decode the LR value from panic log into source code location
    """
    global paniclog_data
    panic_found = 1

    if not paniclog_data:
        if kern.arch == "x86_64":
            paniclog_data += lldb_run_command("paniclog -v")
        else:
            paniclog_data += lldb_run_command("paniclog")

    if panic_found == 1:
        srch_string = "lr:\s+0x[a-fA-F0-9]+\s"
        lr_pc_srch = re.findall(srch_string, paniclog_data)
        if lr_pc_srch:
            print(paniclog_data, lr_pc_srch)
            for match in lr_pc_srch:
                sp=match.strip("lr: ")
                print(sp)
                print("(lldb) list *{:s}".format(sp))
                print(lldb_run_command("list *{:s}".format(sp)))

    else:
        print("Currently unsupported on x86_64 architecture")
#EndMacro: parseLR

# Macro: parseLRfromfile
@lldb_command('parseLRfromfile')
def parseLRfromfile(cmd_args=None):
    """ Decode the LR value from file into source code location
        
        Usage: parseLRfromfile [file_path]
    """
    if cmd_args is None or len(cmd_args) == 0:
        raise ArgumentError()
    
    f = open(cmd_args[0], 'r')
    parse_data= f.read()
    srch_string = "lr:\s+0x[a-fA-F0-9]+\s"
    lr_pc_srch = re.findall(srch_string, parse_data)
    if lr_pc_srch:
        print(paniclog_data, lr_pc_srch)
        for match in lr_pc_srch:
            sp=match.strip("lr: ")
            print(sp)
            print("(lldb) list *{:s}".format(sp))
            print(lldb_run_command("list *{:s}".format(sp)))

#EndMacro: parseLRfromfile

