compare-pci-ids.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #!/usr/bin/env python
  2. import argparse
  3. import re
  4. import sys
  5. re_vendor = re.compile(r'^(?P<vendor_id>[0-9a-fA-F]{4})\s*(?P<vendor_name>.*)')
  6. re_vendev = re.compile(r'^\s*(?P<vendor_id>[0-9a-fA-F]{4})\s+'
  7. r'(?P<device_id>[0-9a-fA-F]{4})\s+(?P<device_name>.*)')
  8. re_device = re.compile(r'^\s+(?P<device_id>[0-9a-fA-F]{4})\s+'
  9. r'(?P<device_name>.*)')
  10. re_comment = re.compile(r'^(\s*#.*|\s*)$')
  11. def x2int(x):
  12. if isinstance(x, basestring): return int(x, 16)
  13. return x
  14. def parse_pci_file(ids_file, file_name):
  15. """
  16. @return dictionary in format:
  17. { vendor_id: ( vendor_name
  18. , { device_id : device_name
  19. , ... }
  20. )
  21. , ...
  22. }
  23. where
  24. vendor_id and device_id are integers
  25. """
  26. res = {}
  27. vendor = None
  28. nested = {} # (vendor_id, {device_id, device_name})
  29. def _add_nested(file_name, line, vendor, dev_id, dev_name):
  30. dev_id = x2int(dev_id)
  31. if vendor not in nested:
  32. nested[vendor] = {}
  33. if dev_id in nested[vendor]:
  34. sys.stderr.write('Warning: device "%04x:%04x" redeclared!'
  35. ' File: %s : %d\n'%(vendor, dev_id, file_name, line))
  36. nested[vendor][dev_id] = dev_name
  37. def _add_vendor(file_name, line, vendor_id, vendor_name):
  38. vendor = x2int(vendor_id)
  39. if vendor in res:
  40. sys.stderr.write('Warning: vendor "%04x" redeclared!'
  41. ' File: %s : %d\n'%(vendor, file_name, line))
  42. res[vendor] = (vendor_name, res[vendor][1])
  43. else:
  44. res[vendor] = (vendor_name, {})
  45. if vendor in nested:
  46. for dev_id, dev_name in nested[vendor].items():
  47. _add_device(file_name, line,
  48. vendor, dev_id, dev_name)
  49. del nested[vendor]
  50. def _add_device(file_name, line, vendor, dev_id, dev_name):
  51. """ @note: does not check for a vendor """
  52. dev_id = x2int(dev_id)
  53. if dev_id in res[vendor][1]:
  54. sys.stderr.write('Warning: device "%04x:%04x" redeclared!'
  55. ' File: %s : %d\n'%(vendor, dev_id, file_name, i))
  56. res[vendor][1][dev_id] = dev_name
  57. for i, l in enumerate(ids_file.readlines(), 1):
  58. if re_comment.match(l): continue
  59. m = re_vendor.match(l)
  60. if m:
  61. vendor = x2int(m.group('vendor_id'))
  62. _add_vendor(file_name, i, vendor, m.group('vendor_name'))
  63. continue
  64. m = re_vendev.match(l)
  65. if m:
  66. dev_vendor = x2int(m.group('vendor_id'))
  67. dev_id = x2int(m.group('device_id'))
  68. if vendor != dev_vendor and dev_vendor not in res:
  69. #sys.stderr.write('Warning: nested vendor of device "%x:%x"'
  70. #' does not match top level vendor "%x"! File: %s : %d\n' %(
  71. #dev_vendor, dev_id, vendor, file_name, i))
  72. _add_nested(file_name, i,
  73. dev_vendor, dev_id, m.group('device_name'))
  74. else:
  75. _add_device(file_name, i,
  76. dev_vendor, dev_id, m.group('device_name'))
  77. continue
  78. m = re_device.match(l)
  79. if m:
  80. _add_device(file_name, i,
  81. vendor, m.group('device_id'), m.group('device_name'))
  82. else:
  83. sys.stderr.write("Warning: not handled line: \"%s\"\n" % l[:-1])
  84. for vendor, devices in nested.items():
  85. sys.stderr.write('Warning: no name for vendor "%04x"! File: %s\n'%(
  86. vendor, file_name))
  87. res[vendor] = ('<UNKNOWN>', devices)
  88. return res
  89. if __name__ == '__main__':
  90. parser = argparse.ArgumentParser(description="Compare new pci.ids to old.")
  91. parser.add_argument('old', type=argparse.FileType('r'),
  92. help="old pci.ids file")
  93. parser.add_argument('new', type=argparse.FileType('r'),
  94. help="new pci.ids file")
  95. args = parser.parse_args()
  96. old = parse_pci_file(args.old, args.old.name)
  97. new = parse_pci_file(args.new, args.new.name)
  98. vendors_removed = 0
  99. vendors_added = 0
  100. vendors_renamed = 0
  101. devices_removed = 0
  102. devices_added = 0
  103. devices_renamed = 0
  104. for vendor, (vendor_name, devices) in old.items():
  105. if vendor not in new:
  106. vendors_removed += 1
  107. devices_removed += len(devices)
  108. else:
  109. if vendor_name != new[vendor][0]:
  110. vendors_renamed += 1
  111. for dev_id, dev_name in devices.items():
  112. if dev_id not in new[vendor]:
  113. devices_removed += 1
  114. elif dev_name != new[vendor][dev_id]:
  115. devices_renamed += 1
  116. for vendor, (vendor_name, devices) in new.items():
  117. if vendor not in old:
  118. vendors_added += 1
  119. devices_added += len(devices)
  120. else:
  121. for dev_id, dev_name in devices.items():
  122. if dev_id not in old[vendor]:
  123. devices_added += 1
  124. print "old vendor count: %d" % len(old)
  125. print "new vendor count: %d" % len(new)
  126. print "vendors added: %d" % vendors_added
  127. print "vendors removed: %d" % vendors_removed
  128. print "vendors renamed: %d" % vendors_renamed
  129. print "devices added: %d" % devices_added
  130. print "devices removed: %d" % devices_removed
  131. print "devices renamed: %d" % devices_renamed
  132. sys.exit(0)