| 
									
										
										
										
											1996-11-27 19:52:01 +00:00
										 |  |  | #! /usr/bin/env python | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # 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, | 
					
						
							| 
									
										
										
										
											1997-05-15 21:51:16 +00:00
										 |  |  | # it is a user option (off by default). | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | # | 
					
						
							|  |  |  | # 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(): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     progname = sys.argv[0] | 
					
						
							|  |  |  |     err('Usage: ' + progname + | 
					
						
							|  |  |  |               ' [-c] [-r] [-s file] ... file-or-directory ...\n') | 
					
						
							|  |  |  |     err('\n') | 
					
						
							|  |  |  |     err('-c           : substitute inside comments\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') | 
					
						
							|  |  |  |     err('If an identifier is preceded with a *, it is not substituted\n') | 
					
						
							|  |  |  |     err('inside a comment even when -c is specified.\n') | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     try: | 
					
						
							|  |  |  |         opts, args = getopt.getopt(sys.argv[1:], 'crs:') | 
					
						
							|  |  |  |     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 == '-c': | 
					
						
							|  |  |  |             setdocomments() | 
					
						
							|  |  |  |         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) | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Change this regular expression to select a different set of files | 
					
						
							|  |  |  | Wanted = '^[a-zA-Z0-9_]+\.[ch]$' | 
					
						
							|  |  |  | def wanted(name): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     return regex.match(Wanted, name) >= 0 | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def recursedown(dirname): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     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 | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def fix(filename): | 
					
						
							| 
									
										
										
										
											2001-01-17 09:13:33 +00:00
										 |  |  | ##  dbg('fix(' + `filename` + ')\n') | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     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) | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     # End of file | 
					
						
							|  |  |  |     if filename == '-': return 0 # Done in filter mode | 
					
						
							|  |  |  |     f.close() | 
					
						
							|  |  |  |     if not g: return 0 # No changes | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     # Finishing touch -- move files | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     # 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 | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | # Tokenizing ANSI C (partly) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-10-03 16:45:35 +00:00
										 |  |  | Identifier = '\(struct \)?[a-zA-Z_][a-zA-Z0-9_]+' | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 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(): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     global Program | 
					
						
							|  |  |  |     Program = OutsideCommentProgram | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def fixline(line): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     global Program | 
					
						
							| 
									
										
										
										
											2001-01-17 09:13:33 +00:00
										 |  |  | ##  print '-->', `line` | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     i = 0 | 
					
						
							|  |  |  |     while i < len(line): | 
					
						
							|  |  |  |         i = Program.search(line, i) | 
					
						
							|  |  |  |         if i < 0: break | 
					
						
							|  |  |  |         found = Program.group(0) | 
					
						
							| 
									
										
										
										
											2001-01-17 09:13:33 +00:00
										 |  |  | ##      if Program is InsideCommentProgram: print '...', | 
					
						
							|  |  |  | ##      else: print '   ', | 
					
						
							|  |  |  | ##      print found | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |         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 not Docomments: | 
					
						
							|  |  |  |                     print 'Found in comment:', found | 
					
						
							|  |  |  |                     i = i + n | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 if NotInComment.has_key(found): | 
					
						
							| 
									
										
										
										
											2001-01-17 09:13:33 +00:00
										 |  |  | ##                  print 'Ignored in comment:', | 
					
						
							|  |  |  | ##                  print found, '-->', subst | 
					
						
							|  |  |  | ##                  print 'Line:', line, | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |                     subst = found | 
					
						
							| 
									
										
										
										
											2001-01-17 09:13:33 +00:00
										 |  |  | ##              else: | 
					
						
							|  |  |  | ##                  print 'Substituting in comment:', | 
					
						
							|  |  |  | ##                  print found, '-->', subst | 
					
						
							|  |  |  | ##                  print 'Line:', line, | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |             line = line[:i] + subst + line[i+n:] | 
					
						
							|  |  |  |             n = len(subst) | 
					
						
							|  |  |  |         i = i + n | 
					
						
							|  |  |  |     return line | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | Docomments = 0 | 
					
						
							|  |  |  | def setdocomments(): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     global Docomments | 
					
						
							|  |  |  |     Docomments = 1 | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | Reverse = 0 | 
					
						
							|  |  |  | def setreverse(): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     global Reverse | 
					
						
							|  |  |  |     Reverse = (not Reverse) | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | Dict = {} | 
					
						
							|  |  |  | NotInComment = {} | 
					
						
							|  |  |  | def addsubst(substfile): | 
					
						
							| 
									
										
										
										
											2001-01-17 08:48:39 +00:00
										 |  |  |     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) == 3 and words[0] == 'struct': | 
					
						
							|  |  |  |             words[:2] = [words[0] + ' ' + words[1]] | 
					
						
							|  |  |  |         elif 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() | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | main() |