1#! /usr/bin/env python3 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 with open(filename) as f: 49 process(f, sys.stdout) 50 51def process(fpi, fpo): 52 keywords = ('if', 'ifdef', 'ifndef', 'else', 'endif') 53 ok = 1 54 stack = [] 55 while 1: 56 line = fpi.readline() 57 if not line: break 58 while line[-2:] == '\\\n': 59 nextline = fpi.readline() 60 if not nextline: break 61 line = line + nextline 62 tmp = line.strip() 63 if tmp[:1] != '#': 64 if ok: fpo.write(line) 65 continue 66 tmp = tmp[1:].strip() 67 words = tmp.split() 68 keyword = words[0] 69 if keyword not in keywords: 70 if ok: fpo.write(line) 71 continue 72 if keyword in ('ifdef', 'ifndef') and len(words) == 2: 73 if keyword == 'ifdef': 74 ko = 1 75 else: 76 ko = 0 77 word = words[1] 78 if word in defs: 79 stack.append((ok, ko, word)) 80 if not ko: ok = 0 81 elif word in undefs: 82 stack.append((ok, not ko, word)) 83 if ko: ok = 0 84 else: 85 stack.append((ok, -1, word)) 86 if ok: fpo.write(line) 87 elif keyword == 'if': 88 stack.append((ok, -1, '')) 89 if ok: fpo.write(line) 90 elif keyword == 'else' and stack: 91 s_ok, s_ko, s_word = stack[-1] 92 if s_ko < 0: 93 if ok: fpo.write(line) 94 else: 95 s_ko = not s_ko 96 ok = s_ok 97 if not s_ko: ok = 0 98 stack[-1] = s_ok, s_ko, s_word 99 elif keyword == 'endif' and stack: 100 s_ok, s_ko, s_word = stack[-1] 101 if s_ko < 0: 102 if ok: fpo.write(line) 103 del stack[-1] 104 ok = s_ok 105 else: 106 sys.stderr.write('Unknown keyword %s\n' % keyword) 107 if stack: 108 sys.stderr.write('stack: %s\n' % stack) 109 110if __name__ == '__main__': 111 main() 112