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