From aa895c752b17e5c6b57f9061c9a4ed04cf01bf7b Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Thu, 10 Jun 1993 14:43:53 +0000 Subject: Initial revision --- Tools/scripts/fixcid.py | 303 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100755 Tools/scripts/fixcid.py diff --git a/Tools/scripts/fixcid.py b/Tools/scripts/fixcid.py new file mode 100755 index 0000000..4f72e9e --- /dev/null +++ b/Tools/scripts/fixcid.py @@ -0,0 +1,303 @@ +#! /ufs/guido/bin/sgi/python +#! /usr/local/bin/python + +# Perform massive identifier substitution on C source files. +# This actually tokenizes the files (to some extent) so it can +# avoid making substitutions inside strings or comments. +# Inside strings, substitutions are never made; inside comments, +# it is a user option (on by default). +# +# The substitutions are read from one or more files whose lines, +# when not empty, after stripping comments starting with #, +# must contain exactly two words separated by whitespace: the +# old identifier and its replacement. +# +# The option -r reverses the sense of the substitutions (this may be +# useful to undo a particular substitution). +# +# If the old identifier is prefixed with a '*' (with no intervening +# whitespace), then it will not be substituted inside comments. +# +# Command line arguments are files or directories to be processed. +# Directories are searched recursively for files whose name looks +# like a C file (ends in .h or .c). The special filename '-' means +# operate in filter mode: read stdin, write stdout. +# +# Symbolic links are always ignored (except as explicit directory +# arguments). +# +# The original files are kept as back-up with a "~" suffix. +# +# Changes made are reported to stdout in a diff-like format. +# +# NB: by changing only the function fixline() you can turn this +# into a program for different changes to C source files; by +# changing the function wanted() you can make a different selection of +# files. + +import sys +import regex +import string +import os +from stat import * +import getopt + +err = sys.stderr.write +dbg = err +rep = sys.stdout.write + +def usage(): + err('Usage: ' + sys.argv[0] + \ + ' [-r] [-s file] ... file-or-directory ...\n') + err('\n') + err('-r : reverse direction for following -s options\n') + err('-s substfile : add a file of substitutions\n') + err('\n') + err('Each non-empty non-comment line in a substitution file must\n') + err('contain exactly two words: an identifier and its replacement.\n') + err('Comments start with a # character and end at end of line.\n') + +def main(): + try: + opts, args = getopt.getopt(sys.argv[1:], 'rs:') + except getopt.error, msg: + err('Options error: ' + str(msg) + '\n') + usage() + sys.exit(2) + bad = 0 + if not args: # No arguments + usage() + sys.exit(2) + for opt, arg in opts: + if opt == '-r': + setreverse() + if opt == '-s': + addsubst(arg) + for arg in args: + if os.path.isdir(arg): + if recursedown(arg): bad = 1 + elif os.path.islink(arg): + err(arg + ': will not process symbolic links\n') + bad = 1 + else: + if fix(arg): bad = 1 + sys.exit(bad) + +# Change this regular expression to select a different set of files +Wanted = '^[a-zA-Z0-9_]+\.[ch]$' +def wanted(name): + return regex.match(Wanted, name) >= 0 + +def recursedown(dirname): + dbg('recursedown(' + `dirname` + ')\n') + bad = 0 + try: + names = os.listdir(dirname) + except os.error, msg: + err(dirname + ': cannot list directory: ' + str(msg) + '\n') + return 1 + names.sort() + subdirs = [] + for name in names: + if name in (os.curdir, os.pardir): continue + fullname = os.path.join(dirname, name) + if os.path.islink(fullname): pass + elif os.path.isdir(fullname): + subdirs.append(fullname) + elif wanted(name): + if fix(fullname): bad = 1 + for fullname in subdirs: + if recursedown(fullname): bad = 1 + return bad + +def fix(filename): +## dbg('fix(' + `filename` + ')\n') + if filename == '-': + # Filter mode + f = sys.stdin + g = sys.stdout + else: + # File replacement mode + try: + f = open(filename, 'r') + except IOError, msg: + err(filename + ': cannot open: ' + str(msg) + '\n') + return 1 + head, tail = os.path.split(filename) + tempname = os.path.join(head, '@' + tail) + g = None + # If we find a match, we rewind the file and start over but + # now copy everything to a temp file. + lineno = 0 + initfixline() + while 1: + line = f.readline() + if not line: break + lineno = lineno + 1 + while line[-2:] == '\\\n': + nextline = f.readline() + if not nextline: break + line = line + nextline + lineno = lineno + 1 + newline = fixline(line) + if newline != line: + if g is None: + try: + g = open(tempname, 'w') + except IOError, msg: + f.close() + err(tempname+': cannot create: '+\ + str(msg)+'\n') + return 1 + f.seek(0) + lineno = 0 + initfixline() + rep(filename + ':\n') + continue # restart from the beginning + rep(`lineno` + '\n') + rep('< ' + line) + rep('> ' + newline) + if g is not None: + g.write(newline) + + # End of file + if filename == '-': return 0 # Done in filter mode + f.close() + if not g: return 0 # No changes + + # Finishing touch -- move files + + # First copy the file's mode to the temp file + try: + statbuf = os.stat(filename) + os.chmod(tempname, statbuf[ST_MODE] & 07777) + except os.error, msg: + err(tempname + ': warning: chmod failed (' + str(msg) + ')\n') + # Then make a backup of the original file as filename~ + try: + os.rename(filename, filename + '~') + except os.error, msg: + err(filename + ': warning: backup failed (' + str(msg) + ')\n') + # Now move the temp file to the original file + try: + os.rename(tempname, filename) + except os.error, msg: + err(filename + ': rename failed (' + str(msg) + ')\n') + return 1 + # Return succes + return 0 + +# Tokenizing ANSI C (partly) + +Identifier = '[a-zA-Z_][a-zA-Z0-9_]+' +String = '"\([^\n\\"]\|\\\\.\)*"' +Char = '\'\([^\n\\\']\|\\\\.\)*\'' +CommentStart = '/\*' +CommentEnd = '\*/' + +Hexnumber = '0[xX][0-9a-fA-F]*[uUlL]*' +Octnumber = '0[0-7]*[uUlL]*' +Decnumber = '[1-9][0-9]*[uUlL]*' +Intnumber = Hexnumber + '\|' + Octnumber + '\|' + Decnumber +Exponent = '[eE][-+]?[0-9]+' +Pointfloat = '\([0-9]+\.[0-9]*\|\.[0-9]+\)\(' + Exponent + '\)?' +Expfloat = '[0-9]+' + Exponent +Floatnumber = Pointfloat + '\|' + Expfloat +Number = Floatnumber + '\|' + Intnumber + +# Anything else is an operator -- don't list this explicitly because of '/*' + +OutsideComment = (Identifier, Number, String, Char, CommentStart) +OutsideCommentPattern = '\(' + string.joinfields(OutsideComment, '\|') + '\)' +OutsideCommentProgram = regex.compile(OutsideCommentPattern) + +InsideComment = (Identifier, Number, CommentEnd) +InsideCommentPattern = '\(' + string.joinfields(InsideComment, '\|') + '\)' +InsideCommentProgram = regex.compile(InsideCommentPattern) + +def initfixline(): + global Program + Program = OutsideCommentProgram + +def fixline(line): + global Program +## print '-->', `line` + i = 0 + while i < len(line): + i = Program.search(line, i) + if i < 0: break + found = Program.group(0) +## if Program is InsideCommentProgram: print '...', +## else: print ' ', +## print found + if len(found) == 2: + if found == '/*': + Program = InsideCommentProgram + elif found == '*/': + Program = OutsideCommentProgram + n = len(found) + if Dict.has_key(found): + subst = Dict[found] + if Program is InsideCommentProgram: + if NotInComment.has_key(found): + pass +## print 'Ignored in comment:', +## print found, '-->', subst +## print 'Line:', line, + subst = found + else: +## print 'Substituting in comment:', +## print found, '-->', subst +## print 'Line:', line, + line = line[:i] + subst + line[i+n:] + n = len(subst) + i = i + n + return line + +Reverse = 0 +def setreverse(): + global Reverse + Reverse = (not Reverse) + +Dict = {} +NotInComment = {} +def addsubst(substfile): + try: + fp = open(substfile, 'r') + except IOError, msg: + err(substfile + ': cannot read substfile: ' + str(msg) + '\n') + sys.exit(1) + lineno = 0 + while 1: + line = fp.readline() + if not line: break + lineno = lineno + 1 + try: + i = string.index(line, '#') + except string.index_error: + i = -1 # Happens to delete trailing \n + words = string.split(line[:i]) + if not words: continue + if len(words) <> 2: + err(substfile + ':' + `lineno` + \ + ': warning: bad line: ' + line) + continue + if Reverse: + [value, key] = words + else: + [key, value] = words + if value[0] == '*': + value = value[1:] + if key[0] == '*': + key = key[1:] + NotInComment[key] = value + if Dict.has_key(key): + err(substfile + ':' + `lineno` + \ + ': warning: overriding: ' + \ + key + ' ' + value + '\n') + err(substfile + ':' + `lineno` + \ + ': warning: previous: ' + Dict[key] + '\n') + Dict[key] = value + fp.close() + +main() -- cgit v0.12