Index: branches/0.4/jobmond/jobmond.py
===================================================================
--- branches/0.4/jobmond/jobmond.py	(revision 694)
+++ branches/0.4/jobmond/jobmond.py	(revision 699)
@@ -26,7 +26,8 @@
 
 import sys, getopt, ConfigParser, time, os, socket, string, re
-import xdrlib, socket, syslog, xml, xml.sax
+import xdrlib, socket, syslog, xml, xml.sax, shlex
 from xml.sax.handler import feature_namespaces
 from collections import deque
+from glob import glob
 
 VERSION='0.4+SVN'
@@ -94,56 +95,295 @@
     return loadConfig( config_filename )
 
-# Fixme:  This doesn't DTRT with commented-out bits of the file.  E.g.
-# it picked up a commented-out `mcast_join' and tried to use a
-# multicast channel when it shouldn't have done.
 class GangliaConfigParser:
 
-    def __init__( self, config_file ):
-
-        self.config_file    = config_file
-
-        if not os.path.exists( self.config_file ):
-
-            debug_msg( 0, "FATAL ERROR: gmond config '" + self.config_file + "' not found!" )
-            sys.exit( 1 )
+    def __init__( self, filename ):
+
+        self.conf_lijst   = [ ]
+        self.conf_dict    = { }
+        self.filename     = filename
+        self.file_pointer = file( filename, 'r' )
+        self.lexx         = shlex.shlex( self.file_pointer )
+        self.lexx.whitespace_split = True
+
+        self.parse()
+
+    def __del__( self ):
+
+        """
+        Cleanup: close file descriptor
+        """
+
+        self.file_pointer.close()
+        del self.lexx
+        del self.conf_lijst
 
     def removeQuotes( self, value ):
 
-        clean_value    = value
-        clean_value    = clean_value.replace( "'", "" )
-        clean_value    = clean_value.replace( '"', '' )
-        clean_value    = clean_value.strip()
+        clean_value = value
+        clean_value = clean_value.replace( "'", "" )
+        clean_value = clean_value.replace( '"', '' )
+        clean_value = clean_value.strip()
 
         return clean_value
 
