2003-09-21 Seth Nickell <seth@gnome.org>
[platform/upstream/dbus.git] / python / extract.py
1 import commands
2 import glob
3 import re
4 import os
5 import string
6 import sys
7
8 def clean_func(buf):
9     buf = strip_comments(buf)
10     pat = re.compile(r"""\\\n""", re.MULTILINE) 
11     buf = pat.sub('',buf)
12     pat = re.compile(r"""^[#].*?$""", re.MULTILINE) 
13     buf = pat.sub('',buf)
14     pat = re.compile(r"""^(typedef|struct|enum)(\s|.|\n)*?;\s*""", re.MULTILINE) 
15     buf = pat.sub('',buf)
16     pat = re.compile(r"""\s+""", re.MULTILINE) 
17     buf = pat.sub(' ',buf)
18     pat = re.compile(r""";\s*""", re.MULTILINE) 
19     buf = pat.sub('\n',buf)
20     buf = buf.lstrip()
21     #pat=re.compile(r'\s+([*|&]+)\s*(\w+)')
22     pat = re.compile(r' \s+ ([*|&]+) \s* (\w+)',re.VERBOSE)
23     buf = pat.sub(r'\1 \2', buf)
24     pat = re.compile(r'\s+ (\w+) \[ \s* \]',re.VERBOSE)
25     buf = pat.sub(r'[] \1', buf)
26 #    buf = string.replace(buf, 'G_CONST_RETURN ', 'const-')
27     buf = string.replace(buf, 'const ', '')
28     return buf
29
30 def strip_comments(buf):
31     parts = []
32     lastpos = 0
33     while 1:
34         pos = string.find(buf, '/*', lastpos)
35         if pos >= 0:
36             parts.append(buf[lastpos:pos])
37             pos = string.find(buf, '*/', pos)
38             if pos >= 0:
39                 lastpos = pos + 2
40             else:
41                 break
42         else:
43             parts.append(buf[lastpos:])
44             break
45     return string.join(parts, '')
46
47 def find_enums(buf):
48     enums = []    
49     buf = strip_comments(buf)
50     buf = re.sub('\n', ' ', buf)
51     
52     enum_pat = re.compile(r'enum\s*{([^}]*)}\s*([A-Z][A-Za-z]*)(\s|;)')
53     splitter = re.compile(r'\s*,\s', re.MULTILINE)
54     pos = 0
55     while pos < len(buf):
56         m = enum_pat.search(buf, pos)
57         if not m: break
58
59         name = m.group(2)
60         vals = m.group(1)
61         isflags = string.find(vals, '<<') >= 0
62         entries = []
63         for val in splitter.split(vals):
64             if not string.strip(val): continue
65             entries.append(string.split(val)[0])
66         enums.append((name, isflags, entries))
67         
68         pos = m.end()
69     return enums
70
71 #typedef unsigned int   dbus_bool_t;
72 #typedef struct {
73 #
74 # }
75 #typedef struct FooStruct FooStruct;
76 # typedef void (* DBusAddWatchFunction)      (DBusWatch      *watch,
77 #                                           void           *data);
78
79 def find_typedefs(buf):
80     typedefs = []
81     buf = re.sub('\n', ' ', strip_comments(buf))
82     typedef_pat = re.compile(
83         r"""typedef\s*(?P<type>\w*)
84             \s*
85             ([(]\s*\*\s*(?P<callback>[\w* ]*)[)]|{([^}]*)}|)
86             \s*
87             (?P<args1>[(](?P<args2>[\s\w*,_]*)[)]|[\w ]*)""",
88         re.MULTILINE | re.VERBOSE)
89     pat = re.compile(r"""\s+""", re.MULTILINE) 
90     pos = 0
91     while pos < len(buf):
92         m = typedef_pat.search(buf, pos)
93         if not m:
94             break
95         if m.group('type') == 'enum':
96             pos = m.end()
97             continue
98         if m.group('args2') != None:
99             args = pat.sub(' ', m.group('args2'))
100             
101             current = '%s (* %s) (%s)' % (m.group('type'),
102                                           m.group('callback'),
103                                           args)
104         else:
105             current = '%s %s' % (m.group('type'), m.group('args1'))
106         typedefs.append(current)
107         pos = m.end()
108     return typedefs
109
110 proto_pat = re.compile(r"""
111 (?P<ret>(-|\w|\&|\*)+\s*)      # return type
112 \s+                            # skip whitespace
113 (?P<func>\w+)\s*[(]  # match the function name until the opening (
114 (?P<args>.*?)[)]               # group the function arguments
115 """, re.IGNORECASE|re.VERBOSE)
116 arg_split_pat = re.compile("\s*,\s*")
117
118
119 def find_functions(buf):
120     functions = []
121     buf = clean_func(buf)
122     buf = string.split(buf,'\n')
123     for p in buf:
124         if len(p) == 0:
125             continue
126         
127         m = proto_pat.match(p)
128         if m == None:
129             continue
130         
131         func = m.group('func')
132         ret = m.group('ret')
133         args = m.group('args')
134         args = arg_split_pat.split(args)
135 #        for i in range(len(args)):
136 #            spaces = string.count(args[i], ' ')
137 #            if spaces > 1:
138 #                args[i] = string.replace(args[i], ' ', '-', spaces - 1)
139
140         functions.append((func, ret, args))
141     return functions
142
143 class Writer:
144     def __init__(self, filename, enums, typedefs, functions):
145         if not (enums or typedefs or functions):
146             return
147         print 'cdef extern from "%s":' % filename
148
149         self.output_enums(enums)
150         self.output_typedefs(typedefs)
151         self.output_functions(functions)        
152         
153         print '    pass'
154         print
155         
156     def output_enums(self, enums):
157         for enum in enums:
158             print '    ctypedef enum %s:' % enum[0]
159             if enum[1] == 0:
160                 for item in enum[2]:
161                     print '        %s' % item
162             else:
163                 i = 0
164                 for item in enum[2]:
165                     print '        %s' % item                    
166 #                    print '        %s = 1 << %d' % (item, i)
167                     i += 1
168             print
169     def output_typedefs(self, typedefs):
170         for typedef in typedefs:
171             if typedef.find('va_list') != -1:
172                 continue
173             
174             parts = typedef.split()
175             if parts[0] == 'struct':
176                 if parts[-2] == parts[-1]:
177                     parts = parts[:-1]
178                 print '    ctypedef %s' % ' '.join(parts)
179             else:
180                 print '    ctypedef %s' % typedef
181
182     def output_functions(self, functions):
183         for func, ret, args in functions:
184             if func[0] == '_':
185                 continue
186
187             str = ', '.join(args)
188             if str.find('...') != -1:
189                 continue
190             if str.find('va_list') != -1:
191                 continue
192             if str.strip() == 'void':
193                 continue
194             print '    %-20s %s (%s)' % (ret, func, str)
195
196 def do_buffer(name, buffer):
197     functions = find_functions(buffer)
198     typedefs = find_typedefs(buffer)
199     enums = find_enums(buffer)
200
201     Writer(name, enums, typedefs, functions)
202     
203 def do_header(filename, name=None):
204     if name == None:
205         name = filename
206         
207     buffer = ""
208     for line in open(filename).readlines():
209         if line[0] == '#':
210             continue
211         buffer += line
212
213     print '# -- %s -- ' % filename
214     do_buffer(name, buffer)
215     
216 filename = sys.argv[1]
217
218 if filename.endswith('.h'):
219     do_header(filename)
220     raise SystemExit
221
222 cppflags = ""
223
224 for flag in sys.argv[2:]:
225     cppflags = cppflags + " " + flag
226
227 fd = open(filename)
228
229 for line in fd.readlines():
230     if line.startswith('#include'):
231         filename = line.split(' ')[1][1:-2]
232         command = "echo '%s'|cpp %s" % (line, cppflags)
233         sys.stderr.write('running %s' % (command))
234         output = commands.getoutput(command)
235         do_buffer(filename, output)
236     else:
237         print line[:-1]