#!/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()

