| 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 |  | 
|---|
| 24 | import sys, re, getopt, os | 
|---|
| 25 |  | 
|---|
| 26 | p_define = re.compile('^[\t ]*#[\t ]*define[\t ]+([a-zA-Z0-9_]+)[\t ]+') | 
|---|
| 27 |  | 
|---|
| 28 | p_macro = re.compile( | 
|---|
| 29 | '^[\t ]*#[\t ]*define[\t ]+' | 
|---|
| 30 | '([a-zA-Z0-9_]+)\(([_a-zA-Z][_a-zA-Z0-9]*)\)[\t ]+') | 
|---|
| 31 |  | 
|---|
| 32 | p_include = re.compile('^[\t ]*#[\t ]*include[\t ]+<([a-zA-Z0-9_/\.]+)') | 
|---|
| 33 |  | 
|---|
| 34 | p_comment = re.compile(r'/\*([^*]+|\*+[^/])*(\*+/)?') | 
|---|
| 35 | p_cpp_comment = re.compile('//.*') | 
|---|
| 36 |  | 
|---|
| 37 | ignores = [p_comment, p_cpp_comment] | 
|---|
| 38 |  | 
|---|
| 39 | p_char = re.compile(r"'(\\.[^\\]*|[^\\])'") | 
|---|
| 40 |  | 
|---|
| 41 | p_hex = re.compile(r"0x([0-9a-fA-F]+)L?") | 
|---|
| 42 |  | 
|---|
| 43 | filedict = {} | 
|---|
| 44 | importable = {} | 
|---|
| 45 |  | 
|---|
| 46 | try: | 
|---|
| 47 | searchdirs=os.environ['include'].split(';') | 
|---|
| 48 | except 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 |  | 
|---|
| 68 | def 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 |  | 
|---|
| 99 | def 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 |  | 
|---|
| 119 | def 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 |  | 
|---|
| 180 | if __name__ == '__main__': | 
|---|
| 181 | main() | 
|---|