home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyth_os2.zip / python-1.0.2 / Demo / scripts / fixcid.py < prev    next >
Text File  |  1994-01-07  |  9KB  |  316 lines

  1. #! /usr/local/bin/python
  2.  
  3. # Perform massive identifier substitution on C source files.
  4. # This actually tokenizes the files (to some extent) so it can
  5. # avoid making substitutions inside strings or comments.
  6. # Inside strings, substitutions are never made; inside comments,
  7. # it is a user option (on by default).
  8. #
  9. # The substitutions are read from one or more files whose lines,
  10. # when not empty, after stripping comments starting with #,
  11. # must contain exactly two words separated by whitespace: the
  12. # old identifier and its replacement.
  13. #
  14. # The option -r reverses the sense of the substitutions (this may be
  15. # useful to undo a particular substitution).
  16. #
  17. # If the old identifier is prefixed with a '*' (with no intervening
  18. # whitespace), then it will not be substituted inside comments.
  19. #
  20. # Command line arguments are files or directories to be processed.
  21. # Directories are searched recursively for files whose name looks
  22. # like a C file (ends in .h or .c).  The special filename '-' means
  23. # operate in filter mode: read stdin, write stdout.
  24. #
  25. # Symbolic links are always ignored (except as explicit directory
  26. # arguments).
  27. #
  28. # The original files are kept as back-up with a "~" suffix.
  29. #
  30. # Changes made are reported to stdout in a diff-like format.
  31. #
  32. # NB: by changing only the function fixline() you can turn this
  33. # into a program for different changes to C source files; by
  34. # changing the function wanted() you can make a different selection of
  35. # files.
  36.  
  37. import sys
  38. import regex
  39. import string
  40. import os
  41. from stat import *
  42. import getopt
  43.  
  44. err = sys.stderr.write
  45. dbg = err
  46. rep = sys.stdout.write
  47.  
  48. def usage():
  49.     progname = sys.argv[0]
  50.     err('Usage: ' + progname +
  51.           ' [-c] [-r] [-s file] ... file-or-directory ...\n')
  52.     err('\n')
  53.     err('-c           : substitute inside comments\n')
  54.     err('-r           : reverse direction for following -s options\n')
  55.     err('-s substfile : add a file of substitutions\n')
  56.     err('\n')
  57.     err('Each non-empty non-comment line in a substitution file must\n')
  58.     err('contain exactly two words: an identifier and its replacement.\n')
  59.     err('Comments start with a # character and end at end of line.\n')
  60.     err('If an identifier is preceded with a *, it is not substituted\n')
  61.     err('inside a comment even when -c is specified.\n')
  62.  
  63. def main():
  64.     try:
  65.         opts, args = getopt.getopt(sys.argv[1:], 'crs:')
  66.     except getopt.error, msg:
  67.         err('Options error: ' + str(msg) + '\n')
  68.         usage()
  69.         sys.exit(2)
  70.     bad = 0
  71.     if not args: # No arguments
  72.         usage()
  73.         sys.exit(2)
  74.     for opt, arg in opts:
  75.         if opt == '-c':
  76.             setdocomments()
  77.         if opt == '-r':
  78.             setreverse()
  79.         if opt == '-s':
  80.             addsubst(arg)
  81.     for arg in args:
  82.         if os.path.isdir(arg):
  83.             if recursedown(arg): bad = 1
  84.         elif os.path.islink(arg):
  85.             err(arg + ': will not process symbolic links\n')
  86.             bad = 1
  87.         else:
  88.             if fix(arg): bad = 1
  89.     sys.exit(bad)
  90.  
  91. # Change this regular expression to select a different set of files
  92. Wanted = '^[a-zA-Z0-9_]+\.[ch]$'
  93. def wanted(name):
  94.     return regex.match(Wanted, name) >= 0
  95.  
  96. def recursedown(dirname):
  97.     dbg('recursedown(' + `dirname` + ')\n')
  98.     bad = 0
  99.     try:
  100.         names = os.listdir(dirname)
  101.     except os.error, msg:
  102.         err(dirname + ': cannot list directory: ' + str(msg) + '\n')
  103.         return 1
  104.     names.sort()
  105.     subdirs = []
  106.     for name in names:
  107.         if name in (os.curdir, os.pardir): continue
  108.         fullname = os.path.join(dirname, name)
  109.         if os.path.islink(fullname): pass
  110.         elif os.path.isdir(fullname):
  111.             subdirs.append(fullname)
  112.         elif wanted(name):
  113.             if fix(fullname): bad = 1
  114.     for fullname in subdirs:
  115.         if recursedown(fullname): bad = 1
  116.     return bad
  117.  
  118. def fix(filename):
  119. ##    dbg('fix(' + `filename` + ')\n')
  120.     if filename == '-':
  121.         # Filter mode
  122.         f = sys.stdin
  123.         g = sys.stdout
  124.     else:
  125.         # File replacement mode
  126.         try:
  127.             f = open(filename, 'r')
  128.         except IOError, msg:
  129.             err(filename + ': cannot open: ' + str(msg) + '\n')
  130.             return 1
  131.         head, tail = os.path.split(filename)
  132.         tempname = os.path.join(head, '@' + tail)
  133.         g = None
  134.     # If we find a match, we rewind the file and start over but
  135.     # now copy everything to a temp file.
  136.     lineno = 0
  137.     initfixline()
  138.     while 1:
  139.         line = f.readline()
  140.         if not line: break
  141.         lineno = lineno + 1
  142.         while line[-2:] == '\\\n':
  143.             nextline = f.readline()
  144.             if not nextline: break
  145.             line = line + nextline
  146.             lineno = lineno + 1
  147.         newline = fixline(line)
  148.         if newline != line:
  149.             if g is None:
  150.                 try:
  151.                     g = open(tempname, 'w')
  152.                 except IOError, msg:
  153.                     f.close()
  154.                     err(tempname+': cannot create: '+
  155.                         str(msg)+'\n')
  156.                     return 1
  157.                 f.seek(0)
  158.                 lineno = 0
  159.                 initfixline()
  160.                 rep(filename + ':\n')
  161.                 continue # restart from the beginning
  162.             rep(`lineno` + '\n')
  163.             rep('< ' + line)
  164.             rep('> ' + newline)
  165.         if g is not None:
  166.             g.write(newline)
  167.  
  168.     # End of file
  169.     if filename == '-': return 0 # Done in filter mode
  170.     f.close()
  171.     if not g: return 0 # No changes
  172.  
  173.     # Finishing touch -- move files
  174.  
  175.     # First copy the file's mode to the temp file
  176.     try:
  177.         statbuf = os.stat(filename)
  178.         os.chmod(tempname, statbuf[ST_MODE] & 07777)
  179.     except os.error, msg:
  180.         err(tempname + ': warning: chmod failed (' + str(msg) + ')\n')
  181.     # Then make a backup of the original file as filename~
  182.     try:
  183.         os.rename(filename, filename + '~')
  184.     except os.error, msg:
  185.         err(filename + ': warning: backup failed (' + str(msg) + ')\n')
  186.     # Now move the temp file to the original file
  187.     try:
  188.         os.rename(tempname, filename)
  189.     except os.error, msg:
  190.         err(filename + ': rename failed (' + str(msg) + ')\n')
  191.         return 1
  192.     # Return succes
  193.     return 0
  194.  
  195. # Tokenizing ANSI C (partly)
  196.  
  197. Identifier = '[a-zA-Z_][a-zA-Z0-9_]+'
  198. String = '"\([^\n\\"]\|\\\\.\)*"'
  199. Char = '\'\([^\n\\\']\|\\\\.\)*\''
  200. CommentStart = '/\*'
  201. CommentEnd = '\*/'
  202.  
  203. Hexnumber = '0[xX][0-9a-fA-F]*[uUlL]*'
  204. Octnumber = '0[0-7]*[uUlL]*'
  205. Decnumber = '[1-9][0-9]*[uUlL]*'
  206. Intnumber = Hexnumber + '\|' + Octnumber + '\|' + Decnumber
  207. Exponent = '[eE][-+]?[0-9]+'
  208. Pointfloat = '\([0-9]+\.[0-9]*\|\.[0-9]+\)\(' + Exponent + '\)?'
  209. Expfloat = '[0-9]+' + Exponent
  210. Floatnumber = Pointfloat + '\|' + Expfloat
  211. Number = Floatnumber + '\|' + Intnumber
  212.  
  213. # Anything else is an operator -- don't list this explicitly because of '/*'
  214.  
  215. OutsideComment = (Identifier, Number, String, Char, CommentStart)
  216. OutsideCommentPattern = '\(' + string.joinfields(OutsideComment, '\|') + '\)'
  217. OutsideCommentProgram = regex.compile(OutsideCommentPattern)
  218.  
  219. InsideComment = (Identifier, Number, CommentEnd)
  220. InsideCommentPattern = '\(' + string.joinfields(InsideComment, '\|') + '\)'
  221. InsideCommentProgram = regex.compile(InsideCommentPattern)
  222.  
  223. def initfixline():
  224.     global Program
  225.     Program = OutsideCommentProgram
  226.  
  227. def fixline(line):
  228.     global Program
  229. ##    print '-->', `line`
  230.     i = 0
  231.     while i < len(line):
  232.         i = Program.search(line, i)
  233.         if i < 0: break
  234.         found = Program.group(0)
  235. ##        if Program is InsideCommentProgram: print '...',
  236. ##        else: print '   ',
  237. ##        print found
  238.         if len(found) == 2:
  239.             if found == '/*':
  240.                 Program = InsideCommentProgram
  241.             elif found == '*/':
  242.                 Program = OutsideCommentProgram
  243.         n = len(found)
  244.         if Dict.has_key(found):
  245.             subst = Dict[found]
  246.             if Program is InsideCommentProgram:
  247.                 if not Docomments:
  248.                     print 'Found in comment:', found
  249.                     continue
  250.                 if NotInComment.has_key(found):
  251. ##                    print 'Ignored in comment:',
  252. ##                    print found, '-->', subst
  253. ##                    print 'Line:', line,
  254.                     subst = found
  255. ##                else:
  256. ##                    print 'Substituting in comment:',
  257. ##                    print found, '-->', subst
  258. ##                    print 'Line:', line,
  259.             line = line[:i] + subst + line[i+n:]
  260.             n = len(subst)
  261.         i = i + n
  262.     return line
  263.  
  264. Docomments = 0
  265. def setdocomments():
  266.     global Docomments
  267.     Docomments = 1
  268.  
  269. Reverse = 0
  270. def setreverse():
  271.     global Reverse
  272.     Reverse = (not Reverse)
  273.  
  274. Dict = {}
  275. NotInComment = {}
  276. def addsubst(substfile):
  277.     try:
  278.         fp = open(substfile, 'r')
  279.     except IOError, msg:
  280.         err(substfile + ': cannot read substfile: ' + str(msg) + '\n')
  281.         sys.exit(1)
  282.     lineno = 0
  283.     while 1:
  284.         line = fp.readline()
  285.         if not line: break
  286.         lineno = lineno + 1
  287.         try:
  288.             i = string.index(line, '#')
  289.         except string.index_error:
  290.             i = -1        # Happens to delete trailing \n
  291.         words = string.split(line[:i])
  292.         if not words: continue
  293.         if len(words) <> 2:
  294.             err(substfile + ':' + `lineno` +
  295.                   ': warning: bad line: ' + line)
  296.             continue
  297.         if Reverse:
  298.             [value, key] = words
  299.         else:
  300.             [key, value] = words
  301.         if value[0] == '*':
  302.             value = value[1:]
  303.         if key[0] == '*':
  304.             key = key[1:]
  305.             NotInComment[key] = value
  306.         if Dict.has_key(key):
  307.             err(substfile + ':' + `lineno` +
  308.                   ': warning: overriding: ' +
  309.                   key + ' ' + value + '\n')
  310.             err(substfile + ':' + `lineno` +
  311.                   ': warning: previous: ' + Dict[key] + '\n')
  312.         Dict[key] = value
  313.     fp.close()
  314.  
  315. main()
  316.