import re import os import sys import glob import optparse import stat SyntaxHelp = """ ///////////////////////////////////////////////// // // Usage: // Old New // Will convert any string "Old" to "New" provided that // "Old" is _not_ preceeded or succeeded by an identifier character // (A-Z, a-z, 0-9, _) // // In other words: // Old becomes New // Old() New() // OldStuff OldStuff // a+Old a+New // OldOld OldOld // Old Old New New // // C++ style comments like these will be ignored // // If you want to delete the Old string, use "***NULL***" as the New string: // OBSOLETE_ENUM ***NULL*** // (replaces OBSOLETE_ENUM with an empty string) // // If you need spaces in the Old or New strings, use a // "->" to seperate the old and new // #include "core/stream.h" -> #include "file/stream.h" // // whitespace in the Old string will match any amount of whitespace in // the file that's being modified. So "#include " would match // #include // #include // #include // // Also, both "Old" and "New" can be python (and perl, and sed) style // regular expressions if you begin the line with *REGEX* // // So if you want to match exactly one whitespace character you could use // the whitespace matching token \s. // *REGEX* #include\s -> #include // will match // #include // but not // #include // // Characters to watch out for: [ ] ( ) \ ? + * . // These all have special meaning for regular expressions, // you may need to backslash escape them to make them work. // They will work normally on lines that don't begin with *REGEX* """ PerforceCommand = None ForceWrite = False Quiet = False def CreateSubstitutionList(substFileName): if (substFileName == "-"): print "Enter substitutions below.\nType ^Z (Ctrl-Z) + return, or a '.' on its own line to stop" substs = sys.stdin else: print "Reading substitutions from ", substFileName substs = file(substFileName, "r") exprs = [] lineNo = 0 numNormal = 0 numRegexp = 0 while substs: line = substs.readline() if not line: break lineNo = lineNo + 1 strippedLine = line.strip() if len(strippedLine) == 0: continue if strippedLine[0] == "/" and strippedLine[1] == '/': continue if substs == sys.stdin and len(strippedLine) == 1 and strippedLine[0] == ".": break words = line.split() before = "" after = "" if len(words) < 2: print "Error: line %d, not enough tokens: %s" % (lineNo, line) regex = 0 if words[0] == "*REGEX*": regex = 1 words = words[1:] line = line.replace("*REGEX*", "", 1) strippedLine = strippedLine.replace("*REGEX*", "", 1) strippedLine = strippedLine.strip() if len(words) == 2: before = words[0] after = words[1] else: # check for special conversion token -> if "->" in words: wsWords = strippedLine.split("->") if len(wsWords) != 2: print "Error: line %d, too many tokens: %s" % (lineNo, line) continue else: before = wsWords[0].strip() after = wsWords[1].strip() else: print "Error: line %d, too many tokens: %s" % (lineNo, line) continue if after == "***NULL***": after = "" if regex: expr = re.compile(before) sub = after numRegexp += 1 else: before = re.escape(before) before = re.sub("(\\\\\s)+", "\\s+", before) # turns all runs of spaces in the before string into \s+ after = re.sub("\\\\", "\\\\\\\\", after) # turns all \s in the after string into \\s expr = re.compile('(? 0): if p4string: os.system(p4string + " " + fileName) if ForceWrite: os.chmod(fileName, stat.S_IWRITE) try: outFile = file(fileName, "w") for line in outLines: outFile.write(line) except: print sys.stderr.write("Error: Couldn't write to file " + fileName + "\n") return if Quiet: if totalSubs > 0: print "Processing file ", fileName, "...", totalSubs, "substitutions made." else: print totalSubs, "substitutions made." def FindFiles(globs): fileList = [] for i in globs: fileList += (glob.glob(i)) return fileList if __name__ == "__main__": opt = optparse.OptionParser(usage="Usage: %prog [options] +") opt.add_option("-r", "--recurse", dest="recurse", action="store_true", default=False, help="Apply substitutions to all subdirectories under the current directory") opt.add_option("-p", "--perforce", dest="perforce", action="store_true", default=False, help="Check files out from perforce before modifying") opt.add_option("-f", "--force", dest="force", action="store_true", default=False, help="Make files writable if they are read only") opt.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False, help="Only print when substitutions are made") opt.add_option("--syntax", dest="print_syntax", action="store_true", default=False, help="Print a description of the substitution list syntax") (options, outargs) = opt.parse_args(sys.argv[1:]) if options.print_syntax: print SyntaxHelp sys.exit(1) if len(outargs) < 2: opt.print_help() print "If substlist.txt is '-', reads from stdin" sys.exit(1) scriptName = outargs[0] globs = outargs[1:] if options.perforce: PerforceCommand = "p4 edit" ForceWrite = options.force Quiet = options.quiet exprs = CreateSubstitutionList(scriptName) if options.recurse: origDir = os.getcwd() for (dirName, unused1, unused2) in os.walk(os.getcwd()): if not Quiet: print "Checking directory", dirName os.chdir(dirName) for f in FindFiles(globs): BatchSubstituteFile(exprs, f) os.chdir(origDir) else: for f in FindFiles(globs): BatchSubstituteFile(exprs, f)