blob: bd88a18438d08170fe5ef5b1f8c1eb66791286dc [file] [log] [blame]
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001#!/usr/bin/python -u
2#
3# This is the API builder, it parses the C sources and build the
4# API formal description in XML.
5#
6# See Copyright for the status of this software.
7#
8# daniel@veillard.com
9#
Daniel Veillard540a31a2003-01-21 11:21:07 +000010import os, sys
Daniel Veillarda9b66d02002-12-11 14:23:49 +000011import string
12import glob
13
Daniel Veillarde8ba84e2003-11-18 13:54:15 +000014debug=0
Daniel Veillarda2351322004-06-27 12:08:10 +000015#debugsym='ignorableWhitespaceSAXFunc'
16debugsym=None
Daniel Veillarde8ba84e2003-11-18 13:54:15 +000017
Daniel Veillardde0a0a52003-04-24 17:12:57 +000018#
19# C parser analysis code
20#
21ignored_files = {
22 "trio": "too many non standard macros",
23 "trio.c": "too many non standard macros",
24 "trionan.c": "too many non standard macros",
25 "triostr.c": "too many non standard macros",
26 "acconfig.h": "generated portability layer",
27 "config.h": "generated portability layer",
28 "libxml.h": "internal only",
Daniel Veillard92fc02c2003-04-24 23:12:35 +000029 "testOOM.c": "out of memory tester",
30 "testOOMlib.h": "out of memory tester",
31 "testOOMlib.c": "out of memory tester",
Daniel Veillard189f46b2004-01-25 21:03:04 +000032 "rngparser.c": "not yet integrated",
33 "rngparser.h": "not yet integrated",
Daniel Veillarda2351322004-06-27 12:08:10 +000034 "elfgcchack.h": "not a normal header",
Daniel Veillarde43cc572004-11-03 11:50:29 +000035 "testapi.c": "generated regression tests",
Daniel Veillardf6b71bd2005-01-04 17:50:14 +000036 "testdso.c": "test for dynamid shared libraries",
Daniel Veillardde0a0a52003-04-24 17:12:57 +000037}
38
39ignored_words = {
40 "WINAPI": (0, "Windows keyword"),
41 "LIBXML_DLL_IMPORT": (0, "Special macro to flag external keywords"),
William M. Brackcdfa2862003-08-29 06:03:38 +000042 "XMLPUBVAR": (0, "Special macro for extern vars for win32"),
Daniel Veillarde8ba84e2003-11-18 13:54:15 +000043 "XSLTPUBVAR": (0, "Special macro for extern vars for win32"),
44 "EXSLTPUBVAR": (0, "Special macro for extern vars for win32"),
William M. Brackcdfa2862003-08-29 06:03:38 +000045 "XMLPUBFUN": (0, "Special macro for extern funcs for win32"),
Daniel Veillarde8ba84e2003-11-18 13:54:15 +000046 "XSLTPUBFUN": (0, "Special macro for extern funcs for win32"),
47 "EXSLTPUBFUN": (0, "Special macro for extern funcs for win32"),
William M. Brackcdfa2862003-08-29 06:03:38 +000048 "XMLCALL": (0, "Special macro for win32 calls"),
Daniel Veillarde8ba84e2003-11-18 13:54:15 +000049 "XSLTCALL": (0, "Special macro for win32 calls"),
50 "EXSLTCALL": (0, "Special macro for win32 calls"),
Daniel Veillardde0a0a52003-04-24 17:12:57 +000051 "__declspec": (3, "Windows keyword"),
Daniel Veillarda2351322004-06-27 12:08:10 +000052 "__stdcall": (0, "Windows keyword"),
Daniel Veillardde0a0a52003-04-24 17:12:57 +000053 "ATTRIBUTE_UNUSED": (0, "macro keyword"),
54 "LIBEXSLT_PUBLIC": (0, "macro keyword"),
55 "X_IN_Y": (5, "macro function builder"),
56}
57
Daniel Veillarda9b66d02002-12-11 14:23:49 +000058def escape(raw):
59 raw = string.replace(raw, '&', '&')
60 raw = string.replace(raw, '<', '&lt;')
61 raw = string.replace(raw, '>', '&gt;')
62 raw = string.replace(raw, "'", '&apos;')
63 raw = string.replace(raw, '"', '&quot;')
64 return raw
65
Daniel Veillard2925c0a2003-11-17 13:58:17 +000066def uniq(items):
67 d = {}
68 for item in items:
69 d[item]=1
70 return d.keys()
71
Daniel Veillarda9b66d02002-12-11 14:23:49 +000072class identifier:
Daniel Veillardbe586972003-11-18 20:56:51 +000073 def __init__(self, name, module=None, type=None, lineno = 0,
Daniel Veillarda2351322004-06-27 12:08:10 +000074 info=None, extra=None, conditionals = None):
Daniel Veillardbe586972003-11-18 20:56:51 +000075 self.name = name
76 self.module = module
77 self.type = type
78 self.info = info
79 self.extra = extra
80 self.lineno = lineno
81 self.static = 0
Daniel Veillarda2351322004-06-27 12:08:10 +000082 if conditionals == None or len(conditionals) == 0:
83 self.conditionals = None
84 else:
85 self.conditionals = conditionals[:]
86 if self.name == debugsym:
87 print "=> define %s : %s" % (debugsym, (module, type, info,
88 extra, conditionals))
Daniel Veillarda9b66d02002-12-11 14:23:49 +000089
Daniel Veillardbe586972003-11-18 20:56:51 +000090 def __repr__(self):
91 r = "%s %s:" % (self.type, self.name)
92 if self.static:
93 r = r + " static"
94 if self.module != None:
95 r = r + " from %s" % (self.module)
96 if self.info != None:
97 r = r + " " + `self.info`
98 if self.extra != None:
99 r = r + " " + `self.extra`
Daniel Veillarda2351322004-06-27 12:08:10 +0000100 if self.conditionals != None:
101 r = r + " " + `self.conditionals`
Daniel Veillardbe586972003-11-18 20:56:51 +0000102 return r
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000103
104
Daniel Veillardbe586972003-11-18 20:56:51 +0000105 def set_module(self, module):
106 self.module = module
107 def set_type(self, type):
108 self.type = type
109 def set_info(self, info):
110 self.info = info
111 def set_extra(self, extra):
112 self.extra = extra
113 def set_lineno(self, lineno):
114 self.lineno = lineno
115 def set_static(self, static):
116 self.static = static
Daniel Veillarda2351322004-06-27 12:08:10 +0000117 def set_conditionals(self, conditionals):
118 if conditionals == None or len(conditionals) == 0:
119 self.conditionals = None
120 else:
121 self.conditionals = conditionals[:]
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000122
Daniel Veillardbe586972003-11-18 20:56:51 +0000123 def get_name(self):
124 return self.name
125 def get_module(self):
126 return self.module
127 def get_type(self):
128 return self.type
129 def get_info(self):
130 return self.info
131 def get_lineno(self):
132 return self.lineno
133 def get_extra(self):
134 return self.extra
135 def get_static(self):
136 return self.static
Daniel Veillarda2351322004-06-27 12:08:10 +0000137 def get_conditionals(self):
138 return self.conditionals
Daniel Veillardd8cf9062003-11-11 21:12:36 +0000139
Daniel Veillarda2351322004-06-27 12:08:10 +0000140 def update(self, module, type = None, info = None, extra=None,
141 conditionals=None):
142 if self.name == debugsym:
143 print "=> update %s : %s" % (debugsym, (module, type, info,
144 extra, conditionals))
Daniel Veillardbe586972003-11-18 20:56:51 +0000145 if module != None and self.module == None:
146 self.set_module(module)
147 if type != None and self.type == None:
148 self.set_type(type)
149 if info != None:
150 self.set_info(info)
151 if extra != None:
152 self.set_extra(extra)
Daniel Veillarda2351322004-06-27 12:08:10 +0000153 if conditionals != None:
154 self.set_conditionals(conditionals)
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000155
156
157class index:
Daniel Veillardbe586972003-11-18 20:56:51 +0000158 def __init__(self, name = "noname"):
159 self.name = name
160 self.identifiers = {}
161 self.functions = {}
162 self.variables = {}
163 self.includes = {}
164 self.structs = {}
165 self.enums = {}
166 self.typedefs = {}
167 self.macros = {}
168 self.references = {}
169 self.info = {}
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000170
Daniel Veillarda2351322004-06-27 12:08:10 +0000171 def add_ref(self, name, module, static, type, lineno, info=None, extra=None, conditionals = None):
Daniel Veillardbe586972003-11-18 20:56:51 +0000172 if name[0:2] == '__':
173 return None
174 d = None
175 try:
176 d = self.identifiers[name]
Daniel Veillarda2351322004-06-27 12:08:10 +0000177 d.update(module, type, lineno, info, extra, conditionals)
Daniel Veillardbe586972003-11-18 20:56:51 +0000178 except:
Daniel Veillarda2351322004-06-27 12:08:10 +0000179 d = identifier(name, module, type, lineno, info, extra, conditionals)
Daniel Veillardbe586972003-11-18 20:56:51 +0000180 self.identifiers[name] = d
Daniel Veillardd8cf9062003-11-11 21:12:36 +0000181
Daniel Veillardbe586972003-11-18 20:56:51 +0000182 if d != None and static == 1:
183 d.set_static(1)
Daniel Veillardd8cf9062003-11-11 21:12:36 +0000184
Daniel Veillardbe586972003-11-18 20:56:51 +0000185 if d != None and name != None and type != None:
186 self.references[name] = d
Daniel Veillardd8cf9062003-11-11 21:12:36 +0000187
Daniel Veillarda2351322004-06-27 12:08:10 +0000188 if name == debugsym:
189 print "New ref: %s" % (d)
190
191 return d
192
193 def add(self, name, module, static, type, lineno, info=None, extra=None, conditionals = None):
Daniel Veillardbe586972003-11-18 20:56:51 +0000194 if name[0:2] == '__':
195 return None
196 d = None
197 try:
198 d = self.identifiers[name]
Daniel Veillarda2351322004-06-27 12:08:10 +0000199 d.update(module, type, lineno, info, extra, conditionals)
Daniel Veillardbe586972003-11-18 20:56:51 +0000200 except:
Daniel Veillarda2351322004-06-27 12:08:10 +0000201 d = identifier(name, module, type, lineno, info, extra, conditionals)
Daniel Veillardbe586972003-11-18 20:56:51 +0000202 self.identifiers[name] = d
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000203
Daniel Veillardbe586972003-11-18 20:56:51 +0000204 if d != None and static == 1:
205 d.set_static(1)
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000206
Daniel Veillardbe586972003-11-18 20:56:51 +0000207 if d != None and name != None and type != None:
208 if type == "function":
209 self.functions[name] = d
210 elif type == "functype":
211 self.functions[name] = d
212 elif type == "variable":
213 self.variables[name] = d
214 elif type == "include":
215 self.includes[name] = d
216 elif type == "struct":
217 self.structs[name] = d
218 elif type == "enum":
219 self.enums[name] = d
220 elif type == "typedef":
221 self.typedefs[name] = d
222 elif type == "macro":
223 self.macros[name] = d
224 else:
225 print "Unable to register type ", type
Daniel Veillarda2351322004-06-27 12:08:10 +0000226
227 if name == debugsym:
228 print "New symbol: %s" % (d)
229
Daniel Veillardbe586972003-11-18 20:56:51 +0000230 return d
231
232 def merge(self, idx):
233 for id in idx.functions.keys():
234 #
235 # macro might be used to override functions or variables
236 # definitions
237 #
238 if self.macros.has_key(id):
239 del self.macros[id]
240 if self.functions.has_key(id):
241 print "function %s from %s redeclared in %s" % (
242 id, self.functions[id].module, idx.functions[id].module)
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000243 else:
Daniel Veillardbe586972003-11-18 20:56:51 +0000244 self.functions[id] = idx.functions[id]
245 self.identifiers[id] = idx.functions[id]
246 for id in idx.variables.keys():
247 #
248 # macro might be used to override functions or variables
249 # definitions
250 #
251 if self.macros.has_key(id):
252 del self.macros[id]
253 if self.variables.has_key(id):
254 print "variable %s from %s redeclared in %s" % (
255 id, self.variables[id].module, idx.variables[id].module)
256 else:
257 self.variables[id] = idx.variables[id]
258 self.identifiers[id] = idx.variables[id]
259 for id in idx.structs.keys():
260 if self.structs.has_key(id):
261 print "struct %s from %s redeclared in %s" % (
262 id, self.structs[id].module, idx.structs[id].module)
263 else:
264 self.structs[id] = idx.structs[id]
265 self.identifiers[id] = idx.structs[id]
266 for id in idx.typedefs.keys():
267 if self.typedefs.has_key(id):
268 print "typedef %s from %s redeclared in %s" % (
269 id, self.typedefs[id].module, idx.typedefs[id].module)
270 else:
271 self.typedefs[id] = idx.typedefs[id]
272 self.identifiers[id] = idx.typedefs[id]
273 for id in idx.macros.keys():
274 #
275 # macro might be used to override functions or variables
276 # definitions
277 #
278 if self.variables.has_key(id):
279 continue
280 if self.functions.has_key(id):
281 continue
282 if self.enums.has_key(id):
283 continue
284 if self.macros.has_key(id):
285 print "macro %s from %s redeclared in %s" % (
286 id, self.macros[id].module, idx.macros[id].module)
287 else:
288 self.macros[id] = idx.macros[id]
289 self.identifiers[id] = idx.macros[id]
290 for id in idx.enums.keys():
291 if self.enums.has_key(id):
292 print "enum %s from %s redeclared in %s" % (
293 id, self.enums[id].module, idx.enums[id].module)
294 else:
295 self.enums[id] = idx.enums[id]
296 self.identifiers[id] = idx.enums[id]
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000297
Daniel Veillardbe586972003-11-18 20:56:51 +0000298 def merge_public(self, idx):
299 for id in idx.functions.keys():
300 if self.functions.has_key(id):
William M. Brack21e4ef22005-01-02 09:53:13 +0000301 # check that function condition agrees with header
302 if idx.functions[id].conditionals != \
303 self.functions[id].conditionals:
304 print "Header condition differs from Function for %s:" \
305 % id
306 print " H: %s" % self.functions[id].conditionals
307 print " C: %s" % idx.functions[id].conditionals
Daniel Veillardbe586972003-11-18 20:56:51 +0000308 up = idx.functions[id]
309 self.functions[id].update(None, up.type, up.info, up.extra)
Daniel Veillardc1eed322002-12-12 11:01:32 +0000310 # else:
311 # print "Function %s from %s is not declared in headers" % (
William M. Brack21e4ef22005-01-02 09:53:13 +0000312 # id, idx.functions[id].module)
Daniel Veillardc1eed322002-12-12 11:01:32 +0000313 # TODO: do the same for variables.
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000314
Daniel Veillardbe586972003-11-18 20:56:51 +0000315 def analyze_dict(self, type, dict):
316 count = 0
317 public = 0
318 for name in dict.keys():
319 id = dict[name]
320 count = count + 1
321 if id.static == 0:
322 public = public + 1
323 if count != public:
324 print " %d %s , %d public" % (count, type, public)
325 elif count != 0:
326 print " %d public %s" % (count, type)
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000327
328
Daniel Veillardbe586972003-11-18 20:56:51 +0000329 def analyze(self):
330 self.analyze_dict("functions", self.functions)
331 self.analyze_dict("variables", self.variables)
332 self.analyze_dict("structs", self.structs)
333 self.analyze_dict("typedefs", self.typedefs)
334 self.analyze_dict("macros", self.macros)
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000335
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000336class CLexer:
Daniel Veillardbe586972003-11-18 20:56:51 +0000337 """A lexer for the C language, tokenize the input by reading and
338 analyzing it line by line"""
339 def __init__(self, input):
340 self.input = input
341 self.tokens = []
342 self.line = ""
343 self.lineno = 0
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000344
Daniel Veillardbe586972003-11-18 20:56:51 +0000345 def getline(self):
346 line = ''
347 while line == '':
348 line = self.input.readline()
349 if not line:
350 return None
351 self.lineno = self.lineno + 1
352 line = string.lstrip(line)
353 line = string.rstrip(line)
354 if line == '':
355 continue
356 while line[-1] == '\\':
357 line = line[:-1]
358 n = self.input.readline()
359 self.lineno = self.lineno + 1
360 n = string.lstrip(n)
361 n = string.rstrip(n)
362 if not n:
363 break
364 else:
365 line = line + n
366 return line
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000367
Daniel Veillardbe586972003-11-18 20:56:51 +0000368 def getlineno(self):
369 return self.lineno
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000370
Daniel Veillardbe586972003-11-18 20:56:51 +0000371 def push(self, token):
372 self.tokens.insert(0, token);
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000373
Daniel Veillardbe586972003-11-18 20:56:51 +0000374 def debug(self):
375 print "Last token: ", self.last
376 print "Token queue: ", self.tokens
377 print "Line %d end: " % (self.lineno), self.line
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000378
Daniel Veillardbe586972003-11-18 20:56:51 +0000379 def token(self):
380 while self.tokens == []:
381 if self.line == "":
382 line = self.getline()
383 else:
384 line = self.line
385 self.line = ""
386 if line == None:
387 return None
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000388
Daniel Veillardbe586972003-11-18 20:56:51 +0000389 if line[0] == '#':
390 self.tokens = map((lambda x: ('preproc', x)),
391 string.split(line))
392 break;
393 l = len(line)
394 if line[0] == '"' or line[0] == "'":
395 end = line[0]
396 line = line[1:]
397 found = 0
398 tok = ""
399 while found == 0:
400 i = 0
401 l = len(line)
402 while i < l:
403 if line[i] == end:
404 self.line = line[i+1:]
405 line = line[:i]
406 l = i
407 found = 1
408 break
409 if line[i] == '\\':
410 i = i + 1
411 i = i + 1
412 tok = tok + line
413 if found == 0:
414 line = self.getline()
415 if line == None:
416 return None
417 self.last = ('string', tok)
418 return self.last
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000419
Daniel Veillardbe586972003-11-18 20:56:51 +0000420 if l >= 2 and line[0] == '/' and line[1] == '*':
421 line = line[2:]
422 found = 0
423 tok = ""
424 while found == 0:
425 i = 0
426 l = len(line)
427 while i < l:
428 if line[i] == '*' and i+1 < l and line[i+1] == '/':
429 self.line = line[i+2:]
430 line = line[:i-1]
431 l = i
432 found = 1
433 break
434 i = i + 1
435 if tok != "":
436 tok = tok + "\n"
437 tok = tok + line
438 if found == 0:
439 line = self.getline()
440 if line == None:
441 return None
442 self.last = ('comment', tok)
443 return self.last
444 if l >= 2 and line[0] == '/' and line[1] == '/':
445 line = line[2:]
446 self.last = ('comment', line)
447 return self.last
448 i = 0
449 while i < l:
450 if line[i] == '/' and i+1 < l and line[i+1] == '/':
451 self.line = line[i:]
452 line = line[:i]
453 break
454 if line[i] == '/' and i+1 < l and line[i+1] == '*':
455 self.line = line[i:]
456 line = line[:i]
457 break
458 if line[i] == '"' or line[i] == "'":
459 self.line = line[i:]
460 line = line[:i]
461 break
462 i = i + 1
463 l = len(line)
464 i = 0
465 while i < l:
466 if line[i] == ' ' or line[i] == '\t':
467 i = i + 1
468 continue
469 o = ord(line[i])
470 if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
471 (o >= 48 and o <= 57):
472 s = i
473 while i < l:
474 o = ord(line[i])
475 if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
476 (o >= 48 and o <= 57) or string.find(
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000477 " \t(){}:;,+-*/%&!|[]=><", line[i]) == -1:
Daniel Veillardbe586972003-11-18 20:56:51 +0000478 i = i + 1
479 else:
480 break
481 self.tokens.append(('name', line[s:i]))
482 continue
483 if string.find("(){}:;,[]", line[i]) != -1:
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000484# if line[i] == '(' or line[i] == ')' or line[i] == '{' or \
485# line[i] == '}' or line[i] == ':' or line[i] == ';' or \
486# line[i] == ',' or line[i] == '[' or line[i] == ']':
Daniel Veillardbe586972003-11-18 20:56:51 +0000487 self.tokens.append(('sep', line[i]))
488 i = i + 1
489 continue
490 if string.find("+-*><=/%&!|.", line[i]) != -1:
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000491# if line[i] == '+' or line[i] == '-' or line[i] == '*' or \
492# line[i] == '>' or line[i] == '<' or line[i] == '=' or \
493# line[i] == '/' or line[i] == '%' or line[i] == '&' or \
494# line[i] == '!' or line[i] == '|' or line[i] == '.':
Daniel Veillardbe586972003-11-18 20:56:51 +0000495 if line[i] == '.' and i + 2 < l and \
496 line[i+1] == '.' and line[i+2] == '.':
497 self.tokens.append(('name', '...'))
498 i = i + 3
499 continue
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000500
Daniel Veillardbe586972003-11-18 20:56:51 +0000501 j = i + 1
502 if j < l and (
503 string.find("+-*><=/%&!|", line[j]) != -1):
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000504# line[j] == '+' or line[j] == '-' or line[j] == '*' or \
505# line[j] == '>' or line[j] == '<' or line[j] == '=' or \
506# line[j] == '/' or line[j] == '%' or line[j] == '&' or \
507# line[j] == '!' or line[j] == '|'):
Daniel Veillardbe586972003-11-18 20:56:51 +0000508 self.tokens.append(('op', line[i:j+1]))
509 i = j + 1
510 else:
511 self.tokens.append(('op', line[i]))
512 i = i + 1
513 continue
514 s = i
515 while i < l:
516 o = ord(line[i])
517 if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
518 (o >= 48 and o <= 57) or (
519 string.find(" \t(){}:;,+-*/%&!|[]=><", line[i]) == -1):
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000520# line[i] != ' ' and line[i] != '\t' and
521# line[i] != '(' and line[i] != ')' and
522# line[i] != '{' and line[i] != '}' and
523# line[i] != ':' and line[i] != ';' and
524# line[i] != ',' and line[i] != '+' and
525# line[i] != '-' and line[i] != '*' and
526# line[i] != '/' and line[i] != '%' and
527# line[i] != '&' and line[i] != '!' and
528# line[i] != '|' and line[i] != '[' and
529# line[i] != ']' and line[i] != '=' and
530# line[i] != '*' and line[i] != '>' and
531# line[i] != '<'):
Daniel Veillardbe586972003-11-18 20:56:51 +0000532 i = i + 1
533 else:
534 break
535 self.tokens.append(('name', line[s:i]))
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000536
Daniel Veillardbe586972003-11-18 20:56:51 +0000537 tok = self.tokens[0]
538 self.tokens = self.tokens[1:]
539 self.last = tok
540 return tok
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000541
542class CParser:
Daniel Veillardbe586972003-11-18 20:56:51 +0000543 """The C module parser"""
544 def __init__(self, filename, idx = None):
545 self.filename = filename
546 if len(filename) > 2 and filename[-2:] == '.h':
547 self.is_header = 1
548 else:
549 self.is_header = 0
550 self.input = open(filename)
551 self.lexer = CLexer(self.input)
552 if idx == None:
553 self.index = index()
554 else:
555 self.index = idx
556 self.top_comment = ""
557 self.last_comment = ""
558 self.comment = None
559 self.collect_ref = 0
Daniel Veillard1e906612003-12-05 14:57:46 +0000560 self.no_error = 0
Daniel Veillarda2351322004-06-27 12:08:10 +0000561 self.conditionals = []
562 self.defines = []
Daniel Veillardd8cf9062003-11-11 21:12:36 +0000563
Daniel Veillardbe586972003-11-18 20:56:51 +0000564 def collect_references(self):
565 self.collect_ref = 1
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000566
Daniel Veillard1e906612003-12-05 14:57:46 +0000567 def stop_error(self):
568 self.no_error = 1
569
570 def start_error(self):
571 self.no_error = 0
572
Daniel Veillardbe586972003-11-18 20:56:51 +0000573 def lineno(self):
574 return self.lexer.getlineno()
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000575
Daniel Veillardbe586972003-11-18 20:56:51 +0000576 def index_add(self, name, module, static, type, info=None, extra = None):
577 self.index.add(name, module, static, type, self.lineno(),
Daniel Veillarda2351322004-06-27 12:08:10 +0000578 info, extra, self.conditionals)
Daniel Veillardd8cf9062003-11-11 21:12:36 +0000579
Daniel Veillardbe586972003-11-18 20:56:51 +0000580 def index_add_ref(self, name, module, static, type, info=None,
581 extra = None):
582 self.index.add_ref(name, module, static, type, self.lineno(),
Daniel Veillarda2351322004-06-27 12:08:10 +0000583 info, extra, self.conditionals)
Daniel Veillardd8cf9062003-11-11 21:12:36 +0000584
Daniel Veillard1e906612003-12-05 14:57:46 +0000585 def warning(self, msg):
586 if self.no_error:
587 return
588 print msg
589
Daniel Veillardbe586972003-11-18 20:56:51 +0000590 def error(self, msg, token=-1):
Daniel Veillard1e906612003-12-05 14:57:46 +0000591 if self.no_error:
592 return
593
Daniel Veillardbe586972003-11-18 20:56:51 +0000594 print "Parse Error: " + msg
595 if token != -1:
596 print "Got token ", token
597 self.lexer.debug()
598 sys.exit(1)
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000599
Daniel Veillardbe586972003-11-18 20:56:51 +0000600 def debug(self, msg, token=-1):
601 print "Debug: " + msg
602 if token != -1:
603 print "Got token ", token
604 self.lexer.debug()
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000605
Daniel Veillardbe586972003-11-18 20:56:51 +0000606 def parseTopComment(self, comment):
607 res = {}
608 lines = string.split(comment, "\n")
609 item = None
610 for line in lines:
611 while line != "" and (line[0] == ' ' or line[0] == '\t'):
612 line = line[1:]
613 while line != "" and line[0] == '*':
614 line = line[1:]
615 while line != "" and (line[0] == ' ' or line[0] == '\t'):
616 line = line[1:]
617 try:
618 (it, line) = string.split(line, ":", 1)
619 item = it
620 while line != "" and (line[0] == ' ' or line[0] == '\t'):
621 line = line[1:]
622 if res.has_key(item):
623 res[item] = res[item] + " " + line
624 else:
625 res[item] = line
626 except:
627 if item != None:
628 if res.has_key(item):
629 res[item] = res[item] + " " + line
630 else:
631 res[item] = line
632 self.index.info = res
633
634 def parseComment(self, token):
635 if self.top_comment == "":
636 self.top_comment = token[1]
637 if self.comment == None or token[1][0] == '*':
638 self.comment = token[1];
639 else:
640 self.comment = self.comment + token[1]
641 token = self.lexer.token()
Daniel Veillard1e906612003-12-05 14:57:46 +0000642
643 if string.find(self.comment, "DOC_DISABLE") != -1:
644 self.stop_error()
645
646 if string.find(self.comment, "DOC_ENABLE") != -1:
647 self.start_error()
648
Daniel Veillardbe586972003-11-18 20:56:51 +0000649 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000650
651 #
652 # Parse a comment block associate to a macro
653 #
Daniel Veillardbe586972003-11-18 20:56:51 +0000654 def parseMacroComment(self, name, quiet = 0):
655 if name[0:2] == '__':
656 quiet = 1
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000657
Daniel Veillardbe586972003-11-18 20:56:51 +0000658 args = []
659 desc = ""
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000660
Daniel Veillardbe586972003-11-18 20:56:51 +0000661 if self.comment == None:
662 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000663 self.warning("Missing comment for macro %s" % (name))
Daniel Veillardbe586972003-11-18 20:56:51 +0000664 return((args, desc))
665 if self.comment[0] != '*':
666 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000667 self.warning("Missing * in macro comment for %s" % (name))
Daniel Veillardbe586972003-11-18 20:56:51 +0000668 return((args, desc))
669 lines = string.split(self.comment, '\n')
670 if lines[0] == '*':
671 del lines[0]
672 if lines[0] != "* %s:" % (name):
673 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000674 self.warning("Misformatted macro comment for %s" % (name))
675 self.warning(" Expecting '* %s:' got '%s'" % (name, lines[0]))
Daniel Veillardbe586972003-11-18 20:56:51 +0000676 return((args, desc))
677 del lines[0]
678 while lines[0] == '*':
679 del lines[0]
680 while len(lines) > 0 and lines[0][0:3] == '* @':
681 l = lines[0][3:]
682 try:
683 (arg, desc) = string.split(l, ':', 1)
684 desc=string.strip(desc)
685 arg=string.strip(arg)
686 except:
687 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000688 self.warning("Misformatted macro comment for %s" % (name))
689 self.warning(" problem with '%s'" % (lines[0]))
Daniel Veillardbe586972003-11-18 20:56:51 +0000690 del lines[0]
691 continue
692 del lines[0]
693 l = string.strip(lines[0])
694 while len(l) > 2 and l[0:3] != '* @':
695 while l[0] == '*':
696 l = l[1:]
697 desc = desc + ' ' + string.strip(l)
698 del lines[0]
699 if len(lines) == 0:
700 break
701 l = lines[0]
702 args.append((arg, desc))
703 while len(lines) > 0 and lines[0] == '*':
704 del lines[0]
705 desc = ""
706 while len(lines) > 0:
707 l = lines[0]
708 while len(l) > 0 and l[0] == '*':
709 l = l[1:]
710 l = string.strip(l)
711 desc = desc + " " + l
712 del lines[0]
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000713
Daniel Veillardbe586972003-11-18 20:56:51 +0000714 desc = string.strip(desc)
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000715
Daniel Veillardbe586972003-11-18 20:56:51 +0000716 if quiet == 0:
717 if desc == "":
Daniel Veillard1e906612003-12-05 14:57:46 +0000718 self.warning("Macro comment for %s lack description of the macro" % (name))
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000719
Daniel Veillardbe586972003-11-18 20:56:51 +0000720 return((args, desc))
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000721
722 #
723 # Parse a comment block and merge the informations found in the
724 # parameters descriptions, finally returns a block as complete
725 # as possible
726 #
Daniel Veillardbe586972003-11-18 20:56:51 +0000727 def mergeFunctionComment(self, name, description, quiet = 0):
728 if name == 'main':
729 quiet = 1
730 if name[0:2] == '__':
731 quiet = 1
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000732
Daniel Veillardbe586972003-11-18 20:56:51 +0000733 (ret, args) = description
734 desc = ""
735 retdesc = ""
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000736
Daniel Veillardbe586972003-11-18 20:56:51 +0000737 if self.comment == None:
738 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000739 self.warning("Missing comment for function %s" % (name))
Daniel Veillardbe586972003-11-18 20:56:51 +0000740 return(((ret[0], retdesc), args, desc))
741 if self.comment[0] != '*':
742 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000743 self.warning("Missing * in function comment for %s" % (name))
Daniel Veillardbe586972003-11-18 20:56:51 +0000744 return(((ret[0], retdesc), args, desc))
745 lines = string.split(self.comment, '\n')
746 if lines[0] == '*':
747 del lines[0]
748 if lines[0] != "* %s:" % (name):
749 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000750 self.warning("Misformatted function comment for %s" % (name))
751 self.warning(" Expecting '* %s:' got '%s'" % (name, lines[0]))
Daniel Veillardbe586972003-11-18 20:56:51 +0000752 return(((ret[0], retdesc), args, desc))
753 del lines[0]
754 while lines[0] == '*':
755 del lines[0]
756 nbargs = len(args)
757 while len(lines) > 0 and lines[0][0:3] == '* @':
758 l = lines[0][3:]
759 try:
760 (arg, desc) = string.split(l, ':', 1)
761 desc=string.strip(desc)
762 arg=string.strip(arg)
763 except:
764 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000765 self.warning("Misformatted function comment for %s" % (name))
766 self.warning(" problem with '%s'" % (lines[0]))
Daniel Veillardbe586972003-11-18 20:56:51 +0000767 del lines[0]
768 continue
769 del lines[0]
770 l = string.strip(lines[0])
771 while len(l) > 2 and l[0:3] != '* @':
772 while l[0] == '*':
773 l = l[1:]
774 desc = desc + ' ' + string.strip(l)
775 del lines[0]
776 if len(lines) == 0:
777 break
778 l = lines[0]
779 i = 0
780 while i < nbargs:
781 if args[i][1] == arg:
782 args[i] = (args[i][0], arg, desc)
783 break;
784 i = i + 1
785 if i >= nbargs:
786 if not quiet:
Daniel Veillard1e906612003-12-05 14:57:46 +0000787 self.warning("Unable to find arg %s from function comment for %s" % (
788 arg, name))
Daniel Veillardbe586972003-11-18 20:56:51 +0000789 while len(lines) > 0 and lines[0] == '*':
790 del lines[0]
791 desc = ""
792 while len(lines) > 0:
793 l = lines[0]
794 while len(l) > 0 and l[0] == '*':
795 l = l[1:]
796 l = string.strip(l)
797 if len(l) >= 6 and l[0:6] == "return" or l[0:6] == "Return":
798 try:
799 l = string.split(l, ' ', 1)[1]
800 except:
801 l = ""
802 retdesc = string.strip(l)
803 del lines[0]
804 while len(lines) > 0:
805 l = lines[0]
806 while len(l) > 0 and l[0] == '*':
807 l = l[1:]
808 l = string.strip(l)
809 retdesc = retdesc + " " + l
810 del lines[0]
811 else:
812 desc = desc + " " + l
813 del lines[0]
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000814
Daniel Veillardbe586972003-11-18 20:56:51 +0000815 retdesc = string.strip(retdesc)
816 desc = string.strip(desc)
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000817
Daniel Veillardbe586972003-11-18 20:56:51 +0000818 if quiet == 0:
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000819 #
820 # report missing comments
821 #
Daniel Veillardbe586972003-11-18 20:56:51 +0000822 i = 0
823 while i < nbargs:
824 if args[i][2] == None and args[i][0] != "void" and args[i][1] != None:
William M. Brack21e4ef22005-01-02 09:53:13 +0000825 self.warning("Function comment for %s lacks description of arg %s" % (name, args[i][1]))
Daniel Veillardbe586972003-11-18 20:56:51 +0000826 i = i + 1
827 if retdesc == "" and ret[0] != "void":
William M. Brack21e4ef22005-01-02 09:53:13 +0000828 self.warning("Function comment for %s lacks description of return value" % (name))
Daniel Veillardbe586972003-11-18 20:56:51 +0000829 if desc == "":
William M. Brack21e4ef22005-01-02 09:53:13 +0000830 self.warning("Function comment for %s lacks description of the function" % (name))
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000831
832
Daniel Veillardbe586972003-11-18 20:56:51 +0000833 return(((ret[0], retdesc), args, desc))
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000834
Daniel Veillardbe586972003-11-18 20:56:51 +0000835 def parsePreproc(self, token):
Daniel Veillarda2351322004-06-27 12:08:10 +0000836 if debug:
837 print "=> preproc ", token, self.lexer.tokens
Daniel Veillardbe586972003-11-18 20:56:51 +0000838 name = token[1]
839 if name == "#include":
840 token = self.lexer.token()
841 if token == None:
842 return None
843 if token[0] == 'preproc':
844 self.index_add(token[1], self.filename, not self.is_header,
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000845 "include")
Daniel Veillardbe586972003-11-18 20:56:51 +0000846 return self.lexer.token()
847 return token
848 if name == "#define":
849 token = self.lexer.token()
850 if token == None:
851 return None
852 if token[0] == 'preproc':
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000853 # TODO macros with arguments
Daniel Veillardbe586972003-11-18 20:56:51 +0000854 name = token[1]
855 lst = []
856 token = self.lexer.token()
857 while token != None and token[0] == 'preproc' and \
858 token[1][0] != '#':
859 lst.append(token[1])
860 token = self.lexer.token()
861 try:
862 name = string.split(name, '(') [0]
863 except:
864 pass
865 info = self.parseMacroComment(name, not self.is_header)
866 self.index_add(name, self.filename, not self.is_header,
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000867 "macro", info)
Daniel Veillardbe586972003-11-18 20:56:51 +0000868 return token
William M. Brack21e4ef22005-01-02 09:53:13 +0000869
870 #
871 # Processing of conditionals modified by Bill 1/1/05
872 #
873 # We process conditionals (i.e. tokens from #ifdef, #ifndef,
874 # #if, #else and #endif) for headers and mainline code,
875 # store the ones from the header in libxml2-api.xml, and later
876 # (in the routine merge_public) verify that the two (header and
877 # mainline code) agree.
878 #
879 # There is a small problem with processing the headers. Some of
880 # the variables are not concerned with enabling / disabling of
881 # library functions (e.g. '__XML_PARSER_H__'), and we don't want
882 # them to be included in libxml2-api.xml, or involved in
883 # the check between the header and the mainline code. To
884 # accomplish this, we ignore any conditional which doesn't include
885 # the string 'ENABLED'
886 #
887 if name == "#ifdef":
888 apstr = self.lexer.tokens[0][1]
Daniel Veillarda2351322004-06-27 12:08:10 +0000889 try:
William M. Brack21e4ef22005-01-02 09:53:13 +0000890 self.defines.append(apstr)
891 if string.find(apstr, 'ENABLED') != -1:
892 self.conditionals.append("defined(%s)" % apstr)
Daniel Veillarda2351322004-06-27 12:08:10 +0000893 except:
894 pass
William M. Brack21e4ef22005-01-02 09:53:13 +0000895 elif name == "#ifndef":
896 apstr = self.lexer.tokens[0][1]
897 try:
898 self.defines.append(apstr)
899 if string.find(apstr, 'ENABLED') != -1:
900 self.conditionals.append("!defined(%s)" % apstr)
901 except:
902 pass
903 elif name == "#if":
904 apstr = ""
905 for tok in self.lexer.tokens:
906 if apstr != "":
907 apstr = apstr + " "
908 apstr = apstr + tok[1]
909 try:
910 self.defines.append(apstr)
911 if string.find(apstr, 'ENABLED') != -1:
912 self.conditionals.append(apstr)
913 except:
914 pass
915 elif name == "#else":
916 if self.conditionals != [] and \
917 string.find(self.defines[-1], 'ENABLED') != -1:
918 self.conditionals[-1] = "!(%s)" % self.conditionals[-1]
919 elif name == "#endif":
920 if self.conditionals != [] and \
921 string.find(self.defines[-1], 'ENABLED') != -1:
Daniel Veillarda2351322004-06-27 12:08:10 +0000922 self.conditionals = self.conditionals[:-1]
923 self.defines = self.defines[:-1]
Daniel Veillardbe586972003-11-18 20:56:51 +0000924 token = self.lexer.token()
925 while token != None and token[0] == 'preproc' and \
926 token[1][0] != '#':
927 token = self.lexer.token()
928 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000929
930 #
931 # token acquisition on top of the lexer, it handle internally
932 # preprocessor and comments since they are logically not part of
933 # the program structure.
934 #
Daniel Veillardbe586972003-11-18 20:56:51 +0000935 def token(self):
936 global ignored_words
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000937
Daniel Veillardbe586972003-11-18 20:56:51 +0000938 token = self.lexer.token()
939 while token != None:
940 if token[0] == 'comment':
941 token = self.parseComment(token)
942 continue
943 elif token[0] == 'preproc':
944 token = self.parsePreproc(token)
945 continue
946 elif token[0] == "name" and ignored_words.has_key(token[1]):
947 (n, info) = ignored_words[token[1]]
948 i = 0
949 while i < n:
950 token = self.lexer.token()
951 i = i + 1
952 token = self.lexer.token()
953 continue
954 else:
955 if debug:
956 print "=> ", token
957 return token
958 return None
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000959
960 #
961 # Parse a typedef, it records the type and its name.
962 #
Daniel Veillardbe586972003-11-18 20:56:51 +0000963 def parseTypedef(self, token):
964 if token == None:
965 return None
966 token = self.parseType(token)
967 if token == None:
968 self.error("parsing typedef")
969 return None
970 base_type = self.type
971 type = base_type
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000972 #self.debug("end typedef type", token)
Daniel Veillardbe586972003-11-18 20:56:51 +0000973 while token != None:
974 if token[0] == "name":
975 name = token[1]
976 signature = self.signature
977 if signature != None:
978 type = string.split(type, '(')[0]
979 d = self.mergeFunctionComment(name,
980 ((type, None), signature), 1)
981 self.index_add(name, self.filename, not self.is_header,
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000982 "functype", d)
Daniel Veillardbe586972003-11-18 20:56:51 +0000983 else:
984 if base_type == "struct":
985 self.index_add(name, self.filename, not self.is_header,
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000986 "struct", type)
Daniel Veillardbe586972003-11-18 20:56:51 +0000987 base_type = "struct " + name
988 else:
989 self.index_add(name, self.filename, not self.is_header,
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000990 "typedef", type)
Daniel Veillardbe586972003-11-18 20:56:51 +0000991 token = self.token()
992 else:
993 self.error("parsing typedef: expecting a name")
994 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +0000995 #self.debug("end typedef", token)
Daniel Veillardbe586972003-11-18 20:56:51 +0000996 if token != None and token[0] == 'sep' and token[1] == ',':
997 type = base_type
998 token = self.token()
999 while token != None and token[0] == "op":
1000 type = type + token[1]
1001 token = self.token()
1002 elif token != None and token[0] == 'sep' and token[1] == ';':
1003 break;
1004 elif token != None and token[0] == 'name':
1005 type = base_type
1006 continue;
1007 else:
1008 self.error("parsing typedef: expecting ';'", token)
1009 return token
1010 token = self.token()
1011 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001012
1013 #
1014 # Parse a C code block, used for functions it parse till
1015 # the balancing } included
1016 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001017 def parseBlock(self, token):
1018 while token != None:
1019 if token[0] == "sep" and token[1] == "{":
1020 token = self.token()
1021 token = self.parseBlock(token)
1022 elif token[0] == "sep" and token[1] == "}":
1023 self.comment = None
1024 token = self.token()
1025 return token
1026 else:
1027 if self.collect_ref == 1:
1028 oldtok = token
1029 token = self.token()
1030 if oldtok[0] == "name" and oldtok[1][0:3] == "xml":
1031 if token[0] == "sep" and token[1] == "(":
1032 self.index_add_ref(oldtok[1], self.filename,
Daniel Veillardd8cf9062003-11-11 21:12:36 +00001033 0, "function")
Daniel Veillardbe586972003-11-18 20:56:51 +00001034 token = self.token()
1035 elif token[0] == "name":
1036 token = self.token()
1037 if token[0] == "sep" and (token[1] == ";" or
1038 token[1] == "," or token[1] == "="):
1039 self.index_add_ref(oldtok[1], self.filename,
Daniel Veillardd8cf9062003-11-11 21:12:36 +00001040 0, "type")
Daniel Veillardbe586972003-11-18 20:56:51 +00001041 elif oldtok[0] == "name" and oldtok[1][0:4] == "XML_":
1042 self.index_add_ref(oldtok[1], self.filename,
Daniel Veillardd8cf9062003-11-11 21:12:36 +00001043 0, "typedef")
Daniel Veillardbe586972003-11-18 20:56:51 +00001044 elif oldtok[0] == "name" and oldtok[1][0:7] == "LIBXML_":
1045 self.index_add_ref(oldtok[1], self.filename,
Daniel Veillardd8cf9062003-11-11 21:12:36 +00001046 0, "typedef")
1047
Daniel Veillardbe586972003-11-18 20:56:51 +00001048 else:
1049 token = self.token()
1050 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001051
1052 #
1053 # Parse a C struct definition till the balancing }
1054 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001055 def parseStruct(self, token):
1056 fields = []
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001057 #self.debug("start parseStruct", token)
Daniel Veillardbe586972003-11-18 20:56:51 +00001058 while token != None:
1059 if token[0] == "sep" and token[1] == "{":
1060 token = self.token()
1061 token = self.parseTypeBlock(token)
1062 elif token[0] == "sep" and token[1] == "}":
1063 self.struct_fields = fields
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001064 #self.debug("end parseStruct", token)
1065 #print fields
Daniel Veillardbe586972003-11-18 20:56:51 +00001066 token = self.token()
1067 return token
1068 else:
1069 base_type = self.type
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001070 #self.debug("before parseType", token)
Daniel Veillardbe586972003-11-18 20:56:51 +00001071 token = self.parseType(token)
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001072 #self.debug("after parseType", token)
Daniel Veillardbe586972003-11-18 20:56:51 +00001073 if token != None and token[0] == "name":
1074 fname = token[1]
1075 token = self.token()
1076 if token[0] == "sep" and token[1] == ";":
1077 self.comment = None
1078 token = self.token()
1079 fields.append((self.type, fname, self.comment))
1080 self.comment = None
1081 else:
1082 self.error("parseStruct: expecting ;", token)
1083 elif token != None and token[0] == "sep" and token[1] == "{":
1084 token = self.token()
1085 token = self.parseTypeBlock(token)
1086 if token != None and token[0] == "name":
1087 token = self.token()
1088 if token != None and token[0] == "sep" and token[1] == ";":
1089 token = self.token()
1090 else:
1091 self.error("parseStruct: expecting ;", token)
1092 else:
1093 self.error("parseStruct: name", token)
1094 token = self.token()
1095 self.type = base_type;
1096 self.struct_fields = fields
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001097 #self.debug("end parseStruct", token)
1098 #print fields
Daniel Veillardbe586972003-11-18 20:56:51 +00001099 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001100
1101 #
1102 # Parse a C enum block, parse till the balancing }
1103 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001104 def parseEnumBlock(self, token):
1105 self.enums = []
1106 name = None
1107 self.comment = None
1108 comment = ""
1109 value = "0"
1110 while token != None:
1111 if token[0] == "sep" and token[1] == "{":
1112 token = self.token()
1113 token = self.parseTypeBlock(token)
1114 elif token[0] == "sep" and token[1] == "}":
1115 if name != None:
1116 if self.comment != None:
1117 comment = self.comment
1118 self.comment = None
1119 self.enums.append((name, value, comment))
1120 token = self.token()
1121 return token
1122 elif token[0] == "name":
1123 if name != None:
1124 if self.comment != None:
1125 comment = string.strip(self.comment)
1126 self.comment = None
1127 self.enums.append((name, value, comment))
1128 name = token[1]
1129 comment = ""
1130 token = self.token()
1131 if token[0] == "op" and token[1][0] == "=":
1132 value = ""
1133 if len(token[1]) > 1:
1134 value = token[1][1:]
1135 token = self.token()
1136 while token[0] != "sep" or (token[1] != ',' and
1137 token[1] != '}'):
1138 value = value + token[1]
1139 token = self.token()
1140 else:
1141 try:
1142 value = "%d" % (int(value) + 1)
1143 except:
Daniel Veillard1e906612003-12-05 14:57:46 +00001144 self.warning("Failed to compute value of enum %s" % (name))
Daniel Veillardbe586972003-11-18 20:56:51 +00001145 value=""
1146 if token[0] == "sep" and token[1] == ",":
1147 token = self.token()
1148 else:
1149 token = self.token()
1150 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001151
1152 #
1153 # Parse a C definition block, used for structs it parse till
1154 # the balancing }
1155 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001156 def parseTypeBlock(self, token):
1157 while token != None:
1158 if token[0] == "sep" and token[1] == "{":
1159 token = self.token()
1160 token = self.parseTypeBlock(token)
1161 elif token[0] == "sep" and token[1] == "}":
1162 token = self.token()
1163 return token
1164 else:
1165 token = self.token()
1166 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001167
1168 #
1169 # Parse a type: the fact that the type name can either occur after
1170 # the definition or within the definition makes it a little harder
1171 # if inside, the name token is pushed back before returning
1172 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001173 def parseType(self, token):
1174 self.type = ""
1175 self.struct_fields = []
1176 self.signature = None
1177 if token == None:
1178 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001179
Daniel Veillardbe586972003-11-18 20:56:51 +00001180 while token[0] == "name" and (
1181 token[1] == "const" or token[1] == "unsigned"):
1182 if self.type == "":
1183 self.type = token[1]
1184 else:
1185 self.type = self.type + " " + token[1]
1186 token = self.token()
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001187
Daniel Veillardbe586972003-11-18 20:56:51 +00001188 if token[0] == "name" and (token[1] == "long" or token[1] == "short"):
1189 if self.type == "":
1190 self.type = token[1]
1191 else:
1192 self.type = self.type + " " + token[1]
1193 if token[0] == "name" and token[1] == "int":
1194 if self.type == "":
1195 self.type = tmp[1]
1196 else:
1197 self.type = self.type + " " + tmp[1]
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001198
Daniel Veillardbe586972003-11-18 20:56:51 +00001199 elif token[0] == "name" and token[1] == "struct":
1200 if self.type == "":
1201 self.type = token[1]
1202 else:
1203 self.type = self.type + " " + token[1]
1204 token = self.token()
1205 nametok = None
1206 if token[0] == "name":
1207 nametok = token
1208 token = self.token()
1209 if token != None and token[0] == "sep" and token[1] == "{":
1210 token = self.token()
1211 token = self.parseStruct(token)
1212 elif token != None and token[0] == "op" and token[1] == "*":
1213 self.type = self.type + " " + nametok[1] + " *"
1214 token = self.token()
1215 while token != None and token[0] == "op" and token[1] == "*":
1216 self.type = self.type + " *"
1217 token = self.token()
1218 if token[0] == "name":
1219 nametok = token
1220 token = self.token()
1221 else:
1222 self.error("struct : expecting name", token)
1223 return token
1224 elif token != None and token[0] == "name" and nametok != None:
1225 self.type = self.type + " " + nametok[1]
1226 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001227
Daniel Veillardbe586972003-11-18 20:56:51 +00001228 if nametok != None:
1229 self.lexer.push(token)
1230 token = nametok
1231 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001232
Daniel Veillardbe586972003-11-18 20:56:51 +00001233 elif token[0] == "name" and token[1] == "enum":
1234 if self.type == "":
1235 self.type = token[1]
1236 else:
1237 self.type = self.type + " " + token[1]
1238 self.enums = []
1239 token = self.token()
1240 if token != None and token[0] == "sep" and token[1] == "{":
1241 token = self.token()
1242 token = self.parseEnumBlock(token)
1243 else:
1244 self.error("parsing enum: expecting '{'", token)
1245 enum_type = None
1246 if token != None and token[0] != "name":
1247 self.lexer.push(token)
1248 token = ("name", "enum")
1249 else:
1250 enum_type = token[1]
1251 for enum in self.enums:
1252 self.index_add(enum[0], self.filename,
1253 not self.is_header, "enum",
1254 (enum[1], enum[2], enum_type))
1255 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001256
Daniel Veillardbe586972003-11-18 20:56:51 +00001257 elif token[0] == "name":
1258 if self.type == "":
1259 self.type = token[1]
1260 else:
1261 self.type = self.type + " " + token[1]
1262 else:
1263 self.error("parsing type %s: expecting a name" % (self.type),
1264 token)
1265 return token
1266 token = self.token()
1267 while token != None and (token[0] == "op" or
1268 token[0] == "name" and token[1] == "const"):
1269 self.type = self.type + " " + token[1]
1270 token = self.token()
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001271
1272 #
1273 # if there is a parenthesis here, this means a function type
1274 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001275 if token != None and token[0] == "sep" and token[1] == '(':
1276 self.type = self.type + token[1]
1277 token = self.token()
1278 while token != None and token[0] == "op" and token[1] == '*':
1279 self.type = self.type + token[1]
1280 token = self.token()
1281 if token == None or token[0] != "name" :
1282 self.error("parsing function type, name expected", token);
1283 return token
1284 self.type = self.type + token[1]
1285 nametok = token
1286 token = self.token()
1287 if token != None and token[0] == "sep" and token[1] == ')':
1288 self.type = self.type + token[1]
1289 token = self.token()
1290 if token != None and token[0] == "sep" and token[1] == '(':
1291 token = self.token()
1292 type = self.type;
1293 token = self.parseSignature(token);
1294 self.type = type;
1295 else:
1296 self.error("parsing function type, '(' expected", token);
1297 return token
1298 else:
1299 self.error("parsing function type, ')' expected", token);
1300 return token
1301 self.lexer.push(token)
1302 token = nametok
1303 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001304
1305 #
1306 # do some lookahead for arrays
1307 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001308 if token != None and token[0] == "name":
1309 nametok = token
1310 token = self.token()
1311 if token != None and token[0] == "sep" and token[1] == '[':
1312 self.type = self.type + nametok[1]
1313 while token != None and token[0] == "sep" and token[1] == '[':
1314 self.type = self.type + token[1]
1315 token = self.token()
1316 while token != None and token[0] != 'sep' and \
1317 token[1] != ']' and token[1] != ';':
1318 self.type = self.type + token[1]
1319 token = self.token()
1320 if token != None and token[0] == 'sep' and token[1] == ']':
1321 self.type = self.type + token[1]
1322 token = self.token()
1323 else:
1324 self.error("parsing array type, ']' expected", token);
1325 return token
1326 elif token != None and token[0] == "sep" and token[1] == ':':
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001327 # remove :12 in case it's a limited int size
Daniel Veillardbe586972003-11-18 20:56:51 +00001328 token = self.token()
1329 token = self.token()
1330 self.lexer.push(token)
1331 token = nametok
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001332
Daniel Veillardbe586972003-11-18 20:56:51 +00001333 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001334
1335 #
1336 # Parse a signature: '(' has been parsed and we scan the type definition
1337 # up to the ')' included
Daniel Veillardbe586972003-11-18 20:56:51 +00001338 def parseSignature(self, token):
1339 signature = []
1340 if token != None and token[0] == "sep" and token[1] == ')':
1341 self.signature = []
1342 token = self.token()
1343 return token
1344 while token != None:
1345 token = self.parseType(token)
1346 if token != None and token[0] == "name":
1347 signature.append((self.type, token[1], None))
1348 token = self.token()
1349 elif token != None and token[0] == "sep" and token[1] == ',':
1350 token = self.token()
1351 continue
1352 elif token != None and token[0] == "sep" and token[1] == ')':
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001353 # only the type was provided
Daniel Veillardbe586972003-11-18 20:56:51 +00001354 if self.type == "...":
1355 signature.append((self.type, "...", None))
1356 else:
1357 signature.append((self.type, None, None))
1358 if token != None and token[0] == "sep":
1359 if token[1] == ',':
1360 token = self.token()
1361 continue
1362 elif token[1] == ')':
1363 token = self.token()
1364 break
1365 self.signature = signature
1366 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001367
1368 #
1369 # Parse a global definition, be it a type, variable or function
1370 # the extern "C" blocks are a bit nasty and require it to recurse.
1371 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001372 def parseGlobal(self, token):
1373 static = 0
1374 if token[1] == 'extern':
1375 token = self.token()
1376 if token == None:
1377 return token
1378 if token[0] == 'string':
1379 if token[1] == 'C':
1380 token = self.token()
1381 if token == None:
1382 return token
1383 if token[0] == 'sep' and token[1] == "{":
1384 token = self.token()
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001385# print 'Entering extern "C line ', self.lineno()
Daniel Veillardbe586972003-11-18 20:56:51 +00001386 while token != None and (token[0] != 'sep' or
1387 token[1] != "}"):
1388 if token[0] == 'name':
1389 token = self.parseGlobal(token)
1390 else:
1391 self.error(
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001392 "token %s %s unexpected at the top level" % (
1393 token[0], token[1]))
Daniel Veillardbe586972003-11-18 20:56:51 +00001394 token = self.parseGlobal(token)
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001395# print 'Exiting extern "C" line', self.lineno()
Daniel Veillardbe586972003-11-18 20:56:51 +00001396 token = self.token()
1397 return token
1398 else:
1399 return token
1400 elif token[1] == 'static':
1401 static = 1
1402 token = self.token()
1403 if token == None or token[0] != 'name':
1404 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001405
Daniel Veillardbe586972003-11-18 20:56:51 +00001406 if token[1] == 'typedef':
1407 token = self.token()
1408 return self.parseTypedef(token)
1409 else:
1410 token = self.parseType(token)
1411 type_orig = self.type
1412 if token == None or token[0] != "name":
1413 return token
1414 type = type_orig
1415 self.name = token[1]
1416 token = self.token()
1417 while token != None and (token[0] == "sep" or token[0] == "op"):
1418 if token[0] == "sep":
1419 if token[1] == "[":
1420 type = type + token[1]
1421 token = self.token()
1422 while token != None and (token[0] != "sep" or \
1423 token[1] != ";"):
1424 type = type + token[1]
1425 token = self.token()
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001426
Daniel Veillardbe586972003-11-18 20:56:51 +00001427 if token != None and token[0] == "op" and token[1] == "=":
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001428 #
1429 # Skip the initialization of the variable
1430 #
Daniel Veillardbe586972003-11-18 20:56:51 +00001431 token = self.token()
1432 if token[0] == 'sep' and token[1] == '{':
1433 token = self.token()
1434 token = self.parseBlock(token)
1435 else:
1436 self.comment = None
1437 while token != None and (token[0] != "sep" or \
1438 (token[1] != ';' and token[1] != ',')):
1439 token = self.token()
1440 self.comment = None
1441 if token == None or token[0] != "sep" or (token[1] != ';' and
1442 token[1] != ','):
1443 self.error("missing ';' or ',' after value")
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001444
Daniel Veillardbe586972003-11-18 20:56:51 +00001445 if token != None and token[0] == "sep":
1446 if token[1] == ";":
1447 self.comment = None
1448 token = self.token()
1449 if type == "struct":
1450 self.index_add(self.name, self.filename,
1451 not self.is_header, "struct", self.struct_fields)
1452 else:
1453 self.index_add(self.name, self.filename,
1454 not self.is_header, "variable", type)
1455 break
1456 elif token[1] == "(":
1457 token = self.token()
1458 token = self.parseSignature(token)
1459 if token == None:
1460 return None
1461 if token[0] == "sep" and token[1] == ";":
1462 d = self.mergeFunctionComment(self.name,
1463 ((type, None), self.signature), 1)
1464 self.index_add(self.name, self.filename, static,
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001465 "function", d)
Daniel Veillardbe586972003-11-18 20:56:51 +00001466 token = self.token()
1467 elif token[0] == "sep" and token[1] == "{":
1468 d = self.mergeFunctionComment(self.name,
1469 ((type, None), self.signature), static)
1470 self.index_add(self.name, self.filename, static,
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001471 "function", d)
Daniel Veillardbe586972003-11-18 20:56:51 +00001472 token = self.token()
1473 token = self.parseBlock(token);
1474 elif token[1] == ',':
1475 self.comment = None
1476 self.index_add(self.name, self.filename, static,
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001477 "variable", type)
Daniel Veillardbe586972003-11-18 20:56:51 +00001478 type = type_orig
1479 token = self.token()
1480 while token != None and token[0] == "sep":
1481 type = type + token[1]
1482 token = self.token()
1483 if token != None and token[0] == "name":
1484 self.name = token[1]
1485 token = self.token()
1486 else:
1487 break
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001488
Daniel Veillardbe586972003-11-18 20:56:51 +00001489 return token
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001490
Daniel Veillardbe586972003-11-18 20:56:51 +00001491 def parse(self):
Daniel Veillard1e906612003-12-05 14:57:46 +00001492 self.warning("Parsing %s" % (self.filename))
Daniel Veillardbe586972003-11-18 20:56:51 +00001493 token = self.token()
1494 while token != None:
1495 if token[0] == 'name':
1496 token = self.parseGlobal(token)
1497 else:
1498 self.error("token %s %s unexpected at the top level" % (
1499 token[0], token[1]))
1500 token = self.parseGlobal(token)
1501 return
1502 self.parseTopComment(self.top_comment)
1503 return self.index
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001504
1505
1506class docBuilder:
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001507 """A documentation builder"""
1508 def __init__(self, name, directories=['.'], excludes=[]):
1509 self.name = name
1510 self.directories = directories
1511 self.excludes = excludes + ignored_files.keys()
1512 self.modules = {}
1513 self.headers = {}
1514 self.idx = index()
1515 self.xref = {}
1516 self.index = {}
1517 if name == 'libxml2':
1518 self.basename = 'libxml'
1519 else:
1520 self.basename = name
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001521
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001522 def indexString(self, id, str):
1523 if str == None:
1524 return
1525 str = string.replace(str, "'", ' ')
1526 str = string.replace(str, '"', ' ')
1527 str = string.replace(str, "/", ' ')
1528 str = string.replace(str, '*', ' ')
1529 str = string.replace(str, "[", ' ')
1530 str = string.replace(str, "]", ' ')
1531 str = string.replace(str, "(", ' ')
1532 str = string.replace(str, ")", ' ')
1533 str = string.replace(str, "<", ' ')
1534 str = string.replace(str, '>', ' ')
1535 str = string.replace(str, "&", ' ')
1536 str = string.replace(str, '#', ' ')
1537 str = string.replace(str, ",", ' ')
1538 str = string.replace(str, '.', ' ')
1539 str = string.replace(str, ';', ' ')
1540 tokens = string.split(str)
1541 for token in tokens:
1542 try:
1543 c = token[0]
1544 if string.find(string.letters, c) < 0:
1545 pass
1546 elif len(token) < 3:
1547 pass
1548 else:
1549 lower = string.lower(token)
1550 # TODO: generalize this a bit
1551 if lower == 'and' or lower == 'the':
1552 pass
1553 elif self.xref.has_key(token):
1554 self.xref[token].append(id)
1555 else:
1556 self.xref[token] = [id]
1557 except:
1558 pass
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001559
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001560 def analyze(self):
1561 print "Project %s : %d headers, %d modules" % (self.name, len(self.headers.keys()), len(self.modules.keys()))
1562 self.idx.analyze()
1563
1564 def scanHeaders(self):
1565 for header in self.headers.keys():
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001566 parser = CParser(header)
1567 idx = parser.parse()
1568 self.headers[header] = idx;
1569 self.idx.merge(idx)
1570
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001571 def scanModules(self):
1572 for module in self.modules.keys():
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001573 parser = CParser(module)
1574 idx = parser.parse()
1575 # idx.analyze()
1576 self.modules[module] = idx
1577 self.idx.merge_public(idx)
1578
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001579 def scan(self):
1580 for directory in self.directories:
1581 files = glob.glob(directory + "/*.c")
1582 for file in files:
1583 skip = 0
1584 for excl in self.excludes:
1585 if string.find(file, excl) != -1:
1586 skip = 1;
1587 break
1588 if skip == 0:
1589 self.modules[file] = None;
1590 files = glob.glob(directory + "/*.h")
1591 for file in files:
1592 skip = 0
1593 for excl in self.excludes:
1594 if string.find(file, excl) != -1:
1595 skip = 1;
1596 break
1597 if skip == 0:
1598 self.headers[file] = None;
1599 self.scanHeaders()
1600 self.scanModules()
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001601
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001602 def modulename_file(self, file):
1603 module = os.path.basename(file)
1604 if module[-2:] == '.h':
1605 module = module[:-2]
1606 return module
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001607
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001608 def serialize_enum(self, output, name):
1609 id = self.idx.enums[name]
1610 output.write(" <enum name='%s' file='%s'" % (name,
1611 self.modulename_file(id.module)))
1612 if id.info != None:
1613 info = id.info
1614 if info[0] != None and info[0] != '':
1615 try:
1616 val = eval(info[0])
1617 except:
1618 val = info[0]
1619 output.write(" value='%s'" % (val));
1620 if info[2] != None and info[2] != '':
1621 output.write(" type='%s'" % info[2]);
1622 if info[1] != None and info[1] != '':
1623 output.write(" info='%s'" % escape(info[1]));
1624 output.write("/>\n")
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001625
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001626 def serialize_macro(self, output, name):
1627 id = self.idx.macros[name]
1628 output.write(" <macro name='%s' file='%s'>\n" % (name,
1629 self.modulename_file(id.module)))
1630 if id.info != None:
1631 try:
1632 (args, desc) = id.info
1633 if desc != None and desc != "":
1634 output.write(" <info>%s</info>\n" % (escape(desc)))
1635 self.indexString(name, desc)
1636 for arg in args:
1637 (name, desc) = arg
1638 if desc != None and desc != "":
1639 output.write(" <arg name='%s' info='%s'/>\n" % (
1640 name, escape(desc)))
1641 self.indexString(name, desc)
1642 else:
1643 output.write(" <arg name='%s'/>\n" % (name))
1644 except:
1645 pass
1646 output.write(" </macro>\n")
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001647
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001648 def serialize_typedef(self, output, name):
1649 id = self.idx.typedefs[name]
1650 if id.info[0:7] == 'struct ':
1651 output.write(" <struct name='%s' file='%s' type='%s'" % (
1652 name, self.modulename_file(id.module), id.info))
1653 name = id.info[7:]
1654 if self.idx.structs.has_key(name) and ( \
1655 type(self.idx.structs[name].info) == type(()) or
Daniel Veillardc1eed322002-12-12 11:01:32 +00001656 type(self.idx.structs[name].info) == type([])):
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001657 output.write(">\n");
1658 try:
1659 for field in self.idx.structs[name].info:
1660 desc = field[2]
1661 self.indexString(name, desc)
1662 if desc == None:
1663 desc = ''
1664 else:
1665 desc = escape(desc)
1666 output.write(" <field name='%s' type='%s' info='%s'/>\n" % (field[1] , field[0], desc))
1667 except:
1668 print "Failed to serialize struct %s" % (name)
1669 output.write(" </struct>\n")
1670 else:
1671 output.write("/>\n");
1672 else :
1673 output.write(" <typedef name='%s' file='%s' type='%s'/>\n" % (
1674 name, self.modulename_file(id.module), id.info))
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001675
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001676 def serialize_variable(self, output, name):
1677 id = self.idx.variables[name]
1678 if id.info != None:
1679 output.write(" <variable name='%s' file='%s' type='%s'/>\n" % (
1680 name, self.modulename_file(id.module), id.info))
1681 else:
1682 output.write(" <variable name='%s' file='%s'/>\n" % (
1683 name, self.modulename_file(id.module)))
Daniel Veillardc1eed322002-12-12 11:01:32 +00001684
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001685 def serialize_function(self, output, name):
1686 id = self.idx.functions[name]
Daniel Veillarda2351322004-06-27 12:08:10 +00001687 if name == debugsym:
1688 print "=>", id
1689
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001690 output.write(" <%s name='%s' file='%s'>\n" % (id.type, name,
1691 self.modulename_file(id.module)))
William M. Brack21e4ef22005-01-02 09:53:13 +00001692 #
1693 # Processing of conditionals modified by Bill 1/1/05
1694 #
Daniel Veillarda2351322004-06-27 12:08:10 +00001695 if id.conditionals != None:
William M. Brack21e4ef22005-01-02 09:53:13 +00001696 apstr = ""
Daniel Veillarda2351322004-06-27 12:08:10 +00001697 for cond in id.conditionals:
William M. Brack21e4ef22005-01-02 09:53:13 +00001698 if apstr != "":
1699 apstr = apstr + " &amp;&amp; "
1700 apstr = apstr + cond
1701 output.write(" <cond>%s</cond>\n"% (apstr));
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001702 try:
1703 (ret, params, desc) = id.info
1704 output.write(" <info>%s</info>\n" % (escape(desc)))
1705 self.indexString(name, desc)
1706 if ret[0] != None:
1707 if ret[0] == "void":
1708 output.write(" <return type='void'/>\n")
1709 else:
1710 output.write(" <return type='%s' info='%s'/>\n" % (
1711 ret[0], escape(ret[1])))
1712 self.indexString(name, ret[1])
1713 for param in params:
1714 if param[0] == 'void':
1715 continue
1716 if param[2] == None:
1717 output.write(" <arg name='%s' type='%s' info=''/>\n" % (param[1], param[0]))
1718 else:
1719 output.write(" <arg name='%s' type='%s' info='%s'/>\n" % (param[1], param[0], escape(param[2])))
1720 self.indexString(name, param[2])
1721 except:
1722 print "Failed to save function %s info: " % name, `id.info`
1723 output.write(" </%s>\n" % (id.type))
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001724
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001725 def serialize_exports(self, output, file):
1726 module = self.modulename_file(file)
1727 output.write(" <file name='%s'>\n" % (module))
1728 dict = self.headers[file]
Daniel Veillardbe586972003-11-18 20:56:51 +00001729 if dict.info != None:
1730 for data in ('Summary', 'Description', 'Author'):
1731 try:
1732 output.write(" <%s>%s</%s>\n" % (
1733 string.lower(data),
1734 escape(dict.info[data]),
1735 string.lower(data)))
1736 except:
1737 print "Header %s lacks a %s description" % (module, data)
1738 if dict.info.has_key('Description'):
1739 desc = dict.info['Description']
1740 if string.find(desc, "DEPRECATED") != -1:
1741 output.write(" <deprecated/>\n")
1742
Daniel Veillard1a792412003-11-18 23:52:38 +00001743 ids = dict.macros.keys()
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001744 ids.sort()
1745 for id in uniq(ids):
Daniel Veillard1a792412003-11-18 23:52:38 +00001746 # Macros are sometime used to masquerade other types.
1747 if dict.functions.has_key(id):
1748 continue
1749 if dict.variables.has_key(id):
1750 continue
1751 if dict.typedefs.has_key(id):
1752 continue
1753 if dict.structs.has_key(id):
1754 continue
1755 if dict.enums.has_key(id):
1756 continue
1757 output.write(" <exports symbol='%s' type='macro'/>\n" % (id))
1758 ids = dict.enums.keys()
1759 ids.sort()
1760 for id in uniq(ids):
1761 output.write(" <exports symbol='%s' type='enum'/>\n" % (id))
1762 ids = dict.typedefs.keys()
1763 ids.sort()
1764 for id in uniq(ids):
1765 output.write(" <exports symbol='%s' type='typedef'/>\n" % (id))
1766 ids = dict.structs.keys()
1767 ids.sort()
1768 for id in uniq(ids):
1769 output.write(" <exports symbol='%s' type='struct'/>\n" % (id))
1770 ids = dict.variables.keys()
1771 ids.sort()
1772 for id in uniq(ids):
1773 output.write(" <exports symbol='%s' type='variable'/>\n" % (id))
1774 ids = dict.functions.keys()
1775 ids.sort()
1776 for id in uniq(ids):
1777 output.write(" <exports symbol='%s' type='function'/>\n" % (id))
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001778 output.write(" </file>\n")
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001779
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001780 def serialize_xrefs_files(self, output):
1781 headers = self.headers.keys()
1782 headers.sort()
1783 for file in headers:
1784 module = self.modulename_file(file)
1785 output.write(" <file name='%s'>\n" % (module))
1786 dict = self.headers[file]
William M. Bracka2e844a2004-01-06 11:52:13 +00001787 ids = uniq(dict.functions.keys() + dict.variables.keys() + \
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001788 dict.macros.keys() + dict.typedefs.keys() + \
William M. Bracka2e844a2004-01-06 11:52:13 +00001789 dict.structs.keys() + dict.enums.keys())
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001790 ids.sort()
William M. Bracka2e844a2004-01-06 11:52:13 +00001791 for id in ids:
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001792 output.write(" <ref name='%s'/>\n" % (id))
1793 output.write(" </file>\n")
1794 pass
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001795
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001796 def serialize_xrefs_functions(self, output):
1797 funcs = {}
1798 for name in self.idx.functions.keys():
1799 id = self.idx.functions[name]
1800 try:
1801 (ret, params, desc) = id.info
1802 for param in params:
1803 if param[0] == 'void':
1804 continue
1805 if funcs.has_key(param[0]):
1806 funcs[param[0]].append(name)
1807 else:
1808 funcs[param[0]] = [name]
1809 except:
1810 pass
1811 typ = funcs.keys()
1812 typ.sort()
1813 for type in typ:
1814 if type == '' or type == 'void' or type == "int" or \
1815 type == "char *" or type == "const char *" :
1816 continue
1817 output.write(" <type name='%s'>\n" % (type))
1818 ids = funcs[type]
1819 ids.sort()
William M. Brackcf9eadf2003-12-25 13:24:05 +00001820 pid = '' # not sure why we have dups, but get rid of them!
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001821 for id in ids:
William M. Brackcf9eadf2003-12-25 13:24:05 +00001822 if id != pid:
1823 output.write(" <ref name='%s'/>\n" % (id))
1824 pid = id
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001825 output.write(" </type>\n")
1826
1827 def serialize_xrefs_constructors(self, output):
1828 funcs = {}
1829 for name in self.idx.functions.keys():
1830 id = self.idx.functions[name]
1831 try:
1832 (ret, params, desc) = id.info
1833 if ret[0] == "void":
1834 continue
1835 if funcs.has_key(ret[0]):
1836 funcs[ret[0]].append(name)
1837 else:
1838 funcs[ret[0]] = [name]
1839 except:
1840 pass
1841 typ = funcs.keys()
1842 typ.sort()
1843 for type in typ:
1844 if type == '' or type == 'void' or type == "int" or \
1845 type == "char *" or type == "const char *" :
1846 continue
1847 output.write(" <type name='%s'>\n" % (type))
1848 ids = funcs[type]
William M. Brack09a4d0a2004-01-05 14:28:43 +00001849 ids.sort()
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001850 for id in ids:
1851 output.write(" <ref name='%s'/>\n" % (id))
1852 output.write(" </type>\n")
1853
1854 def serialize_xrefs_alpha(self, output):
1855 letter = None
1856 ids = self.idx.identifiers.keys()
1857 ids.sort()
1858 for id in ids:
1859 if id[0] != letter:
1860 if letter != None:
1861 output.write(" </letter>\n")
1862 letter = id[0]
1863 output.write(" <letter name='%s'>\n" % (letter))
1864 output.write(" <ref name='%s'/>\n" % (id))
1865 if letter != None:
1866 output.write(" </letter>\n")
1867
1868 def serialize_xrefs_references(self, output):
1869 typ = self.idx.identifiers.keys()
1870 typ.sort()
1871 for id in typ:
1872 idf = self.idx.identifiers[id]
1873 module = idf.module
1874 output.write(" <reference name='%s' href='%s'/>\n" % (id,
1875 'html/' + self.basename + '-' +
1876 self.modulename_file(module) + '.html#' +
1877 id))
1878
1879 def serialize_xrefs_index(self, output):
1880 index = self.xref
1881 typ = index.keys()
1882 typ.sort()
1883 letter = None
1884 count = 0
1885 chunk = 0
1886 chunks = []
1887 for id in typ:
1888 if len(index[id]) > 30:
1889 continue
1890 if id[0] != letter:
1891 if letter == None or count > 200:
1892 if letter != None:
1893 output.write(" </letter>\n")
1894 output.write(" </chunk>\n")
1895 count = 0
1896 chunks.append(["chunk%s" % (chunk -1), first_letter, letter])
1897 output.write(" <chunk name='chunk%s'>\n" % (chunk))
1898 first_letter = id[0]
1899 chunk = chunk + 1
1900 elif letter != None:
1901 output.write(" </letter>\n")
1902 letter = id[0]
1903 output.write(" <letter name='%s'>\n" % (letter))
1904 output.write(" <word name='%s'>\n" % (id))
1905 tokens = index[id];
1906 tokens.sort()
1907 tok = None
William M. Bracka2e844a2004-01-06 11:52:13 +00001908 for token in tokens:
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001909 if tok == token:
1910 continue
1911 tok = token
1912 output.write(" <ref name='%s'/>\n" % (token))
1913 count = count + 1
1914 output.write(" </word>\n")
1915 if letter != None:
1916 output.write(" </letter>\n")
1917 output.write(" </chunk>\n")
William M. Brack966668a2003-12-20 02:10:28 +00001918 if count != 0:
1919 chunks.append(["chunk%s" % (chunk -1), first_letter, letter])
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001920 output.write(" <chunks>\n")
1921 for ch in chunks:
1922 output.write(" <chunk name='%s' start='%s' end='%s'/>\n" % (
1923 ch[0], ch[1], ch[2]))
1924 output.write(" </chunks>\n")
1925
1926 def serialize_xrefs(self, output):
1927 output.write(" <references>\n")
1928 self.serialize_xrefs_references(output)
1929 output.write(" </references>\n")
1930 output.write(" <alpha>\n")
1931 self.serialize_xrefs_alpha(output)
1932 output.write(" </alpha>\n")
1933 output.write(" <constructors>\n")
1934 self.serialize_xrefs_constructors(output)
1935 output.write(" </constructors>\n")
1936 output.write(" <functions>\n")
1937 self.serialize_xrefs_functions(output)
1938 output.write(" </functions>\n")
1939 output.write(" <files>\n")
1940 self.serialize_xrefs_files(output)
1941 output.write(" </files>\n")
1942 output.write(" <index>\n")
1943 self.serialize_xrefs_index(output)
1944 output.write(" </index>\n")
1945
1946 def serialize(self):
1947 filename = "%s-api.xml" % self.name
1948 print "Saving XML description %s" % (filename)
1949 output = open(filename, "w")
1950 output.write('<?xml version="1.0" encoding="ISO-8859-1"?>\n')
1951 output.write("<api name='%s'>\n" % self.name)
1952 output.write(" <files>\n")
1953 headers = self.headers.keys()
1954 headers.sort()
1955 for file in headers:
1956 self.serialize_exports(output, file)
1957 output.write(" </files>\n")
1958 output.write(" <symbols>\n")
1959 macros = self.idx.macros.keys()
1960 macros.sort()
1961 for macro in macros:
1962 self.serialize_macro(output, macro)
1963 enums = self.idx.enums.keys()
1964 enums.sort()
1965 for enum in enums:
1966 self.serialize_enum(output, enum)
1967 typedefs = self.idx.typedefs.keys()
1968 typedefs.sort()
1969 for typedef in typedefs:
1970 self.serialize_typedef(output, typedef)
1971 variables = self.idx.variables.keys()
1972 variables.sort()
1973 for variable in variables:
1974 self.serialize_variable(output, variable)
1975 functions = self.idx.functions.keys()
1976 functions.sort()
1977 for function in functions:
1978 self.serialize_function(output, function)
1979 output.write(" </symbols>\n")
1980 output.write("</api>\n")
1981 output.close()
1982
1983 filename = "%s-refs.xml" % self.name
1984 print "Saving XML Cross References %s" % (filename)
1985 output = open(filename, "w")
1986 output.write('<?xml version="1.0" encoding="ISO-8859-1"?>\n')
1987 output.write("<apirefs name='%s'>\n" % self.name)
1988 self.serialize_xrefs(output)
1989 output.write("</apirefs>\n")
1990 output.close()
Daniel Veillarda9b66d02002-12-11 14:23:49 +00001991
1992
1993def rebuild():
1994 builder = None
Daniel Veillarde8ba84e2003-11-18 13:54:15 +00001995 if glob.glob("parser.c") != [] :
1996 print "Rebuilding API description for libxml2"
1997 builder = docBuilder("libxml2", [".", "."],
1998 ["xmlwin32version.h", "tst.c"])
1999 elif glob.glob("../parser.c") != [] :
Daniel Veillarda9b66d02002-12-11 14:23:49 +00002000 print "Rebuilding API description for libxml2"
2001 builder = docBuilder("libxml2", ["..", "../include/libxml"],
Daniel Veillardd4330462003-04-29 12:40:16 +00002002 ["xmlwin32version.h", "tst.c"])
Daniel Veillarda9b66d02002-12-11 14:23:49 +00002003 elif glob.glob("../libxslt/transform.c") != [] :
2004 print "Rebuilding API description for libxslt"
2005 builder = docBuilder("libxslt", ["../libxslt"],
Daniel Veillard024b5702002-12-12 00:15:55 +00002006 ["win32config.h", "libxslt.h", "tst.c"])
Daniel Veillarda9b66d02002-12-11 14:23:49 +00002007 else:
2008 print "rebuild() failed, unable to guess the module"
2009 return None
2010 builder.scan()
2011 builder.analyze()
2012 builder.serialize()
Daniel Veillard024b5702002-12-12 00:15:55 +00002013 if glob.glob("../libexslt/exslt.c") != [] :
2014 extra = docBuilder("libexslt", ["../libexslt"], ["libexslt.h"])
2015 extra.scan()
2016 extra.analyze()
2017 extra.serialize()
Daniel Veillarda9b66d02002-12-11 14:23:49 +00002018 return builder
2019
2020#
2021# for debugging the parser
2022#
2023def parse(filename):
2024 parser = CParser(filename)
2025 idx = parser.parse()
2026 return idx
2027
2028if __name__ == "__main__":
Daniel Veillarda2351322004-06-27 12:08:10 +00002029 if len(sys.argv) > 1:
2030 debug = 1
2031 parse(sys.argv[1])
2032 else:
2033 rebuild()