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 ]+<([a-zA-Z0-9_/\.]+)') 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 62def main(): 63 global filedict 64 opts, args = getopt.getopt(sys.argv[1:], 'i:') 65 for o, a in opts: 66 if o == '-i': 67 ignores.append(re.compile(a)) 68 if not args: 69 args = ['-'] 70 for filename in args: 71 if filename == '-': 72 sys.stdout.write('# Generated by h2py from stdin\n') 73 process(sys.stdin, sys.stdout) 74 else: 75 fp = open(filename, 'r') 76 outfile = os.path.basename(filename) 77 i = outfile.rfind('.') 78 if i > 0: outfile = outfile[:i] 79 modname = outfile.upper() 80 outfile = modname + '.py' 81 outfp = open(outfile, 'w') 82 outfp.write('# Generated by h2py from %s\n' % filename) 83 filedict = {} 84 for dir in searchdirs: 85 if filename[:len(dir)] == dir: 86 filedict[filename[len(dir)+1:]] = None # no '/' trailing 87 importable[filename[len(dir)+1:]] = modname 88 break 89 process(fp, outfp) 90 outfp.close() 91 fp.close() 92 93def pytify(body): 94 # replace ignored patterns by spaces 95 for p in ignores: 96 body = p.sub(' ', body) 97 # replace char literals by ord(...) 98 body = p_char.sub("ord('\\1')", body) 99 # Compute negative hexadecimal constants 100 start = 0 101 UMAX = 2*(sys.maxint+1) 102 while 1: 103 m = p_hex.search(body, start) 104 if not m: break 105 s,e = m.span() 106 val = long(body[slice(*m.span(1))], 16) 107 if val > sys.maxint: 108 val -= UMAX 109 body = body[:s] + "(" + str(val) + ")" + body[e:] 110 start = s + 1 111 return body 112 113def process(fp, outfp, env = {}): 114 lineno = 0 115 while 1: 116 line = fp.readline() 117 if not line: break 118 lineno = lineno + 1 119 match = p_define.match(line) 120 if match: 121 # gobble up continuation lines 122 while line[-2:] == '\\\n': 123 nextline = fp.readline() 124 if not nextline: break 125 lineno = lineno + 1 126 line = line + nextline 127 name = match.group(1) 128 body = line[match.end():] 129 body = pytify(body) 130 ok = 0 131 stmt = '%s = %s\n' % (name, body.strip()) 132 try: 133 exec stmt in env 134 except: 135 sys.stderr.write('Skipping: %s' % stmt) 136 else: 137 outfp.write(stmt) 138 match = p_macro.match(line) 139 if match: 140 macro, arg = match.group(1, 2) 141 body = line[match.end():] 142 body = pytify(body) 143 stmt = 'def %s(%s): return %s\n' % (macro, arg, body) 144 try: 145 exec stmt in env 146 except: 147 sys.stderr.write('Skipping: %s' % stmt) 148 else: 149 outfp.write(stmt) 150 match = p_include.match(line) 151 if match: 152 regs = match.regs 153 a, b = regs[1] 154 filename = line[a:b] 155 if importable.has_key(filename): 156 outfp.write('from %s import *\n' % importable[filename]) 157 elif not filedict.has_key(filename): 158 filedict[filename] = None 159 inclfp = None 160 for dir in searchdirs: 161 try: 162 inclfp = open(dir + '/' + filename) 163 break 164 except IOError: 165 pass 166 if inclfp: 167 outfp.write( 168 '\n# Included from %s\n' % filename) 169 process(inclfp, outfp, env) 170 else: 171 sys.stderr.write('Warning - could not find file %s\n' % 172 filename) 173 174if __name__ == '__main__': 175 main() 176