Files
gtav-src/tools_ng/bin/batchsub.py
T
2025-09-29 00:52:08 +02:00

233 lines
7.6 KiB
Python
Executable File

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 <system>" would match
// #include <system>
// #include <system>
// #include <system>
//
// 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<system> -> #include <system>
// will match
// #include <system>
// but not
// #include <system>
//
// 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('(?<!\w)%s(?!\w)' % before) # disallow matches in the middle of a word
sub = after
numNormal += 1
exprs.append((expr, sub))
if not Quiet:
print "Read %d expressions (%d normal, %d regexp)" % (len(exprs), numNormal, numRegexp)
return exprs
def BatchSubstituteFile(exprs, fileName):
global PerforceCommand, ForceWrite
if not Quiet:
print "Processing file ", fileName, "...",
inFile = file(fileName, "r")
inLines = inFile.readlines()
inFile.close()
outLines = []
totalSubs = 0
for line in inLines:
for (expr, sub) in exprs:
numSubs = 0
(line, numSubs) = expr.subn(sub, line)
totalSubs = totalSubs + numSubs
outLines.append(line)
p4string = PerforceCommand
if (totalSubs > 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] <substlist.txt> <fileglob>+")
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)