17db96d56Sopenharmony_ci#! /usr/bin/env python3
27db96d56Sopenharmony_ci
37db96d56Sopenharmony_ci# Selectively preprocess #ifdef / #ifndef statements.
47db96d56Sopenharmony_ci# Usage:
57db96d56Sopenharmony_ci# ifdef [-Dname] ... [-Uname] ... [file] ...
67db96d56Sopenharmony_ci#
77db96d56Sopenharmony_ci# This scans the file(s), looking for #ifdef and #ifndef preprocessor
87db96d56Sopenharmony_ci# commands that test for one of the names mentioned in the -D and -U
97db96d56Sopenharmony_ci# options.  On standard output it writes a copy of the input file(s)
107db96d56Sopenharmony_ci# minus those code sections that are suppressed by the selected
117db96d56Sopenharmony_ci# combination of defined/undefined symbols.  The #if(n)def/#else/#else
127db96d56Sopenharmony_ci# lines themselves (if the #if(n)def tests for one of the mentioned
137db96d56Sopenharmony_ci# names) are removed as well.
147db96d56Sopenharmony_ci
157db96d56Sopenharmony_ci# Features: Arbitrary nesting of recognized and unrecognized
167db96d56Sopenharmony_ci# preprocessor statements works correctly.  Unrecognized #if* commands
177db96d56Sopenharmony_ci# are left in place, so it will never remove too much, only too
187db96d56Sopenharmony_ci# little.  It does accept whitespace around the '#' character.
197db96d56Sopenharmony_ci
207db96d56Sopenharmony_ci# Restrictions: There should be no comments or other symbols on the
217db96d56Sopenharmony_ci# #if(n)def lines.  The effect of #define/#undef commands in the input
227db96d56Sopenharmony_ci# file or in included files is not taken into account.  Tests using
237db96d56Sopenharmony_ci# #if and the defined() pseudo function are not recognized.  The #elif
247db96d56Sopenharmony_ci# command is not recognized.  Improperly nesting is not detected.
257db96d56Sopenharmony_ci# Lines that look like preprocessor commands but which are actually
267db96d56Sopenharmony_ci# part of comments or string literals will be mistaken for
277db96d56Sopenharmony_ci# preprocessor commands.
287db96d56Sopenharmony_ci
297db96d56Sopenharmony_ciimport sys
307db96d56Sopenharmony_ciimport getopt
317db96d56Sopenharmony_ci
327db96d56Sopenharmony_cidefs = []
337db96d56Sopenharmony_ciundefs = []
347db96d56Sopenharmony_ci
357db96d56Sopenharmony_cidef main():
367db96d56Sopenharmony_ci    opts, args = getopt.getopt(sys.argv[1:], 'D:U:')
377db96d56Sopenharmony_ci    for o, a in opts:
387db96d56Sopenharmony_ci        if o == '-D':
397db96d56Sopenharmony_ci            defs.append(a)
407db96d56Sopenharmony_ci        if o == '-U':
417db96d56Sopenharmony_ci            undefs.append(a)
427db96d56Sopenharmony_ci    if not args:
437db96d56Sopenharmony_ci        args = ['-']
447db96d56Sopenharmony_ci    for filename in args:
457db96d56Sopenharmony_ci        if filename == '-':
467db96d56Sopenharmony_ci            process(sys.stdin, sys.stdout)
477db96d56Sopenharmony_ci        else:
487db96d56Sopenharmony_ci            with open(filename) as f:
497db96d56Sopenharmony_ci                process(f, sys.stdout)
507db96d56Sopenharmony_ci
517db96d56Sopenharmony_cidef process(fpi, fpo):
527db96d56Sopenharmony_ci    keywords = ('if', 'ifdef', 'ifndef', 'else', 'endif')
537db96d56Sopenharmony_ci    ok = 1
547db96d56Sopenharmony_ci    stack = []
557db96d56Sopenharmony_ci    while 1:
567db96d56Sopenharmony_ci        line = fpi.readline()
577db96d56Sopenharmony_ci        if not line: break
587db96d56Sopenharmony_ci        while line[-2:] == '\\\n':
597db96d56Sopenharmony_ci            nextline = fpi.readline()
607db96d56Sopenharmony_ci            if not nextline: break
617db96d56Sopenharmony_ci            line = line + nextline
627db96d56Sopenharmony_ci        tmp = line.strip()
637db96d56Sopenharmony_ci        if tmp[:1] != '#':
647db96d56Sopenharmony_ci            if ok: fpo.write(line)
657db96d56Sopenharmony_ci            continue
667db96d56Sopenharmony_ci        tmp = tmp[1:].strip()
677db96d56Sopenharmony_ci        words = tmp.split()
687db96d56Sopenharmony_ci        keyword = words[0]
697db96d56Sopenharmony_ci        if keyword not in keywords:
707db96d56Sopenharmony_ci            if ok: fpo.write(line)
717db96d56Sopenharmony_ci            continue
727db96d56Sopenharmony_ci        if keyword in ('ifdef', 'ifndef') and len(words) == 2:
737db96d56Sopenharmony_ci            if keyword == 'ifdef':
747db96d56Sopenharmony_ci                ko = 1
757db96d56Sopenharmony_ci            else:
767db96d56Sopenharmony_ci                ko = 0
777db96d56Sopenharmony_ci            word = words[1]
787db96d56Sopenharmony_ci            if word in defs:
797db96d56Sopenharmony_ci                stack.append((ok, ko, word))
807db96d56Sopenharmony_ci                if not ko: ok = 0
817db96d56Sopenharmony_ci            elif word in undefs:
827db96d56Sopenharmony_ci                stack.append((ok, not ko, word))
837db96d56Sopenharmony_ci                if ko: ok = 0
847db96d56Sopenharmony_ci            else:
857db96d56Sopenharmony_ci                stack.append((ok, -1, word))
867db96d56Sopenharmony_ci                if ok: fpo.write(line)
877db96d56Sopenharmony_ci        elif keyword == 'if':
887db96d56Sopenharmony_ci            stack.append((ok, -1, ''))
897db96d56Sopenharmony_ci            if ok: fpo.write(line)
907db96d56Sopenharmony_ci        elif keyword == 'else' and stack:
917db96d56Sopenharmony_ci            s_ok, s_ko, s_word = stack[-1]
927db96d56Sopenharmony_ci            if s_ko < 0:
937db96d56Sopenharmony_ci                if ok: fpo.write(line)
947db96d56Sopenharmony_ci            else:
957db96d56Sopenharmony_ci                s_ko = not s_ko
967db96d56Sopenharmony_ci                ok = s_ok
977db96d56Sopenharmony_ci                if not s_ko: ok = 0
987db96d56Sopenharmony_ci                stack[-1] = s_ok, s_ko, s_word
997db96d56Sopenharmony_ci        elif keyword == 'endif' and stack:
1007db96d56Sopenharmony_ci            s_ok, s_ko, s_word = stack[-1]
1017db96d56Sopenharmony_ci            if s_ko < 0:
1027db96d56Sopenharmony_ci                if ok: fpo.write(line)
1037db96d56Sopenharmony_ci            del stack[-1]
1047db96d56Sopenharmony_ci            ok = s_ok
1057db96d56Sopenharmony_ci        else:
1067db96d56Sopenharmony_ci            sys.stderr.write('Unknown keyword %s\n' % keyword)
1077db96d56Sopenharmony_ci    if stack:
1087db96d56Sopenharmony_ci        sys.stderr.write('stack: %s\n' % stack)
1097db96d56Sopenharmony_ci
1107db96d56Sopenharmony_ciif __name__ == '__main__':
1117db96d56Sopenharmony_ci    main()
112