| 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()
|
|---|