diff --git a/tools/lsar.py b/tools/lsar.py new file mode 100755 index 0000000..1eab4ad --- /dev/null +++ b/tools/lsar.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python2.7 -B + +import sys +from objtools import ar + + +if __name__ == '__main__': + for path in sys.argv[1:]: + print '%s:' % path + for num, entry in enumerate(ar.ReadFile(path), start=1): + print '%5d:' % num, entry.name, '(length: %d)' % len(entry.data) + print '' diff --git a/tools/objtools/ar.py b/tools/objtools/ar.py new file mode 100644 index 0000000..cf86f85 --- /dev/null +++ b/tools/objtools/ar.py @@ -0,0 +1,48 @@ +import os +import struct + +from collections import namedtuple + + +class ArEntry(namedtuple('ArEntry', + 'name modtime owner group mode data')): + @classmethod + def decode(cls, ar): + name, modtime, owner, group, mode, length, magic = \ + struct.unpack('16s12s6s6s8s10s2s', ar.read(60)) + + length = int(length.strip()) + modtime = int(modtime.strip()) + owner = int(owner.strip() or '0') + group = int(group.strip() or '0') + mode = mode.strip() or '100644' + + if name.startswith('#1/'): + name_length = int(name[3:]) + name = ar.read(name_length).strip('\0') + else: + name_length = 0 + name = name.strip() + + data = ar.read(length - name_length) + + # next block starts at even boundary + if length & 1: + ar.seek(1, os.SEEK_CUR) + + return cls(name, modtime, owner, group, mode, data) + + +def ReadFile(path): + entries = [] + + with open(path) as ar: + if ar.read(8) != '!\n': + raise RuntimeError('%s is not an ar archive', path) + + ar_size = os.stat(path).st_size + + while ar.tell() < ar_size: + entries.append(ArEntry.decode(ar)) + + return entries