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