You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
298 lines
11 KiB
298 lines
11 KiB
15 years ago
|
#!/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])
|