use 2to3 to make compatible with python3
1 parent 224c3f5 commit 9718d8b6e712d817fd3bfc4c59be2056d93c7d25
@tundra tundra authored on 25 Oct 2020
Showing 2 changed files
View
203
tren.py
#!/usr/bin/env python
# tren.py
# Copyright (c) 2010-2011 TundraWare Inc.
# Copyright (c) 2010-2020 TundraWare Inc.
# For Updates See: http://www.tundraware.com/Software/tren
 
# Program Information
 
 
# Copyright Information
 
CPRT = "(c)"
DATE = "2010-2011"
DATE = "2010-2020"
OWNER = "TundraWare Inc."
RIGHTS = "All Rights Reserved."
COPYRIGHT = "Copyright %s %s, %s %s" % (CPRT, DATE, OWNER, RIGHTS)
 
elif OSNAME == 'posix':
POSIX = True
 
# Set up Windows-specific stuff
 
if WINDOWS:
 
# Try to load win32all stuff if it's available
 
 
else:
sys.stderr.write("Unsupported Operating System! Aborting ...\n")
sys.exit(1)
 
 
#----------------------------------------------------------#
# Aliases & Redefinitions #
#----------------------------------------------------------#
 
# Rename target keys
 
BASE = "BASENAME"
PATHNAME = "PATHNAME"
PATHNAME = "PATHNAME"
STATS = "STATS"
 
# These literals serve two purposes:
#
't' : str.title,
'u' : str.upper
}
 
CASEOPS = CASETBL.keys()
CASEOPS = list(CASETBL.keys())
CASEOPS.sort()
 
 
# Day And Month Conversion Tables
 
 
DAYS = {0:"Mon", 1:"Tue", 2:"Wed", 3:"Thu", 4:"Fri", 5:"Sat", 6:"Sun"}
 
MONTHS = {1:"Jan", 2:"Feb", 3:"Mar", 4:"Apr", 5:"May", 6:"Jun",
7:"Jul", 8:"Aug", 9:"Sep", 10:"Oct", 11:"Nov", 12:"Dec"}
 
# File Time Renaming Token Lookup Table
 
ALPHABETS = {
 
BINARY : ["0", "1"],
 
DECIMAL : ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
 
HEXLOWER : ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"],
 
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,
TARGETEND : False,
TESTMODE : False # Test mode off
}
 
 
#####
 
class RenameTargets:
 
"""
"""
This class is used to keep track of all the files and/or
directories we're renaming. After the class is constructed
and the command line fully parsed, this will contain:
 
ORDERBYADATE-date... : [fullnames in order by atime within same 'date'] ... (repeated for each date),
ORDERBYCDATE-date... : [fullnames in order by ctime within same 'date'] ... (repeated for each date),
ORDERBYMDATE-date... : [fullnames in order by mtime within same 'date'] ... (repeated for each date)
}
 
