diff --git a/tren.py b/tren.py index 7d05e50..cd00a2b 100755 --- a/tren.py +++ b/tren.py @@ -8,7 +8,7 @@ PROGNAME = "tren.py" BASENAME = PROGNAME.split(".py")[0] PROGENV = BASENAME.upper() -RCSID = "$Id: tren.py,v 1.172 2010/03/06 16:26:17 tundra Exp $" +RCSID = "$Id: tren.py,v 1.173 2010/03/08 16:41:23 tundra Exp $" VERSION = RCSID.split()[2] # Copyright Information @@ -63,7 +63,7 @@ # General Program Constants ##### -MAXINCLUDES = 50 # Maximum number of includes allowed +MAXINCLUDES = 1000 # Maximum number of includes allowed - used to catch circular references MAXNAMELEN = 255 # Maximum file or directory name length MINNAMELEN = 1 # Minimum file or directory name length @@ -82,6 +82,16 @@ ##### +# Command Line Option Strings +##### + +# List all legal command line options that will be processed by getopt() later. +# We exclude -I here because it is parsed manually before the getopt() call. + +OPTIONSLIST = "Ccdfhi:P:qR:r:S:tvw:Xx" # All legal command line options in getopt() format + + +##### # Literals ##### @@ -109,12 +119,12 @@ TOKCMDEXEC = "`" # Delimiter for command execution renaming tokens TOKDELIM = "/" # Delimiter for all renaming tokens TOKENV = "$" # Introducer for environment variable replacement tokens -TOKFILGID = "g" # File GID replacement token -TOKFILGRP = "G" # File Group Name replacement token -TOKFILLEN = "L" # File Length replacement token -TOKFILNAM = "F" # File Name replacement token -TOKFILUID = "u" # File UID replacement token -TOKFILUNAM = "U" # File User Name replacement token +TOKFILGID = "GID" # File GID replacement token +TOKFILGRP = "GNAME" # File Group Name replacement token +TOKFILLEN = "LEN" # File Length replacement token +TOKFILNAM = "FNAME" # File Name replacement token +TOKFILUID = "UID" # File UID replacement token +TOKFILUNAM = "UNAME" # File User Name replacement token # Internal program state literals @@ -234,7 +244,7 @@ " -S suffix Suffix to use when renaming existing filenames (Default: %s)" % DEFSUFFIX, " -t Test mode, don't rename, just show what the program *would* do (Default: False)", " -v Print detailed program version information and continue (Default: False)", - " -w Line length of diagnostic and error output (Default: %s)" % DEFLEN, + " -w width Line length of diagnostic and error output (Default: %s)" % DEFLEN, " -X Treat the renaming strings literally (Default)", " -x Treat the old replacement string as a Python regular expression (Default: False)", ] @@ -1000,8 +1010,9 @@ def ProcessIncludes(OPTIONS): """ Resolve include file references allowing for nested includes. - This has to be done here separate from the command line options so - that getopt() processing below will "see" the included statements. + This has to be done here separate from the command line + options so that normal getopt() processing below will "see" + the included statements. This is a bit tricky because we have to handle every possible legal command line syntax for option specification: @@ -1012,6 +1023,14 @@ -....Ifilename """ + # Build a list of all the options that take arguments. This is + # needed to determine whether the include symbol is an include + # option or part of an argument to a preceding option. + + OPTIONSWITHARGS = "" + for i in re.finditer(":", OPTIONSLIST): + OPTIONSWITHARGS += OPTIONSLIST[i.start() - 1] + NUMINCLUDES = 0 FoundNewInclude = True @@ -1026,64 +1045,86 @@ # contents at that position in the command line. field = OPTIONS[i] - if field.startswith(OPTINTRO) and (field.find(INCL) > -1): - - FoundNewInclude = True - lhs, rhs = field.split(INCL) - - if lhs == OPTINTRO: - lhs = "" - - if rhs == "": - - if i < len(OPTIONS)-1: - - inclfile = OPTIONS[i+1] - OPTIONS = OPTIONS[:i+1] + OPTIONS[i+2:] - - # We have an include without a filename at the end - # of the command line which is bogus. - else: - ErrorMsg(eBADARG % eBADINCL) - - else: - inclfile = rhs - - # Before actually doing the include, make sure we've - # not exceeded the limit. This is here mostly to make - # sure we stop recursive/circular includes. - - NUMINCLUDES += 1 - if NUMINCLUDES > MAXINCLUDES: - ErrorMsg(eTOOMANYINC) - - # Read the included file, stripping out comments - - try: - n = [] - f = open(inclfile) - for line in f.readlines(): - line = line.split(COMMENT)[0] - n += line.split() - f.close() - - # Keep track of the filenames being included - - IncludedFiles.append(os.path.abspath(inclfile)) - - # Insert content of included file at current - # command line position - - # A non-null left hand side means that there were - # options before the include we need to preserve - - if lhs: - n = [lhs] + n - - OPTIONS = OPTIONS[:i] + n + OPTIONS[i+1:] + position = field.find(INCL) - except IOError as e: - ErrorMsg(eFILEOPEN % (inclfile, e.args[1])) + if field.startswith(OPTINTRO) and (position > -1): + + + lhs = field[:position] + rhs = field[position+1:] + + # Make sure the include symbol isn't part of some + # previous option argument + + previousopt = False + for c in OPTIONSWITHARGS: + + if c in lhs: + + previousopt = True + break + + # If the include symbol appears in the context of a + # previous option, skip this field, otherwise process + # it as an include. + + + if not previousopt: + + FoundNewInclude = True + if lhs == OPTINTRO: + lhs = "" + + if rhs == "": + + if i < len(OPTIONS)-1: + + inclfile = OPTIONS[i+1] + OPTIONS = OPTIONS[:i+1] + OPTIONS[i+2:] + + # We have an include without a filename at the end + # of the command line which is bogus. + else: + ErrorMsg(eBADARG % eBADINCL) + + else: + inclfile = rhs + + # Before actually doing the include, make sure we've + # not exceeded the limit. This is here mostly to make + # sure we stop recursive/circular includes. + + NUMINCLUDES += 1 + if NUMINCLUDES > MAXINCLUDES: + ErrorMsg(eTOOMANYINC) + + # Read the included file, stripping out comments + + try: + n = [] + f = open(inclfile) + for line in f.readlines(): + line = line.split(COMMENT)[0] + n += line.split() + f.close() + + # Keep track of the filenames being included for debug output + + IncludedFiles.append(os.path.abspath(inclfile)) + + # Insert content of included file at current + # command line position + + # A non-null left hand side means that there were + # options before the include we need to preserve + + if lhs: + n = [lhs] + n + + OPTIONS = OPTIONS[:i] + n + OPTIONS[i+1:] + + except IOError as e: + ErrorMsg(eFILEOPEN % (inclfile, e.args[1])) i += 1 @@ -1145,7 +1186,7 @@ # And parse the command line try: - opts, args = getopt.getopt(OPTIONS, 'Ccdfhi:P:qR:r:S:tvw:Xx]') + opts, args = getopt.getopt(OPTIONS, OPTIONSLIST) except getopt.GetoptError as e: ErrorMsg(eBADARG % e.args[0])