diff options
Diffstat (limited to 'displayconfig/infimport.py')
-rwxr-xr-x | displayconfig/infimport.py | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/displayconfig/infimport.py b/displayconfig/infimport.py new file mode 100755 index 0000000..f51475d --- /dev/null +++ b/displayconfig/infimport.py @@ -0,0 +1,297 @@ +#!/usr/bin/python +# +# Based on inf2mondb.py from RedHat +# +# originally by Matt Wilson <msw@redhat.com> +# option parsing and database comparison by Fred New +# ini parsing completely rewritten by Matt Domsch <Matt_Domsch@dell.com> 2006 +# +# Copyright 2002 Red Hat, Inc. +# Copyright 2006 Dell, Inc. +# Copyright 2007 Sebastian Heinlein +# +# This software may be freely redistributed under the terms of the GNU +# library public license. +# +# You should have received a copy of the GNU Library Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +""" +Provides an importer for Microsoft Windows monitor descriptions + +The code can be used as a python module for or as a script to add new monitor +definitions to a monitor database. + +In code example: Read the list of monitors from an inf file. + +import infimport +monitors = infimport.get_monitors_from_inf(PATH) + +Script example: To check for monitors of an inf file that are not yet in the database. + +./infimport.py MONITORS.inf /usr/share/hwdata/MonitorsDB +""" + +import sys +import string +import re +import ConfigParser +import os + +import logging + +logging.basicConfig() +log = logging.getLogger("infimport") +#log.setLevel(logging.DEBUG) +log.setLevel(logging.INFO) + +# this is a class to deal with various file line endings and leading whitespace +# converts all \r line endings to \n. +# It also strips leading whitespace. +# NOTE: be sure to always return _something_, even if it is just "\n", or we +# break the file API. (nothing == eof) +class myFile(object): + def __init__(self, *args): + self.fd = open(*args) + + def close(self): + return self.fd.close() + + def readline(self, *args): + line = self.fd.readline(*args) + line = line.replace('\r', '\n') + line = line.replace('\n\n', '\n') + line = line.lstrip(" \t") + return line + + +# we will use this to override default option parsing in ConfigParser to handle +# Microsoft-style "INI" files. (Which do not necessarily have " = value " after +# the option name +OPTCRE = re.compile( + r'(?P<option>[^:=\s][^:=]*)' # very permissive! + r'\s*(?P<vi>[:=]{0,1})\s*' # any number of space/tab, + # optionally followed by + # separator (either : or =) + # optionally followed + # by any # space/tab + r'(?P<value>.*)$' # everything up to eol + ) + +percentSplit = re.compile(r'%(?P<field>.*)%') +def _percent_to_string(ini, strings, name): + mo = percentSplit.match(name) + if (mo): + field = mo.group('field') + try: + val = strings[field.lower()] + except KeyError: + return "" + return val.strip(" '\"") + return "" + +def get_monitors_from_database(path): + """Returns a dictonary of the found monitor models in the given + monitor models database""" + monitors = {} + try: + mdb = open(path, 'r') + except IOError, (errno, str): + log.error("Unable to open %s: %s" % (path, str)) + return {} + for line in mdb.readlines(): + if len(line.strip()) == 0 or line.startswith('#'): + continue + line_split = line.split(";") + vendor = line_split[0].strip() + name = line_split[1].strip() + id = line_split[2].strip() + if monitors.has_key((vendor, name, id)): + log.warn("Duplicated entry: %s" % line) + else: + monitors[(vendor, name, id)] = line + mdb.close() + return monitors + +def get_monitors_from_inf(path): + """Returns a dictonary of the found monitor models in the given .inf file""" + monitors = {} + ini = ConfigParser.ConfigParser() + # FIXME: perhaps could be done in a nicer way, but __builtins__ is a dict + # for imported modules + #ini.optionxform = __builtins__.str + ini.optionxform = type("") + ini.OPTCRE = OPTCRE + try: + f = myFile(path) + ini.readfp(f) + f.close() + except IOError, (errno, str): + log.error("Unable to open %s: %s" % (path, str)) + sys.exit(1) + + # a dictionary of manufacturers we're looking at + manufacturers = {} + # a big fat dictionary of strings to use later on. + strings = {} + + # This RE is for EISA info lines + # %D5259A%=D5259A, Monitor\HWP0487 + monitor1Re = re.compile(r'.*,.*Monitor\\(?P<id>[^\s]*)') + # This one is for legacy entries + # %3020% =PB3020, MonID_PB3020 + monitor2Re = re.compile(r'.*,.*MonID_(?P<id>[^\s]*)') + + for section in ini.sections(): + if section.lower() == "manufacturer": + for mfr in ini.options(section): + # generate the vendor.arch funny entries + manufacturer_values = string.split(ini.get(section, mfr), + ',') + manufacturers[manufacturer_values[0]] = mfr + while len(manufacturer_values) > 1: + manufacturers["%s.%s" % (manufacturer_values[0], + manufacturer_values[-1])] = mfr + manufacturer_values = manufacturer_values[0:-1] + + elif section.lower() == "strings": + for key in ini.options(section): + strings[key.lower()] = string.strip(ini.get(section, key)) + + for mfr in manufacturers.keys(): + if ini.has_section(mfr): + monitor_vendor_name = manufacturers[mfr] + for monitor_name in ini.options(mfr): + v = ini.get(mfr, monitor_name) + v = v.split(',') + install_key = v[0].strip() + + line = ini.get(mfr, monitor_name) + # Find monitor inf IDs and EISA ids + + edid = "0" + mo = monitor1Re.match(line) + if mo: + edid = mo.group('id') + else: + mo = monitor2Re.match(line) + if mo: + edid = mo.group('id').strip() + + #if self.monitors.has_key(edid.lower()): + # continue + + if ini.has_section(install_key): + line = ini.get(install_key, "AddReg") + if line: + sline = line.split(',') + registry = sline[0] + try: + resolution = sline[1] + except IndexError: + resolution = "" + try: + dpms = sline[2] + except IndexError: + dpms = "" + + if ini.has_section(registry): + for line in ini.options(registry): + if string.find(line, 'HKR,"MODES') >= 0: + sline = line.split('"') + try: + syncline = sline[3] + except IndexError: + syncline = "," + syncline = syncline.split(',') + hsync = syncline[0].strip() + vsync = syncline[1].strip() + + vendor_clear = _percent_to_string(ini, + strings, monitor_vendor_name) + monitor_clear = _percent_to_string(ini, + strings, monitor_name) + + output = "%s; %s; %s; %s; %s" % \ + (vendor_clear, monitor_clear, + edid, hsync, vsync) + if dpms.lower().strip() == "dpms": + output = output + "; 1" + + if not monitors.has_key((vendor_clear, + monitor_clear, edid.lower())): + log.debug("added %s" % output) + monitors[(vendor_clear, + monitor_clear, + edid.lower())] = output + else: + log.warn("duplicated entry %s" % output) + return monitors + +def write_monitors_to_file(monitors, path): + """Writes monitors as a monitor models database""" + try: + if os.path.exists(path): + os.remove(path) + mdb = open(path, 'w') + mdb.writelines(map(lambda l: "%s\n" % l, monitors.values())) + mdb.close() + except IOError, (errno, str): + log.error("Unable to write %s: %s" % (path, str)) + return False + +def append_monitors_to_file(monitors, path): + """Appends monitors to a monitor models database""" + try: + if os.path.exists(path): + os.remove(path) + mdb = open(path, 'a') + mdb.writelines(map(lambda l: "%s\n" % l, monitors.values())) + mdb.close() + except IOError, (errno, str): + log.error("Unable to write %s: %s" % (path, str)) + return False + +if __name__ == "__main__": + from optparse import OptionParser + import sys + + parser = OptionParser() + parser.add_option("-a", "--append", + action="store_true", dest="append", + help="Append new models to the database") + parser.add_option("-o", "--output", + default=None, + action="store", type="string", dest="output", + help="Write changes to an alternative file") + parser.usage = "%prog [options] INF_FILE [MONITOR_DATABASE]" + (options, args) = parser.parse_args() + + if len(args) == 2: + # continue with normal operation + pass + elif len(args) == 1: + # jsut print the monitors from the given inf file + monitors_inf = get_monitors_from_inf(args[0]) + for mon in monitors_inf.values(): + print "%s" % mon + sys.exit() + else: + parser.error("You have to specify an .inf file that contains the " + "monitor models that you want to add and a " + "monitor model database") + + monitors_inf = get_monitors_from_inf(args[0]) + monitors_db = get_monitors_from_database(args[1]) + + monitors_new = {} + for mon in monitors_inf.keys(): + if not monitors_db.has_key(mon): + log.info("New monitor: %s" % monitors_inf[mon]) + monitors_new[mon] = monitors_inf[mon] + + if options.append: + if options.output: + append_monitors_to_file(monitors_new, options.output) + else: + append_monitors_to_file(new_monitors, args[1]) |