ifdef.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. #! /usr/bin/env python3
  2. # Selectively preprocess #ifdef / #ifndef statements.
  3. # Usage:
  4. # ifdef [-Dname] ... [-Uname] ... [file] ...
  5. #
  6. # This scans the file(s), looking for #ifdef and #ifndef preprocessor
  7. # commands that test for one of the names mentioned in the -D and -U
  8. # options. On standard output it writes a copy of the input file(s)
  9. # minus those code sections that are suppressed by the selected
  10. # combination of defined/undefined symbols. The #if(n)def/#else/#else
  11. # lines themselves (if the #if(n)def tests for one of the mentioned
  12. # names) are removed as well.
  13. # Features: Arbitrary nesting of recognized and unrecognized
  14. # preprocessor statements works correctly. Unrecognized #if* commands
  15. # are left in place, so it will never remove too much, only too
  16. # little. It does accept whitespace around the '#' character.
  17. # Restrictions: There should be no comments or other symbols on the
  18. # #if(n)def lines. The effect of #define/#undef commands in the input
  19. # file or in included files is not taken into account. Tests using
  20. # #if and the defined() pseudo function are not recognized. The #elif
  21. # command is not recognized. Improperly nesting is not detected.
  22. # Lines that look like preprocessor commands but which are actually
  23. # part of comments or string literals will be mistaken for
  24. # preprocessor commands.
  25. import sys
  26. import getopt
  27. defs = []
  28. undefs = []
  29. def main():
  30. opts, args = getopt.getopt(sys.argv[1:], 'D:U:')
  31. for o, a in opts:
  32. if o == '-D':
  33. defs.append(a)
  34. if o == '-U':
  35. undefs.append(a)
  36. if not args:
  37. args = ['-']
  38. for filename in args:
  39. if filename == '-':
  40. process(sys.stdin, sys.stdout)
  41. else:
  42. f = open(filename, 'r')
  43. process(f, sys.stdout)
  44. f.close()
  45. def process(fpi, fpo):
  46. keywords = ('if', 'ifdef', 'ifndef', 'else', 'endif')
  47. ok = 1
  48. stack = []
  49. while 1:
  50. line = fpi.readline()
  51. if not line: break
  52. while line[-2:] == '\\\n':
  53. nextline = fpi.readline()
  54. if not nextline: break
  55. line = line + nextline
  56. tmp = line.strip()
  57. if tmp[:1] != '#':
  58. if ok: fpo.write(line)
  59. continue
  60. tmp = tmp[1:].strip()
  61. words = tmp.split()
  62. keyword = words[0]
  63. if keyword not in keywords:
  64. if ok: fpo.write(line)
  65. continue
  66. if keyword in ('ifdef', 'ifndef') and len(words) == 2:
  67. if keyword == 'ifdef':
  68. ko = 1
  69. else:
  70. ko = 0
  71. word = words[1]
  72. if word in defs:
  73. stack.append((ok, ko, word))
  74. if not ko: ok = 0
  75. elif word in undefs:
  76. stack.append((ok, not ko, word))
  77. if ko: ok = 0
  78. else:
  79. stack.append((ok, -1, word))
  80. if ok: fpo.write(line)
  81. elif keyword == 'if':
  82. stack.append((ok, -1, ''))
  83. if ok: fpo.write(line)
  84. elif keyword == 'else' and stack:
  85. s_ok, s_ko, s_word = stack[-1]
  86. if s_ko < 0:
  87. if ok: fpo.write(line)
  88. else:
  89. s_ko = not s_ko
  90. ok = s_ok
  91. if not s_ko: ok = 0
  92. stack[-1] = s_ok, s_ko, s_word
  93. elif keyword == 'endif' and stack:
  94. s_ok, s_ko, s_word = stack[-1]
  95. if s_ko < 0:
  96. if ok: fpo.write(line)
  97. del stack[-1]
  98. ok = s_ok
  99. else:
  100. sys.stderr.write('Unknown keyword %s\n' % keyword)
  101. if stack:
  102. sys.stderr.write('stack: %s\n' % stack)
  103. if __name__ == '__main__':
  104. main()