source: trunk/third/libxslt/doc/apibuild.py @ 20733

Revision 20733, 55.7 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r20732, which included commits to RCS files with non-trunk default branches.
  • Property svn:executable set to *
Line 
1#!/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#
10import os, sys
11import string
12import glob
13
14debug=0
15
16#
17# C parser analysis code
18#
19ignored_files = {
20  "trio": "too many non standard macros",
21  "trio.c": "too many non standard macros",
22  "trionan.c": "too many non standard macros",
23  "triostr.c": "too many non standard macros",
24  "acconfig.h": "generated portability layer",
25  "config.h": "generated portability layer",
26  "libxml.h": "internal only",
27  "testOOM.c": "out of memory tester",
28  "testOOMlib.h": "out of memory tester",
29  "testOOMlib.c": "out of memory tester",
30}
31
32ignored_words = {
33  "WINAPI": (0, "Windows keyword"),
34  "LIBXML_DLL_IMPORT": (0, "Special macro to flag external keywords"),
35  "XMLPUBVAR": (0, "Special macro for extern vars for win32"),
36  "XSLTPUBVAR": (0, "Special macro for extern vars for win32"),
37  "EXSLTPUBVAR": (0, "Special macro for extern vars for win32"),
38  "XMLPUBFUN": (0, "Special macro for extern funcs for win32"),
39  "XSLTPUBFUN": (0, "Special macro for extern funcs for win32"),
40  "EXSLTPUBFUN": (0, "Special macro for extern funcs for win32"),
41  "XMLCALL": (0, "Special macro for win32 calls"),
42  "XSLTCALL": (0, "Special macro for win32 calls"),
43  "EXSLTCALL": (0, "Special macro for win32 calls"),
44  "__declspec": (3, "Windows keyword"),
45  "ATTRIBUTE_UNUSED": (0, "macro keyword"),
46  "LIBEXSLT_PUBLIC": (0, "macro keyword"),
47  "X_IN_Y": (5, "macro function builder"),
48}
49
50def escape(raw):
51    raw = string.replace(raw, '&', '&')
52    raw = string.replace(raw, '<', '&lt;')
53    raw = string.replace(raw, '>', '&gt;')
54    raw = string.replace(raw, "'", '&apos;')
55    raw = string.replace(raw, '"', '&quot;')
56    return raw
57
58def uniq(items):
59    d = {}
60    for item in items:
61        d[item]=1
62    return d.keys()
63
64class identifier:
65    def __init__(self, name, module=None, type=None, lineno = 0,
66                 info=None, extra=None):
67        self.name = name
68        self.module = module
69        self.type = type
70        self.info = info
71        self.extra = extra
72        self.lineno = lineno
73        self.static = 0
74
75    def __repr__(self):
76        r = "%s %s:" % (self.type, self.name)
77        if self.static:
78            r = r + " static"
79        if self.module != None:
80            r = r + " from %s" % (self.module)
81        if self.info != None:
82            r = r + " " +  `self.info`
83        if self.extra != None:
84            r = r + " " + `self.extra`
85        return r
86
87
88    def set_module(self, module):
89        self.module = module
90    def set_type(self, type):
91        self.type = type
92    def set_info(self, info):
93        self.info = info
94    def set_extra(self, extra):
95        self.extra = extra
96    def set_lineno(self, lineno):
97        self.lineno = lineno
98    def set_static(self, static):
99        self.static = static
100
101    def get_name(self):
102        return self.name
103    def get_module(self):
104        return self.module
105    def get_type(self):
106        return self.type
107    def get_info(self):
108        return self.info
109    def get_lineno(self):
110        return self.lineno
111    def get_extra(self):
112        return self.extra
113    def get_static(self):
114        return self.static
115
116    def update(self, module, type = None, info = None, extra=None):
117        if module != None and self.module == None:
118            self.set_module(module)
119        if type != None and self.type == None:
120            self.set_type(type)
121        if info != None:
122            self.set_info(info)
123        if extra != None:
124            self.set_extra(extra)
125
126
127class index:
128    def __init__(self, name = "noname"):
129        self.name = name
130        self.identifiers = {}
131        self.functions = {}
132        self.variables = {}
133        self.includes = {}
134        self.structs = {}
135        self.enums = {}
136        self.typedefs = {}
137        self.macros = {}
138        self.references = {}
139        self.info = {}
140
141    def add_ref(self, name, module, static, type, lineno, info=None, extra=None):
142        if name[0:2] == '__':
143            return None
144        d = None
145        try:
146           d = self.identifiers[name]
147           d.update(module, type, lineno, info, extra)
148        except:
149           d = identifier(name, module, type, lineno, info, extra)
150           self.identifiers[name] = d
151
152        if d != None and static == 1:
153            d.set_static(1)
154
155        if d != None and name != None and type != None:
156            self.references[name] = d
157
158    def add(self, name, module, static, type, lineno, info=None, extra=None):
159        if name[0:2] == '__':
160            return None
161        d = None
162        try:
163           d = self.identifiers[name]
164           d.update(module, type, lineno, info, extra)
165        except:
166           d = identifier(name, module, type, lineno, info, extra)
167           self.identifiers[name] = d
168
169        if d != None and static == 1:
170            d.set_static(1)
171
172        if d != None and name != None and type != None:
173            if type == "function":
174                self.functions[name] = d
175            elif type == "functype":
176                self.functions[name] = d
177            elif type == "variable":
178                self.variables[name] = d
179            elif type == "include":
180                self.includes[name] = d
181            elif type == "struct":
182                self.structs[name] = d
183            elif type == "enum":
184                self.enums[name] = d
185            elif type == "typedef":
186                self.typedefs[name] = d
187            elif type == "macro":
188                self.macros[name] = d
189            else:
190                print "Unable to register type ", type
191        return d
192
193    def merge(self, idx):
194        for id in idx.functions.keys():
195              #
196              # macro might be used to override functions or variables
197              # definitions
198              #
199             if self.macros.has_key(id):
200                 del self.macros[id]
201             if self.functions.has_key(id):
202                 print "function %s from %s redeclared in %s" % (
203                    id, self.functions[id].module, idx.functions[id].module)
204             else:
205                 self.functions[id] = idx.functions[id]
206                 self.identifiers[id] = idx.functions[id]
207        for id in idx.variables.keys():
208              #
209              # macro might be used to override functions or variables
210              # definitions
211              #
212             if self.macros.has_key(id):
213                 del self.macros[id]
214             if self.variables.has_key(id):
215                 print "variable %s from %s redeclared in %s" % (
216                    id, self.variables[id].module, idx.variables[id].module)
217             else:
218                 self.variables[id] = idx.variables[id]
219                 self.identifiers[id] = idx.variables[id]
220        for id in idx.structs.keys():
221             if self.structs.has_key(id):
222                 print "struct %s from %s redeclared in %s" % (
223                    id, self.structs[id].module, idx.structs[id].module)
224             else:
225                 self.structs[id] = idx.structs[id]
226                 self.identifiers[id] = idx.structs[id]
227        for id in idx.typedefs.keys():
228             if self.typedefs.has_key(id):
229                 print "typedef %s from %s redeclared in %s" % (
230                    id, self.typedefs[id].module, idx.typedefs[id].module)
231             else:
232                 self.typedefs[id] = idx.typedefs[id]
233                 self.identifiers[id] = idx.typedefs[id]
234        for id in idx.macros.keys():
235              #
236              # macro might be used to override functions or variables
237              # definitions
238              #
239             if self.variables.has_key(id):
240                 continue
241             if self.functions.has_key(id):
242                 continue
243             if self.enums.has_key(id):
244                 continue
245             if self.macros.has_key(id):
246                 print "macro %s from %s redeclared in %s" % (
247                    id, self.macros[id].module, idx.macros[id].module)
248             else:
249                 self.macros[id] = idx.macros[id]
250                 self.identifiers[id] = idx.macros[id]
251        for id in idx.enums.keys():
252             if self.enums.has_key(id):
253                 print "enum %s from %s redeclared in %s" % (
254                    id, self.enums[id].module, idx.enums[id].module)
255             else:
256                 self.enums[id] = idx.enums[id]
257                 self.identifiers[id] = idx.enums[id]
258
259    def merge_public(self, idx):
260        for id in idx.functions.keys():
261             if self.functions.has_key(id):
262                 up = idx.functions[id]
263                 self.functions[id].update(None, up.type, up.info, up.extra)
264         #     else:
265         #         print "Function %s from %s is not declared in headers" % (
266        #               id, idx.functions[id].module)
267         # TODO: do the same for variables.
268
269    def analyze_dict(self, type, dict):
270        count = 0
271        public = 0
272        for name in dict.keys():
273            id = dict[name]
274            count = count + 1
275            if id.static == 0:
276                public = public + 1
277        if count != public:
278            print "  %d %s , %d public" % (count, type, public)
279        elif count != 0:
280            print "  %d public %s" % (count, type)
281
282
283    def analyze(self):
284        self.analyze_dict("functions", self.functions)
285        self.analyze_dict("variables", self.variables)
286        self.analyze_dict("structs", self.structs)
287        self.analyze_dict("typedefs", self.typedefs)
288        self.analyze_dict("macros", self.macros)
289         
290class CLexer:
291    """A lexer for the C language, tokenize the input by reading and
292       analyzing it line by line"""
293    def __init__(self, input):
294        self.input = input
295        self.tokens = []
296        self.line = ""
297        self.lineno = 0
298
299    def getline(self):
300        line = ''
301        while line == '':
302            line = self.input.readline()
303            if not line:
304                return None
305            self.lineno = self.lineno + 1
306            line = string.lstrip(line)
307            line = string.rstrip(line)
308            if line == '':
309                continue
310            while line[-1] == '\\':
311                line = line[:-1]
312                n = self.input.readline()
313                self.lineno = self.lineno + 1
314                n = string.lstrip(n)
315                n = string.rstrip(n)
316                if not n:
317                    break
318                else:
319                    line = line + n
320        return line
321         
322    def getlineno(self):
323        return self.lineno
324
325    def push(self, token):
326        self.tokens.insert(0, token);
327
328    def debug(self):
329        print "Last token: ", self.last
330        print "Token queue: ", self.tokens
331        print "Line %d end: " % (self.lineno), self.line
332
333    def token(self):
334        while self.tokens == []:
335            if self.line == "":
336                line = self.getline()
337            else:
338                line = self.line
339                self.line = ""
340            if line == None:
341                return None
342
343            if line[0] == '#':
344                self.tokens = map((lambda x: ('preproc', x)),
345                                  string.split(line))
346                break;
347            l = len(line)
348            if line[0] == '"' or line[0] == "'":
349                end = line[0]
350                line = line[1:]
351                found = 0
352                tok = ""
353                while found == 0:
354                    i = 0
355                    l = len(line)
356                    while i < l:
357                        if line[i] == end:
358                            self.line = line[i+1:]
359                            line = line[:i]
360                            l = i
361                            found = 1
362                            break
363                        if line[i] == '\\':
364                            i = i + 1
365                        i = i + 1
366                    tok = tok + line
367                    if found == 0:
368                        line = self.getline()
369                        if line == None:
370                            return None
371                self.last = ('string', tok)
372                return self.last
373
374            if l >= 2 and line[0] == '/' and line[1] == '*':
375                line = line[2:]
376                found = 0
377                tok = ""
378                while found == 0:
379                    i = 0
380                    l = len(line)
381                    while i < l:
382                        if line[i] == '*' and i+1 < l and line[i+1] == '/':
383                            self.line = line[i+2:]
384                            line = line[:i-1]
385                            l = i
386                            found = 1
387                            break
388                        i = i + 1
389                    if tok != "":
390                        tok = tok + "\n"
391                    tok = tok + line
392                    if found == 0:
393                        line = self.getline()
394                        if line == None:
395                            return None
396                self.last = ('comment', tok)
397                return self.last
398            if l >= 2 and line[0] == '/' and line[1] == '/':
399                line = line[2:]
400                self.last = ('comment', line)
401                return self.last
402            i = 0
403            while i < l:
404                if line[i] == '/' and i+1 < l and line[i+1] == '/':
405                    self.line = line[i:]
406                    line = line[:i]
407                    break
408                if line[i] == '/' and i+1 < l and line[i+1] == '*':
409                    self.line = line[i:]
410                    line = line[:i]
411                    break
412                if line[i] == '"' or line[i] == "'":
413                    self.line = line[i:]
414                    line = line[:i]
415                    break
416                i = i + 1
417            l = len(line)
418            i = 0
419            while i < l:
420                if line[i] == ' ' or line[i] == '\t':
421                    i = i + 1
422                    continue
423                o = ord(line[i])
424                if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
425                   (o >= 48 and o <= 57):
426                    s = i
427                    while i < l:
428                        o = ord(line[i])
429                        if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
430                           (o >= 48 and o <= 57) or string.find(
431                               " \t(){}:;,+-*/%&!|[]=><", line[i]) == -1:
432                            i = i + 1
433                        else:
434                            break
435                    self.tokens.append(('name', line[s:i]))
436                    continue
437                if string.find("(){}:;,[]", line[i]) != -1:
438#                 if line[i] == '(' or line[i] == ')' or line[i] == '{' or \
439#                   line[i] == '}' or line[i] == ':' or line[i] == ';' or \
440#                   line[i] == ',' or line[i] == '[' or line[i] == ']':
441                    self.tokens.append(('sep', line[i]))
442                    i = i + 1
443                    continue
444                if string.find("+-*><=/%&!|.", line[i]) != -1:
445#                 if line[i] == '+' or line[i] == '-' or line[i] == '*' or \
446#                   line[i] == '>' or line[i] == '<' or line[i] == '=' or \
447#                   line[i] == '/' or line[i] == '%' or line[i] == '&' or \
448#                   line[i] == '!' or line[i] == '|' or line[i] == '.':
449                    if line[i] == '.' and  i + 2 < l and \
450                       line[i+1] == '.' and line[i+2] == '.':
451                        self.tokens.append(('name', '...'))
452                        i = i + 3
453                        continue
454
455                    j = i + 1
456                    if j < l and (
457                       string.find("+-*><=/%&!|", line[j]) != -1):
458#                       line[j] == '+' or line[j] == '-' or line[j] == '*' or \
459#                       line[j] == '>' or line[j] == '<' or line[j] == '=' or \
460#                       line[j] == '/' or line[j] == '%' or line[j] == '&' or \
461#                       line[j] == '!' or line[j] == '|'):
462                        self.tokens.append(('op', line[i:j+1]))
463                        i = j + 1
464                    else:
465                        self.tokens.append(('op', line[i]))
466                        i = i + 1
467                    continue
468                s = i
469                while i < l:
470                    o = ord(line[i])
471                    if (o >= 97 and o <= 122) or (o >= 65 and o <= 90) or \
472                       (o >= 48 and o <= 57) or (
473                        string.find(" \t(){}:;,+-*/%&!|[]=><", line[i]) == -1):
474#                        line[i] != ' ' and line[i] != '\t' and
475#                        line[i] != '(' and line[i] != ')' and
476#                        line[i] != '{'  and line[i] != '}' and
477#                        line[i] != ':' and line[i] != ';' and
478#                        line[i] != ',' and line[i] != '+' and
479#                        line[i] != '-' and line[i] != '*' and
480#                        line[i] != '/' and line[i] != '%' and
481#                        line[i] != '&' and line[i] != '!' and
482#                        line[i] != '|' and line[i] != '[' and
483#                        line[i] != ']' and line[i] != '=' and
484#                        line[i] != '*' and line[i] != '>' and
485#                        line[i] != '<'):
486                        i = i + 1
487                    else:
488                        break
489                self.tokens.append(('name', line[s:i]))
490             
491        tok = self.tokens[0]
492        self.tokens = self.tokens[1:]
493        self.last = tok
494        return tok
495     
496class CParser:
497    """The C module parser"""
498    def __init__(self, filename, idx = None):
499        self.filename = filename
500        if len(filename) > 2 and filename[-2:] == '.h':
501            self.is_header = 1
502        else:
503            self.is_header = 0
504        self.input = open(filename)
505        self.lexer = CLexer(self.input)
506        if idx == None:
507            self.index = index()
508        else:
509            self.index = idx
510        self.top_comment = ""
511        self.last_comment = ""
512        self.comment = None
513        self.collect_ref = 0
514        self.no_error = 0
515
516    def collect_references(self):
517        self.collect_ref = 1
518
519    def stop_error(self):
520        self.no_error = 1
521
522    def start_error(self):
523        self.no_error = 0
524
525    def lineno(self):
526        return self.lexer.getlineno()
527
528    def index_add(self, name, module, static, type, info=None, extra = None):
529        self.index.add(name, module, static, type, self.lineno(),
530                       info, extra)
531
532    def index_add_ref(self, name, module, static, type, info=None,
533                      extra = None):
534        self.index.add_ref(name, module, static, type, self.lineno(),
535                       info, extra)
536
537    def warning(self, msg):
538        if self.no_error:
539            return
540        print msg
541
542    def error(self, msg, token=-1):
543        if self.no_error:
544            return
545
546        print "Parse Error: " + msg
547        if token != -1:
548            print "Got token ", token
549        self.lexer.debug()
550        sys.exit(1)
551
552    def debug(self, msg, token=-1):
553        print "Debug: " + msg
554        if token != -1:
555            print "Got token ", token
556        self.lexer.debug()
557
558    def parseTopComment(self, comment):
559        res = {}
560        lines = string.split(comment, "\n")
561        item = None
562        for line in lines:
563            while line != "" and (line[0] == ' ' or line[0] == '\t'):
564                line = line[1:]
565            while line != "" and line[0] == '*':
566                line = line[1:]
567            while line != "" and (line[0] == ' ' or line[0] == '\t'):
568                line = line[1:]
569            try:
570                (it, line) = string.split(line, ":", 1)
571                item = it
572                while line != "" and (line[0] == ' ' or line[0] == '\t'):
573                    line = line[1:]
574                if res.has_key(item):
575                    res[item] = res[item] + " " + line
576                else:
577                    res[item] = line
578            except:
579                if item != None:
580                    if res.has_key(item):
581                        res[item] = res[item] + " " + line
582                    else:
583                        res[item] = line
584        self.index.info = res
585
586    def parseComment(self, token):
587        if self.top_comment == "":
588            self.top_comment = token[1]
589        if self.comment == None or token[1][0] == '*':
590            self.comment = token[1];
591        else:
592            self.comment = self.comment + token[1]
593        token = self.lexer.token()
594
595        if string.find(self.comment, "DOC_DISABLE") != -1:
596            self.stop_error()
597
598        if string.find(self.comment, "DOC_ENABLE") != -1:
599            self.start_error()
600
601        return token
602
603     #
604     # Parse a comment block associate to a macro
605     #
606    def parseMacroComment(self, name, quiet = 0):
607        if name[0:2] == '__':
608            quiet = 1
609
610        args = []
611        desc = ""
612
613        if self.comment == None:
614            if not quiet:
615                self.warning("Missing comment for macro %s" % (name))
616            return((args, desc))
617        if self.comment[0] != '*':
618            if not quiet:
619                self.warning("Missing * in macro comment for %s" % (name))
620            return((args, desc))
621        lines = string.split(self.comment, '\n')
622        if lines[0] == '*':
623            del lines[0]
624        if lines[0] != "* %s:" % (name):
625            if not quiet:
626                self.warning("Misformatted macro comment for %s" % (name))
627                self.warning("  Expecting '* %s:' got '%s'" % (name, lines[0]))
628            return((args, desc))
629        del lines[0]
630        while lines[0] == '*':
631            del lines[0]
632        while len(lines) > 0 and lines[0][0:3] == '* @':
633            l = lines[0][3:]
634            try:
635                (arg, desc) = string.split(l, ':', 1)
636                desc=string.strip(desc)
637                arg=string.strip(arg)
638            except:
639                if not quiet:
640                    self.warning("Misformatted macro comment for %s" % (name))
641                    self.warning("  problem with '%s'" % (lines[0]))
642                del lines[0]
643                continue
644            del lines[0]
645            l = string.strip(lines[0])
646            while len(l) > 2 and l[0:3] != '* @':
647                while l[0] == '*':
648                    l = l[1:]
649                desc = desc + ' ' + string.strip(l)
650                del lines[0]
651                if len(lines) == 0:
652                    break
653                l = lines[0]
654            args.append((arg, desc))
655        while len(lines) > 0 and lines[0] == '*':
656            del lines[0]
657        desc = ""
658        while len(lines) > 0:
659            l = lines[0]
660            while len(l) > 0 and l[0] == '*':
661                l = l[1:]
662            l = string.strip(l)
663            desc = desc + " " + l
664            del lines[0]
665                     
666        desc = string.strip(desc)
667
668        if quiet == 0:
669            if desc == "":
670                self.warning("Macro comment for %s lack description of the macro" % (name))
671
672        return((args, desc))
673
674     #
675     # Parse a comment block and merge the informations found in the
676     # parameters descriptions, finally returns a block as complete
677     # as possible
678     #
679    def mergeFunctionComment(self, name, description, quiet = 0):
680        if name == 'main':
681            quiet = 1
682        if name[0:2] == '__':
683            quiet = 1
684
685        (ret, args) = description
686        desc = ""
687        retdesc = ""
688
689        if self.comment == None:
690            if not quiet:
691                self.warning("Missing comment for function %s" % (name))
692            return(((ret[0], retdesc), args, desc))
693        if self.comment[0] != '*':
694            if not quiet:
695                self.warning("Missing * in function comment for %s" % (name))
696            return(((ret[0], retdesc), args, desc))
697        lines = string.split(self.comment, '\n')
698        if lines[0] == '*':
699            del lines[0]
700        if lines[0] != "* %s:" % (name):
701            if not quiet:
702                self.warning("Misformatted function comment for %s" % (name))
703                self.warning("  Expecting '* %s:' got '%s'" % (name, lines[0]))
704            return(((ret[0], retdesc), args, desc))
705        del lines[0]
706        while lines[0] == '*':
707            del lines[0]
708        nbargs = len(args)
709        while len(lines) > 0 and lines[0][0:3] == '* @':
710            l = lines[0][3:]
711            try:
712                (arg, desc) = string.split(l, ':', 1)
713                desc=string.strip(desc)
714                arg=string.strip(arg)
715            except:
716                if not quiet:
717                    self.warning("Misformatted function comment for %s" % (name))
718                    self.warning("  problem with '%s'" % (lines[0]))
719                del lines[0]
720                continue
721            del lines[0]
722            l = string.strip(lines[0])
723            while len(l) > 2 and l[0:3] != '* @':
724                while l[0] == '*':
725                    l = l[1:]
726                desc = desc + ' ' + string.strip(l)
727                del lines[0]
728                if len(lines) == 0:
729                    break
730                l = lines[0]
731            i = 0
732            while i < nbargs:
733                if args[i][1] == arg:
734                    args[i] = (args[i][0], arg, desc)
735                    break;
736                i = i + 1
737            if i >= nbargs:
738                if not quiet:
739                    self.warning("Unable to find arg %s from function comment for %s" % (
740                       arg, name))
741        while len(lines) > 0 and lines[0] == '*':
742            del lines[0]
743        desc = ""
744        while len(lines) > 0:
745            l = lines[0]
746            while len(l) > 0 and l[0] == '*':
747                l = l[1:]
748            l = string.strip(l)
749            if len(l) >= 6 and  l[0:6] == "return" or l[0:6] == "Return":
750                try:
751                    l = string.split(l, ' ', 1)[1]
752                except:
753                    l = ""
754                retdesc = string.strip(l)
755                del lines[0]
756                while len(lines) > 0:
757                    l = lines[0]
758                    while len(l) > 0 and l[0] == '*':
759                        l = l[1:]
760                    l = string.strip(l)
761                    retdesc = retdesc + " " + l
762                    del lines[0]
763            else:
764                desc = desc + " " + l
765                del lines[0]
766                     
767        retdesc = string.strip(retdesc)
768        desc = string.strip(desc)
769
770        if quiet == 0:
771             #
772             # report missing comments
773             #
774            i = 0
775            while i < nbargs:
776                if args[i][2] == None and args[i][0] != "void" and args[i][1] != None:
777                    self.warning("Function comment for %s lack description of arg %s" % (name, args[i][1]))
778                i = i + 1
779            if retdesc == "" and ret[0] != "void":
780                self.warning("Function comment for %s lack description of return value" % (name))
781            if desc == "":
782                self.warning("Function comment for %s lack description of the function" % (name))
783
784
785        return(((ret[0], retdesc), args, desc))
786
787    def parsePreproc(self, token):
788        name = token[1]
789        if name == "#include":
790            token = self.lexer.token()
791            if token == None:
792                return None
793            if token[0] == 'preproc':
794                self.index_add(token[1], self.filename, not self.is_header,
795                                "include")
796                return self.lexer.token()
797            return token
798        if name == "#define":
799            token = self.lexer.token()
800            if token == None:
801                return None
802            if token[0] == 'preproc':
803                 # TODO macros with arguments
804                name = token[1]
805                lst = []
806                token = self.lexer.token()
807                while token != None and token[0] == 'preproc' and \
808                      token[1][0] != '#':
809                    lst.append(token[1])
810                    token = self.lexer.token()
811                try:
812                    name = string.split(name, '(') [0]
813                except:
814                    pass
815                info = self.parseMacroComment(name, not self.is_header)
816                self.index_add(name, self.filename, not self.is_header,
817                                "macro", info)
818                return token
819        token = self.lexer.token()
820        while token != None and token[0] == 'preproc' and \
821            token[1][0] != '#':
822            token = self.lexer.token()
823        return token
824
825     #
826     # token acquisition on top of the lexer, it handle internally
827     # preprocessor and comments since they are logically not part of
828     # the program structure.
829     #
830    def token(self):
831        global ignored_words
832
833        token = self.lexer.token()
834        while token != None:
835            if token[0] == 'comment':
836                token = self.parseComment(token)
837                continue
838            elif token[0] == 'preproc':
839                token = self.parsePreproc(token)
840                continue
841            elif token[0] == "name" and ignored_words.has_key(token[1]):
842                (n, info) = ignored_words[token[1]]
843                i = 0
844                while i < n:
845                    token = self.lexer.token()
846                    i = i + 1
847                token = self.lexer.token()
848                continue
849            else:
850                if debug:
851                    print "=> ", token
852                return token
853        return None
854
855     #
856     # Parse a typedef, it records the type and its name.
857     #
858    def parseTypedef(self, token):
859        if token == None:
860            return None
861        token = self.parseType(token)
862        if token == None:
863            self.error("parsing typedef")
864            return None
865        base_type = self.type
866        type = base_type
867         #self.debug("end typedef type", token)
868        while token != None:
869            if token[0] == "name":
870                name = token[1]
871                signature = self.signature
872                if signature != None:
873                    type = string.split(type, '(')[0]
874                    d = self.mergeFunctionComment(name,
875                            ((type, None), signature), 1)
876                    self.index_add(name, self.filename, not self.is_header,
877                                    "functype", d)
878                else:
879                    if base_type == "struct":
880                        self.index_add(name, self.filename, not self.is_header,
881                                        "struct", type)
882                        base_type = "struct " + name
883                    else:
884                        self.index_add(name, self.filename, not self.is_header,
885                                    "typedef", type)
886                token = self.token()
887            else:
888                self.error("parsing typedef: expecting a name")
889                return token
890             #self.debug("end typedef", token)
891            if token != None and token[0] == 'sep' and token[1] == ',':
892                type = base_type
893                token = self.token()
894                while token != None and token[0] == "op":
895                    type = type + token[1]
896                    token = self.token()
897            elif token != None and token[0] == 'sep' and token[1] == ';':
898                break;
899            elif token != None and token[0] == 'name':
900                type = base_type
901                continue;
902            else:
903                self.error("parsing typedef: expecting ';'", token)
904                return token
905        token = self.token()
906        return token
907             
908     #
909     # Parse a C code block, used for functions it parse till
910     # the balancing } included
911     #
912    def parseBlock(self, token):
913        while token != None:
914            if token[0] == "sep" and token[1] == "{":
915                token = self.token()
916                token = self.parseBlock(token)
917            elif token[0] == "sep" and token[1] == "}":
918                self.comment = None
919                token = self.token()
920                return token
921            else:
922                if self.collect_ref == 1:
923                    oldtok = token
924                    token = self.token()
925                    if oldtok[0] == "name" and oldtok[1][0:3] == "xml":
926                        if token[0] == "sep" and token[1] == "(":
927                            self.index_add_ref(oldtok[1], self.filename,
928                                                0, "function")
929                            token = self.token()
930                        elif token[0] == "name":
931                            token = self.token()
932                            if token[0] == "sep" and (token[1] == ";" or
933                               token[1] == "," or token[1] == "="):
934                                self.index_add_ref(oldtok[1], self.filename,
935                                                    0, "type")
936                    elif oldtok[0] == "name" and oldtok[1][0:4] == "XML_":
937                        self.index_add_ref(oldtok[1], self.filename,
938                                            0, "typedef")
939                    elif oldtok[0] == "name" and oldtok[1][0:7] == "LIBXML_":
940                        self.index_add_ref(oldtok[1], self.filename,
941                                            0, "typedef")
942                         
943                else:
944                    token = self.token()
945        return token
946
947     #
948     # Parse a C struct definition till the balancing }
949     #
950    def parseStruct(self, token):
951        fields = []
952         #self.debug("start parseStruct", token)
953        while token != None:
954            if token[0] == "sep" and token[1] == "{":
955                token = self.token()
956                token = self.parseTypeBlock(token)
957            elif token[0] == "sep" and token[1] == "}":
958                self.struct_fields = fields
959                 #self.debug("end parseStruct", token)
960                 #print fields
961                token = self.token()
962                return token
963            else:
964                base_type = self.type
965                 #self.debug("before parseType", token)
966                token = self.parseType(token)
967                 #self.debug("after parseType", token)
968                if token != None and token[0] == "name":
969                    fname = token[1]
970                    token = self.token()
971                    if token[0] == "sep" and token[1] == ";":
972                        self.comment = None
973                        token = self.token()
974                        fields.append((self.type, fname, self.comment))
975                        self.comment = None
976                    else:
977                        self.error("parseStruct: expecting ;", token)
978                elif token != None and token[0] == "sep" and token[1] == "{":
979                    token = self.token()
980                    token = self.parseTypeBlock(token)
981                    if token != None and token[0] == "name":
982                        token = self.token()
983                    if token != None and token[0] == "sep" and token[1] == ";":
984                        token = self.token()
985                    else:
986                        self.error("parseStruct: expecting ;", token)
987                else:
988                    self.error("parseStruct: name", token)
989                    token = self.token()
990                self.type = base_type;
991        self.struct_fields = fields
992         #self.debug("end parseStruct", token)
993         #print fields
994        return token
995
996     #
997     # Parse a C enum block, parse till the balancing }
998     #
999    def parseEnumBlock(self, token):
1000        self.enums = []
1001        name = None
1002        self.comment = None
1003        comment = ""
1004        value = "0"
1005        while token != None:
1006            if token[0] == "sep" and token[1] == "{":
1007                token = self.token()
1008                token = self.parseTypeBlock(token)
1009            elif token[0] == "sep" and token[1] == "}":
1010                if name != None:
1011                    if self.comment != None:
1012                        comment = self.comment
1013                        self.comment = None
1014                    self.enums.append((name, value, comment))
1015                token = self.token()
1016                return token
1017            elif token[0] == "name":
1018                    if name != None:
1019                        if self.comment != None:
1020                            comment = string.strip(self.comment)
1021                            self.comment = None
1022                        self.enums.append((name, value, comment))
1023                    name = token[1]
1024                    comment = ""
1025                    token = self.token()
1026                    if token[0] == "op" and token[1][0] == "=":
1027                        value = ""
1028                        if len(token[1]) > 1:
1029                            value = token[1][1:]
1030                        token = self.token()
1031                        while token[0] != "sep" or (token[1] != ',' and
1032                              token[1] != '}'):
1033                            value = value + token[1]
1034                            token = self.token()
1035                    else:
1036                        try:
1037                            value = "%d" % (int(value) + 1)
1038                        except:
1039                            self.warning("Failed to compute value of enum %s" % (name))
1040                            value=""
1041                    if token[0] == "sep" and token[1] == ",":
1042                        token = self.token()
1043            else:
1044                token = self.token()
1045        return token
1046
1047     #
1048     # Parse a C definition block, used for structs it parse till
1049     # the balancing }
1050     #
1051    def parseTypeBlock(self, token):
1052        while token != None:
1053            if token[0] == "sep" and token[1] == "{":
1054                token = self.token()
1055                token = self.parseTypeBlock(token)
1056            elif token[0] == "sep" and token[1] == "}":
1057                token = self.token()
1058                return token
1059            else:
1060                token = self.token()
1061        return token
1062
1063     #
1064     # Parse a type: the fact that the type name can either occur after
1065     #    the definition or within the definition makes it a little harder
1066     #    if inside, the name token is pushed back before returning
1067     #
1068    def parseType(self, token):
1069        self.type = ""
1070        self.struct_fields = []
1071        self.signature = None
1072        if token == None:
1073            return token
1074
1075        while token[0] == "name" and (
1076              token[1] == "const" or token[1] == "unsigned"):
1077            if self.type == "":
1078                self.type = token[1]
1079            else:
1080                self.type = self.type + " " + token[1]
1081            token = self.token()
1082
1083        if token[0] == "name" and (token[1] == "long" or token[1] == "short"):
1084            if self.type == "":
1085                self.type = token[1]
1086            else:
1087                self.type = self.type + " " + token[1]
1088            if token[0] == "name" and token[1] == "int":
1089                if self.type == "":
1090                    self.type = tmp[1]
1091                else:
1092                    self.type = self.type + " " + tmp[1]
1093             
1094        elif token[0] == "name" and token[1] == "struct":
1095            if self.type == "":
1096                self.type = token[1]
1097            else:
1098                self.type = self.type + " " + token[1]
1099            token = self.token()
1100            nametok = None
1101            if token[0] == "name":
1102                nametok = token
1103                token = self.token()
1104            if token != None and token[0] == "sep" and token[1] == "{":
1105                token = self.token()
1106                token = self.parseStruct(token)
1107            elif token != None and token[0] == "op" and token[1] == "*":
1108                self.type = self.type + " " + nametok[1] + " *"
1109                token = self.token()
1110                while token != None and token[0] == "op" and token[1] == "*":
1111                    self.type = self.type + " *"
1112                    token = self.token()
1113                if token[0] == "name":
1114                    nametok = token
1115                    token = self.token()
1116                else:
1117                    self.error("struct : expecting name", token)
1118                    return token
1119            elif token != None and token[0] == "name" and nametok != None:
1120                self.type = self.type + " " + nametok[1]
1121                return token
1122
1123            if nametok != None:
1124                self.lexer.push(token)
1125                token = nametok
1126            return token
1127
1128        elif token[0] == "name" and token[1] == "enum":
1129            if self.type == "":
1130                self.type = token[1]
1131            else:
1132                self.type = self.type + " " + token[1]
1133            self.enums = []
1134            token = self.token()
1135            if token != None and token[0] == "sep" and token[1] == "{":
1136                token = self.token()
1137                token = self.parseEnumBlock(token)
1138            else:
1139                self.error("parsing enum: expecting '{'", token)
1140            enum_type = None
1141            if token != None and token[0] != "name":
1142                self.lexer.push(token)
1143                token = ("name", "enum")
1144            else:
1145                enum_type = token[1]
1146            for enum in self.enums:
1147                self.index_add(enum[0], self.filename,
1148                               not self.is_header, "enum",
1149                               (enum[1], enum[2], enum_type))
1150            return token
1151
1152        elif token[0] == "name":
1153            if self.type == "":
1154                self.type = token[1]
1155            else:
1156                self.type = self.type + " " + token[1]
1157        else:
1158            self.error("parsing type %s: expecting a name" % (self.type),
1159                       token)
1160            return token
1161        token = self.token()
1162        while token != None and (token[0] == "op" or
1163              token[0] == "name" and token[1] == "const"):
1164            self.type = self.type + " " + token[1]
1165            token = self.token()
1166
1167         #
1168         # if there is a parenthesis here, this means a function type
1169         #
1170        if token != None and token[0] == "sep" and token[1] == '(':
1171            self.type = self.type + token[1]
1172            token = self.token()
1173            while token != None and token[0] == "op" and token[1] == '*':
1174                self.type = self.type + token[1]
1175                token = self.token()
1176            if token == None or token[0] != "name" :
1177                self.error("parsing function type, name expected", token);
1178                return token
1179            self.type = self.type + token[1]
1180            nametok = token
1181            token = self.token()
1182            if token != None and token[0] == "sep" and token[1] == ')':
1183                self.type = self.type + token[1]
1184                token = self.token()
1185                if token != None and token[0] == "sep" and token[1] == '(':
1186                    token = self.token()
1187                    type = self.type;
1188                    token = self.parseSignature(token);
1189                    self.type = type;
1190                else:
1191                    self.error("parsing function type, '(' expected", token);
1192                    return token
1193            else:
1194                self.error("parsing function type, ')' expected", token);
1195                return token
1196            self.lexer.push(token)
1197            token = nametok
1198            return token
1199
1200         #
1201         # do some lookahead for arrays
1202         #
1203        if token != None and token[0] == "name":
1204            nametok = token
1205            token = self.token()
1206            if token != None and token[0] == "sep" and token[1] == '[':
1207                self.type = self.type + nametok[1]
1208                while token != None and token[0] == "sep" and token[1] == '[':
1209                    self.type = self.type + token[1]
1210                    token = self.token()
1211                    while token != None and token[0] != 'sep' and \
1212                          token[1] != ']' and token[1] != ';':
1213                        self.type = self.type + token[1]
1214                        token = self.token()
1215                if token != None and token[0] == 'sep' and token[1] == ']':
1216                    self.type = self.type + token[1]
1217                    token = self.token()
1218                else:
1219                    self.error("parsing array type, ']' expected", token);
1220                    return token
1221            elif token != None and token[0] == "sep" and token[1] == ':':
1222                 # remove :12 in case it's a limited int size
1223                token = self.token()
1224                token = self.token()
1225            self.lexer.push(token)
1226            token = nametok
1227
1228        return token
1229
1230     #
1231     # Parse a signature: '(' has been parsed and we scan the type definition
1232     #    up to the ')' included
1233    def parseSignature(self, token):
1234        signature = []
1235        if token != None and token[0] == "sep" and token[1] == ')':
1236            self.signature = []
1237            token = self.token()
1238            return token
1239        while token != None:
1240            token = self.parseType(token)
1241            if token != None and token[0] == "name":
1242                signature.append((self.type, token[1], None))
1243                token = self.token()
1244            elif token != None and token[0] == "sep" and token[1] == ',':
1245                token = self.token()
1246                continue
1247            elif token != None and token[0] == "sep" and token[1] == ')':
1248                 # only the type was provided
1249                if self.type == "...":
1250                    signature.append((self.type, "...", None))
1251                else:
1252                    signature.append((self.type, None, None))
1253            if token != None and token[0] == "sep":
1254                if token[1] == ',':
1255                    token = self.token()
1256                    continue
1257                elif token[1] == ')':
1258                    token = self.token()
1259                    break
1260        self.signature = signature
1261        return token
1262
1263     #
1264     # Parse a global definition, be it a type, variable or function
1265     # the extern "C" blocks are a bit nasty and require it to recurse.
1266     #
1267    def parseGlobal(self, token):
1268        static = 0
1269        if token[1] == 'extern':
1270            token = self.token()
1271            if token == None:
1272                return token
1273            if token[0] == 'string':
1274                if token[1] == 'C':
1275                    token = self.token()
1276                    if token == None:
1277                        return token
1278                    if token[0] == 'sep' and token[1] == "{":
1279                        token = self.token()
1280#                        print 'Entering extern "C line ', self.lineno()
1281                        while token != None and (token[0] != 'sep' or
1282                              token[1] != "}"):
1283                            if token[0] == 'name':
1284                                token = self.parseGlobal(token)
1285                            else:
1286                                self.error(
1287                                 "token %s %s unexpected at the top level" % (
1288                                        token[0], token[1]))
1289                                token = self.parseGlobal(token)
1290#                        print 'Exiting extern "C" line', self.lineno()
1291                        token = self.token()
1292                        return token
1293                else:
1294                    return token
1295        elif token[1] == 'static':
1296            static = 1
1297            token = self.token()
1298            if token == None or  token[0] != 'name':
1299                return token
1300
1301        if token[1] == 'typedef':
1302            token = self.token()
1303            return self.parseTypedef(token)
1304        else:
1305            token = self.parseType(token)
1306            type_orig = self.type
1307        if token == None or token[0] != "name":
1308            return token
1309        type = type_orig
1310        self.name = token[1]
1311        token = self.token()
1312        while token != None and (token[0] == "sep" or token[0] == "op"):
1313            if token[0] == "sep":
1314                if token[1] == "[":
1315                    type = type + token[1]
1316                    token = self.token()
1317                    while token != None and (token[0] != "sep" or \
1318                          token[1] != ";"):
1319                        type = type + token[1]
1320                        token = self.token()
1321
1322            if token != None and token[0] == "op" and token[1] == "=":
1323                 #
1324                 # Skip the initialization of the variable
1325                 #
1326                token = self.token()
1327                if token[0] == 'sep' and token[1] == '{':
1328                    token = self.token()
1329                    token = self.parseBlock(token)
1330                else:
1331                    self.comment = None
1332                    while token != None and (token[0] != "sep" or \
1333                          (token[1] != ';' and token[1] != ',')):
1334                            token = self.token()
1335                self.comment = None
1336                if token == None or token[0] != "sep" or (token[1] != ';' and
1337                   token[1] != ','):
1338                    self.error("missing ';' or ',' after value")
1339
1340            if token != None and token[0] == "sep":
1341                if token[1] == ";":
1342                    self.comment = None
1343                    token = self.token()
1344                    if type == "struct":
1345                        self.index_add(self.name, self.filename,
1346                             not self.is_header, "struct", self.struct_fields)
1347                    else:
1348                        self.index_add(self.name, self.filename,
1349                             not self.is_header, "variable", type)
1350                    break
1351                elif token[1] == "(":
1352                    token = self.token()
1353                    token = self.parseSignature(token)
1354                    if token == None:
1355                        return None
1356                    if token[0] == "sep" and token[1] == ";":
1357                        d = self.mergeFunctionComment(self.name,
1358                                ((type, None), self.signature), 1)
1359                        self.index_add(self.name, self.filename, static,
1360                                        "function", d)
1361                        token = self.token()
1362                    elif token[0] == "sep" and token[1] == "{":
1363                        d = self.mergeFunctionComment(self.name,
1364                                ((type, None), self.signature), static)
1365                        self.index_add(self.name, self.filename, static,
1366                                        "function", d)
1367                        token = self.token()
1368                        token = self.parseBlock(token);
1369                elif token[1] == ',':
1370                    self.comment = None
1371                    self.index_add(self.name, self.filename, static,
1372                                    "variable", type)
1373                    type = type_orig
1374                    token = self.token()
1375                    while token != None and token[0] == "sep":
1376                        type = type + token[1]
1377                        token = self.token()
1378                    if token != None and token[0] == "name":
1379                        self.name = token[1]
1380                        token = self.token()
1381                else:
1382                    break
1383                   
1384        return token
1385
1386    def parse(self):
1387        self.warning("Parsing %s" % (self.filename))
1388        token = self.token()
1389        while token != None:
1390            if token[0] == 'name':
1391                token = self.parseGlobal(token)
1392            else:
1393                self.error("token %s %s unexpected at the top level" % (
1394                       token[0], token[1]))
1395                token = self.parseGlobal(token)
1396                return
1397        self.parseTopComment(self.top_comment)
1398        return self.index
1399                 
1400
1401class docBuilder:
1402    """A documentation builder"""
1403    def __init__(self, name, directories=['.'], excludes=[]):
1404        self.name = name
1405        self.directories = directories
1406        self.excludes = excludes + ignored_files.keys()
1407        self.modules = {}
1408        self.headers = {}
1409        self.idx = index()
1410        self.xref = {}
1411        self.index = {}
1412        if name == 'libxml2':
1413            self.basename = 'libxml'
1414        else:
1415            self.basename = name
1416
1417    def indexString(self, id, str):
1418        if str == None:
1419            return
1420        str = string.replace(str, "'", ' ')
1421        str = string.replace(str, '"', ' ')
1422        str = string.replace(str, "/", ' ')
1423        str = string.replace(str, '*', ' ')
1424        str = string.replace(str, "[", ' ')
1425        str = string.replace(str, "]", ' ')
1426        str = string.replace(str, "(", ' ')
1427        str = string.replace(str, ")", ' ')
1428        str = string.replace(str, "<", ' ')
1429        str = string.replace(str, '>', ' ')
1430        str = string.replace(str, "&", ' ')
1431        str = string.replace(str, '#', ' ')
1432        str = string.replace(str, ",", ' ')
1433        str = string.replace(str, '.', ' ')
1434        str = string.replace(str, ';', ' ')
1435        tokens = string.split(str)
1436        for token in tokens:
1437            try:
1438                c = token[0]
1439                if string.find(string.letters, c) < 0:
1440                    pass
1441                elif len(token) < 3:
1442                    pass
1443                else:
1444                    lower = string.lower(token)
1445                    # TODO: generalize this a bit
1446                    if lower == 'and' or lower == 'the':
1447                        pass
1448                    elif self.xref.has_key(token):
1449                        self.xref[token].append(id)
1450                    else:
1451                        self.xref[token] = [id]
1452            except:
1453                pass
1454
1455    def analyze(self):
1456        print "Project %s : %d headers, %d modules" % (self.name, len(self.headers.keys()), len(self.modules.keys()))
1457        self.idx.analyze()
1458
1459    def scanHeaders(self):
1460        for header in self.headers.keys():
1461            parser = CParser(header)
1462            idx = parser.parse()
1463            self.headers[header] = idx;
1464            self.idx.merge(idx)
1465
1466    def scanModules(self):
1467        for module in self.modules.keys():
1468            parser = CParser(module)
1469            idx = parser.parse()
1470            # idx.analyze()
1471            self.modules[module] = idx
1472            self.idx.merge_public(idx)
1473
1474    def scan(self):
1475        for directory in self.directories:
1476            files = glob.glob(directory + "/*.c")
1477            for file in files:
1478                skip = 0
1479                for excl in self.excludes:
1480                    if string.find(file, excl) != -1:
1481                        skip = 1;
1482                        break
1483                if skip == 0:
1484                    self.modules[file] = None;
1485            files = glob.glob(directory + "/*.h")
1486            for file in files:
1487                skip = 0
1488                for excl in self.excludes:
1489                    if string.find(file, excl) != -1:
1490                        skip = 1;
1491                        break
1492                if skip == 0:
1493                    self.headers[file] = None;
1494        self.scanHeaders()
1495        self.scanModules()
1496         
1497    def modulename_file(self, file):
1498        module = os.path.basename(file)
1499        if module[-2:] == '.h':
1500            module = module[:-2]
1501        return module
1502
1503    def serialize_enum(self, output, name):
1504        id = self.idx.enums[name]
1505        output.write("    <enum name='%s' file='%s'" % (name,
1506                     self.modulename_file(id.module)))
1507        if id.info != None:
1508            info = id.info
1509            if info[0] != None and info[0] != '':
1510                try:
1511                    val = eval(info[0])
1512                except:
1513                    val = info[0]
1514                output.write(" value='%s'" % (val));
1515            if info[2] != None and info[2] != '':
1516                output.write(" type='%s'" % info[2]);
1517            if info[1] != None and info[1] != '':
1518                output.write(" info='%s'" % escape(info[1]));
1519        output.write("/>\n")
1520
1521    def serialize_macro(self, output, name):
1522        id = self.idx.macros[name]
1523        output.write("    <macro name='%s' file='%s'>\n" % (name,
1524                     self.modulename_file(id.module)))
1525        if id.info != None:
1526            try:
1527                (args, desc) = id.info
1528                if desc != None and desc != "":
1529                    output.write("      <info>%s</info>\n" % (escape(desc)))
1530                    self.indexString(name, desc)
1531                for arg in args:
1532                    (name, desc) = arg
1533                    if desc != None and desc != "":
1534                        output.write("      <arg name='%s' info='%s'/>\n" % (
1535                                     name, escape(desc)))
1536                        self.indexString(name, desc)
1537                    else:
1538                        output.write("      <arg name='%s'/>\n" % (name))
1539            except:
1540                pass
1541        output.write("    </macro>\n")
1542
1543    def serialize_typedef(self, output, name):
1544        id = self.idx.typedefs[name]
1545        if id.info[0:7] == 'struct ':
1546            output.write("    <struct name='%s' file='%s' type='%s'" % (
1547                     name, self.modulename_file(id.module), id.info))
1548            name = id.info[7:]
1549            if self.idx.structs.has_key(name) and ( \
1550               type(self.idx.structs[name].info) == type(()) or
1551                type(self.idx.structs[name].info) == type([])):
1552                output.write(">\n");
1553                try:
1554                    for field in self.idx.structs[name].info:
1555                        desc = field[2]
1556                        self.indexString(name, desc)
1557                        if desc == None:
1558                            desc = ''
1559                        else:
1560                            desc = escape(desc)
1561                        output.write("      <field name='%s' type='%s' info='%s'/>\n" % (field[1] , field[0], desc))
1562                except:
1563                    print "Failed to serialize struct %s" % (name)
1564                output.write("    </struct>\n")
1565            else:
1566                output.write("/>\n");
1567        else :
1568            output.write("    <typedef name='%s' file='%s' type='%s'/>\n" % (
1569                     name, self.modulename_file(id.module), id.info))
1570
1571    def serialize_variable(self, output, name):
1572        id = self.idx.variables[name]
1573        if id.info != None:
1574            output.write("    <variable name='%s' file='%s' type='%s'/>\n" % (
1575                    name, self.modulename_file(id.module), id.info))
1576        else:
1577            output.write("    <variable name='%s' file='%s'/>\n" % (
1578                    name, self.modulename_file(id.module)))
1579                     
1580    def serialize_function(self, output, name):
1581        id = self.idx.functions[name]
1582        output.write("    <%s name='%s' file='%s'>\n" % (id.type, name,
1583                     self.modulename_file(id.module)))
1584        try:
1585            (ret, params, desc) = id.info
1586            output.write("      <info>%s</info>\n" % (escape(desc)))
1587            self.indexString(name, desc)
1588            if ret[0] != None:
1589                if ret[0] == "void":
1590                    output.write("      <return type='void'/>\n")
1591                else:
1592                    output.write("      <return type='%s' info='%s'/>\n" % (
1593                             ret[0], escape(ret[1])))
1594                    self.indexString(name, ret[1])
1595            for param in params:
1596                if param[0] == 'void':
1597                    continue
1598                if param[2] == None:
1599                    output.write("      <arg name='%s' type='%s' info=''/>\n" % (param[1], param[0]))
1600                else:
1601                    output.write("      <arg name='%s' type='%s' info='%s'/>\n" % (param[1], param[0], escape(param[2])))
1602                    self.indexString(name, param[2])
1603        except:
1604            print "Failed to save function %s info: " % name, `id.info`
1605        output.write("    </%s>\n" % (id.type))
1606
1607    def serialize_exports(self, output, file):
1608        module = self.modulename_file(file)
1609        output.write("    <file name='%s'>\n" % (module))
1610        dict = self.headers[file]
1611        if dict.info != None:
1612            for data in ('Summary', 'Description', 'Author'):
1613                try:
1614                    output.write("     <%s>%s</%s>\n" % (
1615                                 string.lower(data),
1616                                 escape(dict.info[data]),
1617                                 string.lower(data)))
1618                except:
1619                    print "Header %s lacks a %s description" % (module, data)
1620            if dict.info.has_key('Description'):
1621                desc = dict.info['Description']
1622                if string.find(desc, "DEPRECATED") != -1:
1623                    output.write("     <deprecated/>\n")
1624
1625        ids = dict.macros.keys()
1626        ids.sort()
1627        for id in uniq(ids):
1628            # Macros are sometime used to masquerade other types.
1629            if dict.functions.has_key(id):
1630                continue
1631            if dict.variables.has_key(id):
1632                continue
1633            if dict.typedefs.has_key(id):
1634                continue
1635            if dict.structs.has_key(id):
1636                continue
1637            if dict.enums.has_key(id):
1638                continue
1639            output.write("     <exports symbol='%s' type='macro'/>\n" % (id))
1640        ids = dict.enums.keys()
1641        ids.sort()
1642        for id in uniq(ids):
1643            output.write("     <exports symbol='%s' type='enum'/>\n" % (id))
1644        ids = dict.typedefs.keys()
1645        ids.sort()
1646        for id in uniq(ids):
1647            output.write("     <exports symbol='%s' type='typedef'/>\n" % (id))
1648        ids = dict.structs.keys()
1649        ids.sort()
1650        for id in uniq(ids):
1651            output.write("     <exports symbol='%s' type='struct'/>\n" % (id))
1652        ids = dict.variables.keys()
1653        ids.sort()
1654        for id in uniq(ids):
1655            output.write("     <exports symbol='%s' type='variable'/>\n" % (id))
1656        ids = dict.functions.keys()
1657        ids.sort()
1658        for id in uniq(ids):
1659            output.write("     <exports symbol='%s' type='function'/>\n" % (id))
1660        output.write("    </file>\n")
1661
1662    def serialize_xrefs_files(self, output):
1663        headers = self.headers.keys()
1664        headers.sort()
1665        for file in headers:
1666            module = self.modulename_file(file)
1667            output.write("    <file name='%s'>\n" % (module))
1668            dict = self.headers[file]
1669            ids = uniq(dict.functions.keys() + dict.variables.keys() + \
1670                  dict.macros.keys() + dict.typedefs.keys() + \
1671                  dict.structs.keys() + dict.enums.keys())
1672            ids.sort()
1673            for id in ids:
1674                output.write("      <ref name='%s'/>\n" % (id))
1675            output.write("    </file>\n")
1676        pass
1677
1678    def serialize_xrefs_functions(self, output):
1679        funcs = {}
1680        for name in self.idx.functions.keys():
1681            id = self.idx.functions[name]
1682            try:
1683                (ret, params, desc) = id.info
1684                for param in params:
1685                    if param[0] == 'void':
1686                        continue
1687                    if funcs.has_key(param[0]):
1688                        funcs[param[0]].append(name)
1689                    else:
1690                        funcs[param[0]] = [name]
1691            except:
1692                pass
1693        typ = funcs.keys()
1694        typ.sort()
1695        for type in typ:
1696            if type == '' or type == 'void' or type == "int" or \
1697               type == "char *" or type == "const char *" :
1698                continue
1699            output.write("    <type name='%s'>\n" % (type))
1700            ids = funcs[type]
1701            ids.sort()
1702            pid = ''    # not sure why we have dups, but get rid of them!
1703            for id in ids:
1704                if id != pid:
1705                    output.write("      <ref name='%s'/>\n" % (id))
1706                    pid = id
1707            output.write("    </type>\n")
1708
1709    def serialize_xrefs_constructors(self, output):
1710        funcs = {}
1711        for name in self.idx.functions.keys():
1712            id = self.idx.functions[name]
1713            try:
1714                (ret, params, desc) = id.info
1715                if ret[0] == "void":
1716                    continue
1717                if funcs.has_key(ret[0]):
1718                    funcs[ret[0]].append(name)
1719                else:
1720                    funcs[ret[0]] = [name]
1721            except:
1722                pass
1723        typ = funcs.keys()
1724        typ.sort()
1725        for type in typ:
1726            if type == '' or type == 'void' or type == "int" or \
1727               type == "char *" or type == "const char *" :
1728                continue
1729            output.write("    <type name='%s'>\n" % (type))
1730            ids = funcs[type]
1731            ids.sort()
1732            for id in ids:
1733                output.write("      <ref name='%s'/>\n" % (id))
1734            output.write("    </type>\n")
1735
1736    def serialize_xrefs_alpha(self, output):
1737        letter = None
1738        ids = self.idx.identifiers.keys()
1739        ids.sort()
1740        for id in ids:
1741            if id[0] != letter:
1742                if letter != None:
1743                    output.write("    </letter>\n")
1744                letter = id[0]
1745                output.write("    <letter name='%s'>\n" % (letter))
1746            output.write("      <ref name='%s'/>\n" % (id))
1747        if letter != None:
1748            output.write("    </letter>\n")
1749
1750    def serialize_xrefs_references(self, output):
1751        typ = self.idx.identifiers.keys()
1752        typ.sort()
1753        for id in typ:
1754            idf = self.idx.identifiers[id]
1755            module = idf.module
1756            output.write("    <reference name='%s' href='%s'/>\n" % (id,
1757                         'html/' + self.basename + '-' +
1758                         self.modulename_file(module) + '.html#' +
1759                         id))
1760
1761    def serialize_xrefs_index(self, output):
1762        index = self.xref
1763        typ = index.keys()
1764        typ.sort()
1765        letter = None
1766        count = 0
1767        chunk = 0
1768        chunks = []
1769        for id in typ:
1770            if len(index[id]) > 30:
1771                continue
1772            if id[0] != letter:
1773                if letter == None or count > 200:
1774                    if letter != None:
1775                        output.write("      </letter>\n")
1776                        output.write("    </chunk>\n")
1777                        count = 0
1778                        chunks.append(["chunk%s" % (chunk -1), first_letter, letter])
1779                    output.write("    <chunk name='chunk%s'>\n" % (chunk))
1780                    first_letter = id[0]
1781                    chunk = chunk + 1
1782                elif letter != None:
1783                    output.write("      </letter>\n")
1784                letter = id[0]
1785                output.write("      <letter name='%s'>\n" % (letter))
1786            output.write("        <word name='%s'>\n" % (id))
1787            tokens = index[id];
1788            tokens.sort()
1789            tok = None
1790            for token in tokens:
1791                if tok == token:
1792                    continue
1793                tok = token
1794                output.write("          <ref name='%s'/>\n" % (token))
1795                count = count + 1
1796            output.write("        </word>\n")
1797        if letter != None:
1798            output.write("      </letter>\n")
1799            output.write("    </chunk>\n")
1800            if count != 0:
1801                chunks.append(["chunk%s" % (chunk -1), first_letter, letter])
1802            output.write("    <chunks>\n")
1803            for ch in chunks:
1804                output.write("      <chunk name='%s' start='%s' end='%s'/>\n" % (
1805                             ch[0], ch[1], ch[2]))
1806            output.write("    </chunks>\n")
1807
1808    def serialize_xrefs(self, output):
1809        output.write("  <references>\n")
1810        self.serialize_xrefs_references(output)
1811        output.write("  </references>\n")
1812        output.write("  <alpha>\n")
1813        self.serialize_xrefs_alpha(output)
1814        output.write("  </alpha>\n")
1815        output.write("  <constructors>\n")
1816        self.serialize_xrefs_constructors(output)
1817        output.write("  </constructors>\n")
1818        output.write("  <functions>\n")
1819        self.serialize_xrefs_functions(output)
1820        output.write("  </functions>\n")
1821        output.write("  <files>\n")
1822        self.serialize_xrefs_files(output)
1823        output.write("  </files>\n")
1824        output.write("  <index>\n")
1825        self.serialize_xrefs_index(output)
1826        output.write("  </index>\n")
1827
1828    def serialize(self, outdir):
1829        filename = outdir + "%s-api.xml" % self.name
1830        print "Saving XML description %s" % (filename)
1831        output = open(filename, "w")
1832        output.write('<?xml version="1.0" encoding="ISO-8859-1"?>\n')
1833        output.write("<api name='%s'>\n" % self.name)
1834        output.write("  <files>\n")
1835        headers = self.headers.keys()
1836        headers.sort()
1837        for file in headers:
1838            self.serialize_exports(output, file)
1839        output.write("  </files>\n")
1840        output.write("  <symbols>\n")
1841        macros = self.idx.macros.keys()
1842        macros.sort()
1843        for macro in macros:
1844            self.serialize_macro(output, macro)
1845        enums = self.idx.enums.keys()
1846        enums.sort()
1847        for enum in enums:
1848            self.serialize_enum(output, enum)
1849        typedefs = self.idx.typedefs.keys()
1850        typedefs.sort()
1851        for typedef in typedefs:
1852            self.serialize_typedef(output, typedef)
1853        variables = self.idx.variables.keys()
1854        variables.sort()
1855        for variable in variables:
1856            self.serialize_variable(output, variable)
1857        functions = self.idx.functions.keys()
1858        functions.sort()
1859        for function in functions:
1860            self.serialize_function(output, function)
1861        output.write("  </symbols>\n")
1862        output.write("</api>\n")
1863        output.close()
1864
1865        filename = outdir + "%s-refs.xml" % self.name
1866        print "Saving XML Cross References %s" % (filename)
1867        output = open(filename, "w")
1868        output.write('<?xml version="1.0" encoding="ISO-8859-1"?>\n')
1869        output.write("<apirefs name='%s'>\n" % self.name)
1870        self.serialize_xrefs(output)
1871        output.write("</apirefs>\n")
1872        output.close()
1873
1874
1875def rebuild():
1876    builder = None
1877    if glob.glob("parser.c") != [] :
1878        print "Rebuilding API description for libxml2"
1879        builder = docBuilder("libxml2", [".", "."],
1880                             ["xmlwin32version.h", "tst.c"])
1881    elif glob.glob("../parser.c") != [] :
1882        print "Rebuilding API description for libxml2"
1883        builder = docBuilder("libxml2", ["..", "../include/libxml"],
1884                             ["xmlwin32version.h", "tst.c"])
1885    elif glob.glob("../libxslt/transform.c") != [] :
1886        print "Rebuilding API description for libxslt"
1887        builder = docBuilder("libxslt", ["../libxslt"],
1888                             ["win32config.h", "libxslt.h", "tst.c"])
1889    else:
1890        print "rebuild() failed, unable to guess the module"
1891        return None
1892    builder.scan()
1893    builder.analyze()
1894    builder.serialize("./")
1895    if glob.glob("../libexslt/exslt.c") != [] :
1896        extra = docBuilder("libexslt", ["../libexslt"], ["libexslt.h"])
1897        extra.scan()
1898        extra.analyze()
1899        extra.serialize("EXSLT/")
1900    return builder
1901
1902#
1903# for debugging the parser
1904#
1905def parse(filename):
1906    parser = CParser(filename)
1907    idx = parser.parse()
1908    return idx
1909
1910if __name__ == "__main__":
1911    rebuild()
Note: See TracBrowser for help on using the repository browser.