Logo Search packages:      
Sourcecode: kde-guidance version File versions  Download package

xorgconfig.py

#!/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.
"""
############################################################################
00072 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):
        string = (' ' * depth) + ' '.join([unicode(item) for item in self._row])
        if self._comment is not None:
            string += '#' + self._comment
        return string + '\n'

############################################################################
00088 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 = (' ' * depth)
        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 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)

############################################################################
00195 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:]) )
    
00238     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
    
############################################################################
00311 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
    
00331     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' % \
            (' ' * depth, self._name, ConfigContainer.toString(self,depth+1), ' ' * depth)

        
############################################################################
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"]
    
    def makeSection(self,comment,row):
        return ModuleSubSection(comment,row)
        
    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 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):
        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 readConfig(filename):
    
    context = XorgConfig()
    stack = []
    line = 1
    for row in XorgconfCVSReader(filename=filename).readlines():
        try:
            first = row[0].lower()
            if context.isSection(first):
                section = context.makeSection(row.comment,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(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

    if len(stack)!=0:
        raise ParseException,"Unexpected end of file on line %i" % line
    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:
            fhandle = codecs.open(filename,'r',locale.getpreferredencoding(),'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()
    

Generated by  Doxygen 1.6.0   Back to index