self.RenRequests = [
{
ASK : interactive ask flag
BACKUPS : do backups during forced renaming flag,
[ST_MTIME, {}, ORDERBYMTIME],
[ST_NLINK, {}, ORDERBYNLINK],
[ST_SIZE, {}, ORDERBYSIZE],
[ST_UID, {}, ORDERBYUID],
["", {}, ORDERBYUSER],
["", {}, ORDERBYUSER],
]
 
# Populate the data structures with each targets' stat information
 
 
try:
pathname, basename = os.path.split(fullname)
stats = os.stat(fullname)
except (IOError, OSError), (errno, errstr):
except (IOError, OSError) as xxx_todo_changeme2:
(errno, errstr) = xxx_todo_changeme2.args
ErrorMsg(eFILEOPEN % (fullname, errstr))
 
# Some operating systems (Windows) terminate the path with
# a separator, some (Posix) do not.
 
if pathname[-1] != os.sep:
pathname += os.sep
 
# Store fullname, basename, and stat info for this file
 
if basename:
self.RenNames[fullname] = {BASE : basename, PATHNAME : pathname, STATS : stats}
 
# Catch the case where they're trying to rename the root of the directory tree
elif order == ORDERBYUSER:
sortkey = self.__GetFileUsername(fullname)
 
# Save into storage
 
if sortkey in storage:
storage[sortkey].append(fullname)
else:
storage[sortkey] = [fullname]
for seqtype in SeqTypes:
 
statflag, storage, order = seqtype
 
vieworder = storage.keys()
vieworder.sort()
vieworder = list(storage.keys())
vieworder.sort()
 
# Sort alphabetically when multiple filenames
# map to the same key, creating overall
# ordering as we go.
for dateorder, timeorder, year, mon, day in ((ORDERBYADATE, ORDERBYATIME,
FILETIMETOKS[TOKAYEAR],
FILETIMETOKS[TOKAMON],
FILETIMETOKS[TOKADAY]),
 
(ORDERBYCDATE, ORDERBYCTIME,
FILETIMETOKS[TOKCYEAR],
FILETIMETOKS[TOKCMON],
FILETIMETOKS[TOKCDAY]),
(ORDERBYMDATE, ORDERBYMTIME,
FILETIMETOKS[TOKMYEAR],
FILETIMETOKS[TOKMMON],
FILETIMETOKS[TOKMDAY])):
 
 
lastdate = ""
for fullname in self.SortViews[timeorder]:
 
if newdate != lastdate:
 
self.DateViews[key] = [fullname]
lastdate = newdate
 
# Add file to existing list of others sharing that date
 
else:
self.DateViews[key].append(fullname)
# End of '__init__()'
 
 
#####
# Debug Dump
# Debug Dump
#####
 
def DumpObj(self):
 
# Determine File's Group Name
#####
 
def __GetFileGroupname(self, fullname):
 
if POSIX:
return grp.getgrgid(self.RenNames[fullname][STATS][ST_GID])[0]
 
else:
 
retval = LookupAccountSid(fnhost, sidg)[0]
 
# On any error, just act like win32all isn't there
 
except:
retval = WINGROUPNOT
 
return retval
return pwd.getpwuid(self.RenNames[fullname][STATS][ST_UID])[0]
 
else:
retval = WINDOWSUSER
 
if WIN32ALL:
 
try:
 
 
retval = LookupAccountSid(fnhost, sido)[0]
 
# On any error, just act like win32all isn't there
 
except:
retval = WINUSERNOT
 
return retval
newname = 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 = ""
# 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(newname)
 
if tstart or tend:
 
bound = len(newname)
 
# Normalize negative refs so we can use consistent
# logic below
 
if tstart < 0:
if newname[tstart:tend]:
lname, newname, rname = newname[:tstart], newname[tstart:tend], newname[tend:]
 
else:
lname, newname, rname = newname, "", ""
lname, newname, rname = newname, "", ""
 
 
# Handle conventional string replacement renaming requests
# An empty newname here means that the -T argument processing
# selected a new string and/or was out of bounds -> we ignore the request.
 
# Find every instance of the 'old' string in the
# current filename. 'Find' in this case can be either
# a regular expression pattern match or a literal
# string match.
# string match.
#
# Either way, each 'hit' is recorded as a tuple:
#
# (index to beginning of hit, beginning of next non-hit text)
 
else:
 
searchtarget = newname
 
# Collapse case if requested
 
if not renrequest[CASESENSITIVE]:
 
searchtarget = searchtarget.lower()
old = old.lower()
newname = newname[:i[0]] + new + newname[i[1]:]
 
 
# Handle case conversion renaming requests
 
elif renrequest[CASECONV]:
newname = CASETBL[renrequest[CASECONV]](newname)
 
# Any subsequent replacements operate on the modified name
newname = lname + newname + rname
 
# Keep track of incremental renaming for use by debug
RenSequence.append(newname)
 
# Show the incremental renaming steps if debug is on
 
if ProgramOptions[DEBUG]:
DebugMsg(dRENSEQ % ARROW.join(RenSequence))
# Nothing to do, if old- and new names are the same
 
if newname != oldname:
self.__RenameIt(pathname, oldname, newname)
 
# End of 'ProcessRenameRequests()'
 
 
#####
 
self.indentlevel += 1
indent = self.indentlevel * INDENT
newlen = len(newname)
 
# First make sure the new name meets length constraints
 
if newlen < MINNAMELEN:
ErrorMsg(indent + eNAMESHORT% (oldname, newname, MINNAMELEN))
self.__RenameIt(pathname, newname, bkuname)
 
else:
InfoMsg(iFORCEDNOBKU % (fullold, fullnew))
 
else:
InfoMsg(indent + iRENSKIPPED % (fullnew, fullold))
doit = False
 
# A blank line means take the default - do nothing.
 
if not answer:
answer = ASKNO.lower()
 
if answer == ASKNO.lower():
doit = False
 
if answer == ASKYES.lower():
if answer == ASKQUIT.lower():
sys.exit(1)
 
if doit:
 
# In test mode, track file names that would be produced.
 
if ProgramOptions[TESTMODE]:
 
self.NewFiles.append(fullnew)
self.RenamedFiles.append(fullold)
 
if fullold in self.NewFiles:
self.NewFiles.remove(fullold)
 
if fullnew in self.RenamedFiles:
self.RenamedFiles.remove(fullnew)
 
InfoMsg(indent + iRENAMING % (fullold, fullnew))
if not ProgramOptions[TESTMODE]:
 
try:
os.rename(fullold, fullnew)
except OSError, (errno, errstr):
except OSError as xxx_todo_changeme:
(errno, errstr) = xxx_todo_changeme.args
ErrorMsg(eRENAMEFAIL % (fullold, fullnew, errstr))
 
self.indentlevel -= 1
 
# End of '__RenameIt()'
 
 
#####
# Resolve Rename Tokens
#####
 
 
for r in rentokens:
 
fullrentoken = "%s%s%s" % (TOKDELIM, r[2], TOKDELIM) # Need this for error messages.
 
###
# File Attribute Renaming Tokens
###
 
if r[2] == TOKFILDEV:
r[2] = str(self.RenNames[target][STATS][ST_DEV])
 
elif r[2] == TOKFILFNAME:
r[2] = os.path.basename(target)
 
elif r[2] == TOKFILGID:
r[2] = str(self.RenNames[target][STATS][ST_UID])
 
elif r[2] == TOKFILUSER:
r[2] = self.__GetFileUsername(target)
 
 
###
# File Time Renaming Tokens
###
r[2] = MONTHS[val]
 
elif parms[2] == "tm_wday":
r[2] = DAYS[val]
 
###
# System Renaming Tokens
###
 
# Environment Variable replacement token
 
elif r[2].startswith(TOKENV):
 
r[2] = os.getenv(r[2][1:])
 
# Handle case for nonexistent environment variable
 
if not r[2]:
r[2] = ""
 
 
# Command Run replacement token
 
elif r[2].startswith(TOKCMDEXEC) and r[2].endswith(TOKCMDEXEC):
 
pipe = os.popen(command, 'r')
 
# Handle Unix variants
 
else:
else:
pipe = os.popen('{ ' + command + '; } 2>&1', 'r')
 
output = pipe.read()
status = pipe.close()
if status == None:
status = 0
 
# Nonzero status means error attempting to execute the command
 
if status:
ErrorMsg(eEXECFAIL % (fullrentoken, command))
 
# Otherwise swap the command with its results, stripping newlines
 
else:
r[2] = output.replace("\n", "")
 
 
# Random Number Replacement token
 
elif r[2].startswith(TOKRAND):
 
random.seed()
 
ErrorMsg(eTOKRANDIG % fullrentoken)
 
if precision < 1:
ErrorMsg(eTOKRANDIG % fullrentoken)
 
fmt = '"%0' + str(precision) + 'd" % random.randint(0, pow(10, precision)-1)'
r[2] = eval(fmt)
 
# Name So Far Replacement Token
 
###
# Sequence Renaming Tokens
###
 
elif r[2] and (r[2][0] == TOKASCEND or r[2][0] == TOKDESCEND):
 
# Parse the Sequence Renaming Token into the token itself
# and its corresponding formatting field.
 
token = r[2][1:]
 
found = False
for seqtoken in self.SortViews.keys() + [ORDERBYADATE, ORDERBYCDATE, ORDERBYMDATE]:
for seqtoken in list(self.SortViews.keys()) + [ORDERBYADATE, ORDERBYCDATE, ORDERBYMDATE]:
 
if token.split(ALPHADELIM)[0] == (seqtoken):
 
token, field = token[:len(seqtoken)], token[len(seqtoken):]
if not alphadelim:
ErrorMsg(eALPHABETMISSING % fullrentoken)
 
# Empty alphabet string means default to decimal counting
 
if not alphabet:
alphabet = DECIMAL
 
if alphabet not in ALPHABETS:
ErrorMsg(eALPHABETEXIST % fullrentoken)
 
 
# (which is just the index of that filename in the
# list).
 
# One of the standard sorted views requested
 
if token in self.SortViews:
orderedlist = self.SortViews[token][:]
 
# One of the views sorted within dates requested
 
###
# Unrecognized Renaming Token
###
 
else:
ErrorMsg(eTOKUNKNOWN % fullrentoken)
 
###
# Successful Lookup, Do the actual replacement
###
 
renstring = renstring[:r[0]] + r[2] + renstring[r[1]+1:]
 
return renstring
 
# End of '__ResolveRenameTokens()'
 
 
# End of class 'RenameTargets'
 
 
#----------------------------------------------------------#
# Supporting Function Definitions #
#----------------------------------------------------------#
 
 
def ComputeSeqString(fmt, incr, alphabet):
 
"""
"""
fmt = A literal "format field" string
incr = A integer to be "added" to the field
alphabet = The alphabet of characters to use, in ascending order
 
result longer than fmt has MSD dropped, thereby effectively
rolling over the count. If 'fmt' is null on entry, the final
result length is unlimited.
"""
 
base = len(alphabet)
 
# Do position-wise "addition" via symbol substitution moving from
# right-to-left adjusting for the fact that not all symbols in the
# format string will be in the alphabet.
 
 
# First convert the increment into a string in the base of the
# alphabet
 
if abs(i) <= incrlen:
sum += alphabet.index(incr[i])
 
# Do arithmetic modulo alphabet length
 
carry, digit = sum/base, sum % base
 
if not carry:
carry = None
 
# Result length constrained by format string
 
if fmtlen:
 
if len(newval) > fmtlen:
InfoMsg(iSEQTOOLONG % (newval,fmt))
newval = newval[-fmtlen:]
 
#####
# Condition Line Length With Fancy Wrap And Formatting
#####
 
def ConditionLine(msg,
def ConditionLine(msg,
PAD=PADCHAR, \
WIDTH=PADWIDTH, \
wrapindent=WRAPINDENT ):
 
# Print A Debug Message
#####
 
def DebugMsg(msg):
 
l = ConditionLine(msg)
for msg in l:
PrintStderr(PROGNAME + " " + dDEBUG + ": " + msg)
 
DebugMsg(SEPARATOR)
DebugMsg(dCURSTATE)
DebugMsg(SEPARATOR)
 
opts = ProgramOptions.keys()
opts = list(ProgramOptions.keys())
opts.sort()
for o in opts:
DebugMsg(ColumnPad([o, ProgramOptions[o]]))
 
def GetOldNew(arg):
 
 
escaping = False
numseps = 0
numseps = 0
sepindex = 0
oldnewsep = ProgramOptions[RENSEP]
 
i = 0
 
if (i > 0 and (arg[i-1] != ProgramOptions[ESCAPE])) or i == 0:
sepindex = i
numseps += 1
 
i += len(oldnewsep)
 
else:
i += 1
# contents at that position in the command line.
 
field = OPTIONS[i]
position = field.find(INCL)
 
if field.startswith(OPTINTRO) and (position > -1):
 
 
lhs = field[:position]
rhs = field[position+1:]
 
# Make sure the include symbol isn't part of some
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 = ""
 
n = [lhs] + n
 
OPTIONS = OPTIONS[:i] + n + OPTIONS[i+1:]
 
except IOError, (errno, errstr):
except IOError as xxx_todo_changeme1:
(errno, errstr) = xxx_todo_changeme1.args
ErrorMsg(eFILEOPEN % (inclfile, errstr))
 
i += 1
 
return OPTIONS
 
 
def searchpath(filename, pathlist, pathdelim):
 
# What we'll return if we find nothing
 
retval = []
 
# Find all instances of filename in specified paths
 
# Program Entry Point #
#----------------------------------------------------------#
 
# Set up proper include path delimiter
 
 
if WINDOWS:
PATHDEL = PATHDELWIN
 
 
 
#####
# Command Line Preprocessing
#
#
# Some things have to be done *before* the command line
# options can actually be processed. This includes:
#
# 1) Prepending any options specified in the environment variable.
# And parse the command line
 
try:
opts, args = getopt.getopt(OPTIONS, OPTIONSLIST)
except getopt.GetoptError, (errmsg, badarg):
except getopt.GetoptError as xxx_todo_changeme3:
(errmsg, badarg) = xxx_todo_changeme3.args
ErrorMsg(eBADARG % errmsg)
 
# Create and populate an object with rename targets. This must be
# done here because this object also stores the -r renaming requests
if opt == "-b":
ProgramOptions[BACKUPS] = False
 
# Select case-sensitivity for replacements (or not)
 
if opt == "-C":
ProgramOptions[CASESENSITIVE] = True
 
if opt == "-c":
ProgramOptions[DEBUG] = True
DumpState()
 
# Force case conversion
 
if opt == "-e":
 
# Make sure we support the requested case conversion
if val in CASEOPS:
 
ProgramOptions[CASECONV] = val
 
# Construct a renaming request
 
req = {}
req[OLD], req[NEW] = None, None
for opt in ProgramOptions:
req[opt] = ProgramOptions[opt]
 
# Error out if we don't recognize it
else:
ErrorMsg(eBADCASECONV % (val, ", ".join(CASEOPS)))
 
 
# Force renaming of existing targets
 
if opt == "-f":
else:
ErrorMsg(eARGLENGTH % (NULLESC, 1))
 
# Set quiet mode
 
if opt == "-q":
ProgramOptions[QUIET] = True
 
# Set the separator character for replacement specifications
# Release the target container if we created one
 
del targs
View
0
■■■■■
tren2.py 0 → 100755
Too large (Show diff)