#!/usr/bin/python3
# xml2lst - convert Discover 2 XML files back to Discover 1 format
import sys
import optparse
import string
import re
import xml.sax
import xml.sax.handler
recognized_data_paths = { "linux/module/name": "%s",
"xfree86/server/device/driver": "Server:XFree86(%s)",
"xfree86/server/name": "Server:%s"
}
recognized_precedence = ("xfree86/server/device/driver",
"xfree86/server/name", "linux/module/name")
class DeviceHandler(xml.sax.handler.ContentHandler):
def __init__(self, busclass, vendor, old_busclass_list, versions,
explicit_version):
self.busclasses = busclass
self.vendors = vendor
self.old_busclasses = old_busclass_list
self.versions = versions
self.explicit_version = explicit_version
self.devices = {}
for vendor in self.vendors.keys():
self.devices[vendor] = {}
self.clearDevice()
def clearDevice(self):
self.current_device = None
self.current_device_items = {}
self.data_path = []
self.data_version = None
self.data_version_node = None
self.content = ""
def startElement(self, name, attrs):
if name == 'data':
# This code fail to take into account that there might be
# several data tags with different version settings.
if attrs.has_key("version"):
self.data_version = attrs["version"]
self.data_version_node = attrs["class"]
self.data_path.append(attrs["class"])
elif name == 'device':
self.clearDevice()
self.current_device = attrs.copy()
def endElement(self, name):
if name == 'data':
data_key = string.join(self.data_path, "/")
if not self.current_device_items.has_key(data_key):
self.current_device_items[data_key] = []
if len(self.content) > 0:
self.current_device_items[data_key].append((self.content.strip(),
self.data_version))
self.content = ""
old_class = self.data_path.pop()
if old_class == self.data_version_node:
self.data_version = None
self.data_version_node = None
elif name == 'device':
self.saveDevice()
self.clearDevice()
def characters(self, content):
if len(self.data_path) < 1 or \
not re.search(r'\S+', content):
return
self.content += content
def saveDevice(self):
item = {}
if self.current_device.has_key("subvendor"):
return # discover 1 do not understand subvendors, ignore entry
vendor_model_id = self.current_device["vendor"] + self.current_device["model"]
if self.current_device.has_key("busclass"):
item["busclass"] = self.busclasses[self.current_device["busclass"]]
elif self.old_busclasses.has_key(vendor_model_id):
item["busclass"] = self.old_busclasses[vendor_model_id][0]
else:
item["busclass"] = "unknown"
item["name"] = self.current_device["model_name"]
if self.old_busclasses.has_key(vendor_model_id):
item["driver"] = self.old_busclasses[vendor_model_id][1]
else:
item["driver"] = "unknown"
found = False
for key in recognized_precedence:
if not self.current_device_items.has_key(key):
continue
for (content, version) in self.current_device_items[key]:
if self.explicit_version and version is None:
continue
if self.versions.has_key(key) and \
version is not None and \
not version_in_set(version, self.versions[key]):
continue
item["driver"] = recognized_data_paths[key] % (content,)
found = True
break
if found:
break
vendor = self.current_device["vendor"]
model = self.current_device["model"]
self.devices[vendor][model] = item
def version_compare(ver1, ver2):
"""Return negative if the second version is bigger, positive if the
first version is bigger, or zero if the versions are equal."""
if ver1 == ver2:
return 0
elif ver1 == "inf":
return 1
elif ver2 == "inf":
return -1
ver1_parts = map(lambda x: int(x), string.split(ver1, "."))
ver2_parts = map(lambda x: int(x), string.split(ver2, "."))
ver1_len = len(ver1_parts)
ver2_len = len(ver2_parts)
if ver1_len > ver2_len:
compare_len = ver2_len
else:
compare_len = ver1_len
for i in range(0, compare_len):
if ver1_parts[i] != ver2_parts[i]:
return ver1_parts[i] - ver2_parts[i]
return ver1_len - ver2_len
def version_in_set(set, version):
(low_version, high_version) = re.split(r",\s*", set[1:-1], 1)
low_inclusive = set[0] == "["
high_inclusive = set[-1] == "]"
low_comp = version_compare(low_version, version)
if low_comp > 0 or (low_comp == 0 and not low_inclusive):
return False
high_comp = version_compare(version, high_version)
if high_comp > 0 or (high_comp == 0 and not high_inclusive):
return False
return True
def load_id_db(infile):
class IdHandler(xml.sax.handler.ContentHandler):
def __init__(self):
self.idmap = {}
def startElement(self, name, attrs):
if len(filter(lambda x: x not in ('id', 'name'),
attrs.keys())) == 0:
self.idmap[attrs['id']] = attrs['name']
def getResults(self):
return self.idmap
handler = IdHandler()
xml.sax.parse(infile, handler)
return handler.getResults()
def main():
usage = "usage: %prog [options] busclass_file vendor_file device_file"
cmdline = optparse.OptionParser(usage)
cmdline.add_option("-o", dest="output_fn", metavar="FILE",
help="file to write instead of stdout")
cmdline.add_option("--previous-file", dest="input_fn", default=None,
metavar="FILE",
help="previous lst file to base updates on")
cmdline.add_option("--data-version", dest="data_versions", action="append",
default=[], metavar="path:version",
help="limit output for path to version")
cmdline.add_option("--explicit-version", dest="explicit_version",
action="store_true", default=False,
help="skip data missing version info")
(options, args) = cmdline.parse_args()
(busclass_fn, vendor_fn, device_fn) = args
old_busclasses = {}
old_vendors = []
if options.input_fn:
input_file = open(options.input_fn)
for line in input_file:
if line[0] not in string.whitespace:
(id, rest) = string.split(line, None, 1)
old_vendors.append(id)
else:
(id, busclass, module, rest) = \
string.split(string.lstrip(line), None, 3)
if busclass == "unknown":
continue
old_busclasses[id] = (busclass, module)
input_file.close()
if options.output_fn:
output_file = open(options.output_fn, "w")
else:
output_file = sys.stdout
version_dict = {}
if options.data_versions:
for optstr in options.data_versions:
(path, ver) = string.split(optstr, ":", 1)
version_dict[path] = ver
busclasses = load_id_db(busclass_fn)
vendors = load_id_db(vendor_fn)
parser = DeviceHandler(busclasses, vendors, old_busclasses, version_dict,
options.explicit_version)
xml.sax.parse(device_fn, parser)
vendor_ids = vendors.keys()
vendor_ids.sort()
for vendor in vendor_ids:
device_ids = parser.devices[vendor].keys()
if len(device_ids) < 1:
if vendor not in old_vendors:
continue
result_unicode = "%s %s\n" % (vendor, vendors[vendor])
output_file.write(result_unicode.encode("iso-8859-1"))
device_ids.sort()
for device in device_ids:
vendor_out = vendor
device_out = device
if 'default' == device:
vendor_out = 'ffff'
device_out = 'ffff'
result_unicode = "\t%s%s\t%s\t%s\t%s\n" \
% (vendor_out, device_out,
parser.devices[vendor][device]["busclass"],
parser.devices[vendor][device]["driver"],
parser.devices[vendor][device]["name"])
output_file.write(result_unicode.encode("iso-8859-1"))
if __name__ == "__main__":
main()
|