| |
---|
| | |
---|
| | PROGNAME = "tren.py" |
---|
| | BASENAME = PROGNAME.split(".py")[0] |
---|
| | PROGENV = BASENAME.upper() |
---|
| | RCSID = "$Id: tren.py,v 1.152 2010/02/17 16:31:35 tundra Exp $" |
---|
| | RCSID = "$Id: tren.py,v 1.153 2010/02/17 20:07:53 tundra Exp $" |
---|
| | VERSION = RCSID.split()[2] |
---|
| | |
---|
| | # Copyright Information |
---|
| | |
---|
| |
---|
| | ESCAPE = "ESCAPE" |
---|
| | ERRORCONTINUE = "ERRORCONTINUE" |
---|
| | EXISTSUFFIX = "EXISTSUFFIX" |
---|
| | EXTDELIM = "EXTDELIM" |
---|
| | FORCERENAM = "FORCERENAM" |
---|
| | FORCERENAME = "FORCERENAME" |
---|
| | GLOBAL = "GLOBAL" |
---|
| | MAXLINELEN = "MAXLINELEN" |
---|
| | QUIET = "QUIET" |
---|
| | REGEX = "REGEX" |
---|
| |
---|
| | eFILEOPEN = "Cannot open file '%s': %s!" |
---|
| | eLINELEN = "Specified line length too short! Must be at least %s" % MINLEN |
---|
| | eNAMELONG = "Renaming '%s' to new name '%s' too long! (Maximum length is %s.)" |
---|
| | eNAMESHORT = "Renaming '%s' to new name '%s' too short! (Minimum length is %s.)" |
---|
| | eNOTHINGTODO = "Nothing to do!" |
---|
| | eNULLARG = "%s cannot be empty!" |
---|
| | eTOOMANYINC = "Too many includes! (Max is %d) Possible circular reference?" % MAXINCLUDES |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Informational Messages |
---|
| | ##### |
---|
| | |
---|
| | iFORCEDREN = "Forced Rename:" |
---|
| | iRENAME = "Rename:" |
---|
| | iSKIPPING = "Target Exists, Rename Skipped:" |
---|
| | iNEWOLDSAME = "New Name '%s' Same As The Old Name. %s" |
---|
| | iNOTHINGTODO = "Nothing to do." |
---|
| | iRENFORCED = "Target '%s' Exists. Creating Backup." |
---|
| | iRENSKIPPED = "Target '%s' Exists. Renaming Of '%s' Skipped." |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Usage Prompts |
---|
| |
---|
| | "usage: " + PROGNAME + " [-abCcdEefGghqtvwXx] [-I file] [-L string] [-r old=new]... [-S suffix] file|dir file|dir ...", |
---|
| | " where,", |
---|
| | " -a Rename within the entire file or directory name (Default)", |
---|
| | " -C Do case-sensitive renaming (Default)", |
---|
| | " -c Collapse case when doing string substitution", |
---|
| | " -d Dump debugging information", |
---|
| | " -e Only perform renaming within extension portion of or directory name", |
---|
| | " -E Continue renaming even after an error is encountered", |
---|
| | " -f Force renaming even if target file or directory name already exists", |
---|
| | " -c Collapse case when doing string substitution (Default: False)", |
---|
| | " -d Dump debugging information (Default: False)", |
---|
| | " -e Only perform renaming within extension portion of or directory name (Default: False)", |
---|
| | " -E Continue after an error is encountered (Default: False)", |
---|
| | " -f Force renaming even if target file or directory name already exists (Default: False)", |
---|
| | " -G Rename only the first instance of the specified string (Default)", |
---|
| | " -g Replace all instances (global rename) of the old string with the new", |
---|
| | " -h Print help information", |
---|
| | " -g Replace all instances (global rename) of the old string with the new (Default: False)", |
---|
| | " -h Print help information (Default: False)", |
---|
| | " -I file Include command line arguments from file", |
---|
| | " -L string File extension delimiter string (Default: %s)" % DEFEXT, |
---|
| | " -P char Use 'char' as the escape sequence (Default: %s)" % DEFESC, |
---|
| | " -q Quiet mode, do not show progress", |
---|
| | " -q Quiet mode, do not show progress (Default: False)", |
---|
| | " -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", |
---|
| | " -v Print detailed program version information and continue", |
---|
| | " -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, |
---|
| | " -X Treat the renaming strings literally (Default)", |
---|
| | " -x Treat the old replacement string as a Python regular expression", |
---|
| | " -x Treat the old replacement string as a Python regular expression (Default: False)", |
---|
| | ] |
---|
| | |
---|
| | #----------------------------------------------------------# |
---|
| | # Global Variables & Data Structures # |
---|
| |
---|
| | ESCAPE : DEFESC, # Escape string |
---|
| | ERRORCONTINUE : False, # Do not continue after error |
---|
| | EXISTSUFFIX : DEFSUFFIX, # What to tack on when renaming existing targets |
---|
| | EXTDELIM : DEFEXT, # Name/Extension delimiter |
---|
| | FORCERENAM : False, # Do not rename if target already exists |
---|
| | FORCERENAME : False, # Do not rename if target already exists |
---|
| | GLOBAL : False, # Only rename first instance of old string |
---|
| | MAXLINELEN : DEFLEN, # Width of output messages |
---|
| | QUIET : False, # Display progress |
---|
| | REGEX : False, # Do not treat old string as a regex |
---|
| |
---|
| | DEBUG : debug flag, |
---|
| | CASESENSITIVE : case sensitivity flag, |
---|
| | ERRORCONTINUE : error continuation flag, |
---|
| | EXTDELIM : name/Extension delimiter string, |
---|
| | FORCERENAM : force renaming flag, |
---|
| | FORCERENAME : force renaming flag, |
---|
| | GLOBAL : global replace flag, |
---|
| | MAXLINELEN : max output line length, |
---|
| | QUIET : quiet output flag, |
---|
| | REGEX : regular expression enable flag, |
---|
| |
---|
| | ##### |
---|
| | |
---|
| | def Rename(self): |
---|
| | |
---|
| | |
---|
| | # Make sure we actually have work to do |
---|
| | |
---|
| | if not self.SortViews[ORDERBYCMDLINE] or not self.RenRequests: |
---|
| | |
---|
| | ErrorMsg(eNOTHINGTODO) |
---|
| | return |
---|
| | |
---|
| | # Create a list of all renaming to be done. |
---|
| | # This includes the renaming of any existing targets. |
---|
| | |
---|
| | |
---|
| | renamelist = [] |
---|
| | for target in self.SortViews[ORDERBYCMDLINE]: |
---|
| | |
---|
| | basename, pathname = self.RenNames[target][BASE], self.RenNames[target][PATHNAME] |
---|
| | oldname, pathname = self.RenNames[target][BASE], self.RenNames[target][PATHNAME] |
---|
| | newname = oldname |
---|
| | |
---|
| | for renrequest in self.RenRequests: |
---|
| | |
---|
| | old, new = self.ResolveRenameStrings(renrequest[OLD], renrequest[NEW]) |
---|
| | oldstrings = [] |
---|
| |
---|
| | # Handle the case when old = "". |
---|
| | # This means to *replace the entire* old name with new. |
---|
| | |
---|
| | if not old: |
---|
| | old = basename |
---|
| | old = oldname |
---|
| | |
---|
| | # Collapse case if requested |
---|
| | |
---|
| | name = basename |
---|
| | name = oldname |
---|
| | if not renrequest[CASESENSITIVE]: |
---|
| | |
---|
| | name = name.lower() |
---|
| | old = old.lower() |
---|
| |
---|
| | # so as not to mess up the replacement indicies. |
---|
| | |
---|
| | oldstrings.reverse() |
---|
| | for i in oldstrings: |
---|
| | basename = basename[:i] + new + basename[i + len(old):] |
---|
| | |
---|
| | # Now that we have a new name, attempt the actual renaming |
---|
| | # operation |
---|
| | |
---|
| | # First make sure the new name meets length constraints |
---|
| | |
---|
| | RenamingError = False |
---|
| | newname = newname[:i] + new + newname[i + len(old):] |
---|
| | |
---|
| | # If the new name is different from the old one, add |
---|
| | # result to renaming table in the format: |
---|
| | # |
---|
| | # (path, old name, new name) |
---|
| | |
---|
| | |
---|
| | if newname != oldname: |
---|
| | renamelist.append((pathname, oldname, newname)) |
---|
| | |
---|
| | else: |
---|
| | InfoMsg(iNEWOLDSAME % newname) |
---|
| | |
---|
| | # Make sure we actually have something to do |
---|
| | |
---|
| | if not renamelist: |
---|
| | |
---|
| | InfoMsg(iNOTHINGTODO) |
---|
| | return |
---|
| | |
---|
| | # Iterate over the list of renamings |
---|
| | |
---|
| | newnames = [] |
---|
| | for pathname, oldname, newname in renamelist: |
---|
| | |
---|
| | # Get names into absolute path form |
---|
| | |
---|
| | oldname = pathname + oldname |
---|
| | newname = pathname + newname |
---|
| | |
---|
| | # See if our proposed renaming is about to stomp on an |
---|
| | # existing file, and create a backup if forced renaming |
---|
| | # requested. |
---|
| | |
---|
| | # NOTE: We have to keep track of every new name produced. |
---|
| | # When running in test mode, this is the only way to know |
---|
| | # what *would* end up on the disk (in case a previous |
---|
| | # renaming operation creates a filename that is now in |
---|
| | # conflict with a subseqent renaming target. |
---|
| | |
---|
| | if (newname in newnames) or os.path.exists(newname): |
---|
| | |
---|
| | if ProgramOptions[FORCERENAME]: |
---|
| | |
---|
| | # Create the backup |
---|
| | |
---|
| | bkuname = newname + ProgramOptions[EXISTSUFFIX] |
---|
| | newnames.append(bkuname) |
---|
| | InfoMsg(iRENFORCED % newname) |
---|
| | self.RenameIt(newname, bkuname) |
---|
| | |
---|
| | # Rename the original |
---|
| | |
---|
| | newnames.append(newname) |
---|
| | self.RenameIt(oldname, newname) |
---|
| | |
---|
| | |
---|
| | else: |
---|
| | InfoMsg(iRENSKIPPED % (newname, oldname)) |
---|
| | |
---|
| | # No target conflict, just do the requested renaming |
---|
| | |
---|
| | else: |
---|
| | |
---|
| | newnames.append(newname) |
---|
| | self.RenameIt(oldname, newname) |
---|
| | |
---|
| | # End of 'Rename()' |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Actually Rename A File (Or Show What Would Happen) |
---|
| | ##### |
---|
| | |
---|
| | def RenameIt(self, oldname, newname): |
---|
| | return |
---|
| | |
---|
| | # End of 'RenameIt()' |
---|
| | |
---|
| | """ |
---|
| | # First make sure the new name meets length constraints |
---|
| | |
---|
| | RenamingError = False |
---|
| | if len(basename) < MINNAMELEN: |
---|
| | ErrorMsg(eNAMESHORT% (target, basename, MINNAMELEN), EXIT=not ProgramOptions[ERRORCONTINUE]) |
---|
| | RenamingError = True |
---|
| | |
---|
| | if len(basename) > MAXNAMELEN: |
---|
| | ErrorMsg(eNAMELONG % (target, basename, MAXNAMELEN), EXIT=not ProgramOptions[ERRORCONTINUE]) |
---|
| | RenamingError = True |
---|
| | |
---|
| | newname = pathname + basename |
---|
| | if (target != newname) and not RenamingError: |
---|
| | |
---|
| | renamejobs = [(target, newname)] |
---|
| | renaming = " %s %s %s %s" % (iRENAME, renamejobs[0][0], ARROW, renamejobs[0][1]) |
---|
| | |
---|
| | # Figure out if the newname already exists |
---|
| | |
---|
| | newexists = False |
---|
| | if os.path.exists(newname): |
---|
| | newexists = True |
---|
| | renameexisting = newname + ProgramOptions[EXISTSUFFIX] |
---|
| | renamejobs.append((newname, renameexisting)) |
---|
| | forcedren = " %s %s %s %s" % (iFORCEDREN, renamejobs[1][0], ARROW, renamejobs[1][1]) |
---|
| | skipping = " %s %s %s %s" % (iSKIPPING, renamejobs[0][0], ARROW, renamejobs[0][1]) |
---|
| | |
---|
| | # In test mode, just show what we would do |
---|
| | |
---|
| | if ProgramOptions[TESTMODE]: |
---|
| | |
---|
| | if newexists: |
---|
| | |
---|
| | # Forced renaming of existing targets |
---|
| | |
---|
| | if ProgramOptions[FORCERENAM]: |
---|
| | InfoMsg(forcedren, TESTMODE) |
---|
| | |
---|
| | # Skip renaming if target already exists |
---|
| | |
---|
| | else: |
---|
| | InfoMsg(skipping, TESTMODE) |
---|
| | |
---|
| | # Target does not exist or forced renaming is in effect, do the rename |
---|
| | |
---|
| | if not newexists or ProgramOptions[FORCERENAM]: |
---|
| | InfoMsg(renaming, TESTMODE) |
---|
| | |
---|
| | # Actually do the renaming |
---|
| | else: |
---|
| | print target + "---->"+ newname |
---|
| | |
---|
| | # End of 'Rename()' |
---|
| | |
---|
| | """ |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Resolve Rename Strings |
---|
| |
---|
| | |
---|
| | |
---|
| | return [old, new] |
---|
| | |
---|
| | # End of 'ReolveRenameStrings()' |
---|
| | # End of 'ResolveRenameStrings()' |
---|
| | |
---|
| | |
---|
| | # End of class 'RenameTargets' |
---|
| | |
---|
| |
---|
| | # End of 'DumpState()' |
---|
| | |
---|
| | |
---|
| | ##### |
---|
| | # Print An Error Message |
---|
| | # Print An Error Message, Exiting Program As Required |
---|
| | ##### |
---|
| | |
---|
| | def ErrorMsg(emsg, EXIT=False): |
---|
| | |
---|
| |
---|
| | |
---|
| | for emsg in l: |
---|
| | PrintStderr(PROGNAME + " " + eERROR + ": " + emsg) |
---|
| | |
---|
| | if EXIT: |
---|
| | if EXIT or not ProgramOptions[ERRORCONTINUE]: |
---|
| | sys.exit(1) |
---|
| | |
---|
| | # End of 'ErrorMsg()' |
---|
| | |
---|
| |
---|
| | ##### |
---|
| | # Print An Informational Message |
---|
| | ##### |
---|
| | |
---|
| | def InfoMsg(dmsg, msgtype): |
---|
| | |
---|
| | l = ConditionLine(dmsg) |
---|
| | |
---|
| | for dmsg in l: |
---|
| | PrintStdout(PROGNAME + " " + msgtype + ": " + dmsg) |
---|
| | def InfoMsg(imsg): |
---|
| | |
---|
| | l = ConditionLine(imsg) |
---|
| | |
---|
| | msgtype = "" |
---|
| | if ProgramOptions[TESTMODE]: |
---|
| | msgtype = TESTMODE |
---|
| | |
---|
| | if not ProgramOptions[QUIET]: |
---|
| | for dmsg in l: |
---|
| | PrintStdout(PROGNAME + " " + msgtype + ": " + imsg) |
---|
| | |
---|
| | # End of 'InfoMsg()' |
---|
| | |
---|
| | |
---|
| |
---|
| | if opt == "-e": |
---|
| | ProgramOptions[TARGET] = EXT |
---|
| | |
---|
| | if opt == "-f": |
---|
| | ProgramOptions[FORCERENAM] = True |
---|
| | ProgramOptions[FORCERENAME] = True |
---|
| | |
---|
| | if opt == "-G": |
---|
| | ProgramOptions[GLOBAL] = False |
---|
| | |
---|
| |
---|
| | |