17db96d56Sopenharmony_ci#! /usr/bin/env python3 27db96d56Sopenharmony_ci 37db96d56Sopenharmony_ci# Perform massive identifier substitution on C source files. 47db96d56Sopenharmony_ci# This actually tokenizes the files (to some extent) so it can 57db96d56Sopenharmony_ci# avoid making substitutions inside strings or comments. 67db96d56Sopenharmony_ci# Inside strings, substitutions are never made; inside comments, 77db96d56Sopenharmony_ci# it is a user option (off by default). 87db96d56Sopenharmony_ci# 97db96d56Sopenharmony_ci# The substitutions are read from one or more files whose lines, 107db96d56Sopenharmony_ci# when not empty, after stripping comments starting with #, 117db96d56Sopenharmony_ci# must contain exactly two words separated by whitespace: the 127db96d56Sopenharmony_ci# old identifier and its replacement. 137db96d56Sopenharmony_ci# 147db96d56Sopenharmony_ci# The option -r reverses the sense of the substitutions (this may be 157db96d56Sopenharmony_ci# useful to undo a particular substitution). 167db96d56Sopenharmony_ci# 177db96d56Sopenharmony_ci# If the old identifier is prefixed with a '*' (with no intervening 187db96d56Sopenharmony_ci# whitespace), then it will not be substituted inside comments. 197db96d56Sopenharmony_ci# 207db96d56Sopenharmony_ci# Command line arguments are files or directories to be processed. 217db96d56Sopenharmony_ci# Directories are searched recursively for files whose name looks 227db96d56Sopenharmony_ci# like a C file (ends in .h or .c). The special filename '-' means 237db96d56Sopenharmony_ci# operate in filter mode: read stdin, write stdout. 247db96d56Sopenharmony_ci# 257db96d56Sopenharmony_ci# Symbolic links are always ignored (except as explicit directory 267db96d56Sopenharmony_ci# arguments). 277db96d56Sopenharmony_ci# 287db96d56Sopenharmony_ci# The original files are kept as back-up with a "~" suffix. 297db96d56Sopenharmony_ci# 307db96d56Sopenharmony_ci# Changes made are reported to stdout in a diff-like format. 317db96d56Sopenharmony_ci# 327db96d56Sopenharmony_ci# NB: by changing only the function fixline() you can turn this 337db96d56Sopenharmony_ci# into a program for different changes to C source files; by 347db96d56Sopenharmony_ci# changing the function wanted() you can make a different selection of 357db96d56Sopenharmony_ci# files. 367db96d56Sopenharmony_ci 377db96d56Sopenharmony_ciimport sys 387db96d56Sopenharmony_ciimport re 397db96d56Sopenharmony_ciimport os 407db96d56Sopenharmony_cifrom stat import * 417db96d56Sopenharmony_ciimport getopt 427db96d56Sopenharmony_ci 437db96d56Sopenharmony_cierr = sys.stderr.write 447db96d56Sopenharmony_cidbg = err 457db96d56Sopenharmony_cirep = sys.stdout.write 467db96d56Sopenharmony_ci 477db96d56Sopenharmony_cidef usage(): 487db96d56Sopenharmony_ci progname = sys.argv[0] 497db96d56Sopenharmony_ci err('Usage: ' + progname + 507db96d56Sopenharmony_ci ' [-c] [-r] [-s file] ... file-or-directory ...\n') 517db96d56Sopenharmony_ci err('\n') 527db96d56Sopenharmony_ci err('-c : substitute inside comments\n') 537db96d56Sopenharmony_ci err('-r : reverse direction for following -s options\n') 547db96d56Sopenharmony_ci err('-s substfile : add a file of substitutions\n') 557db96d56Sopenharmony_ci err('\n') 567db96d56Sopenharmony_ci err('Each non-empty non-comment line in a substitution file must\n') 577db96d56Sopenharmony_ci err('contain exactly two words: an identifier and its replacement.\n') 587db96d56Sopenharmony_ci err('Comments start with a # character and end at end of line.\n') 597db96d56Sopenharmony_ci err('If an identifier is preceded with a *, it is not substituted\n') 607db96d56Sopenharmony_ci err('inside a comment even when -c is specified.\n') 617db96d56Sopenharmony_ci 627db96d56Sopenharmony_cidef main(): 637db96d56Sopenharmony_ci try: 647db96d56Sopenharmony_ci opts, args = getopt.getopt(sys.argv[1:], 'crs:') 657db96d56Sopenharmony_ci except getopt.error as msg: 667db96d56Sopenharmony_ci err('Options error: ' + str(msg) + '\n') 677db96d56Sopenharmony_ci usage() 687db96d56Sopenharmony_ci sys.exit(2) 697db96d56Sopenharmony_ci bad = 0 707db96d56Sopenharmony_ci if not args: # No arguments 717db96d56Sopenharmony_ci usage() 727db96d56Sopenharmony_ci sys.exit(2) 737db96d56Sopenharmony_ci for opt, arg in opts: 747db96d56Sopenharmony_ci if opt == '-c': 757db96d56Sopenharmony_ci setdocomments() 767db96d56Sopenharmony_ci if opt == '-r': 777db96d56Sopenharmony_ci setreverse() 787db96d56Sopenharmony_ci if opt == '-s': 797db96d56Sopenharmony_ci addsubst(arg) 807db96d56Sopenharmony_ci for arg in args: 817db96d56Sopenharmony_ci if os.path.isdir(arg): 827db96d56Sopenharmony_ci if recursedown(arg): bad = 1 837db96d56Sopenharmony_ci elif os.path.islink(arg): 847db96d56Sopenharmony_ci err(arg + ': will not process symbolic links\n') 857db96d56Sopenharmony_ci bad = 1 867db96d56Sopenharmony_ci else: 877db96d56Sopenharmony_ci if fix(arg): bad = 1 887db96d56Sopenharmony_ci sys.exit(bad) 897db96d56Sopenharmony_ci 907db96d56Sopenharmony_ci# Change this regular expression to select a different set of files 917db96d56Sopenharmony_ciWanted = r'^[a-zA-Z0-9_]+\.[ch]$' 927db96d56Sopenharmony_cidef wanted(name): 937db96d56Sopenharmony_ci return re.match(Wanted, name) 947db96d56Sopenharmony_ci 957db96d56Sopenharmony_cidef recursedown(dirname): 967db96d56Sopenharmony_ci dbg('recursedown(%r)\n' % (dirname,)) 977db96d56Sopenharmony_ci bad = 0 987db96d56Sopenharmony_ci try: 997db96d56Sopenharmony_ci names = os.listdir(dirname) 1007db96d56Sopenharmony_ci except OSError as msg: 1017db96d56Sopenharmony_ci err(dirname + ': cannot list directory: ' + str(msg) + '\n') 1027db96d56Sopenharmony_ci return 1 1037db96d56Sopenharmony_ci names.sort() 1047db96d56Sopenharmony_ci subdirs = [] 1057db96d56Sopenharmony_ci for name in names: 1067db96d56Sopenharmony_ci if name in (os.curdir, os.pardir): continue 1077db96d56Sopenharmony_ci fullname = os.path.join(dirname, name) 1087db96d56Sopenharmony_ci if os.path.islink(fullname): pass 1097db96d56Sopenharmony_ci elif os.path.isdir(fullname): 1107db96d56Sopenharmony_ci subdirs.append(fullname) 1117db96d56Sopenharmony_ci elif wanted(name): 1127db96d56Sopenharmony_ci if fix(fullname): bad = 1 1137db96d56Sopenharmony_ci for fullname in subdirs: 1147db96d56Sopenharmony_ci if recursedown(fullname): bad = 1 1157db96d56Sopenharmony_ci return bad 1167db96d56Sopenharmony_ci 1177db96d56Sopenharmony_cidef fix(filename): 1187db96d56Sopenharmony_ci## dbg('fix(%r)\n' % (filename,)) 1197db96d56Sopenharmony_ci if filename == '-': 1207db96d56Sopenharmony_ci # Filter mode 1217db96d56Sopenharmony_ci f = sys.stdin 1227db96d56Sopenharmony_ci g = sys.stdout 1237db96d56Sopenharmony_ci else: 1247db96d56Sopenharmony_ci # File replacement mode 1257db96d56Sopenharmony_ci try: 1267db96d56Sopenharmony_ci f = open(filename, 'r') 1277db96d56Sopenharmony_ci except IOError as msg: 1287db96d56Sopenharmony_ci err(filename + ': cannot open: ' + str(msg) + '\n') 1297db96d56Sopenharmony_ci return 1 1307db96d56Sopenharmony_ci head, tail = os.path.split(filename) 1317db96d56Sopenharmony_ci tempname = os.path.join(head, '@' + tail) 1327db96d56Sopenharmony_ci g = None 1337db96d56Sopenharmony_ci # If we find a match, we rewind the file and start over but 1347db96d56Sopenharmony_ci # now copy everything to a temp file. 1357db96d56Sopenharmony_ci lineno = 0 1367db96d56Sopenharmony_ci initfixline() 1377db96d56Sopenharmony_ci while 1: 1387db96d56Sopenharmony_ci line = f.readline() 1397db96d56Sopenharmony_ci if not line: break 1407db96d56Sopenharmony_ci lineno = lineno + 1 1417db96d56Sopenharmony_ci while line[-2:] == '\\\n': 1427db96d56Sopenharmony_ci nextline = f.readline() 1437db96d56Sopenharmony_ci if not nextline: break 1447db96d56Sopenharmony_ci line = line + nextline 1457db96d56Sopenharmony_ci lineno = lineno + 1 1467db96d56Sopenharmony_ci newline = fixline(line) 1477db96d56Sopenharmony_ci if newline != line: 1487db96d56Sopenharmony_ci if g is None: 1497db96d56Sopenharmony_ci try: 1507db96d56Sopenharmony_ci g = open(tempname, 'w') 1517db96d56Sopenharmony_ci except IOError as msg: 1527db96d56Sopenharmony_ci f.close() 1537db96d56Sopenharmony_ci err(tempname+': cannot create: '+ 1547db96d56Sopenharmony_ci str(msg)+'\n') 1557db96d56Sopenharmony_ci return 1 1567db96d56Sopenharmony_ci f.seek(0) 1577db96d56Sopenharmony_ci lineno = 0 1587db96d56Sopenharmony_ci initfixline() 1597db96d56Sopenharmony_ci rep(filename + ':\n') 1607db96d56Sopenharmony_ci continue # restart from the beginning 1617db96d56Sopenharmony_ci rep(repr(lineno) + '\n') 1627db96d56Sopenharmony_ci rep('< ' + line) 1637db96d56Sopenharmony_ci rep('> ' + newline) 1647db96d56Sopenharmony_ci if g is not None: 1657db96d56Sopenharmony_ci g.write(newline) 1667db96d56Sopenharmony_ci 1677db96d56Sopenharmony_ci # End of file 1687db96d56Sopenharmony_ci if filename == '-': return 0 # Done in filter mode 1697db96d56Sopenharmony_ci f.close() 1707db96d56Sopenharmony_ci if not g: return 0 # No changes 1717db96d56Sopenharmony_ci g.close() 1727db96d56Sopenharmony_ci 1737db96d56Sopenharmony_ci # Finishing touch -- move files 1747db96d56Sopenharmony_ci 1757db96d56Sopenharmony_ci # First copy the file's mode to the temp file 1767db96d56Sopenharmony_ci try: 1777db96d56Sopenharmony_ci statbuf = os.stat(filename) 1787db96d56Sopenharmony_ci os.chmod(tempname, statbuf[ST_MODE] & 0o7777) 1797db96d56Sopenharmony_ci except OSError as msg: 1807db96d56Sopenharmony_ci err(tempname + ': warning: chmod failed (' + str(msg) + ')\n') 1817db96d56Sopenharmony_ci # Then make a backup of the original file as filename~ 1827db96d56Sopenharmony_ci try: 1837db96d56Sopenharmony_ci os.rename(filename, filename + '~') 1847db96d56Sopenharmony_ci except OSError as msg: 1857db96d56Sopenharmony_ci err(filename + ': warning: backup failed (' + str(msg) + ')\n') 1867db96d56Sopenharmony_ci # Now move the temp file to the original file 1877db96d56Sopenharmony_ci try: 1887db96d56Sopenharmony_ci os.rename(tempname, filename) 1897db96d56Sopenharmony_ci except OSError as msg: 1907db96d56Sopenharmony_ci err(filename + ': rename failed (' + str(msg) + ')\n') 1917db96d56Sopenharmony_ci return 1 1927db96d56Sopenharmony_ci # Return success 1937db96d56Sopenharmony_ci return 0 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_ci# Tokenizing ANSI C (partly) 1967db96d56Sopenharmony_ci 1977db96d56Sopenharmony_ciIdentifier = '(struct )?[a-zA-Z_][a-zA-Z0-9_]+' 1987db96d56Sopenharmony_ciString = r'"([^\n\\"]|\\.)*"' 1997db96d56Sopenharmony_ciChar = r"'([^\n\\']|\\.)*'" 2007db96d56Sopenharmony_ciCommentStart = r'/\*' 2017db96d56Sopenharmony_ciCommentEnd = r'\*/' 2027db96d56Sopenharmony_ci 2037db96d56Sopenharmony_ciHexnumber = '0[xX][0-9a-fA-F]*[uUlL]*' 2047db96d56Sopenharmony_ciOctnumber = '0[0-7]*[uUlL]*' 2057db96d56Sopenharmony_ciDecnumber = '[1-9][0-9]*[uUlL]*' 2067db96d56Sopenharmony_ciIntnumber = Hexnumber + '|' + Octnumber + '|' + Decnumber 2077db96d56Sopenharmony_ciExponent = '[eE][-+]?[0-9]+' 2087db96d56Sopenharmony_ciPointfloat = r'([0-9]+\.[0-9]*|\.[0-9]+)(' + Exponent + r')?' 2097db96d56Sopenharmony_ciExpfloat = '[0-9]+' + Exponent 2107db96d56Sopenharmony_ciFloatnumber = Pointfloat + '|' + Expfloat 2117db96d56Sopenharmony_ciNumber = Floatnumber + '|' + Intnumber 2127db96d56Sopenharmony_ci 2137db96d56Sopenharmony_ci# Anything else is an operator -- don't list this explicitly because of '/*' 2147db96d56Sopenharmony_ci 2157db96d56Sopenharmony_ciOutsideComment = (Identifier, Number, String, Char, CommentStart) 2167db96d56Sopenharmony_ciOutsideCommentPattern = '(' + '|'.join(OutsideComment) + ')' 2177db96d56Sopenharmony_ciOutsideCommentProgram = re.compile(OutsideCommentPattern) 2187db96d56Sopenharmony_ci 2197db96d56Sopenharmony_ciInsideComment = (Identifier, Number, CommentEnd) 2207db96d56Sopenharmony_ciInsideCommentPattern = '(' + '|'.join(InsideComment) + ')' 2217db96d56Sopenharmony_ciInsideCommentProgram = re.compile(InsideCommentPattern) 2227db96d56Sopenharmony_ci 2237db96d56Sopenharmony_cidef initfixline(): 2247db96d56Sopenharmony_ci global Program 2257db96d56Sopenharmony_ci Program = OutsideCommentProgram 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_cidef fixline(line): 2287db96d56Sopenharmony_ci global Program 2297db96d56Sopenharmony_ci## print('-->', repr(line)) 2307db96d56Sopenharmony_ci i = 0 2317db96d56Sopenharmony_ci while i < len(line): 2327db96d56Sopenharmony_ci match = Program.search(line, i) 2337db96d56Sopenharmony_ci if match is None: break 2347db96d56Sopenharmony_ci i = match.start() 2357db96d56Sopenharmony_ci found = match.group(0) 2367db96d56Sopenharmony_ci## if Program is InsideCommentProgram: print(end='... ') 2377db96d56Sopenharmony_ci## else: print(end=' ') 2387db96d56Sopenharmony_ci## print(found) 2397db96d56Sopenharmony_ci if len(found) == 2: 2407db96d56Sopenharmony_ci if found == '/*': 2417db96d56Sopenharmony_ci Program = InsideCommentProgram 2427db96d56Sopenharmony_ci elif found == '*/': 2437db96d56Sopenharmony_ci Program = OutsideCommentProgram 2447db96d56Sopenharmony_ci n = len(found) 2457db96d56Sopenharmony_ci if found in Dict: 2467db96d56Sopenharmony_ci subst = Dict[found] 2477db96d56Sopenharmony_ci if Program is InsideCommentProgram: 2487db96d56Sopenharmony_ci if not Docomments: 2497db96d56Sopenharmony_ci print('Found in comment:', found) 2507db96d56Sopenharmony_ci i = i + n 2517db96d56Sopenharmony_ci continue 2527db96d56Sopenharmony_ci if found in NotInComment: 2537db96d56Sopenharmony_ci## print(end='Ignored in comment: ') 2547db96d56Sopenharmony_ci## print(found, '-->', subst) 2557db96d56Sopenharmony_ci## print('Line:', line, end='') 2567db96d56Sopenharmony_ci subst = found 2577db96d56Sopenharmony_ci## else: 2587db96d56Sopenharmony_ci## print(end='Substituting in comment: ') 2597db96d56Sopenharmony_ci## print(found, '-->', subst) 2607db96d56Sopenharmony_ci## print('Line:', line, end='') 2617db96d56Sopenharmony_ci line = line[:i] + subst + line[i+n:] 2627db96d56Sopenharmony_ci n = len(subst) 2637db96d56Sopenharmony_ci i = i + n 2647db96d56Sopenharmony_ci return line 2657db96d56Sopenharmony_ci 2667db96d56Sopenharmony_ciDocomments = 0 2677db96d56Sopenharmony_cidef setdocomments(): 2687db96d56Sopenharmony_ci global Docomments 2697db96d56Sopenharmony_ci Docomments = 1 2707db96d56Sopenharmony_ci 2717db96d56Sopenharmony_ciReverse = 0 2727db96d56Sopenharmony_cidef setreverse(): 2737db96d56Sopenharmony_ci global Reverse 2747db96d56Sopenharmony_ci Reverse = (not Reverse) 2757db96d56Sopenharmony_ci 2767db96d56Sopenharmony_ciDict = {} 2777db96d56Sopenharmony_ciNotInComment = {} 2787db96d56Sopenharmony_cidef addsubst(substfile): 2797db96d56Sopenharmony_ci try: 2807db96d56Sopenharmony_ci fp = open(substfile, 'r') 2817db96d56Sopenharmony_ci except IOError as msg: 2827db96d56Sopenharmony_ci err(substfile + ': cannot read substfile: ' + str(msg) + '\n') 2837db96d56Sopenharmony_ci sys.exit(1) 2847db96d56Sopenharmony_ci with fp: 2857db96d56Sopenharmony_ci lineno = 0 2867db96d56Sopenharmony_ci while 1: 2877db96d56Sopenharmony_ci line = fp.readline() 2887db96d56Sopenharmony_ci if not line: break 2897db96d56Sopenharmony_ci lineno = lineno + 1 2907db96d56Sopenharmony_ci try: 2917db96d56Sopenharmony_ci i = line.index('#') 2927db96d56Sopenharmony_ci except ValueError: 2937db96d56Sopenharmony_ci i = -1 # Happens to delete trailing \n 2947db96d56Sopenharmony_ci words = line[:i].split() 2957db96d56Sopenharmony_ci if not words: continue 2967db96d56Sopenharmony_ci if len(words) == 3 and words[0] == 'struct': 2977db96d56Sopenharmony_ci words[:2] = [words[0] + ' ' + words[1]] 2987db96d56Sopenharmony_ci elif len(words) != 2: 2997db96d56Sopenharmony_ci err(substfile + '%s:%r: warning: bad line: %r' % (substfile, lineno, line)) 3007db96d56Sopenharmony_ci continue 3017db96d56Sopenharmony_ci if Reverse: 3027db96d56Sopenharmony_ci [value, key] = words 3037db96d56Sopenharmony_ci else: 3047db96d56Sopenharmony_ci [key, value] = words 3057db96d56Sopenharmony_ci if value[0] == '*': 3067db96d56Sopenharmony_ci value = value[1:] 3077db96d56Sopenharmony_ci if key[0] == '*': 3087db96d56Sopenharmony_ci key = key[1:] 3097db96d56Sopenharmony_ci NotInComment[key] = value 3107db96d56Sopenharmony_ci if key in Dict: 3117db96d56Sopenharmony_ci err('%s:%r: warning: overriding: %r %r\n' % (substfile, lineno, key, value)) 3127db96d56Sopenharmony_ci err('%s:%r: warning: previous: %r\n' % (substfile, lineno, Dict[key])) 3137db96d56Sopenharmony_ci Dict[key] = value 3147db96d56Sopenharmony_ci 3157db96d56Sopenharmony_ciif __name__ == '__main__': 3167db96d56Sopenharmony_ci main() 317