| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | #! /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(): | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 	progname = sys.argv[0] | 
					
						
							|  |  |  | 	err('Usage: ' + progname + | 
					
						
							|  |  |  | 		  ' [-c] [-r] [-s file] ... file-or-directory ...\n') | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 	err('\n') | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 	err('-c           : substitute inside comments\n') | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 	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') | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 	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(): | 
					
						
							|  |  |  | 	try: | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 		opts, args = getopt.getopt(sys.argv[1:], 'crs:') | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 	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: | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 		if opt == '-c': | 
					
						
							|  |  |  | 			setdocomments() | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 		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() | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 					err(tempname+': cannot create: '+ | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 					    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) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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(): | 
					
						
							|  |  |  | 	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: | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 				if not Docomments: | 
					
						
							|  |  |  | 					print 'Found in comment:', found | 
					
						
							| 
									
										
										
										
											1994-10-03 16:45:35 +00:00
										 |  |  | 					i = i + n | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 					continue | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 				if NotInComment.has_key(found): | 
					
						
							|  |  |  | ##					print 'Ignored in comment:', | 
					
						
							|  |  |  | ##					print found, '-->', subst | 
					
						
							|  |  |  | ##					print 'Line:', line, | 
					
						
							|  |  |  | 					subst = found | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | ##				else: | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | ##					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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | Docomments = 0 | 
					
						
							|  |  |  | def setdocomments(): | 
					
						
							|  |  |  | 	global Docomments | 
					
						
							|  |  |  | 	Docomments = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 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 | 
					
						
							| 
									
										
										
										
											1994-10-03 16:45:35 +00:00
										 |  |  | 		if len(words) == 3 and words[0] == 'struct': | 
					
						
							|  |  |  | 			words[:2] == [words[0] + ' ' + words[1]] | 
					
						
							|  |  |  | 		elif len(words) <> 2: | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 			err(substfile + ':' + `lineno` + | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 				  ': 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): | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 			err(substfile + ':' + `lineno` + | 
					
						
							|  |  |  | 				  ': warning: overriding: ' + | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 				  key + ' ' + value + '\n') | 
					
						
							| 
									
										
										
										
											1994-01-07 10:55:15 +00:00
										 |  |  | 			err(substfile + ':' + `lineno` + | 
					
						
							| 
									
										
										
										
											1993-06-10 14:43:53 +00:00
										 |  |  | 				  ': warning: previous: ' + Dict[key] + '\n') | 
					
						
							|  |  |  | 		Dict[key] = value | 
					
						
							|  |  |  | 	fp.close() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | main() |