mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 07:31:38 +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()
 |