#!/usr/bin/python3 import sys import optparse import string from xml.etree import ElementTree from xml.etree.ElementTree import XMLTreeBuilder class LstParser: """Parser for discover 1 device lists. Once initialized, the object appears to be a mapping (indexed by vendor ID) of mappings with a name key and a devices key. The devices key contains a sequence of mappings, each having an ID, name, class, and module key. """ def __init__(self, path): self.file = open(path) self.vendors = {} self.last_vendor = None def _read_one_vendor(self): """Read a single vendor, starting at the current file position. """ retval = None while True: offset = self.file.tell() line = self.file.readline() if not line: break if line[0] not in string.whitespace: (vid, name) = line.strip().split(None, 1) self.vendors[vid] = offset retval = self.last_vendor = vid break return retval def _read_vendors(self, key=None): """Read vendors until EOF or until the vendor with the given key is read. """ while True: found = self._read_one_vendor() if (key and found == key) or not found: break def _read_devices(self): """Read and return the vendor and device information for the vendor at the current file position. """ retval = {} (vid, vname) = self.file.readline().strip().split(None, 1) retval["name"] = vname retval["devices"] = [] while True: line = self.file.readline() if not line: break if line[0] not in string.whitespace: break (did, dclass, dmod, dname) = line.strip().split(None, 3) retval["devices"].append({ "name": dname, "id": did, "class": dclass, "module": dmod }) return retval def __getitem__(self, key): """For a given vendor key, return the vendor and device information. """ if not self.vendors.has_key(key): if self.last_vendor: self.file.seek(self.vendors[self.last_vendor]) self._read_vendors(key) self.file.seek(self.vendors[key]) return self._read_devices() def __iter__(self): """Iterate over the entire file's worth of vendors, returning the keys available. """ read_vendors = self.vendors.keys() for key in read_vendors: yield key if self.last_vendor: self.file.seek(self.vendors[self.last_vendor]) while True: key = self._read_one_vendor() if key: if key not in read_vendors: yield key else: break iterkeys = __iter__ def has_key(self, key): """Check that a vendor ID is represented in this file. If we haven't read it already, look for it in the file. """ if self.vendors.has_key(key): return True if self.last_vendor: self.file.seek(self.vendors[self.last_vendor]) while True: if self._read_one_vendor() == key: return True return False class TreeBuilderWithComments(XMLTreeBuilder): """This class extends ElementTree's to parse comments, which no builder in ElementTree seems able to do by itself. """ def __init__(self): XMLTreeBuilder.__init__(self) self._parser.CommentHandler = self._comment def _comment(self, data): """When a comment is encountered, handle it. """ self._target.start(ElementTree.Comment, {}) self._target.data(data) self._target.end(ElementTree.Comment) def _end(self, tag): elem = ElementTree.XMLTreeBuilder._end(self, tag) self.end(elem) class ElementWriter: """Write elements in similar fashion to ElementTree's write method, but with control over the quote characters and the sort order for attributes. """ def __init__(self, f): self.quotechar = "'" self.sort_method = None if hasattr(f, "write"): self.file = f else: self.file = open(f, "w") def xml_encode(self, string): string = string.replace("&", "&") string = string.replace("<", "<") string = string.replace(">", ">") string = string.replace("'", "'") return string def write(self, element): if element.tag is ElementTree.Comment: self.file.write("" % (element.text,)) elif element.tag is ElementTree.ProcessingInstruction: self.file.write("" % (element.text,)) else: self.file.write("<" + element.tag) if element.attrib: keylist = element.keys() keylist.sort(self.sort_method) for key in keylist: element.attrib[key] = self.xml_encode(element.attrib[key]) self.file.write(" %s=%s%s%s" % (key, self.quotechar, element.attrib[key], self.quotechar)) if element or element.text: self.file.write(">") if element.text: self.file.write(element.text) for subnode in element: self.write(subnode) self.file.write("" % (element.tag,)) else: self.file.write("/>") if element.tail: self.file.write(element.tail) def sort_device_attrs(x, y): """Keep track of the order of certain attributes in certain elements, in an almost-vain hope of minimizing the number of gratuitous changes made in the XML device list. """ special_attrs = [ ["vendor", "model", "subvendor", "subdevice", "model_name", "subsystem_name", "busclass"], ["version", "class"] ] for attrlist in special_attrs: if x in attrlist and y in attrlist: return attrlist.index(x) - attrlist.index(y) return cmp(x, y) def gen_devices(f): """Yield complete ElementTree device nodes. Adapted from the element generator example at: http://online.effbot.org/2004_12_01_archive.htm#element-generator """ if not hasattr(f, "read"): f = open(f, "rb") elements = [] parser = TreeBuilderWithComments() parser.end = elements.append while 1: data = f.read(16384) if not data: break parser.feed(data) for e in elements: if e.tag == "device": yield e del elements[:] parser.close() for e in elements: if e.tag == "device": yield e def main(): option_parser = optparse.OptionParser(option_list=[ optparse.make_option("-b", "--bus", action="store", dest="bustype", default="pci"), optparse.make_option("-i", "--input", action="store", dest="infile"), optparse.make_option("-o", "--output", action="store", dest="outfile"), optparse.make_option("--lst-24", action="store", dest="lst_kernel24"), optparse.make_option("--lst-26", action="store", dest="lst_kernel26") ]) (options, args) = option_parser.parse_args() if options.infile: in_f = open(options.infile) else: in_f = sys.stdin if options.outfile: out_f = open(options.outfile) else: out_f = sys.stdout kernel24_list = kernel26_list = None if options.lst_kernel24: kernel24_list = LstParser(options.lst_kernel24) if options.lst_kernel26: kernel26_list = LstParser(options.lst_kernel26) out_f.write(""" """ % (options.bustype,)) out_x = ElementWriter(out_f) out_x.sort_method = sort_device_attrs for device in gen_devices(in_f): out_x.write(device) out_f.write("\n") out_f.close() in_f.close() if __name__ == "__main__": main()