import ast
import distutils.spawn
import signal
import subprocess
import sys


def execute_command(cmd, input_str=None):
    """
    Execute a command, capture and return its output.
    """
    kwargs = {
        'stdin': subprocess.PIPE,
        'stdout': subprocess.PIPE,
        'stderr': subprocess.PIPE,
    }
    p = subprocess.Popen(cmd, **kwargs)
    out, err = p.communicate(input=input_str)
    exitCode = p.wait()
    if exitCode == -signal.SIGINT:
        raise KeyboardInterrupt
    return out, err, exitCode


def execute_command_verbose(cmd, input_str=None):
    """
    Execute a command and print its output on failure.
    """
    out, err, exitCode = execute_command(cmd, input_str=input_str)
    if exitCode != 0:
        report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd])
        report += "Exit Code: %d\n" % exitCode
        if out:
            report += "Standard Output:\n--\n%s--" % out
        if err:
            report += "Standard Error:\n--\n%s--" % err
        report += "\n\nFailed!"
        sys.stderr.write('%s\n' % report)
    return out, err, exitCode


def read_syms_from_list(slist):
    """
    Read a list of symbols from a list of strings.
    Each string is one symbol.
    """
    return [ast.literal_eval(l) for l in slist]


def read_syms_from_file(filename):
    """
    Read a list of symbols in from a file.
    """
    with open(filename, 'r') as f:
        data = f.read()
    return read_syms_from_list(data.splitlines())


def read_blacklist(filename):
    with open(filename, 'r') as f:
        data = f.read()
    lines = [l.strip() for l in data.splitlines() if l.strip()]
    lines = [l for l in lines if not l.startswith('#')]
    return lines


def write_syms(sym_list, out=None, names_only=False):
    """
    Write a list of symbols to the file named by out.
    """
    out_str = ''
    out_list = sym_list
    if names_only:
        out_list = [sym['name'] for sym in sym_list]
        out_list.sort()
    for sym in out_list:
        out_str += '%s\n' % sym
    if out is None:
        sys.stdout.write(out_str)
    else:
        with open(out, 'w') as f:
            f.write(out_str)


_cppfilt_exe = distutils.spawn.find_executable('c++filt')


def demangle_symbol(symbol):
    if _cppfilt_exe is None:
        return symbol
    out, _, exit_code = execute_command_verbose(
        [_cppfilt_exe], input_str=symbol)
    if exit_code != 0:
        return symbol
    return out


def extract_or_load(filename):
    import sym_check.extract
    if filename.endswith('.so') or filename.endswith('.dylib'):
        return sym_check.extract.extract_symbols(filename)
    return read_syms_from_file(filename)

@@ -2458,125 +2458,741 @@
 def 
-extract_or_load(filename):%0A    import sym_check.extract%0A    if filename.endswith('.so') or filename.endswith('.dylib'
+is_elf(filename):%0A    with open(filename, 'r') as f:%0A        magic_bytes = f.read(4)%0A    return magic_bytes == '%5Cx7fELF'%0A%0A%0Adef is_mach_o(filename):%0A    with open(filename, 'r') as f:%0A        magic_bytes = f.read(4)%0A    return magic_bytes in %5B%0A        '%5Cxfe%5Cxed%5Cxfa%5Cxce',  # MH_MAGIC%0A        '%5Cxce%5Cxfa%5Cxed%5Cxfe',  # MH_CIGAM%0A        '%5Cxfe%5Cxed%5Cxfa%5Cxcf',  # MH_MAGIC_64%0A        '%5Cxcf%5Cxfa%5Cxed%5Cxfe',  # MH_CIGAM_64%0A        '%5Cxca%5Cxfe%5Cxba%5Cxbe',  # FAT_MAGIC%0A        '%5Cxbe%5Cxba%5Cxfe%5Cxca'   # FAT_CIGAM%0A    %5D%0A%0A%0Adef is_library_file(filename):%0A    if sys.platform == 'darwin':%0A        return is_mach_o(filename)%0A    else:%0A        return is_elf(filename)%0A%0A%0Adef extract_or_load(filename):%0A    import sym_check.extract%0A    if is_library_file(filename
 ):%0A 

Improve automatic detection of filetype for sym_check