#!/usr/bin/python

import bz2, errno, gzip, os, re, shutil, struct, sys
from debian_bundle import deb822

arch = 'sparc'
mirror = 'http://mirror/debian/'

def check_shlib(f):
    result = False

    code = f.read()
    i = -1
    while True:
        # Look for save %sp, offset1, %sp .. ld [ %sp + offset2 ], reg
        # where offset1 + offset2 < 0
        # and intermediate instructions do not include ret
        # offset1 will be treated as unsigned when we extract it, so wea
        # compare with 0x2000 not 0.
        i = code.find('\x9d\xe3', i + 1)
        if i < 0 or i + 4 > len(code):
            break
        if i % 4 != 0:
            continue
        word1 = struct.unpack('>I', code[i : i + 4])[0]
        if (word1 & 0xffffe000) == 0x9de3a000:
            j = 4
            while j <= 44 and i + j + 4 <= len(code):
                word2 = struct.unpack('>I', code[i + j : i + j + 4])[0]
                if word2 == 0x81c7e008:
                    break
                if ((word2 & 0xc1ffe000) == 0xc003a000 and
                    (word1 & 0x00001fff) + (word2 & 0x00001fff) < 0x2000):
                    print ('%s matches [0x%x..0x%x: 0x%x..0x%x]' %
                           (f.name, i, i + j, word1, word2))
                    result = True
                j += 4

    return result

def find_shlibs():
    result = {}    

    contents = gzip.GzipFile('Contents-%s.gz' % arch, 'r')

    # Skip header
    for line in contents:
        if line.startswith('FILE'):
            break

    # Make a map of package -> shlib paths
    shlib_re = re.compile(r'([^\s]*/lib[^/\s]+\.so(?:\.[^/\s]*)?)\s')
    for line in contents:
        match = shlib_re.match(line)
        if match:
            path = match.group(1)
            pp = line[match.end():].strip()
            for p in pp.split(','):
                name = p.split('/')[-1]
                result.setdefault(name, []).append(path)

    return result

def get_filenames():
    result = {}
    for dfsg_section in ['main', 'contrib', 'non-free']:
        packages = bz2.BZ2File(
            '%s/binary-%s/Packages.bz2' % (dfsg_section, arch), 'r')
        for package in deb822.Deb822.iter_paragraphs(packages,
                                                     ['Package', 'Filename']):
            result[package['Package']] = package['Filename']
    return result

def unpack_package(name, filename):
    assert "'" not in name
    assert "'" not in filename
    deb_url = mirror + filename
    deb_name = 'archives/' + os.path.basename(filename)
    print '# downloading', deb_url
    sys.stdout.flush()
    if os.system("wget --quiet -c -O '%s' '%s'" % (deb_name, deb_url)):
        raise OSError()
    unpack_dir = 'unpack/' + name
    try:
        shutil.rmtree(unpack_dir)
    except OSError, e:
        if e.errno != errno.ENOENT:
            raise
    print '# unpacking', name
    sys.stdout.flush()
    if os.system("dpkg-deb -x '%s' '%s'" % (deb_name, unpack_dir)):
        raise OSError()
    return unpack_dir

if __name__ == '__main__':
    import os.path

    filenames = get_filenames()
    shlibs = find_shlibs()
    for name in sorted(shlibs.keys()):
        if not filenames.has_key(name):
            print name, 'is missing'
            continue
        matches = False
        d = unpack_package(name, filenames[name])
        for path in shlibs[name]:
            unpack_path = os.path.join(d, path)
            if os.path.islink(unpack_path):
                pass
            elif os.path.isfile(unpack_path):
                if check_shlib(open(unpack_path, 'rb')):
                    matches = True
            else:
                print name, 'is missing', path
        if matches:
            print name, 'matches'
        else:
            shutil.rmtree(d)
