1#! /usr/bin/env python 2 3# Read #define's and translate to Python code. 4# Handle #include statements. 5# Handle #define macros with one argument. 6# Anything that isn't recognized or doesn't translate into valid 7# Python is ignored. 8 9# Without filename arguments, acts as a filter. 10# If one or more filenames are given, output is written to corresponding 11# filenames in the local directory, translated to all uppercase, with 12# the extension replaced by ".py". 13 14# By passing one or more options of the form "-i regular_expression" 15# you can specify additional strings to be ignored. This is useful 16# e.g. to ignore casts to u_long: simply specify "-i '(u_long)'". 17 18# XXX To do: 19# - turn trailing C comments into Python comments 20# - turn C Boolean operators "&& || !" into Python "and or not" 21# - what to do about #if(def)? 22# - what to do about macros with multiple parameters? 23 24import sys, re, getopt, os 25 26p_define = re.compile('^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+') 27 28p_macro = re.compile( 29 '^[\t ]*#[\t ]*define[\t ]+' 30 '([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+') 31 32p_include = re.compile('^[\t ]*#[\t ]*include[\t ]+<([^>\n]+)>') 33 34p_comment = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?') 35p_cpp_comment = re.compile('//.*') 36 37ignores = [p_comment, p_cpp_comment] 38 39p_char = re.compile(r"'(\\.[^\\]*|[^\\])'") 40 41p_hex = re.compile(r"0x([0-9a-fA-F]+)L?") 42 43filedict = {} 44importable = {} 45 46try: 47 searchdirs=os.environ['include'].split(';') 48except KeyError: 49 try: 50 searchdirs=os.environ['INCLUDE'].split(';') 51 except KeyError: 52 try: 53 if sys.platform.find("beos") == 0: 54 searchdirs=os.environ['BEINCLUDES'].split(';') 55 elif sys.platform.startswith("atheos"): 56 searchdirs=os.environ['C_INCLUDE_PATH'].split(':') 57 else: 58 raise KeyError 59 except KeyError: 60 searchdirs=['/usr/include'] 61 try: 62 searchdirs.insert(0, os.path.join('/usr/include', 63 os.environ['MULTIARCH'])) 64 except KeyError: 65 pass 66 67 68def main(): 69 global filedict 70 opts, args = getopt.getopt(sys.argv[1:], 'i:') 71 for o, a in opts: 72 if o == '-i': 73 ignores.append(re.compile(a)) 74 if not args: 75 args = ['-'] 76 for filename in args: 77 if filename == '-': 78 sys.stdout.write('# Generated by h2py from stdin\n') 79 process(sys.stdin, sys.stdout) 80 else: 81 fp = open(filename, 'r') 82 outfile = os.path.basename(filename) 83 i = outfile.rfind('.') 84 if i > 0: outfile = outfile[:i] 85 modname = outfile.upper() 86 outfile = modname + '.py' 87 outfp = open(outfile, 'w') 88 outfp.write('# Generated by h2py from %s\n' % filename) 89 filedict = {} 90 for dir in searchdirs: 91 if filename[:len(dir)] == dir: 92 filedict[filename[len(dir)+1:]] = None # no '/' trailing 93 importable[filename[len(dir)+1:]] = modname 94 break 95 process(fp, outfp) 96 outfp.close() 97 fp.close() 98 99def pytify(body): 100 # replace ignored patterns by spaces 101 for p in ignores: 102 body = p.sub(' ', body) 103 # replace char literals by ord(...) 104 body = p_char.sub("ord('\\1')", body) 105 # Compute negative hexadecimal constants 106 start = 0 107 UMAX = 2*(sys.maxint+1) 108 while 1: 109 m = p_hex.search(body, start) 110 if not m: break 111 s,e = m.span() 112 val = long(body[slice(*m.span(1))], 16) 113 if val > sys.maxint: 114 val -= UMAX 115 body = body[:s] + "(" + str(val) + ")" + body[e:] 116 start = s + 1 117 return body 118 119def process(fp, outfp, env = {}): 120 lineno = 0 121 while 1: 122 line = fp.readline() 123 if not line: break 124 lineno = lineno + 1 125 match = p_define.match(line) 126 if match: 127 # gobble up continuation lines 128 while line[-2:] == '\\\n': 129 nextline = fp.readline() 130 if not nextline: break 131 lineno = lineno + 1 132 line = line + nextline 133 name = match.group(1) 134 body = line[match.end():] 135 body = pytify(body) 136 ok = 0 137 stmt = '%s = %s\n' % (name, body.strip()) 138 try: 139 exec stmt in env 140 except: 141 sys.stderr.write('Skipping: %s' % stmt) 142 else: 143 outfp.write(stmt) 144 match = p_macro.match(line) 145 if match: 146 macro, arg = match.group(1, 2) 147 body = line[match.end():] 148 body = pytify(body) 149 stmt = 'def %s(%s): return %s\n' % (macro, arg, body) 150 try: 151 exec stmt in env 152 except: 153 sys.stderr.write('Skipping: %s' % stmt) 154 else: 155 outfp.write(stmt) 156 match = p_include.match(line) 157 if match: 158 regs = match.regs 159 a, b = regs[1] 160 filename = line[a:b] 161 if importable.has_key(filename): 162 outfp.write('from %s import *\n' % importable[filename]) 163 elif not filedict.has_key(filename): 164 filedict[filename] = None 165 inclfp = None 166 for dir in searchdirs: 167 try: 168 inclfp = open(dir + '/' + filename) 169 break 170 except IOError: 171 pass 172 if inclfp: 173 outfp.write( 174 '\n# Included from %s\n' % filename) 175 process(inclfp, outfp, env) 176 else: 177 sys.stderr.write('Warning - could not find file %s\n' % 178 filename) 179 180if __name__ == '__main__': 181 main() 182