| |
---|
| | PROGNAME = "tren.py" |
---|
| | BASENAME = PROGNAME.split(".py")[0] |
---|
| | PROGENV = BASENAME.upper() |
---|
| | INCLENV = PROGENV + "INCL" |
---|
| | RCSID = "$Id: tren.py,v 1.230 2010/08/25 20:16:17 tundra Exp $" |
---|
| | RCSID = "$Id: tren.py,v 1.231 2010/08/26 18:50:09 tundra Exp $" |
---|
| | VERSION = RCSID.split()[2] |
---|
| | |
---|
| | # Copyright Information |
---|
| | |
---|
| |
---|
| | |
---|
| | # 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 = "A:abCcde:fhi:P:qR:r:S:tvw:Xx" # All legal command line options in getopt() format |
---|
| | OPTIONSLIST = "A:abCcde:fhi:P:qR:r:S:T:tvw:Xx" # All legal command line options in getopt() format |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Literals |
---|
| |
---|
| | PATHDELUNIX = ":" # Separates include path elements on Unix systems |
---|
| | PATHDELWIN = ";" # Separates include path elements on Windows systems |
---|
| | PATHSEP = os.path.sep # File path separator character |
---|
| | RANGESEP = ":" # Separator for instance ranges |
---|
| | SINGLEINST = "SINGLEINST" # Indicates a single, not range, replacement instance |
---|
| | SINGLEINST = "SINGLEINST" # Indicates a single, not range, in a slice |
---|
| | WINDOWSGROUP = "WindowsGroup" # Returned on Windows w/o win32all |
---|
| | WINDOWSUSER = "WindowsUser" # Reutrned on Windows w/o win32all |
---|
| | WINGROUPNOT = "GroupNotAvailable" # Returned when win32all can't get a group name |
---|
| | WINUSERNOT = "UserNotAvailable" # Returned when win32all can't get a user name |
---|
| |
---|
| | MAXLINELEN = "MAXLINELEN" |
---|
| | QUIET = "QUIET" |
---|
| | REGEX = "REGEX" |
---|
| | RENSEP = "RENSEP" |
---|
| | TARGETSTART = "TARGETSTART" |
---|
| | TARGETEND = "TARGETEND" |
---|
| | TESTMODE = "TESTMODE" |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| |
---|
| | eARGLENGTH = "%s must contain exactly %s character(s)!" |
---|
| | eBADARG = "Invalid command line: %s!" |
---|
| | eBADCASECONV = "Invalid case conversion argument: %s! Must be one of: %s" |
---|
| | eBADINCL = "option -%s requires argument" % INCL |
---|
| | eBADINSTANCE = "%s is an invalid replacement instance! Must be integer values in the form: n, n:n, :n, n:, or :" |
---|
| | eBADLEN = "Bad line length '%s'!" |
---|
| | eBADNEWOLD = "Bad -r argument '%s'! Requires exactly one new, old string separator (Default: " + DEFSEP + ")" |
---|
| | eBADREGEX = "Invalid Regular Expression: %s" |
---|
| | eBADSLICE = "%s invalid slice format! Must be integer values in the form: n, start:end (start<=end), :n, n:, or :" |
---|
| | eERROR = "ERROR" |
---|
| | eEXECFAIL = "Renaming token: '%s', command '%s' Failed To Execute!" |
---|
| | eFILEOPEN = "Cannot open file '%s': %s!" |
---|
| | eLINELEN = "Specified line length too short! Must be at least %s" % MINLEN |
---|
| |
---|
| | ##### |
---|
| | |
---|
| | uTable = [PROGVER, |
---|
| | HOMEPAGE, |
---|
| | "usage: " + PROGNAME + " [[-abCcdfhqtvwXx] [-e type] [-I file] [-i instance] [-P escape] [ -R separator] [-r old=new] [-S suffix] [-w width]] ... file|dir file|dir ...", |
---|
| | "usage: " + PROGNAME + " [[-abCcdfhqtvwXx] [-e type] [-I file] [-i instance] [-P escape] [ -R separator] [-r old=new] [-S suffix] [-T target] [-w width]] ... file|dir file|dir ...", |
---|
| | " where,", |
---|
| | " -A alphabet Install \"alphabet\" for use by sequence renaming tokens", |
---|
| | " -a Ask interactively before renaming (Default: Off)", |
---|
| | " -b Turn off backups during forced renaming (Default: Do Backups)", |
---|
| |
---|
| | " -R char Separator character for -r rename arguments (Default: %s)" % DEFSEP, |
---|
| | " -r old=new Replace old with new in file or directory names", |
---|
| | " -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)", |
---|
| | " -T num|range Specify which characters in file name are targeted for renaming (Default: Whole Name)", |
---|
| | " -v Print detailed program version information and continue (Default: False)", |
---|
| | " -w length 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)", |
---|
| |
---|
| | MAXLINELEN : DEFLEN, # Width of output messages |
---|
| | QUIET : False, # Display progress |
---|
| | REGEX : False, # Do not treat old string as a regex |
---|
| | RENSEP : DEFSEP, # Old, New string separator for -r |
---|
| | TARGETSTART : False, # Entire file name is renaming target by default |
---|
| | TARGETEND : False, |
---|
| | TESTMODE : False # Test mode off |
---|
| | } |
---|
| | |
---|
| | |
---|
| |
---|
| | DEBUG : debug flag, |
---|
| | CASECONV : type of case conversion, |
---|
| | CASESENSITIVE : case sensitivity flag, |
---|
| | FORCERENAME : force renaming flag, |
---|
| | INSTANCESTART : DReplace first, leftmost instance by default |
---|
| | INSTANCEEND : |
---|
| | INSTANCESTART : Replace first, leftmost instance by default, |
---|
| | INSTANCEEND : , |
---|
| | MAXLINELEN : max output line length, |
---|
| | QUIET : quiet output flag, |
---|
| | REGEX : regular expression enable flag, |
---|
| | RENSEP : old/new rename separator string, |
---|
| | TARGETSTART : Entire file name target for renaming by default, |
---|
| | TARGETEND : , |
---|
| | TESTMODE : testmode flag |
---|
| | } ... (repeated for each rename request) |
---|
| | ] |
---|
| | |
---|
| |
---|
| | |
---|
| | for target in self.SortViews[ORDERBYCMDLINE]: |
---|
| | |
---|
| | oldname, pathname = self.RenNames[target][BASE], self.RenNames[target][PATHNAME] |
---|
| | newname = oldname |
---|
| | name = oldname |
---|
| | |
---|
| | # Keep track of incremental renaming for use by debug |
---|
| | RenSequence = [oldname] |
---|
| | |
---|
| | for renrequest in self.RenRequests: |
---|
| | |
---|
| | |
---|
| | # Select portion of name targeted for renaming |
---|
| | |
---|
| | lname = "" |
---|
| | rname = "" |
---|
| | tstart = renrequest[TARGETSTART] |
---|
| | tend = renrequest[TARGETEND] |
---|
| | |
---|
| | # Condition values so that range slicing works properly below. |
---|
| | # This couldn't be done at the time the target range was |
---|
| | # saved intially, because the length of the name being processed |
---|
| | # isn't known until here. |
---|
| | |
---|
| | if tstart == None: |
---|
| | tstart = 0 |
---|
| | |
---|
| | if tend == None: |
---|
| | tend = len(name) |
---|
| | |
---|
| | if tstart or tend: |
---|
| | |
---|
| | # Condition and bounds check the target range as needed |
---|
| | |
---|
| | # Handle single position references |
---|
| | if (tend == SINGLEINST): |
---|
| | |
---|
| | # Compute bounds for positive and negative |
---|
| | # indicies. This is necessary because positive |
---|
| | # indicies are 0-based, but negative indicies |
---|
| | # start at -1. |
---|
| | |
---|
| | bound = len(name) |
---|
| | if tstart < 0: |
---|
| | bound += 1 |
---|
| | |
---|
| | # Select the desired position. Notice that |
---|
| | # out-of-bounds references are ignored. This is |
---|
| | # so the user can specify renaming operations on |
---|
| | # file names of varying lengths and have them |
---|
| | # apply only to those files long enough to |
---|
| | # accommodate the request without blowing up on |
---|
| | # the ones that are not long enough. |
---|
| | |
---|
| | if abs(tstart) < bound: |
---|
| | lname, name, rname = name[:tstart], name[tstart], name[tstart+1:] |
---|
| | |
---|
| | # Handle slice range requests |
---|
| | |
---|
| | else: |
---|
| | lname, name, rname = name[:tstart], name[tstart:tend], name[tend:] |
---|
| | |
---|
| | |
---|
| | # Save current state of name |
---|
| | |
---|
| | newname = name |
---|
| | |
---|
| | # Handle conventional string replacement renaming requests |
---|
| | |
---|
| | if renrequest[OLD] or renrequest[NEW]: |
---|
| | |
---|
| |
---|
| | elif renrequest[CASECONV]: |
---|
| | newname = CASETBL[renrequest[CASECONV]](name) |
---|
| | |
---|
| | # Any subsequent replacements operate on the modified name |
---|
| | # which is reconstructed by combining what we've renamed |
---|
| | # with anything that was excluded from the rename operation. |
---|
| | |
---|
| | newname = lname + newname + rname |
---|
| | name = newname |
---|
| | |
---|
| | # Keep track of incremental renaming for use by debug |
---|
| | RenSequence.append(newname) |
---|
| | RenSequence.append(name) |
---|
| | |
---|
| | # Show the incremental renaming steps if debug is on |
---|
| | |
---|
| | if ProgramOptions[DEBUG]: |
---|
| |
---|
| | #----------------------------------------------------------# |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Check For Correct Slice Syntax |
---|
| | ##### |
---|
| | |
---|
| | def CheckSlice(val): |
---|
| | |
---|
| | try: |
---|
| | |
---|
| | # Process ranges |
---|
| | |
---|
| | if val.count(RANGESEP): |
---|
| | |
---|
| | lhs, rhs = val.split(RANGESEP) |
---|
| | |
---|
| | if not lhs: |
---|
| | lhs = None |
---|
| | |
---|
| | else: |
---|
| | lhs = int(lhs) |
---|
| | |
---|
| | if not rhs: |
---|
| | rhs = None |
---|
| | |
---|
| | else: |
---|
| | rhs = int(rhs) |
---|
| | |
---|
| | # In the case of an explicit range, make sure the start <= end |
---|
| | |
---|
| | if (lhs != None and rhs != None) and (lhs > rhs): |
---|
| | raise |
---|
| | |
---|
| | # Process single indexes |
---|
| | |
---|
| | else: |
---|
| | |
---|
| | lhs = int(val) |
---|
| | rhs = SINGLEINST |
---|
| | |
---|
| | # Something about the argument was bogus |
---|
| | |
---|
| | except: |
---|
| | ErrorMsg(eBADSLICE % val) |
---|
| | |
---|
| | return (lhs, rhs) |
---|
| | |
---|
| | # End Of 'CheckSlice()' |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Turn A List Into Columns With Space Padding |
---|
| | ##### |
---|
| | |
---|
| | def ColumnPad(list, PAD=PADCHAR, WIDTH=PADWIDTH): |
---|
| |
---|
| | # Specify which instances to replace |
---|
| | |
---|
| | if opt == "-i": |
---|
| | |
---|
| | # Parse the argument |
---|
| | try: |
---|
| | |
---|
| | # Process ranges |
---|
| | |
---|
| | if val.count(RANGESEP): |
---|
| | |
---|
| | lhs, rhs = val.split(RANGESEP) |
---|
| | |
---|
| | |
---|
| | if not lhs: |
---|
| | lhs = None |
---|
| | |
---|
| | else: |
---|
| | lhs = int(lhs) |
---|
| | |
---|
| | if not rhs: |
---|
| | rhs = None |
---|
| | |
---|
| | else: |
---|
| | rhs = int(rhs) |
---|
| | |
---|
| | # Process single indexes |
---|
| | |
---|
| | else: |
---|
| | |
---|
| | lhs = int(val) |
---|
| | rhs = SINGLEINST |
---|
| | |
---|
| | # Something about the argument was bogus |
---|
| | |
---|
| | except: |
---|
| | ErrorMsg(eBADINSTANCE % val) |
---|
| | |
---|
| | ProgramOptions[INSTANCESTART] = lhs |
---|
| | ProgramOptions[INSTANCEEND] = rhs |
---|
| | ProgramOptions[INSTANCESTART], ProgramOptions[INSTANCEEND] = CheckSlice(val) |
---|
| | |
---|
| | # Set the escape character |
---|
| | |
---|
| | if opt == "-P": |
---|
| |
---|
| | if val: |
---|
| | ProgramOptions[EXISTSUFFIX] = val |
---|
| | else: |
---|
| | ErrorMsg(eNULLARG % NULLSUFFIX) |
---|
| | |
---|
| | # Set substring targeted for renaming |
---|
| | |
---|
| | if opt == "-T": |
---|
| | ProgramOptions[TARGETSTART], ProgramOptions[TARGETEND] = CheckSlice(val) |
---|
| | |
---|
| | # Request test mode |
---|
| | |
---|
| | if opt == "-t": |
---|
| |
---|
| | |