#!/usr/bin/python # Copyright (c) 2017-2019, The Tor Project, Inc. # See LICENSE for licensing information import re LINE_OBVIOUSNESS_LIMIT = 4 class Problem(Exception): pass def uncomment(s): s = re.sub(r'//.*','',s) s = re.sub(r'/\*.*','',s) return s.strip() def translate(f_in, f_out): whole_file = [] stack = [] cur_level = whole_file lineno = 0 for line in f_in: lineno += 1 m = re.match(r'\s*#\s*(if|ifdef|ifndef|else|endif|elif)\b\s*(.*)', line) if not m: f_out.write(line) continue command,rest = m.groups() if command in ("if", "ifdef", "ifndef"): # The #if directive pushes us one level lower on the stack. if command == 'ifdef': rest = "defined(%s)"%uncomment(rest) elif command == 'ifndef': rest = "!defined(%s)"%uncomment(rest) elif rest.endswith("\\"): rest = rest[:-1]+"..." rest = uncomment(rest) new_level = [ (command, rest, lineno) ] stack.append(cur_level) cur_level = new_level f_out.write(line) elif command in ("else", "elif"): if len(cur_level) == 0 or cur_level[-1][0] == 'else': raise Problem("Unexpected #%s on %d"% (command,lineno)) if (len(cur_level) == 1 and command == 'else' and lineno > cur_level[0][2] + LINE_OBVIOUSNESS_LIMIT): f_out.write("#else /* !(%s) */\n"%cur_level[0][1]) else: f_out.write(line) cur_level.append((command, rest, lineno)) else: assert command == 'endif' if len(stack) == 0: raise Problem("Unmatched #%s on %s"% (command,lineno)) if lineno <= cur_level[0][2] + LINE_OBVIOUSNESS_LIMIT: f_out.write(line) elif len(cur_level) == 1 or ( len(cur_level) == 2 and cur_level[1][0] == 'else'): f_out.write("#endif /* %s */\n"%cur_level[0][1]) else: f_out.write("#endif /* %s || ... */\n"%cur_level[0][1]) cur_level = stack.pop() if len(stack) or cur_level != whole_file: raise Problem("Missing #endif") import sys,os for fn in sys.argv[1:]: with open(fn+"_OUT", 'w') as output_file: translate(open(fn, 'r'), output_file) os.rename(fn+"_OUT", fn)