[2] | 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']
|
---|
[391] | 61 | try:
|
---|
| 62 | searchdirs.insert(0, os.path.join('/usr/include',
|
---|
| 63 | os.environ['MULTIARCH']))
|
---|
| 64 | except KeyError:
|
---|
| 65 | pass
|
---|
[2] | 66 |
|
---|
[391] | 67 |
|
---|
[2] | 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(...)
|
---|
[391] | 104 | body = p_char.sub("ord('\\1')", body)
|
---|
[2] | 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()
|
---|