First cut at renaming target substring selection (-T). Still incomplete.
1 parent 5cf960d commit ae97270f12b1438f4a21d92de11ab1a2ded159b6
@tundra tundra authored on 26 Aug 2010
Showing 1 changed file
View
265
tren.py
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":