1f06ee5fa072931fc807527535c91a46c149a6746Guido van Rossum#! /usr/bin/env python
27faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum
37faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# Selectively preprocess #ifdef / #ifndef statements.
47faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# Usage:
57faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# ifdef [-Dname] ... [-Uname] ... [file] ...
670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters#
77faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# This scans the file(s), looking for #ifdef and #ifndef preprocessor
87faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# commands that test for one of the names mentioned in the -D and -U
97faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# options.  On standard output it writes a copy of the input file(s)
107faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# minus those code sections that are suppressed by the selected
117faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# combination of defined/undefined symbols.  The #if(n)def/#else/#else
126d0f0f299b014e79f6079901b560e938e0c5d8dfEzio Melotti# lines themselves (if the #if(n)def tests for one of the mentioned
137faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# names) are removed as well.
147faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum
157faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# Features: Arbitrary nesting of recognized and unrecognized
166d0f0f299b014e79f6079901b560e938e0c5d8dfEzio Melotti# preprocessor statements works correctly.  Unrecognized #if* commands
177faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# are left in place, so it will never remove too much, only too
187faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# little.  It does accept whitespace around the '#' character.
197faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum
207faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# Restrictions: There should be no comments or other symbols on the
217faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# #if(n)def lines.  The effect of #define/#undef commands in the input
227faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# file or in included files is not taken into account.  Tests using
237faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# #if and the defined() pseudo function are not recognized.  The #elif
247faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# command is not recognized.  Improperly nesting is not detected.
257faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# Lines that look like preprocessor commands but which are actually
267faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# part of comments or string literals will be mistaken for
277faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum# preprocessor commands.
287faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum
297faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossumimport sys
307faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossumimport getopt
317faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum
327faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossumdefs = []
337faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossumundefs = []
347faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum
357faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossumdef main():
3670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters    opts, args = getopt.getopt(sys.argv[1:], 'D:U:')
3770c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters    for o, a in opts:
3870c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        if o == '-D':
3970c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            defs.append(a)
4070c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        if o == '-U':
4170c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            undefs.append(a)
4270c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters    if not args:
4370c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        args = ['-']
44ac6df95d07aa3951f8bfc6febce132e09850db73Andrew M. Kuchling    for filename in args:
45ac6df95d07aa3951f8bfc6febce132e09850db73Andrew M. Kuchling        if filename == '-':
4670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            process(sys.stdin, sys.stdout)
4770c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        else:
48ac6df95d07aa3951f8bfc6febce132e09850db73Andrew M. Kuchling            f = open(filename, 'r')
4970c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            process(f, sys.stdout)
5070c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            f.close()
517faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum
527faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossumdef process(fpi, fpo):
5370c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters    keywords = ('if', 'ifdef', 'ifndef', 'else', 'endif')
5470c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters    ok = 1
5570c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters    stack = []
5670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters    while 1:
5770c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        line = fpi.readline()
5870c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        if not line: break
5970c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        while line[-2:] == '\\\n':
6070c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            nextline = fpi.readline()
6170c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            if not nextline: break
6270c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            line = line + nextline
63aaab30e00cc3e8d90c71b8657c284feeb4ac1413Walter Dörwald        tmp = line.strip()
6470c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        if tmp[:1] != '#':
6570c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            if ok: fpo.write(line)
6670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            continue
67aaab30e00cc3e8d90c71b8657c284feeb4ac1413Walter Dörwald        tmp = tmp[1:].strip()
68aaab30e00cc3e8d90c71b8657c284feeb4ac1413Walter Dörwald        words = tmp.split()
6970c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        keyword = words[0]
7070c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        if keyword not in keywords:
7170c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            if ok: fpo.write(line)
7270c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            continue
7370c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        if keyword in ('ifdef', 'ifndef') and len(words) == 2:
7470c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            if keyword == 'ifdef':
7570c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                ko = 1
7670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            else:
7770c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                ko = 0
7870c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            word = words[1]
7970c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            if word in defs:
8070c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                stack.append((ok, ko, word))
8170c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                if not ko: ok = 0
8270c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            elif word in undefs:
8370c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                stack.append((ok, not ko, word))
8470c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                if ko: ok = 0
8570c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            else:
8670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                stack.append((ok, -1, word))
8770c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                if ok: fpo.write(line)
8870c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        elif keyword == 'if':
8970c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            stack.append((ok, -1, ''))
9070c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            if ok: fpo.write(line)
9170c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        elif keyword == 'else' and stack:
9270c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            s_ok, s_ko, s_word = stack[-1]
9370c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            if s_ko < 0:
9470c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                if ok: fpo.write(line)
9570c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            else:
9670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                s_ko = not s_ko
9770c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                ok = s_ok
9870c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                if not s_ko: ok = 0
9970c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                stack[-1] = s_ok, s_ko, s_word
10070c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        elif keyword == 'endif' and stack:
10170c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            s_ok, s_ko, s_word = stack[-1]
10270c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            if s_ko < 0:
10370c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters                if ok: fpo.write(line)
10470c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            del stack[-1]
10570c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            ok = s_ok
10670c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        else:
10770c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters            sys.stderr.write('Unknown keyword %s\n' % keyword)
10870c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters    if stack:
10970c4378dbcfdcbeef6fb3aa348f32ed862fe8eb7Tim Peters        sys.stderr.write('stack: %s\n' % stack)
1107faf67caf563d0e379f41d3b3a24b87b9ec47bd9Guido van Rossum
111e236b38731823aeb87bb6d101062f54a72044954Andrew M. Kuchlingif __name__ == '__main__':
112e236b38731823aeb87bb6d101062f54a72044954Andrew M. Kuchling    main()
113