#!/usr/bin/env python # tconfpy.py # Copyright (c) 2003-2004 TundraWare Inc. All Rights Reserved. # For Updates See: http://www.tundraware.com/Software/tconfpy # Program Information PROGNAME = "tconfpy" RCSID = "$Id: tconfpy.py,v 1.106 2004/03/12 08:32:35 tundra Exp $" VERSION = RCSID.split()[2] # Copyright Information CPRT = chr(169) DATE = "2003-2004" OWNER = "TundraWare Inc." RIGHTS = "All Rights Reserved" COPYRIGHT = "Copyright %s %s %s, %s." % (CPRT, DATE, OWNER, RIGHTS) PROGINFO = PROGNAME + " " + VERSION BANNER = "%s\n%s\n%s\n" % (PROGINFO, COPYRIGHT, RCSID) #----------------------------------------------------------# # Variables User Might Change # #----------------------------------------------------------# #------------------- Nothing Below Here Should Need Changing -----------------# #----------------------------------------------------------# # Public Features Of This Module # #----------------------------------------------------------# __all__ = ["ParseConfig"] #----------------------------------------------------------# # Imports # #----------------------------------------------------------# import os.path #----------------------------------------------------------# # Aliases & Redefinitions # #----------------------------------------------------------# #----------------------------------------------------------# # Constants & Literals # #----------------------------------------------------------# ########### # Constants ########### # General Constants MSGPOS = 10 # Where to start message output # Reserved Symbols COMMENT = '#' # Comment introducer character DELIML = '[' # Left delimiter for vbl reference DELIMR = ']' # Right delimiter for vbl reference DOLLAR = '$' # Used to note enviro vbl # Control and conditional symbols INCLUDE = ".include" ENDIF = ".endif" IF = ".if" # Table of reserved symbols used by parser. User is able # to include this in the right side of a variable definition # via [reserved sym]. Reserved = {"DELIML" : DELIML, "DELIMR" : DELIMR, "DOLLAR" : DOLLAR, "HASH" : COMMENT, "INCLUDE" : INCLUDE, "ENDIF" : ENDIF, "IF" : IF } ########### # Literals ########### #----------------------------------------------------------# # Global Variables & Data Structures # #----------------------------------------------------------# DEBUG = False # Control Debug output IGNORECASE = False # Case observed by default DebugMsg = [] # Place to store and return debug info ErrMsgs = [] # Place to store and return errors WarnMsgs = [] # Place to store and return warnings CondStack = [True,] # Conditional stack SymTable = {} # Results of the parsing stored here TotalLines = 0 # Total number of lines parsed #----------------------------------------------------------# # Prompts, & Application Strings # #----------------------------------------------------------# ########## # Debug Messages ########## dDEBUG = "DEBUG" dLINEIGNORE = "%s Line %d Ignored/Not Included" dNUMLINES = "Processing '%s' Resulted In %d Total Lines Parsed" dPARSEDLINE = "%s Line %d Parses To: %s" ########### # Error Messages ########### eCONFOPEN = "Cannot Open The File '%s'" eENDIFEXTRA = "%s Line %d: " + ENDIF + " Without Matching Condition" eERROR = "ERROR" ########### # Prompts ########### ########### # Warning Messages ########### wENDIFBAD = "%s Line %d: Text After " + ENDIF + " Ignored" wWARNING = "WARNING" #--------------------------- Code Begins Here ---------------------------------# #----------------------------------------------------------# # Object Base Class Definitions # #----------------------------------------------------------# #----------------------------------------------------------# # Utility Function Definitions # #----------------------------------------------------------# ########## # Create A Debug Message ########## def DebugMsg(dmsg): global DebugMsgs DebugMsgs.append(mkmsg(dmsg, dDEBUG)) # End of 'DebugMsg()' ########## # Create An Error Message ########## def ErrorMsg(error): global ErrMsgs ErrMsgs.append(mkmsg(error + "!", eERROR)) # End of 'ErrorMsg()' ########## # Create A Warning Message ########## def WarningMsg(warning): global WarnMsgs WarnMsgs.append(mkmsg(warning + "!", wWARNING)) # End of 'WarningMsg()' ########## # Construct A Standard Application Message String ########## def mkmsg(msg, msgtype=""): if msgtype: msgtype += ":" pad = " " * (MSGPOS - len(msgtype)) return "%s - %s%s%s" % (PROGINFO, msgtype, pad, msg) # End of 'mkmsg()' #----------------------------------------------------------# # Entry Point On Direct Invocation # #----------------------------------------------------------# if __name__ == '__main__': print BANNER #----------------------------------------------------------# # Public API To Module # #----------------------------------------------------------# def ParseConfig(cfgfile, Options={}, IgnoreCase=False, debug=False): global DebugMsgs, ErrMsgs, WarnMsgs global CondStack, DEBUG, IGNORECASE, SymTable, TotalLines # Initialize the globals DEBUG = debug IGNORECASE = IgnoreCase DebugMsgs = [] ErrMsgs = [] WarnMsgs = [] CondStack = [True,] SymTable = Options TotalLines = 0 # Parse the file ParseFile(cfgfile) # Return the parsing results if DEBUG: DebugMsg(dNUMLINES %(cfgfile, TotalLines)) return (SymTable, ErrMsgs, WarnMsgs, DebugMsgs) # End of 'ParseConfig()' #----------------------------------------------------------# # Parser Support Functions # #----------------------------------------------------------# ########## # Condition A Line - Remove Comments & Leading/Trailing Whitespace ########## def ConditionLine(line): return line.split(COMMENT)[0].strip() # End of 'ConditionLine()' ########## # Parse A File ########## def ParseFile(cfgfile): global IgnoreCase, MsgList, SymTable, TotalLines try: cf = open(cfgfile) # Successful open of config file - Begin processing it linenum=0 # Process and massage the configuration file for line in cf.read().splitlines(): linenum += 1 TotalLines += 1 # Parse this line ParseLine(line, cfgfile, linenum) # Close the config file cf.close() # File open failed for some reason except: ErrorMsg(eCONFOPEN % cfgfile) # Record the error # End of 'ParseFile()' ########## # Parse A Line ########## def ParseLine(line, cfgfile, linenum): global CondStack, MsgList, SymTable line = ConditionLine(line) ########## # Beginning Of Line Parser ########## # Only attempt on non-blank lines if line: # Process .endif statements before checking state since # these potentially change the state. if line.startswith(ENDIF): # This should be the only thing on the line if line != ENDIF: WarningMsg(wENDIFBAD % (cfgfile, linenum)) # Remove one level of nesting CondStack.pop() # Error, if there are more .endifs than conditionals if not CondStack: ErrorMsg(eENDIFEXTRA % (cfgfile, linenum)) CondStack.append(False) # Inhibit further parsing # If we are in a False conditional block, this line # is ignored. print linenum, CondStack, line if not CondStack[-1]: if DEBUG: DebugMsg(dLINEIGNORE %(cfgfile, linenum)) return # Process .include statements if line.startswith(INCLUDE): ParseFile(line.split(INCLUDE)[1].strip()) return # Substitute references to reserved symbols for ref in Reserved.keys(): line = line.replace("%s%s%s" % (DELIML, ref, DELIMR), Reserved[ref]) ########## # End Of Line Parser ########## if DEBUG: DebugMsg(dPARSEDLINE %(cfgfile, linenum, line)) # End of 'ParseLine'