bump release
[oweals/hwdata.git] / inf2mondb.py
1 #!/usr/bin/python
2 #
3 # inf2mondb.py: convert MicroSoft .inf files for monitors to MonitorDB
4 #
5 # originally by Matt Wilson <msw@redhat.com>
6 # option parsing and database comparison by Fred New
7 # ini parsing completely rewritten by Matt Domsch <Matt_Domsch@dell.com> 2006
8 #
9 # Copyright 2002 Red Hat, Inc.
10 # Copyright 2006 Dell, Inc.
11 #
12 # This software may be freely redistributed under the terms of the GNU
13 # library public license.
14 #
15 # You should have received a copy of the GNU Library Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 """
19 """
20
21
22 import sys
23 import string
24 import re
25 import ConfigParser
26
27 # this is a class to deal with various file line endings and leading whitespace
28 # converts all \r line endings to \n.
29 # It also strips leading whitespace.
30 # NOTE: be sure to always return _something_, even if it is just "\n", or we 
31 # break the file API.  (nothing == eof)
32 class myFile(object):
33     def __init__(self, *args):
34         self.fd = open(*args)
35
36     def close(self):
37         return self.fd.close()
38     
39     def readline(self, *args):
40         line = self.fd.readline(*args)
41         line = line.replace('\r', '\n')
42         line = line.replace('\n\n', '\n')
43         line = line.lstrip(" \t")
44         return line
45
46
47 # we will use this to override default option parsing in ConfigParser to handle
48 # Microsoft-style "INI" files. (Which do not necessarily have " = value " after
49 # the option name
50 OPTCRE = re.compile(
51         r'(?P<option>[^:=\s][^:=]*)'          # very permissive!
52         r'\s*(?P<vi>[:=]{0,1})\s*'            # any number of space/tab,
53                                               # optionally followed by
54                                               # separator (either : or =)
55                                               # optionally followed
56                                               # by any # space/tab
57         r'(?P<value>.*)$'                     # everything up to eol
58         )
59
60 def usage():
61     print "No monitor.inf file specified."
62     sys.exit(1)
63     
64 percentSplit = re.compile(r'%(?P<field>.*)%')
65 def percent_to_string(ini, strings, name):
66     mo = percentSplit.match(name)
67     if (mo):
68         field = mo.group('field')
69         try:
70             val = strings[field.lower()]
71         except KeyError:
72             return ""
73         return val.strip()
74     return ""
75
76
77 def main():
78     defaultDB = "/usr/share/hwdata/MonitorsDB"
79     
80     from optparse import OptionParser
81     parser = OptionParser(usage= sys.argv[0] + " [options] filename.inf")
82     parser.add_option("-n", "--new",
83                     action="store_true", dest="newonly", default=False,
84                     help="Compare results with the monitors database and only "
85                         "show DB lines that don't already appear in the "
86                         "database.")
87     parser.add_option("-v", "--verbose",
88                     action="store_true", dest="verbose", default=False,
89                     help="Used with --new to permit us to see which monitors "
90                         "already appear in the monitors database.")
91     parser.add_option("-d", "--database",
92                     dest="database",
93                     default=defaultDB,
94                     help="Used with --new to specify a different "
95                         "monitors database.  "
96                         "The default is " + defaultDB + ".",
97                     metavar="FILE")
98     parser.add_option("-i", "--inflist",
99                     dest="inflist",
100                     default=None,
101                     help="Read list of inf files from inflist",
102                     metavar="FILE")
103     
104     (options, args) = parser.parse_args()
105     
106     ini = ConfigParser.ConfigParser()
107     ini.optionxform = __builtins__.str
108     ini.OPTCRE = OPTCRE
109     if options.inflist is not None:
110         f = open(options.inflist)
111         lines = f.readlines()
112         f.close
113         for a in lines:
114             f = myFile(a.strip())
115             ini.readfp(f)
116             f.close
117
118     for a in args:
119         f = myFile(a)
120         ini.readfp(f)
121         f.close()
122
123     # First, build a dictionary of monitors we already know about.
124     # When we get ready to print the monitors we found in the .inf
125     # file, we can decide whether it is new or not.
126     
127     # Dictionary of booleans showing whether certain monitor EDIDs are known.
128     # knownids[knownID] = True
129     # knownids[unknownID] isn't defined.
130     knownids = {}
131     
132     if options.newonly:
133         try:
134             mdb = open(options.database, 'r')
135         except IOError, (errno, str):
136             print "Unable to open %s: %s" % (options.database, str)
137             sys.exit(1)
138     
139         knowns = mdb.readlines()
140         mdb.close()
141     
142         for known in knowns:
143             if len(string.strip(known)) == 0 or known[0] == '#':
144                 continue
145             knownids[string.lower(string.strip(string.split(known, ';')[2]))] = True
146     
147
148     # a dictionary of manufacturers we're looking at
149     manufacturers = {}
150     # a big fat dictionary of strings to use later on.
151     strings = {}
152
153     # This RE is for EISA info lines
154     # %D5259A%=D5259A, Monitor\HWP0487
155     monitor1Re = re.compile(r'.*,.*Monitor\\(?P<id>[^\s]*)')
156     # This one is for legacy entries
157     # %3020%     =PB3020,   MonID_PB3020
158     monitor2Re = re.compile(r'.*,.*MonID_(?P<id>[^\s]*)')
159     
160     for section in ini.sections():
161         if section.lower() == "manufacturer":
162             for mfr in ini.options(section):
163                 # generate the vendor.arch funny entries
164                 manufacturer_values = string.split(ini.get(section, mfr), ',')
165                 manufacturers[manufacturer_values[0]] = mfr
166                 while len(manufacturer_values) > 1:
167                     manufacturers["%s.%s" % (manufacturer_values[0], manufacturer_values[-1])] = mfr
168                     manufacturer_values = manufacturer_values[0:-1]
169     
170         elif section.lower() == "strings":
171             for key in ini.options(section):
172                 strings[key.lower()] = string.strip(ini.get(section, key)).replace('"','')
173             # exceptions
174             strings["dell"] = "Dell"
175     
176     
177     for mfr in manufacturers.keys():
178         if ini.has_section(mfr):
179             monitor_vendor_name = manufacturers[mfr]
180             for monitor_name in ini.options(mfr):
181                 v = ini.get(mfr, monitor_name)
182                 v = v.split(',')
183                 install_key = v[0].strip()
184     
185                 line = ini.get(mfr, monitor_name)
186                 # Find monitor inf IDs and EISA ids
187     
188                 edid = "0"
189                 mo = monitor1Re.match(line)
190                 if mo:
191                     edid = mo.group('id')
192                 else:
193                     mo = monitor2Re.match(line)
194                     if mo:
195                         edid = mo.group('id').strip()
196     
197                 if knownids.has_key(edid.lower()):
198                     continue
199     
200                 if ini.has_section(install_key):
201                     line = ini.get(install_key, "AddReg")
202                     if line:
203                         sline = line.split(',')
204                         registry = sline[0]
205                         try:
206                             resolution = sline[1]
207                         except IndexError:
208                             resolution = ""
209                         try:
210                             dpms = sline[2]
211                         except IndexError:
212                             dpms = ""
213     
214                         if ini.has_section(registry):
215                             for line in ini.options(registry):
216                                 if string.find(line, 'HKR,"MODES') >= 0:
217                                     sline = line.split('"')
218                                     try:
219                                         syncline = sline[3]
220                                     except IndexError:
221                                         syncline = ","
222                                         
223                                     syncline = syncline.split(',')
224                                     hsync = syncline[0].strip()
225                                     vsync = syncline[1].strip()
226     
227                                     output = "%s; %s; %s; %s; %s" % (percent_to_string(ini, strings, monitor_vendor_name),
228                                                                      percent_to_string(ini, strings, monitor_name),
229                                                                      edid, hsync, vsync)
230                                     if dpms.lower().strip() == "dpms":
231                                         output = output + "; 1"
232     
233                                     if not knownids.has_key(edid.lower()):
234                                         print output
235                                         knownids[edid.lower()] = True
236
237
238
239 if __name__ == "__main__":
240     sys.exit(main())