[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[minion-cvs] Checkpointing my work on config file parser. I"m putti...



Update of /home/minion/cvsroot/src/minion/lib/mixminion
In directory moria.seul.org:/tmp/cvs-serv4934

Modified Files:
	Config.py 
Log Message:
Checkpointing my work on config file parser.  I'm putting the rest of this
off until we have the RFC822-style vs XML-style issue put to bed.



Index: Config.py
===================================================================
RCS file: /home/minion/cvsroot/src/minion/lib/mixminion/Config.py,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -d -r1.1 -r1.2
--- Config.py	29 May 2002 03:52:13 -0000	1.1
+++ Config.py	5 Jul 2002 19:51:12 -0000	1.2
@@ -1,2 +1,213 @@
 # Copyright 2002 Nick Mathewson.  See LICENSE for licensing information.
 # $Id$
+
+import re
+from cStringIO import StringIO
+
+from mixminion.Common import MixError
+
+_section_re = re.compile(r'\[([^\]]+)\]')
+_entry_re = re.compile(r'([^:= \t]+)[:= \t]\s*(.*)')
+_control_re = re.compile(r'-----(BEGIN|END) ([^-]+)-----')
+
+class ConfigError(MixError):
+    pass
+    
+def _parseLine(line):
+    if line == '':
+        return None, None
+    if line.startswith('-----'):
+        m = _control_re.match(line)
+        if not m:
+            return m, "Bad control line"
+        return m.group(0), m.group(1)
+
+    space = line[0] and line[0] in ' \t'
+    line = line.trim()
+    if line == '' or line[0] == '#':
+        return None, None
+    elif line[0] == '[':
+        m = _section_re.match(line)
+        if not m:
+            return "ERR", "Bad section declaration"
+        return m.group(1).trim()
+    elif space:
+        return "MORE", line
+    else:
+        m = _entry_re.match(line)
+        if not m:
+            return "ERR", "Bad entry"
+        return "ENT", m.group(1), m.group(2)
+
+def _parseFile(self, file):
+    #XXXX What to do with control lines?
+    sections = []
+    curSection = None
+    lineno = 0
+    for line in f.readlines():
+        lineno += 1
+        type, val = _parseLine(line)
+        if type == 'ERR':
+            raise ConfigError("%s at line %s" % (val, lineno))
+        elif type == 'SEC':
+            curSection = [ ]
+            sections.append( (val, curSection) )
+        elif type == 'ENT':
+            key,val = val
+            if not curSection:
+                raise ConfigError("Unknown section at line %s" %lineno)
+            curSection.append( [key, val, lineno] )
+            lastKey = key
+        elif type == 'MORE':
+            if not lastKey:
+                raise ConfigError("Unexpected indentation at line %s" %lineno)
+            curSection[-1][1] = "%s %s" % (curSection[-1][1], line)
+    return sections
+
+def _formatEntry(key,val,w=79,ind=4):
+    ind = " "*ind
+    if len(val)+len(key)+2 <= 79:
+        return "%s: %s\n" % (key,val)
+
+    lines = [ "%s: " ]
+    #XXXX Bad implementation.
+    for v in " ".split(val):
+        if len(lines[-1])+" "+len(v) <= w:
+            lines[-1] = "%s %s" % (lines[-1],v)
+        else:
+            lines.append(ind+v)
+    lines.append("")
+    return "\n".join(lines)
+    
+class _ConfigFile:
+    # Set in subclass: _syntax is map from sec->{key:
+    #                               ALLOW/REQUIRE/ALLOW*/REQUIRE*/IGNORE}
+    def __init__(self, fname=None, string=None):
+        self.fname = fname
+        if fname:
+            self.reload()
+        elif string:
+            cs = StringIO(string)
+            try:
+                self.__reload(cs)
+            finally:
+                cs.close()
+        else:    
+            self.clear()
+
+    def clear(self):
+        self._sections = {}
+        self._sectionEntries = {}
+        self._sectionNames = []
+        
+    def reload(self):
+        if not self.fname:
+            return
+        f = open(fname, 'r')
+        try:
+            self.__reload(f)
+        finally:
+            f.close()
+
+    def __reload(self, file):
+        sections = _parseFile(file)
+        
+        self_sections = {}
+        self_sectionEntries = {}
+        self_sectionNames = []
+        
+        for secName, secEntries in  sections:
+            self_sectionNames.append(secName)
+
+            if self_sections.has_key(secName):
+                raise ConfigError("Duplicate section [%s]" %secName)
+            
+            section = {}
+            sectionEntries = []
+            self_sections[secName] = section
+            self_sectionEntries[secName] = sectionEntries
+
+            secConfig = self._syntax.get(secName, None)
+
+            if not secConfig:
+                #XXXX FFFF
+                print "Skipping unrecognized section %s" % (secName)
+                continue
+                
+            for k,v,line in secEntries:
+                sectionEntrties.add( (k,v) )
+                rule = secConfig.get(k, None)
+                if not rule:
+                    raise ConfigError("Unrecognized key %s on line %s" %
+                                      (k, line))
+
+                if rule in ('REQUIRE*','ALLOW*'):
+                    if section.has_key(k):
+                        section[k].append(v)
+                    else:
+                        section[k] = [v]
+                else: #rule in ('REQUIRE', 'ALLOW')
+                    if section.has_key(k):
+                        raise ConfigError("Duplicate entry for %s at line %s"
+                                          % (k, line))
+                    else:
+                        section[k] = v
+
+            for k, rule in secRules:
+                if k in ('REQUIRE', 'REQUIRE*') and not section.has_key(k):
+                    raise ConfigError("Missing entry %s from section %s"
+                                      % (k, secName))
+
+        for secName in self._syntax:
+            if (secName.get('__SECTION__', 'ALLOW') == 'REQUIRE'
+                and not self_sections.has_key(secName)):
+                raise ConfigError("Section [%s] not found." %secName)
+
+        for secName in self_sectionNames:
+            for k,v in self_sectionEntries[s]:
+                assert v == self_sections[s][k] or v in self_sections[s][k]
+
+        self.validate(self_sections, self_sectionEntries)
+
+        self.sections = self_sections
+        self.sectionEntries = self_sectionEntries
+        self.sectionName = self_sectionNames
+
+    def validate(sections, sectionEntries):
+        pass
+
+    def __getitem__(self, sec):
+        return self._sections[sec]
+
+    def getSectionItems(self, sec):
+        return self._sectionEntries[sec]
+
+    def __str__(self):
+        lines = []
+        for s in self._sectionNames:
+            lines.append("[%s]\n"%s)
+            for k,v in self._sectionEntries[s]:
+                lines.append(_formatEntry(k,v))
+            lines.append("\n")
+            
+        return "".join(lines)
+
+_serverDescriptorSyntax = {
+    'Server' : { 'Descriptor-Version' : 'REQUIRE',
+                 'IP' : 'REQUIRE',
+                 'Nickname' : 'ALLOW',
+                 'Identity' : 'REQUIRE',
+                 'Digest' : 'REQUIRE',
+                 'Signature' : 'REQUIRE',
+                 'Valid-After' : 'REQUIRE',
+                 'Valid-Until' : 'REQUIRE',
+                 'Contact' : 'ALLOW',
+                 'Comments' : 'ALLOW',
+                 'Packet-Key' : 'REQUIRE',  },
+    'Incoming/MMTP' : { 'MMTP-Decriptor-Version' : 'REQUIRE',
+                        'Port' :  'REQUIRE',
+                        'Key-Digest' : 'REQUIRE', },
+    'Modules/MMTP' : { 'MMTP-Descriptor-Version' : 'REQUIRE',
+                       'Allow' : 'ALLOW*',
+                       'Deny' : 'ALLOW*' }
+    }