-    def getVal( self, section, valname ):
-
-        cfg_fp        = open( self.config_file )
-        section_start    = False
-        section_found    = False
-        value        = None
-
-        for line in cfg_fp.readlines():
-
-            if line.find( section ) != -1:
-
-                section_found    = True
-
-            if line.find( '{' ) != -1 and section_found:
-
-                section_start    = True
-
-            if line.find( '}' ) != -1 and section_found:
-
-                section_start    = False
-                section_found    = False
-
-            if line.find( valname ) != -1 and section_start:
-
-                value         = string.join( line.split( '=' )[1:], '' ).strip()
-
-        cfg_fp.close()
-
-        return value
+    def removeBraces( self, value ):
+
+        clean_value = value
+        clean_value = clean_value.replace( "(", "" )
+        clean_value = clean_value.replace( ')', '' )
+        clean_value = clean_value.strip()
+
+        return clean_value
+
+    def parse( self ):
+
+        """
+        Parse self.filename using shlex scanning.
+        - Removes /* comments */
+        - Traverses (recursively) through all include () statements
+        - Stores complete valid config tokens in self.conf_list
+
+        i.e.:
+            ['globals',
+             '{',
+             'daemonize',
+             '=',
+             'yes',
+             'setuid',
+             '=',
+             'yes',
+             'user',
+             '=',
+             'ganglia',
+             'debug_level',
+             '=',
+             '0', 
+             <etc> ]
+        """
+
+        t = 'bogus'
+        c = False
+        i = False
+
+        while t != self.lexx.eof:
+            #print 'get token'
+            t = self.lexx.get_token()
+
+            if len( t ) >= 2:
+
+                if len( t ) >= 4:
+
+                    if t[:2] == '/*' and t[-2:] == '*/':
+
+                        #print 'comment line'
+                        #print 'skipping: %s' %t
+                        continue
+
+                if t == '/*' or t[:2] == '/*':
+                    c = True
+                    #print 'comment start'
+                    #print 'skipping: %s' %t
+                    continue
+
+                if t == '*/' or t[-2:] == '*/':
+                    c = False
+                    #print 'skipping: %s' %t
+                    #print 'comment end'
+                    continue
+
+            if c:
+                #print 'skipping: %s' %t
+                continue
+
+            if t == 'include':
+                i = True
+                #print 'include start'
+                #print 'skipping: %s' %t
+                continue
+
+            if i:
+
+                #print 'include start: %s' %t
+
+                t2 = self.removeQuotes( t )
+                t2 = self.removeBraces( t )
+
+                for in_file in glob( self.removeQuotes(t2) ):
+
+                    #print 'including file: %s' %in_file
+                    parse_infile = GangliaConfigParser( in_file )
+
+                    self.conf_lijst = self.conf_lijst + parse_infile.getConfLijst()
+
+                    del parse_infile
+
+                i = False
+                #print 'include end'
+                #print 'skipping: %s' %t
+                continue
+
+            #print 'keep: %s' %t
+            self.conf_lijst.append( self.removeQuotes(t) )
+
+    def getConfLijst( self ):
+
+        return self.conf_lijst
+
+    def confListToDict( self, parent_list=None ):
+
+        """
+        Recursively traverses a conf_list and creates dictionary from it
+        """
+
+        new_dict = { }
+        count    = 0
+        skip     = 0
+
+        if not parent_list:
+            parent_list = self.conf_lijst
+
+        #print 'entering confListToDict(): (parent) list size %s' %len(parent_list)
+
+        for n, c in enumerate( parent_list ):
+
+            count = count + 1
+
+            #print 'CL: n %d c %s' %(n, c)
+
+            if skip > 0:
+
+                #print '- skipped'
+                skip = skip - 1
+                continue
+
+            if (n+1) <= (len( parent_list )-1):
+
+                if parent_list[(n+1)] == '{':
+
+                    if not new_dict.has_key( c ):
+                        new_dict[ c ] = [ ]
+
+                    (temp_new_dict, skip) = self.confListToDict( parent_list[(n+2):] )
+                    new_dict[ c ].append( temp_new_dict )
+
+                if parent_list[(n+1)] == '=' and (n+2) <= (len( parent_list )-1):
+
+                    if not new_dict.has_key( c ):
+                        new_dict[ c ] = [ ]
+
+                    new_dict[ c ].append( parent_list[ (n+2) ] )
+
+                    skip = 2
+
+                if parent_list[n] == '}':
+
+                    #print 'leaving confListToDict(): new dict = %s' %new_dict
+                    return (new_dict, count)
+
+    def makeConfDict( self ):
+
+        """
+        Walks through self.conf_list and creates a dictionary based upon config values
+
+        i.e.:
+            'tcp_accept_channel': [{'acl': [{'access': [{'action': ['"allow"'],
+                                                         'ip': ['"127.0.0.1"'],
+                                                         'mask': ['32']}]}],
+                                    'port': ['8649']}],
+            'udp_recv_channel': [{'port': ['8649']}],
+            'udp_send_channel': [{'host': ['145.101.32.3'],
+                                  'port': ['8649']},
+                                 {'host': ['145.101.32.207'],
+                                  'port': ['8649']}]}
+        """
+
+        new_dict = { }
+        skip     = 0
+
+        #print 'entering makeConfDict()'
+
+        for n, c in enumerate( self.conf_lijst ):
+
+            #print 'M: n %d c %s' %(n, c)
+
+            if skip > 0:
+
+                #print '- skipped'
+                skip = skip - 1
+                continue
+
+            if (n+1) <= (len( self.conf_lijst )-1):
+
+                if self.conf_lijst[(n+1)] == '{':
+
+                    if not new_dict.has_key( c ):
+                        new_dict[ c ] = [ ]
+
+                    ( temp_new_dict, skip ) = self.confListToDict( self.conf_lijst[(n+2):] )
+                    new_dict[ c ].append( temp_new_dict )
+
+                if self.conf_lijst[(n+1)] == '=' and (n+2) <= (len( self.conf_lijst )-1):
+
+                    if not new_dict.has_key( c ):
+                        new_dict[ c ] = [ ]
+
+                    new_dict[ c ].append( self.conf_lijst[ (n+2) ] )
+
+                    skip = 2
+
+        self.conf_dict = new_dict
+        #print 'leaving makeConfDict(): conf dict size %d' %len( self.conf_dict )
+
+    def checkConfDict( self ):
+
+        if len( self.conf_lijst ) == 0:
+
+            raise Exception("Something went wrong generating conf list for %s" %self.file_name )
+
+        if len( self.conf_dict ) == 0:
+
+            self.makeConfDict()
+
+    def getConfDict( self ):
+
+        self.checkConfDict()
+        return self.conf_dict
+
+    def getUdpSendChannels( self ):
+
+        self.checkConfDict()
+        return self.conf_dict[ 'udp_send_channel' ]
+
+    def getSectionLastOption( self, section, option ):
+
+        """
+        Get last option set in a config section that could be set multiple times in multiple (include) files.
+
+        i.e.: getSectionLastOption( 'globals', 'send_metadata_interval' )
+        """
+
+        self.checkConfDict()
+        value = None
+
+        if not self.conf_dict.has_key( section ):
+
+            return None
+
+        # Could be set multiple times in multiple (include) files: get last one set
+        for c in self.conf_dict[ section ]:
+
+                if c.has_key( option ):
+
+                    cluster_name = c[ option ][0]
+
+        return cluster_name
+
+    def getClusterName( self ):
+
+        return self.getSectionLastOption( 'cluster', 'name' )
+
+    def getVal( self, section, option ):
+
+        return self.getSectionLastOption( section, option )
 
     def getInt( self, section, valname ):
@@ -152,7 +392,5 @@
 
         if not value:
-            return False
-
-        value    = self.removeQuotes( value )
+            return None
 
         return int( value )
@@ -163,7 +401,5 @@
 
         if not value:
-            return False
-
-        value    = self.removeQuotes( value )
+            return None
 
         return str( value )
