mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 02:15:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			318 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			318 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #! /usr/bin/env 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 (off 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():
 | |
| 	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')
 | |
| 
 | |
| def main():
 | |
| 	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)
 | |
| 
 | |
| # 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 = '\(struct \)?[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 not Docomments:
 | |
| 					print 'Found in comment:', found
 | |
| 					i = i + n
 | |
| 					continue
 | |
| 				if NotInComment.has_key(found):
 | |
| ##					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
 | |
| 
 | |
| Docomments = 0
 | |
| def setdocomments():
 | |
| 	global Docomments
 | |
| 	Docomments = 1
 | |
| 
 | |
| 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) == 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()
 | |
| 
 | |
| main()
 | 
