diff options
Diffstat (limited to 'displayconfig/xorgconfig.py')
-rwxr-xr-x | displayconfig/xorgconfig.py | 903 |
1 files changed, 903 insertions, 0 deletions
diff --git a/displayconfig/xorgconfig.py b/displayconfig/xorgconfig.py new file mode 100755 index 0000000..7683b87 --- /dev/null +++ b/displayconfig/xorgconfig.py @@ -0,0 +1,903 @@ +#!/usr/bin/python +########################################################################### +# xorgconfig.py - description # +# ------------------------------ # +# begin : Wed Feb 9 2004 # +# copyright : (C) 2005 by Simon Edwards # +# email : simon@simonzone.com # +# # +########################################################################### +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation; either version 2 of the License, or # +# (at your option) any later version. # +# # +########################################################################### +import csv +import codecs +import locale +""" +General usage: + + import xorgconfig + config = readConfig("/etc/X11/xorg.conf") + + input_devices = config.getSections("InputDevice") + print input_devices[0].driver + options = input_devices[0].options + for option in options: + # option is of type OptionLine. + print option._row[0], + if len(option._row)>=2: + print "=>",option._row[1] + + # Add line: Option "XkbModel" "pc105" + options.append( options.makeLine("Comment text",["XkbModel" "pc105"]) ) + + +Refactor plan +============= +New usage: + + import xorgconfig + config = readConfig("/etc/X11/xorg.conf") + + input_devices = config.section.InputDevice + print input_devices[0].driver + options = input_devices[0].options + for option in options: + # option is of type OptionLine. + print option[1], + if len(option)>=3: + print "=>",option[2] + + module_section = config.section.module[0] + module_section.append(["load","i2c"]) + assert module_section.existsLoad("i2c") + module_section.removeLoad("i2c") + + device_section = config.section.device[0] + if device_section.busid is not None: + print "Found busid:",device_section.busid + +* direct references to myline._row should be removed. +* A ConfigLine should be a subclass of List. With line[i] accessing the + parts of the line. +* the order of the makeLine() parameters should be reversed. +* it should be possible to directly append a list or tuple that represents + a line to a section. +""" +############################################################################ +class ConfigLine(object): + """Represents one line from the Xorg.conf file. + + Each part of the line is printed without quotes. + """ + def __init__(self,comment,row): + self._row = [item for item in row if item!=''] + self._comment = comment + + def toString(self,depth=0): + caprow = self._row + if len(caprow) > 0: + caprow[0] = caprow[0].capitalize() + string = ('\t' * (depth/2)) + ' ' * (depth%1) + '\t'.join([unicode(item) for item in caprow]) + if self._comment is not None: + string += '#' + self._comment + return string + '\n' + +############################################################################ +class ConfigLineQuote(ConfigLine): + """Represents one line from the Xorg.conf file. + + The first item in the line is not quoted, but the remaining items are. + """ + def toString(self,depth=0): + string = ('\t' * (depth/2) + ' ' * (depth%1)) + if len(self._row)!=0: + string += self._row[0].capitalize() + if len(self._row)>1: + if len(self._row[0]) < 8: + string += '\t' + string += '\t"' + '"\t"'.join([unicode(item) for item in self._row[1:]]) + '"' + if self._comment is not None: + string += '#' + self._comment + return string + '\n' + +############################################################################ +class OptionLine(ConfigLineQuote): + def __init__(self,comment,row): + arg = ['option'] + arg.extend(row) + ConfigLineQuote.__init__(self,comment,arg) + +############################################################################ +class ConfigList(list): + def toString(self,depth=0): + string = "" + for item in self: + string += item.toString(depth) + return string + +############################################################################ +class OptionList(ConfigList): + name = "option" + def __setitem__(self,key,value): + list.__setitem__(self,key,value) + + def makeLine(self,comment,row): + return OptionLine(comment,row) + + def appendOptionRow(self,row): + self.append(self.makeLine(None,row)) + + def removeOptionByName(self,name): + name = name.lower() + i = 0 + while i < len(self): + if self[i]._row[1].lower()==name: + del self[i] + else: + i += 1 + + def getOptionByName(self,name): + name = name.lower() + for item in self: + try: + if item._row[1].lower()==name: + return item + except IndexError: + pass + return None + +############################################################################ +class ScreenConfigLine(ConfigLine): + def __init__(self,comment,row): + arg = ["screen"] + arg.extend(row) + ConfigLine.__init__(self,comment,arg) + + def toString(self,depth=0): + string = (' ' * depth) + + try: # Keep on building up the string until the IndexError is thrown. + string += self._row[0] + i = 1 + if self._row[i].isdigit(): + string += ' ' + self._row[i] + i += 1 + string += ' "' + self._row[i] + '"' + i += 1 + while True: + item = self._row[i].lower() + if item in ['rightof','leftof','above','below']: + string += ' %s "%s"' % (item, self._row[i+1]) + i += 1 + elif item=='absolute': + string += ' %s %d %d' % (item, self._row[i+1], self._row[i+2]) + i += 2 + elif item.isdigit(): + i += 1 + string += ' %s %s' % (item,self._row[i]) + i += 1 + except IndexError: pass + + if self._comment is not None: + string += ' #' + self._comment + return string + '\n' + +############################################################################ +class ScreenConfigList(ConfigList): + name = "screen" + def __setitem__(self,key,value): + list.__setitem__(self,key,value) + + def makeLine(self,comment,row): + return ScreenConfigLine(comment,row) + +############################################################################ +class ConfigContainer(object): + """Acts as a container for ConfigLines and other ConfigContainers. + Is used for representing things like the whole config file, sections + and subsections inside the file. + + """ + def __init__(self): + self._contents = [] + + def append(self,item): + assert (item is not None) + self._contents.append(item) + + def remove(self,item): + self._contents.remove(item) + + def toString(self,depth=0): + string = '' + for item in self._contents: + string += item.toString(depth+1) + return string + + def makeSection(self,comment,row): + return Section(comment,row) + + def isSection(self,name): + lname = name.lower() + return lname=='section' + + def isEndSection(self,name): + return False + + def makeLine(self,comment,row): + return ConfigLine(comment,row) + + def isListAttr(self,name): + lname = name.lower() + return lname in self._listattr + + def makeListAttr(self,comment,row): + listobj = self.__getattr__(row[0].lower()) + listobj.append( listobj.makeLine(comment,row[1:]) ) + + def getSections(self,name): + """Get all sections having the given name. + + Returns a list of ConfigContainer objects. + """ + name = name.lower() + sections = [] + for item in self._contents: + try: + if isinstance(item,ConfigContainer) and item._name.lower()==name: + sections.append(item) + except IndexError: pass + return sections + + def __getattr__(self,name): + if not name.startswith("_"): + lname = name.lower() + if lname in self._listattr: + # Lookup list attributes. + for item in self._contents: + if isinstance(item,ConfigList) and item.name==lname: + return item + else: + listitem = self._listattr[lname]() + self._contents.append(listitem) + return listitem + else: + for item in self._contents: + try: + if isinstance(item,ConfigLine) and item._row[0].lower()==lname: + return item._row[1] + except IndexError: pass + if lname in self._attr or lname in self._quoteattr: + return None + raise AttributeError, name + + def __setattr__(self,name,value): + if name.startswith('_'): + return super(ConfigContainer,self).__setattr__(name,value) + + lname = name.lower() + for item in self._contents: + try: + if isinstance(item,ConfigLine) and item._row[0].lower()==lname: + item._row[1] = value + break + except IndexError: pass + else: + if lname in self._attr or lname in self._quoteattr: + line = self.makeLine(None,[name,value]) + self.append(line) + else: + raise AttributeError, name + + def clear(self): + self._contents = [] + + def getRow(self,name): + if not name.startswith("_"): + lname = name.lower() + for item in self._contents: + try: + if isinstance(item,ConfigLine) and item._row[0].lower()==lname: + return item._row[1:] + except IndexError: pass + + if name in self._attr or name in self._quoteattr: + # is a valid name, just has no real value right now. + return None + + raise AttributeError, name + +############################################################################ +class Section(ConfigContainer): + """Represents a Section in the config file. + + """ + + # List of config line types allowed inside this section. + # A list of strings naming lines that need to be stored in ConfigLine objects. + _attr = [] + + # A list of strings naming the lines that need to be stored in ConfigLineQuote objects. + # This is often overridden in subclasses. + _quoteattr = [] + + _listattr = {} + + def __init__(self,comment,row): + ConfigContainer.__init__(self) + self._name = row[1] + self._comment = comment + + def __show__(self): + """ For debugging """ + for a in self._attr: + print self._name, "Attribute:", a + for a in self._quoteattr: + print self._name, "QuoteAttribute:", a + for a in self._listattr: + print self._name, "ListAttr:", a + + def isSection(self,name): + return name.lower()=='subsection' + + def isEndSection(self,name): + return name.lower()=='endsection' + + def makeLine(self,comment,row): + try: + lname = row[0].lower() + if lname in self._quoteattr: + return ConfigLineQuote(comment,row) + if lname in self._attr: + return ConfigLine(comment,row) + return None + except IndexError: + pass + return ConfigContainer.makeLine(self,comment,row) + + def toString(self,depth=0): + if self._comment is None: + return '%sSection "%s"\n%s%sEndSection\n' % \ + (' ' * depth, self._name, ConfigContainer.toString(self,depth+1), ' ' * depth) + else: + return '%sSection "%s" # %s\n%s%sEndSection\n' % \ + (' ' * depth, self._name, self._comment, ConfigContainer.toString(self,depth+1), ' ' * depth) + +############################################################################ +class SubSection(Section): + def isSection(self,name): + return False + + def isEndSection(self,name): + return name.lower()=='endsubsection' + + def toString(self,depth=0): + return '%sSubSection "%s"\n%s%sEndSubSection\n' % \ + ('\t' * (depth/2) + ' ' * (depth%1), self._name, ConfigContainer.toString(self,depth+1), '\t' * (depth/2) + ' ' * (depth%1)) + + +############################################################################ +class DeviceSection(Section): + _attr = ["endsection","dacspeed","clocks","videoram","biosbase","membase", \ + "iobase","chipid","chiprev","textclockfreq","irq","screen"] + + _quoteattr = ["identifier","vendorname","boardname","chipset","ramdac", \ + "clockchip","card","driver","busid"] + + _listattr = {"option" : OptionList} + +############################################################################ +class DriSection(Section): + _attr = ["group","buffers","mode"] + def makeLine(self,comment,row): + try: + lname = row[0].lower() + if lname=="group" and not row[1].isdigit(): + return ConfigLineQuote(comment,row) + except IndexError: + pass + return Section.makeLine(self,comment,row) + +############################################################################ +class ExtensionsSection(Section): + _listattr = {"option" : OptionList} + +############################################################################ +class FilesSection(Section): + _quoteattr = ["fontpath","rgbpath","modulepath","inputdevices","logfile"] + def makeLine(self,comment,row): + return ConfigLineQuote(comment,row) + +############################################################################ +class ModuleSection(Section): + _quoteattr = ["load","loaddriver","disable"] + + def makeSection(self,comment,row): + return ModuleSubSection(comment,row) + + def allowModule(self,modname): + killlist = [] + for item in self._contents: + try: + if isinstance(item,ConfigLineQuote) \ + and item._row[0].lower()=='disable' \ + and item._row[1]==modname: + killlist.append(item) + except IndexError: pass + + for item in killlist: + self._contents.remove(item) + + def removeModule(self,modname): + killlist = [] + for item in self._contents: + try: + if isinstance(item,ConfigLineQuote) \ + and item._row[0].lower()=='load' \ + and item._row[1]==modname: + killlist.append(item) + except IndexError: pass + + for item in killlist: + self._contents.remove(item) + + def disableModule(self,modname): + self.removeModule(modname) + self._contents.append(ConfigLineQuote(None,['disable',modname])) + + def addModule(self,modname): + self.removeModule(modname) + self._contents.append(ConfigLineQuote(None,['load',modname])) + +############################################################################ +class ModuleSubSection(SubSection): + _listattr = {"option" : OptionList} + +############################################################################ +class ModeSection(Section): + _attr = ["dotclock","htimings","vtimings","hskew","bcast","vscan"] + _quoteattr = ["flags"] + + def __init__(self,comment,row): + Section.__init__(self,comment,row) + self._name = row[1] + + def isEndSection(self,name): + return name.lower()=='endmode' + + def toString(self,depth=0): + if self._comment is None: + return '%sMode "%s"\n%s%sEndMode\n' % \ + (' ' * depth, self._name, ConfigContainer.toString(self,depth+1), ' ' * depth) + else: + return '%sMode "%s" # %s\n%s%sEndMode\n' % \ + (' ' * depth, self._name, self._comment, ConfigContainer.toString(self,depth+1), ' ' * depth) + +############################################################################ +class ModeList(ConfigList): + name = "mode" + def __setitem__(self,key,value): + list.__setitem__(self,key,value) + + def makeLine(self,comment,row): + return ModeLine(comment,row) + +############################################################################ +class ModeLineList(ConfigList): + name = "modeline" + def __setitem__(self,key,value): + list.__setitem__(self,key,value) + + def makeLine(self,comment,row): + return ModeLineConfigLine(comment,row) + +############################################################################ +class MonitorSection(Section): + _attr = ["displaysize","horizsync","vertrefresh","gamma"] + _quoteattr = ["identifier","vendorname","modelname","usemodes"] + _listattr = {"option" : OptionList, "mode" : ModeList, "modeline" : ModeLineList} + + def makeLine(self,comment,row): + return Section.makeLine(self,comment,row) + + def isSection(self,name): + lname = name.lower() + return lname=='mode' + + def isEndSection(self,name): + return name.lower()=='endsection' + + def makeSection(self,comment,row): + if row[0].lower()=='mode': + return ModeSection(comment,row) + else: + return Section.makeSection(self,comment,row) + +############################################################################ +class ModeLineConfigLine(ConfigLine): + def toString(self,depth=0): + string = (' ' * depth)+"modeline " + if len(self._row)>0: + string += ' "' + self._row[0] + '"' + if len(self._row)>1: + string += ' ' + ' '.join([unicode(item) for item in self._row[1:]]) + if self._comment is not None: + string += '#' + self._comment + return string + '\n' + +############################################################################ +class ModesSection(MonitorSection): + # Like a MonitorSection, only smaller. + _attr = ["modeline"] + _quoteattr = ["identifier"] + +############################################################################ +class PointerSection(Section): + _attr = ["emulate3timeout","baudrate","samplerate","resolution",\ + "devicename","buttons"] + _quoteattr = ["protocol","device","port","emulate3buttons","chordmiddle",\ + "cleardtr","clearrts","zaxismapping","alwayscore"] + +############################################################################ +class ScreenSection(Section): + _attr = ["screenno","defaultcolordepth","defaultdepth","defaultbpp","defaultfbbpp"] + _quoteattr = ["identifier","driver","device","monitor","videoadaptor","option"] + _listattr = {"option" : OptionList} + def makeSection(self,comment,row): + if row[1].lower()=='display': + return DisplaySubSection(comment,row) + return SubSection(comment,row) + +############################################################################ +class DisplaySubSection(SubSection): + _attr = ["viewport","virtual","black","white","depth","fbbpp","weight"] + _quoteattr = ["modes","visual","option"] + _listattr = {"option" : OptionList} +############################################################################ +class ServerFlagsSection(Section): + _quoteattr = ["notrapsignals","dontzap","dontzoom","disablevidmodeextension",\ + "allownonlocalxvidtune","disablemodindev","allownonlocalmodindev","allowmouseopenfail", \ + "blanktime","standbytime","suspendtime","offtime","defaultserverlayout"] + _listattr = {"option" : OptionList} + +############################################################################ +class ServerLayoutSection(Section): + _attr = [] + _quoteattr = ["identifier","inactive","inputdevice","option"] + _listattr = {"option" : OptionList, "screen" : ScreenConfigList} + +############################################################################ +class InputDeviceSection(Section): + _quoteattr = ["identifier","driver"] + _listattr = {"option" : OptionList} +############################################################################ +class KeyboardSection(Section): + _attr = ["autorepeat","xleds"] + _quoteattr = ["protocol","panix106","xkbkeymap","xkbcompat","xkbtypes",\ + "xkbkeycodes","xkbgeometry","xkbsymbols","xkbdisable","xkbrules",\ + "xkbmodel","xkblayout","xkbvariant","xkboptions","vtinit","vtsysreq",\ + "servernumlock","leftalt","rightalt","altgr","scrolllock","rightctl"] + +############################################################################ +class VendorSection(Section): + _attr = [] + _quoteattr = ["identifier"] + _listattr = {"option" : OptionList} + def isSection(self,name): return False + +############################################################################ +class VideoAdaptorSection(Section): + _attr = [] + _quoteattr = ["identifier","vendorname","boardname","busid","driver"] + _listattr = {"option" : OptionList} + def makeSection(self,comment,row): + return VideoPortSection(comment,row) + +############################################################################ +class VideoPortSection(SubSection): + _attr = [] + _quoteattr = ["identifier"] + _listattr = {"option" : OptionList} +############################################################################ +class XorgConfig(ConfigContainer): + _sectiontypes = { \ + 'device': DeviceSection, + 'dri': DriSection, + 'extensions': ExtensionsSection, + 'files': FilesSection, + 'inputdevice': InputDeviceSection, + 'keyboard': KeyboardSection, + 'modes': ModesSection, + 'monitor': MonitorSection, + 'module': ModuleSection, + 'pointer': PointerSection, + 'serverflags': ServerFlagsSection, + 'serverlayout': ServerLayoutSection, + 'screen': ScreenSection, + 'videoadaptor': VideoAdaptorSection} + + def makeSection(self,comment,row): + lname = row[1].lower() + try: + return self._sectiontypes[lname](comment,row) + except KeyError: + return ConfigContainer.makeSection(self,comment,row) + + def toString(self,depth=-1): + return ConfigContainer.toString(self,depth) + + def writeConfig(self,filename): + try: + encoding = locale.getpreferredencoding() + except locale.Error: + encoding = 'ANSI_X3.4-1968' + fhandle = codecs.open(filename,'w',locale.getpreferredencoding()) + fhandle.write(self.toString()) + fhandle.close() + + def createUniqueIdentifier(self,stem="id"): + """Create a unique identifier for a section + + """ + # Build a list of used identifiers + used_identifiers = [] + for name in ['monitor','videoadaptor','inputdevice','serverlayout','device','screen']: + for section in self.getSections(name): + if section.identifier is not None: + used_identifiers.append(section.identifier) + + # Generate a identifier that is not in use. + i = 1 + while (stem+str(i)) in used_identifiers: + i += 1 + + return stem+str(i) + +############################################################################ +def addxorg(context, stack): + # Add minimal xorg.conf if it's missing + rows = [[None, [u'Section', u'Device']], [None, [u'Identifier', u'Configured Video Device']], \ + [None, [u'EndSection']], [None, [u'Section', u'Monitor']], \ + [None, [u'Identifier', u'Configured Monitor']], \ + [None, [u'EndSection']], [None, [u'Section', u'Screen']], \ + [None, [u'Identifier', u'Default Screen']], \ + [None, [u'Monitor', u'Configured Monitor']], [None, [u'EndSection']], \ + [None, [u'Section', u'ServerLayout']], \ + [None, [u'Identifier', u'Default Layout']], \ + [None, [u'screen', u'Default Screen']], \ + [None, [u'EndSection']]] + + for data in rows: + rowcomment = data[0] + row = data[1] + try: + first = row[0].lower() + if context.isSection(first): + section = context.makeSection(rowcomment,row) + context.append(section) + stack.append(context) + context = section + context_class = context.__class__ + elif context.isEndSection(first): + context = stack.pop() + elif context.isListAttr(first): + context.makeListAttr(rowcomment,row) + else: + newline = context.makeLine(rowcomment,row) + if newline is None: + raise ParseException,"Unknown line type '%s' on line %i" % (first,line) + context.append(newline) + except IndexError: + context.append(ConfigLine(rowcomment,row)) + + return context, section, stack, first + +############################################################################ +def addServerLayout(context, section, stack, first): + # Add empty server layout section to xorg.conf if it's missing + rows = [[None, [u'Section', u'ServerLayout']], \ + [None, [u'Identifier', u'Default Layout']], \ + [None, [u'screen', u'0', u'Default Screen', u'0', u'0']], \ + [None, [u'Inputdevice', u'Generic Keyboard']], \ + [None, [u'Inputdevice', u'Configured Mouse']], \ + [None, []], ["Uncomment if you have a wacom tablet", []], \ + ["InputDevice \"stylus\" \"SendCoreEvents\"", []], \ + [" InputDevice \"cursor\" \"SendCoreEvents\"", []], \ + [" InputDevice \"eraser\" \"SendCoreEvents\"", []], \ + [None, [u'Inputdevice', u'Synaptics Touchpad']], [None, [u'EndSection']]] + for data in rows: + rowcomment = data[0] + row = data[1] + try: + first = row[0].lower() + if context.isSection(first): + section = context.makeSection(rowcomment,row) + context.append(section) + stack.append(context) + context = section + context_class = context.__class__ + elif context.isEndSection(first): + context = stack.pop() + elif context.isListAttr(first): + context.makeListAttr(rowcomment,row) + else: + newline = context.makeLine(rowcomment,row) + if newline is None: + raise ParseException,"Unknown line type '%s' on line %i" % (first,line) + context.append(newline) + except IndexError: + context.append(ConfigLine(rowcomment,row)) + + return context, section, stack, first + +############################################################################ +def readConfig(filename, check_exists=False): + + context = XorgConfig() + stack = [] + line = 1 + hasserverlayout = False + hasxorg = True + try: + import os + try: + if os.path.isfile('/etc/X11/xorg.conf'): + if os.path.getsize(filename) == 0: + raise IOError, "xorg.conf is empty - making up config" + else: + raise IOError, "xorg.conf is empty - making up config" + except OSError, errmsg: + raise IOError, errmsg + for row in XorgconfCVSReader(filename=filename).readlines(): + try: + first = row[0].lower() + if context.isSection(first): + section = context.makeSection(row.comment,row) + if section._name == 'ServerLayout': + hasserverlayout = True + context.append(section) + stack.append(context) + context = section + context_class = context.__class__ + elif context.isEndSection(first): + context = stack.pop() + elif context.isListAttr(first): + context.makeListAttr(row.comment,row) + else: + newline = context.makeLine(row.comment,row) + if newline is None: + raise ParseException,"Unknown line type '%s' on line %i" % (first,line) + context.append(newline) + except IndexError: + context.append(ConfigLine(row.comment,row)) + line += 1 + except IOError, errmsg: + ermsg = str(errmsg) + print "IOError", ermsg, " - will create xorg.conf if possible." + if ermsg[:9] == "[Errno 2]": # No such file or directory: + hasxorg = False + addxorg(context, stack) + try: + xorgfile = open(filename, 'a') + xorgfile.close() + except IOError, errmsg: + ermsg = str(errmsg) + if ermsg[:9] == "[Errno 13]": #Permission denied: + pass + # Since we aren't root, changes can't be made anyway. + elif ermsg[:9] == "xorg.conf": # xorg.conf exists, but is empty + hasxorg = False + addxorg(context, stack) + + if len(stack)!=0: + raise ParseException,"Unexpected end of file on line %i" % line + if not hasserverlayout and hasxorg: + addServerLayout(context, section, stack, first) + if check_exists: + return context, hasxorg + else: + return context + +############################################################################ +class ParseException(Exception): + def __init__(self,*args): + Exception.__init__(self,*args) + +############################################################################ +def toBoolean(value): + return unicode(value).lower() in ['on','true','1','yes'] + +############################################################################ +# Our own class for reading CSV file. This version supports unicode while +# standard Python (2.4) version doesn't. Hence the need for this class. +# +class XorgconfCVSReader(object): + def __init__(self,filename=None, text=None): + assert filename is not None or text is not None + + STATE_DELIMITER = 0 + STATE_ITEM = 1 + STATE_QUOTE = 2 + QUOTE = '"' + LINE_COMMENT = '#' + + class CommentList(list): + def __init__(self): + list.__init__(self) + self.comment = None + + if filename is not None: + try: + loc = locale.getpreferredencoding() + except locale.Error: + loc = 'ANSI_X3.4-1968' + fhandle = codecs.open(filename,'r',loc,'replace') + source_lines = fhandle.readlines() + fhandle.close() + else: + source_lines = text.split('\n') + + self.lines = [] + for line in source_lines: + if len(line)!=0 and line[-1]=='\n': + line = line[:-1] + + state = STATE_DELIMITER + row = CommentList() + item = None + for i in range(len(line)): + c = line[i] + + if state==STATE_DELIMITER: + if not c.isspace(): + if c==QUOTE: + item = [] + state = STATE_QUOTE + elif c==LINE_COMMENT: + row.comment = line[i+1:] + break + else: + item = [] + item.append(c) + state = STATE_ITEM + + elif state==STATE_ITEM: + if c.isspace(): + row.append(u''.join(item)) + state = STATE_DELIMITER + item = None + else: + item.append(c) + + elif state==STATE_QUOTE: + if c==QUOTE: + row.append(u''.join(item)) + state = STATE_DELIMITER + item = None + else: + item.append(c) + + if item is not None: + row.append(u''.join(item)) + + self.lines.append(row) + + def readlines(self): + return self.lines + +############################################################################ +if __name__=='__main__': + import sys + if len(sys.argv)==2: + filename = sys.argv[1] + else: + filename = "/etc/X11/xorg.conf" + print "Reading",filename + c = readConfig(filename) + print c.toString() + |