apibuild.py 97.6 KB
Newer Older
1
#!/usr/bin/env python
2 3 4 5 6 7 8 9
#
# This is the API builder, it parses the C sources and build the
# API formal description in XML.
#
# See Copyright for the status of this software.
#
# daniel@veillard.com
#
10 11 12

from __future__ import print_function

13 14
import os
import sys
15
import glob
16
import re
17

18 19 20 21
quiet = True
warnings = 0
debug = False
debugsym = None
22 23 24 25

#
# C parser analysis code
#
26
included_files = {
27
  "libvirt-common.h": "header with general libvirt API definitions",
28
  "libvirt-domain.h": "header with general libvirt API definitions",
29
  "libvirt-domain-snapshot.h": "header with general libvirt API definitions",
30
  "libvirt-event.h": "header with general libvirt API definitions",
31
  "libvirt-host.h": "header with general libvirt API definitions",
32
  "libvirt-interface.h": "header with general libvirt API definitions",
33
  "libvirt-network.h": "header with general libvirt API definitions",
34
  "libvirt-nodedev.h": "header with general libvirt API definitions",
35
  "libvirt-nwfilter.h": "header with general libvirt API definitions",
36
  "libvirt-secret.h": "header with general libvirt API definitions",
37
  "libvirt-storage.h": "header with general libvirt API definitions",
38
  "libvirt-stream.h": "header with general libvirt API definitions",
39 40
  "virterror.h": "header with error specific API definitions",
  "libvirt.c": "Main interfaces for the libvirt library",
41
  "libvirt-domain.c": "Domain interfaces for the libvirt library",
42
  "libvirt-domain-snapshot.c": "Domain snapshot interfaces for the libvirt library",
43
  "libvirt-host.c": "Host interfaces for the libvirt library",
44
  "libvirt-interface.c": "Interface interfaces for the libvirt library",
45
  "libvirt-network.c": "Network interfaces for the libvirt library",
46
  "libvirt-nodedev.c": "Node device interfaces for the libvirt library",
47
  "libvirt-nwfilter.c": "NWFilter interfaces for the libvirt library",
48
  "libvirt-secret.c": "Secret interfaces for the libvirt library",
49
  "libvirt-storage.c": "Storage interfaces for the libvirt library",
50
  "libvirt-stream.c": "Stream interfaces for the libvirt library",
51
  "virerror.c": "implements error handling and reporting code for libvirt",
52
  "virevent.c": "event loop for monitoring file handles",
53
  "virtypedparam.c": "virTypedParameters APIs",
54 55
}

56 57 58 59 60
qemu_included_files = {
  "libvirt-qemu.h": "header with QEMU specific API definitions",
  "libvirt-qemu.c": "Implementations for the QEMU specific APIs",
}

61 62 63 64 65
lxc_included_files = {
  "libvirt-lxc.h": "header with LXC specific API definitions",
  "libvirt-lxc.c": "Implementations for the LXC specific APIs",
}

66 67 68 69 70
admin_included_files = {
  "libvirt-admin.h": "header with admin specific API definitions",
  "libvirt-admin.c": "Implementations for the admin specific APIs",
}

71 72
ignored_words = {
  "ATTRIBUTE_UNUSED": (0, "macro keyword"),
73
  "ATTRIBUTE_SENTINEL": (0, "macro keyword"),
74
  "VIR_DEPRECATED": (0, "macro keyword"),
75
  "VIR_EXPORT_VAR": (0, "macro keyword"),
76 77 78
  "WINAPI": (0, "Windows keyword"),
  "__declspec": (3, "Windows keyword"),
  "__stdcall": (0, "Windows keyword"),
79 80
}

D
Daniel Veillard 已提交
81
ignored_functions = {
82
  "virConnectSupportsFeature": "private function for remote access",
D
Daniel Veillard 已提交
83 84 85 86 87
  "virDomainMigrateFinish": "private function for migration",
  "virDomainMigrateFinish2": "private function for migration",
  "virDomainMigratePerform": "private function for migration",
  "virDomainMigratePrepare": "private function for migration",
  "virDomainMigratePrepare2": "private function for migration",
C
Chris Lalancette 已提交
88
  "virDomainMigratePrepareTunnel": "private function for tunnelled migration",
89 90 91 92 93 94
  "virDomainMigrateBegin3": "private function for migration",
  "virDomainMigrateFinish3": "private function for migration",
  "virDomainMigratePerform3": "private function for migration",
  "virDomainMigratePrepare3": "private function for migration",
  "virDomainMigrateConfirm3": "private function for migration",
  "virDomainMigratePrepareTunnel3": "private function for tunnelled migration",
95
  "DllMain": "specific function for Win32",
96
  "virTypedParamsValidate": "internal function in virtypedparam.c",
97
  "virTypedParameterValidateSet": "internal function in virtypedparam.c",
98 99
  "virTypedParameterAssign": "internal function in virtypedparam.c",
  "virTypedParameterAssignFromStr": "internal function in virtypedparam.c",
100
  "virTypedParameterToString": "internal function in virtypedparam.c",
101
  "virTypedParamsCheck": "internal function in virtypedparam.c",
102
  "virTypedParamsCopy": "internal function in virtypedparam.c",
103 104 105 106 107 108
  "virDomainMigrateBegin3Params": "private function for migration",
  "virDomainMigrateFinish3Params": "private function for migration",
  "virDomainMigratePerform3Params": "private function for migration",
  "virDomainMigratePrepare3Params": "private function for migration",
  "virDomainMigrateConfirm3Params": "private function for migration",
  "virDomainMigratePrepareTunnel3Params": "private function for tunnelled migration",
J
Jiri Denemark 已提交
109
  "virErrorCopyNew": "private",
D
Daniel Veillard 已提交
110 111
}

112 113 114 115 116 117
ignored_macros = {
  "_virSchedParameter": "backward compatibility macro for virTypedParameter",
  "_virBlkioParameter": "backward compatibility macro for virTypedParameter",
  "_virMemoryParameter": "backward compatibility macro for virTypedParameter",
}

118 119
# macros that should be completely skipped
hidden_macros = {
120 121
  "VIR_DEPRECATED": "internal macro to mark deprecated apis",
  "VIR_EXPORT_VAR": "internal macro to mark exported vars",
122 123
}

124
def escape(raw):
125 126 127 128 129
    raw = raw.replace('&', '&')
    raw = raw.replace('<', '&lt;')
    raw = raw.replace('>', '&gt;')
    raw = raw.replace("'", '&apos;')
    raw = raw.replace('"', '&quot;')
130 131 132
    return raw

def uniq(items):
133
    return sorted(set(items))
134 135

class identifier:
136 137
    def __init__(self, name, header=None, module=None, type=None, lineno=0,
                 info=None, extra=None, conditionals=None):
138
        self.name = name
139 140 141 142 143 144 145
        self.header = header
        self.module = module
        self.type = type
        self.info = info
        self.extra = extra
        self.lineno = lineno
        self.static = 0
146
        if conditionals is None or len(conditionals) == 0:
147 148 149
            self.conditionals = None
        else:
            self.conditionals = conditionals[:]
150
        if self.name == debugsym and not quiet:
151 152
            print("=> define %s : %s" % (debugsym, (module, type, info,
                                         extra, conditionals)))
153 154 155

    def __repr__(self):
        r = "%s %s:" % (self.type, self.name)
156 157
        if self.static:
            r = r + " static"
158
        if self.module is not None:
159
            r = r + " from %s" % self.module
160
        if self.info is not None:
161
            r = r + " " + repr(self.info)
162
        if self.extra is not None:
163
            r = r + " " + repr(self.extra)
164
        if self.conditionals is not None:
165
            r = r + " " + repr(self.conditionals)
166
        return r
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183


    def set_header(self, header):
        self.header = header
    def set_module(self, module):
        self.module = module
    def set_type(self, type):
        self.type = type
    def set_info(self, info):
        self.info = info
    def set_extra(self, extra):
        self.extra = extra
    def set_lineno(self, lineno):
        self.lineno = lineno
    def set_static(self, static):
        self.static = static
    def set_conditionals(self, conditionals):
184
        if conditionals is None or len(conditionals) == 0:
185 186 187
            self.conditionals = None
        else:
            self.conditionals = conditionals[:]
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207

    def get_name(self):
        return self.name
    def get_header(self):
        return self.module
    def get_module(self):
        return self.module
    def get_type(self):
        return self.type
    def get_info(self):
        return self.info
    def get_lineno(self):
        return self.lineno
    def get_extra(self):
        return self.extra
    def get_static(self):
        return self.static
    def get_conditionals(self):
        return self.conditionals

208
    def update(self, header, module, type=None, info=None, extra=None,
209
               conditionals=None):
210
        if self.name == debugsym and not quiet:
211 212
            print("=> update %s : %s" % (debugsym, (module, type, info,
                                         extra, conditionals)))
213
        if header is not None and self.header is None:
214
            self.set_header(module)
215
        if module is not None and (self.module is None or self.header == self.module):
216
            self.set_module(module)
217
        if type is not None and self.type is None:
218
            self.set_type(type)
219
        if info is not None:
220
            self.set_info(info)
221
        if extra is not None:
222
            self.set_extra(extra)
223
        if conditionals is not None:
224
            self.set_conditionals(conditionals)
225 226

class index:
227
    def __init__(self, name="noname"):
228 229 230
        self.name = name
        self.identifiers = {}
        self.functions = {}
231 232 233
        self.variables = {}
        self.includes = {}
        self.structs = {}
234
        self.unions = {}
235 236 237 238 239
        self.enums = {}
        self.typedefs = {}
        self.macros = {}
        self.references = {}
        self.info = {}
240

241 242 243
    def warning(self, msg):
        global warnings
        warnings = warnings + 1
244
        print(msg)
245

246
    def add_ref(self, name, header, module, static, type, lineno, info=None, extra=None, conditionals=None):
247
        if name[0:2] == '__':
248
            return None
249 250
        d = None
        try:
251 252
            d = self.identifiers[name]
            d.update(header, module, type, lineno, info, extra, conditionals)
253
        except:
254 255 256
            d = identifier(name, header, module, type, lineno, info, extra,
                           conditionals)
            self.identifiers[name] = d
257

258
        if d is not None and static == 1:
259
            d.set_static(1)
260

261
        if d is not None and name is not None and type is not None:
262
            self.references[name] = d
263

264
        if name == debugsym and not quiet:
265
            print("New ref: %s" % (d))
266

267
        return d
268

269 270
    def add(self, name, header, module, static, type, lineno, info=None,
            extra=None, conditionals=None):
271
        if name[0:2] == '__':
272
            return None
273 274
        d = None
        try:
275 276
            d = self.identifiers[name]
            d.update(header, module, type, lineno, info, extra, conditionals)
277
        except:
278 279 280
            d = identifier(name, header, module, type, lineno, info, extra,
                           conditionals)
            self.identifiers[name] = d
281

282
        if d is not None and static == 1:
283 284
            d.set_static(1)

285
        if d is not None and name is not None and type is not None:
286 287 288 289 290 291 292 293 294 295 296 297 298
            type_map = {
                "function": self.functions,
                "functype": self.functions,
                "variable": self.variables,
                "include": self.includes,
                "struct": self.structs,
                "union": self.unions,
                "enum": self.enums,
                "typedef": self.typedefs,
                "macro": self.macros
            }
            if type in type_map:
                type_map[type][name] = d
299
            else:
300
                self.warning("Unable to register type ", type)
301

302
        if name == debugsym and not quiet:
303
            print("New symbol: %s" % (d))
304 305

        return d
306 307 308

    def merge(self, idx):
        for id in idx.functions.keys():
309 310 311 312 313 314 315 316
            #
            # macro might be used to override functions or variables
            # definitions
            #
            if id in self.macros:
                del self.macros[id]
            if id in self.functions:
                self.warning("function %s from %s redeclared in %s" % (
317
                    id, self.functions[id].header, idx.functions[id].header))
318 319 320
            else:
                self.functions[id] = idx.functions[id]
                self.identifiers[id] = idx.functions[id]
321
        for id in idx.variables.keys():
322 323 324 325 326 327 328 329
            #
            # macro might be used to override functions or variables
            # definitions
            #
            if id in self.macros:
                del self.macros[id]
            if id in self.variables:
                self.warning("variable %s from %s redeclared in %s" % (
330
                    id, self.variables[id].header, idx.variables[id].header))
331 332 333
            else:
                self.variables[id] = idx.variables[id]
                self.identifiers[id] = idx.variables[id]
334
        for id in idx.structs.keys():
335 336
            if id in self.structs:
                self.warning("struct %s from %s redeclared in %s" % (
337
                    id, self.structs[id].header, idx.structs[id].header))
338 339 340
            else:
                self.structs[id] = idx.structs[id]
                self.identifiers[id] = idx.structs[id]
341
        for id in idx.unions.keys():
342 343
            if id in self.unions:
                print("union %s from %s redeclared in %s" % (
344
                    id, self.unions[id].header, idx.unions[id].header))
345 346 347
            else:
                self.unions[id] = idx.unions[id]
                self.identifiers[id] = idx.unions[id]
348
        for id in idx.typedefs.keys():
349 350
            if id in self.typedefs:
                self.warning("typedef %s from %s redeclared in %s" % (
351
                    id, self.typedefs[id].header, idx.typedefs[id].header))
352 353 354
            else:
                self.typedefs[id] = idx.typedefs[id]
                self.identifiers[id] = idx.typedefs[id]
355
        for id in idx.macros.keys():
356 357 358 359 360 361 362 363 364 365 366 367
            #
            # macro might be used to override functions or variables
            # definitions
            #
            if id in self.variables:
                continue
            if id in self.functions:
                continue
            if id in self.enums:
                continue
            if id in self.macros:
                self.warning("macro %s from %s redeclared in %s" % (
368
                    id, self.macros[id].header, idx.macros[id].header))
369 370 371
            else:
                self.macros[id] = idx.macros[id]
                self.identifiers[id] = idx.macros[id]
372
        for id in idx.enums.keys():
373 374
            if id in self.enums:
                self.warning("enum %s from %s redeclared in %s" % (
375
                    id, self.enums[id].header, idx.enums[id].header))
376 377 378
            else:
                self.enums[id] = idx.enums[id]
                self.identifiers[id] = idx.enums[id]
379 380 381

    def merge_public(self, idx):
        for id in idx.functions.keys():
382 383 384 385 386 387 388 389 390 391 392 393 394 395
            if id in self.functions:
                up = idx.functions[id]
                # check that function condition agrees with header
                if up.conditionals != self.functions[id].conditionals:
                    self.warning("Header condition differs from Function"
                                 " for %s:" % id)
                    self.warning("  H: %s" % self.functions[id].conditionals)
                    self.warning("  C: %s" % up.conditionals)
                self.functions[id].update(None, up.module, up.type, up.info,
                                          up.extra)
        #     else:
        #         print("Function %s from %s is not declared in headers" % (
        #               id, idx.functions[id].module))
        # TODO: do the same for variables.
396 397 398

    def analyze_dict(self, type, dict):
        count = 0
399
        public = 0
400
        for name in dict.keys():
401 402 403 404
            id = dict[name]
            count = count + 1
            if id.static == 0:
                public = public + 1
405
        if count != public:
406
            print("  %d %s , %d public" % (count, type, public))
407
        elif count != 0:
408
            print("  %d public %s" % (count, type))
409 410 411


    def analyze(self):
412 413 414 415 416 417 418
        if not quiet:
            self.analyze_dict("functions", self.functions)
            self.analyze_dict("variables", self.variables)
            self.analyze_dict("structs", self.structs)
            self.analyze_dict("unions", self.unions)
            self.analyze_dict("typedefs", self.typedefs)
            self.analyze_dict("macros", self.macros)
419

420 421 422 423 424
class CLexer:
    """A lexer for the C language, tokenize the input by reading and
       analyzing it line by line"""
    def __init__(self, input):
        self.input = input
425 426 427
        self.tokens = []
        self.line = ""
        self.lineno = 0
428 429 430

    def getline(self):
        line = ''
431 432 433 434
        while line == '':
            line = self.input.readline()
            if not line:
                return None
R
Radostin Stoyanov 已提交
435 436
            self.lineno += 1
            line = line.strip()
437 438 439 440
            if line == '':
                continue
            while line[-1] == '\\':
                line = line[:-1]
R
Radostin Stoyanov 已提交
441 442
                n = self.input.readline().strip()
                self.lineno += 1
443 444
                if not n:
                    break
R
Radostin Stoyanov 已提交
445
                line += n
446
        return line
447

448 449 450 451
    def getlineno(self):
        return self.lineno

    def push(self, token):
452
        self.tokens.insert(0, token)
453 454

    def debug(self):
455 456
        print("Last token: ", self.last)
        print("Token queue: ", self.tokens)
457
        print("Line %d end: " % self.lineno, self.line)
458 459 460

    def token(self):
        while self.tokens == []:
461 462 463 464 465
            if self.line == "":
                line = self.getline()
            else:
                line = self.line
                self.line = ""
466
            if line is None:
467 468 469
                return None

            if line[0] == '#':
470
                self.tokens = [('preproc', word) for word in line.split()]
471 472 473 474 475 476

                # We might have whitespace between the '#' and preproc
                # macro name, so instead of having a single token element
                # of '#define' we might end up with '#' and 'define'. This
                # merges them back together
                if self.tokens[0][1] == "#":
477 478
                    self.tokens[0] = ('preproc', "#" + self.tokens[1][1])
                    del self.tokens[1]
479
                break
480 481
            l = len(line)
            if line[0] == '"' or line[0] == "'":
482 483 484 485 486 487 488 489 490 491
                quote = line[0]
                i = 1
                while quote not in line[i:]:
                    i = len(line)
                    nextline = self.getline()
                    if nextline is None:
                        return None
                    line += nextline

                tok, self.line = line[1:].split(quote, 1)
492 493 494
                self.last = ('string', tok)
                return self.last

495
            if line.startswith("/*"):
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
                line = line[2:]
                found = 0
                tok = ""
                while found == 0:
                    i = 0
                    l = len(line)
                    while i < l:
                        if line[i] == '*' and i+1 < l and line[i+1] == '/':
                            self.line = line[i+2:]
                            line = line[:i-1]
                            l = i
                            found = 1
                            break
                        i = i + 1
                    if tok != "":
                        tok = tok + "\n"
                    tok = tok + line
                    if found == 0:
                        line = self.getline()
515
                        if line is None:
516 517 518
                            return None
                self.last = ('comment', tok)
                return self.last
519
            if line.startswith("//"):
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
                line = line[2:]
                self.last = ('comment', line)
                return self.last
            i = 0
            while i < l:
                if line[i] == '/' and i+1 < l and line[i+1] == '/':
                    self.line = line[i:]
                    line = line[:i]
                    break
                if line[i] == '/' and i+1 < l and line[i+1] == '*':
                    self.line = line[i:]
                    line = line[:i]
                    break
                if line[i] == '"' or line[i] == "'":
                    self.line = line[i:]
                    line = line[:i]
                    break
                i = i + 1
            l = len(line)
            i = 0
            while i < l:
                if line[i] == ' ' or line[i] == '\t':
                    i = i + 1
                    continue
544
                if line[i].isalnum():
545 546
                    s = i
                    while i < l:
547
                        if line[i] not in " \t(){}:;,+-*/%&!|[]=><":
548 549 550 551 552
                            i = i + 1
                        else:
                            break
                    self.tokens.append(('name', line[s:i]))
                    continue
553
                if line[i] in "(){}:;,[]":
554
#                 if line[i] == '(' or line[i] == ')' or line[i] == '{' or \
555 556 557 558 559
#                   line[i] == '}' or line[i] == ':' or line[i] == ';' or \
#                   line[i] == ',' or line[i] == '[' or line[i] == ']':
                    self.tokens.append(('sep', line[i]))
                    i = i + 1
                    continue
560
                if line[i] in "+-*><=/%&!|.":
561
#                 if line[i] == '+' or line[i] == '-' or line[i] == '*' or \
562 563 564
#                   line[i] == '>' or line[i] == '<' or line[i] == '=' or \
#                   line[i] == '/' or line[i] == '%' or line[i] == '&' or \
#                   line[i] == '!' or line[i] == '|' or line[i] == '.':
565
                    if line[i] == '.' and i + 2 < l and \
566 567 568 569 570 571
                       line[i+1] == '.' and line[i+2] == '.':
                        self.tokens.append(('name', '...'))
                        i = i + 3
                        continue

                    j = i + 1
572
                    if j < l and line[j] in "+-*><=/%&!|":
573 574 575 576 577 578 579 580 581 582 583 584
#                       line[j] == '+' or line[j] == '-' or line[j] == '*' or \
#                       line[j] == '>' or line[j] == '<' or line[j] == '=' or \
#                       line[j] == '/' or line[j] == '%' or line[j] == '&' or \
#                       line[j] == '!' or line[j] == '|'):
                        self.tokens.append(('op', line[i:j+1]))
                        i = j + 1
                    else:
                        self.tokens.append(('op', line[i]))
                        i = i + 1
                    continue
                s = i
                while i < l:
585
                    if line[i] not in " \t(){}:;,+-*/%&!|[]=><":
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
#                        line[i] != ' ' and line[i] != '\t' and
#                        line[i] != '(' and line[i] != ')' and
#                        line[i] != '{'  and line[i] != '}' and
#                        line[i] != ':' and line[i] != ';' and
#                        line[i] != ',' and line[i] != '+' and
#                        line[i] != '-' and line[i] != '*' and
#                        line[i] != '/' and line[i] != '%' and
#                        line[i] != '&' and line[i] != '!' and
#                        line[i] != '|' and line[i] != '[' and
#                        line[i] != ']' and line[i] != '=' and
#                        line[i] != '*' and line[i] != '>' and
#                        line[i] != '<'):
                        i = i + 1
                    else:
                        break
                self.tokens.append(('name', line[s:i]))

        tok = self.tokens[0]
        self.tokens = self.tokens[1:]
        self.last = tok
        return tok
607

608 609
class CParser:
    """The C module parser"""
610
    def __init__(self, filename, idx=None):
611
        self.filename = filename
612 613 614 615
        if len(filename) > 2 and filename[-2:] == '.h':
            self.is_header = 1
        else:
            self.is_header = 0
616
        self.input = open(filename)
617
        self.lexer = CLexer(self.input)
618
        if idx is None:
619 620 621 622 623 624 625 626 627 628
            self.index = index()
        else:
            self.index = idx
        self.top_comment = ""
        self.last_comment = ""
        self.comment = None
        self.collect_ref = 0
        self.no_error = 0
        self.conditionals = []
        self.defines = []
629 630 631 632 633 634 635 636 637 638 639 640 641

    def collect_references(self):
        self.collect_ref = 1

    def stop_error(self):
        self.no_error = 1

    def start_error(self):
        self.no_error = 0

    def lineno(self):
        return self.lexer.getlineno()

642
    def index_add(self, name, module, static, type, info=None, extra=None):
643 644 645 646 647 648
        if self.is_header == 1:
            self.index.add(name, module, module, static, type, self.lineno(),
                           info, extra, self.conditionals)
        else:
            self.index.add(name, None, module, static, type, self.lineno(),
                           info, extra, self.conditionals)
649 650

    def index_add_ref(self, name, module, static, type, info=None,
651
                      extra=None):
652 653 654 655 656 657
        if self.is_header == 1:
            self.index.add_ref(name, module, module, static, type,
                               self.lineno(), info, extra, self.conditionals)
        else:
            self.index.add_ref(name, None, module, static, type, self.lineno(),
                               info, extra, self.conditionals)
658 659

    def warning(self, msg):
660 661
        global warnings
        warnings = warnings + 1
662
        if self.no_error:
663
            return
664
        print(msg)
665 666 667

    def error(self, msg, token=-1):
        if self.no_error:
668
            return
669

670
        print("Parse Error: " + msg)
671
        if token != -1:
672
            print("Got token ", token)
673 674
        self.lexer.debug()
        sys.exit(1)
675 676

    def debug(self, msg, token=-1):
677
        print("Debug: " + msg)
678
        if token != -1:
679
            print("Got token ", token)
680
        self.lexer.debug()
681 682

    def parseTopComment(self, comment):
683
        res = {}
684
        lines = comment.split("\n")
685 686
        item = None
        for line in lines:
C
Claudio Bley 已提交
687
            line = line.lstrip().lstrip('*').lstrip()
688 689 690 691 692 693 694

            m = re.match('([_.a-zA-Z0-9]+):(.*)', line)
            if m:
                item = m.group(1)
                line = m.group(2).lstrip()

            if item:
A
Andrea Bolognani 已提交
695
                if item in res:
696 697 698 699
                    res[item] = res[item] + " " + line
                else:
                    res[item] = line
        self.index.info = res
700

701
    def strip_lead_star(self, line):
702 703
        if line.lstrip().startswith('*'):
            line = line.replace('*', '', 1)
704 705 706
        return line

    def cleanupComment(self):
707
        if not isinstance(self.comment, str):
708 709 710 711 712 713 714 715
            return
        # remove the leading * on multi-line comments
        lines = self.comment.splitlines(True)
        com = ""
        for line in lines:
            com = com + self.strip_lead_star(line)
        self.comment = com.strip()

716
    def parseComment(self, token):
717
        com = token[1]
718
        if self.top_comment == "":
719
            self.top_comment = com
720
        if self.comment is None or com[0] == '*':
721
            self.comment = com
722
        else:
723
            self.comment = self.comment + com
724
        token = self.lexer.token()
725

726
        if self.comment.find("DOC_DISABLE") != -1:
727
            self.stop_error()
728

729
        if self.comment.find("DOC_ENABLE") != -1:
730
            self.start_error()
731

732
        return token
733 734 735 736

    #
    # Parse a comment block associate to a typedef
    #
737
    def parseTypeComment(self, name, quiet=False):
738
        if name[0:2] == '__':
739
            quiet = True
740

741
        if self.comment is None:
742
            if not quiet:
743
                self.warning("Missing comment for type %s" % name)
744 745
            return None
        if not self.comment.startswith('*'):
746
            if not quiet:
747
                self.warning("Missing * in type comment for %s" % name)
748 749
            return None

750
        lines = self.comment.split('\n')
751 752 753
        # Remove lines that contain only single asterisk
        lines[:] = [line for line in lines if line.strip() != '*']

754
        if lines[0] != "* %s:" % name:
755
            if not quiet:
756
                self.warning("Misformatted type comment for %s" % name)
757
                self.warning("  Expecting '* %s:' got '%s'" % (name, lines[0]))
758
            return None
759
        del lines[0]
760 761 762 763 764 765

        # Concatenate all remaining lines by striping leading asterisks
        desc = " ".join([line.lstrip("*").strip() for line in lines]).strip()

        if not (quiet or desc):
            self.warning("Type comment for %s lack description of the macro"
766
                         % name)
767 768

        return desc
769 770 771
    #
    # Parse a comment block associate to a macro
    #
772
    def parseMacroComment(self, name, quiet=0):
773 774
        global ignored_macros

775
        if name[0:2] == '__':
776
            quiet = 1
A
Andrea Bolognani 已提交
777
        if name in ignored_macros:
778
            quiet = 1
779 780

        args = []
781
        desc = ""
782

783
        if self.comment is None:
784
            if not quiet:
785 786
                self.warning("Missing comment for macro %s" % name)
            return args, desc
787
        if self.comment[0] != '*':
788
            if not quiet:
789 790
                self.warning("Missing * in macro comment for %s" % name)
            return args, desc
791
        lines = self.comment.split('\n')
792 793
        if lines[0] == '*':
            del lines[0]
794
        if lines[0] != "* %s:" % name:
795
            if not quiet:
796
                self.warning("Misformatted macro comment for %s" % name)
797
                self.warning("  Expecting '* %s:' got '%s'" % (name, lines[0]))
798
            return args, desc
799 800 801 802 803 804
        del lines[0]
        while lines[0] == '*':
            del lines[0]
        while len(lines) > 0 and lines[0][0:3] == '* @':
            l = lines[0][3:]
            try:
805
                arg, desc = l.split(':', 1)
806 807
                desc = desc.strip()
                arg = arg.strip()
808
            except:
809
                if not quiet:
810 811
                    self.warning("Misformatted macro comment for %s" % name)
                    self.warning("  problem with '%s'" % lines[0])
812 813 814
                del lines[0]
                continue
            del lines[0]
815
            l = lines[0].strip()
816 817 818
            while len(l) > 2 and l[0:3] != '* @':
                while l[0] == '*':
                    l = l[1:]
819
                desc = desc + ' ' + l.strip()
820 821 822 823
                del lines[0]
                if len(lines) == 0:
                    break
                l = lines[0]
824
            args.append((arg, desc))
825 826 827 828 829 830 831
        while len(lines) > 0 and lines[0] == '*':
            del lines[0]
        desc = ""
        while len(lines) > 0:
            l = lines[0]
            while len(l) > 0 and l[0] == '*':
                l = l[1:]
832
            l = l.strip()
833 834
            desc = desc + " " + l
            del lines[0]
835

836
        desc = desc.strip()
837

838 839
        if quiet == 0:
            if desc == "":
840
                self.warning("Macro comment for %s lack description of the macro" % name)
841

842
        return args, desc
843

844 845 846 847 848
    #
    # Parse a comment block and merge the information found in the
    # parameters descriptions, finally returns a block as complete
    # as possible
    #
849
    def mergeFunctionComment(self, name, description, quiet=0):
D
Daniel Veillard 已提交
850 851
        global ignored_functions

852
        if name == 'main':
853
            quiet = 1
854
        if name[0:2] == '__':
855
            quiet = 1
A
Andrea Bolognani 已提交
856
        if name in ignored_functions:
D
Daniel Veillard 已提交
857
            quiet = 1
858

859
        ret, args = description
860 861
        desc = ""
        retdesc = ""
862

863
        if self.comment is None:
864
            if not quiet:
865 866
                self.warning("Missing comment for function %s" % name)
            return (ret[0], retdesc), args, desc
867
        if self.comment[0] != '*':
868
            if not quiet:
869 870
                self.warning("Missing * in function comment for %s" % name)
            return (ret[0], retdesc), args, desc
871
        lines = self.comment.split('\n')
872 873
        if lines[0] == '*':
            del lines[0]
874
        if lines[0] != "* %s:" % name:
875
            if not quiet:
876
                self.warning("Misformatted function comment for %s" % name)
877
                self.warning("  Expecting '* %s:' got '%s'" % (name, lines[0]))
878
            return (ret[0], retdesc), args, desc
879 880 881 882 883 884 885
        del lines[0]
        while lines[0] == '*':
            del lines[0]
        nbargs = len(args)
        while len(lines) > 0 and lines[0][0:3] == '* @':
            l = lines[0][3:]
            try:
886
                arg, desc = l.split(':', 1)
887 888
                desc = desc.strip()
                arg = arg.strip()
889
            except:
890
                if not quiet:
891 892
                    self.warning("Misformatted function comment for %s" % name)
                    self.warning("  problem with '%s'" % lines[0])
893 894 895
                del lines[0]
                continue
            del lines[0]
896
            l = lines[0].strip()
897 898 899
            while len(l) > 2 and l[0:3] != '* @':
                while l[0] == '*':
                    l = l[1:]
900
                desc = desc + ' ' + l.strip()
901 902 903 904 905 906 907 908
                del lines[0]
                if len(lines) == 0:
                    break
                l = lines[0]
            i = 0
            while i < nbargs:
                if args[i][1] == arg:
                    args[i] = (args[i][0], arg, desc)
909
                    break
910 911 912 913 914 915 916 917 918 919 920 921
                i = i + 1
            if i >= nbargs:
                if not quiet:
                    self.warning("Unable to find arg %s from function comment for %s" % (
                       arg, name))
        while len(lines) > 0 and lines[0] == '*':
            del lines[0]
        desc = None
        while len(lines) > 0:
            l = lines[0]
            i = 0
            # Remove all leading '*', followed by at most one ' ' character
922
            # since we need to preserve correct indentation of code examples
923 924 925 926 927 928
            while i < len(l) and l[i] == '*':
                i = i + 1
            if i > 0:
                if i < len(l) and l[i] == ' ':
                    i = i + 1
                l = l[i:]
929
            if len(l) >= 6 and l[0:7] == "Returns":
930
                try:
931
                    l = l.split(' ', 1)[1]
932 933
                except:
                    l = ""
934
                retdesc = l.strip()
935 936 937 938 939
                del lines[0]
                while len(lines) > 0:
                    l = lines[0]
                    while len(l) > 0 and l[0] == '*':
                        l = l[1:]
940
                    l = l.strip()
941 942 943 944 945 946 947 948 949 950 951
                    retdesc = retdesc + " " + l
                    del lines[0]
            else:
                if desc is not None:
                    desc = desc + "\n" + l
                else:
                    desc = l
                del lines[0]

        if desc is None:
            desc = ""
952 953
        retdesc = retdesc.strip()
        desc = desc.strip()
954 955

        if quiet == 0:
956 957 958
            #
            # report missing comments
            #
959 960
            i = 0
            while i < nbargs:
961
                if args[i][2] is None and args[i][0] != "void" and args[i][1] is not None:
962 963 964
                    self.warning("Function comment for %s lacks description of arg %s" % (name, args[i][1]))
                i = i + 1
            if retdesc == "" and ret[0] != "void":
965
                self.warning("Function comment for %s lacks description of return value" % name)
966
            if desc == "":
967
                self.warning("Function comment for %s lacks description of the function" % name)
968 969


970
        return (ret[0], retdesc), args, desc
971 972

    def parsePreproc(self, token):
973
        if debug:
974
            print("=> preproc ", token, self.lexer.tokens)
975
        name = token[1]
976 977
        if name == "#include":
            token = self.lexer.token()
978
            if token is None:
979 980 981 982 983 984 985 986
                return None
            if token[0] == 'preproc':
                self.index_add(token[1], self.filename, not self.is_header,
                                "include")
                return self.lexer.token()
            return token
        if name == "#define":
            token = self.lexer.token()
987
            if token is None:
988 989
                return None
            if token[0] == 'preproc':
990
                # TODO macros with arguments
991 992 993
                name = token[1]
                lst = []
                token = self.lexer.token()
994
                while token is not None and token[0] == 'preproc' and \
995 996 997
                      token[1][0] != '#':
                    lst.append(token[1])
                    token = self.lexer.token()
998
                try:
999
                    name = name.split('(') [0]
1000 1001
                except:
                    pass
1002 1003 1004 1005

                # skip hidden macros
                if name in hidden_macros:
                    return token
1006 1007
                if name[-2:] == "_H" or name[-8:] == "_H_ALLOW":
                    return token
1008

1009 1010 1011 1012
                strValue = None
                if len(lst) == 1 and lst[0][0] == '"' and lst[0][-1] == '"':
                    strValue = lst[0][1:-1]
                (args, desc) = self.parseMacroComment(name, not self.is_header)
1013
                self.index_add(name, self.filename, not self.is_header,
1014
                               "macro", (args, desc, strValue))
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
                return token

        #
        # Processing of conditionals modified by Bill 1/1/05
        #
        # We process conditionals (i.e. tokens from #ifdef, #ifndef,
        # #if, #else and #endif) for headers and mainline code,
        # store the ones from the header in libxml2-api.xml, and later
        # (in the routine merge_public) verify that the two (header and
        # mainline code) agree.
        #
        # There is a small problem with processing the headers. Some of
        # the variables are not concerned with enabling / disabling of
        # library functions (e.g. '__XML_PARSER_H__'), and we don't want
        # them to be included in libxml2-api.xml, or involved in
        # the check between the header and the mainline code.  To
        # accomplish this, we ignore any conditional which doesn't include
        # the string 'ENABLED'
        #
        if name == "#ifdef":
            apstr = self.lexer.tokens[0][1]
            try:
                self.defines.append(apstr)
1038
                if apstr.find('ENABLED') != -1:
1039 1040 1041 1042 1043 1044 1045
                    self.conditionals.append("defined(%s)" % apstr)
            except:
                pass
        elif name == "#ifndef":
            apstr = self.lexer.tokens[0][1]
            try:
                self.defines.append(apstr)
1046
                if apstr.find('ENABLED') != -1:
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057
                    self.conditionals.append("!defined(%s)" % apstr)
            except:
                pass
        elif name == "#if":
            apstr = ""
            for tok in self.lexer.tokens:
                if apstr != "":
                    apstr = apstr + " "
                apstr = apstr + tok[1]
            try:
                self.defines.append(apstr)
1058
                if apstr.find('ENABLED') != -1:
1059 1060 1061 1062 1063
                    self.conditionals.append(apstr)
            except:
                pass
        elif name == "#else":
            if self.conditionals != [] and \
1064
               self.defines[-1].find('ENABLED') != -1:
1065 1066 1067
                self.conditionals[-1] = "!(%s)" % self.conditionals[-1]
        elif name == "#endif":
            if self.conditionals != [] and \
1068
               self.defines[-1].find('ENABLED') != -1:
1069 1070 1071
                self.conditionals = self.conditionals[:-1]
            self.defines = self.defines[:-1]
        token = self.lexer.token()
1072
        while token is not None and token[0] == 'preproc' and \
1073 1074 1075
            token[1][0] != '#':
            token = self.lexer.token()
        return token
1076

1077 1078 1079 1080 1081
    #
    # token acquisition on top of the lexer, it handle internally
    # preprocessor and comments since they are logically not part of
    # the program structure.
    #
1082 1083 1084
    def push(self, tok):
        self.lexer.push(tok)

1085 1086 1087 1088
    def token(self):
        global ignored_words

        token = self.lexer.token()
1089
        while token is not None:
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100
            if token[0] == 'comment':
                token = self.parseComment(token)
                continue
            elif token[0] == 'preproc':
                token = self.parsePreproc(token)
                continue
            elif token[0] == "name" and token[1] == "__const":
                token = ("name", "const")
                return token
            elif token[0] == "name" and token[1] == "__attribute":
                token = self.lexer.token()
1101
                while token is not None and token[1] != ";":
1102 1103
                    token = self.lexer.token()
                return token
A
Andrea Bolognani 已提交
1104
            elif token[0] == "name" and token[1] in ignored_words:
1105 1106 1107 1108 1109 1110 1111 1112 1113
                (n, info) = ignored_words[token[1]]
                i = 0
                while i < n:
                    token = self.lexer.token()
                    i = i + 1
                token = self.lexer.token()
                continue
            else:
                if debug:
1114
                    print("=> ", token)
1115 1116
                return token
        return None
1117

1118 1119 1120
    #
    # Parse a typedef, it records the type and its name.
    #
1121
    def parseTypedef(self, token):
1122
        if token is None:
1123 1124
            return None
        token = self.parseType(token)
1125
        if token is None:
1126 1127 1128 1129
            self.error("parsing typedef")
            return None
        base_type = self.type
        type = base_type
1130
        # self.debug("end typedef type", token)
1131
        while token is not None:
1132 1133 1134
            if token[0] == "name":
                name = token[1]
                signature = self.signature
1135
                if signature is not None:
1136
                    type = type.split('(')[0]
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
                    d = self.mergeFunctionComment(name,
                            ((type, None), signature), 1)
                    self.index_add(name, self.filename, not self.is_header,
                                    "functype", d)
                else:
                    if base_type == "struct":
                        self.index_add(name, self.filename, not self.is_header,
                                        "struct", type)
                        base_type = "struct " + name
                    else:
                        # TODO report missing or misformatted comments
                        info = self.parseTypeComment(name, 1)
                        self.index_add(name, self.filename, not self.is_header,
                                    "typedef", type, info)
                token = self.token()
            else:
                self.error("parsing typedef: expecting a name")
                return token
1155
            # self.debug("end typedef", token)
1156
            if token is not None and token[0] == 'sep' and token[1] == ',':
1157 1158
                type = base_type
                token = self.token()
1159
                while token is not None and token[0] == "op":
1160 1161
                    type = type + token[1]
                    token = self.token()
1162
            elif token is not None and token[0] == 'sep' and token[1] == ';':
1163
                break
1164
            elif token is not None and token[0] == 'name':
1165
                type = base_type
1166
                continue
1167 1168 1169 1170 1171
            else:
                self.error("parsing typedef: expecting ';'", token)
                return token
        token = self.token()
        return token
1172

1173 1174 1175 1176
    #
    # Parse a C code block, used for functions it parse till
    # the balancing } included
    #
1177
    def parseBlock(self, token):
1178
        while token is not None:
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210
            if token[0] == "sep" and token[1] == "{":
                token = self.token()
                token = self.parseBlock(token)
            elif token[0] == "sep" and token[1] == "}":
                self.comment = None
                token = self.token()
                return token
            else:
                if self.collect_ref == 1:
                    oldtok = token
                    token = self.token()
                    if oldtok[0] == "name" and oldtok[1][0:3] == "vir":
                        if token[0] == "sep" and token[1] == "(":
                            self.index_add_ref(oldtok[1], self.filename,
                                                0, "function")
                            token = self.token()
                        elif token[0] == "name":
                            token = self.token()
                            if token[0] == "sep" and (token[1] == ";" or
                               token[1] == "," or token[1] == "="):
                                self.index_add_ref(oldtok[1], self.filename,
                                                    0, "type")
                    elif oldtok[0] == "name" and oldtok[1][0:4] == "XEN_":
                        self.index_add_ref(oldtok[1], self.filename,
                                            0, "typedef")
                    elif oldtok[0] == "name" and oldtok[1][0:7] == "LIBXEN_":
                        self.index_add_ref(oldtok[1], self.filename,
                                            0, "typedef")

                else:
                    token = self.token()
        return token
1211

1212 1213 1214
    #
    # Parse a C struct definition till the balancing }
    #
1215 1216
    def parseStruct(self, token):
        fields = []
1217
        # self.debug("start parseStruct", token)
1218
        while token is not None:
1219 1220 1221 1222 1223
            if token[0] == "sep" and token[1] == "{":
                token = self.token()
                token = self.parseTypeBlock(token)
            elif token[0] == "sep" and token[1] == "}":
                self.struct_fields = fields
1224 1225
                # self.debug("end parseStruct", token)
                # print(fields)
1226 1227 1228 1229
                token = self.token()
                return token
            else:
                base_type = self.type
1230
                # self.debug("before parseType", token)
1231
                token = self.parseType(token)
1232
                # self.debug("after parseType", token)
1233
                if token is not None and token[0] == "name":
1234 1235 1236 1237 1238
                    fname = token[1]
                    token = self.token()
                    if token[0] == "sep" and token[1] == ";":
                        self.comment = None
                        token = self.token()
1239 1240 1241 1242 1243 1244 1245
                        self.cleanupComment()
                        if self.type == "union":
                            fields.append((self.type, fname, self.comment,
                                           self.union_fields))
                            self.union_fields = []
                        else:
                            fields.append((self.type, fname, self.comment))
1246 1247 1248
                        self.comment = None
                    else:
                        self.error("parseStruct: expecting ;", token)
1249
                elif token is not None and token[0] == "sep" and token[1] == "{":
1250 1251
                    token = self.token()
                    token = self.parseTypeBlock(token)
1252
                    if token is not None and token[0] == "name":
1253
                        token = self.token()
1254
                    if token is not None and token[0] == "sep" and token[1] == ";":
1255 1256 1257 1258 1259 1260
                        token = self.token()
                    else:
                        self.error("parseStruct: expecting ;", token)
                else:
                    self.error("parseStruct: name", token)
                    token = self.token()
1261
                self.type = base_type
1262
        self.struct_fields = fields
1263 1264
        # self.debug("end parseStruct", token)
        # print(fields)
1265
        return token
1266

1267 1268 1269
    #
    # Parse a C union definition till the balancing }
    #
1270 1271 1272
    def parseUnion(self, token):
        fields = []
        # self.debug("start parseUnion", token)
1273
        while token is not None:
1274 1275 1276 1277 1278 1279
            if token[0] == "sep" and token[1] == "{":
                token = self.token()
                token = self.parseTypeBlock(token)
            elif token[0] == "sep" and token[1] == "}":
                self.union_fields = fields
                # self.debug("end parseUnion", token)
1280
                # print(fields)
1281 1282 1283 1284 1285 1286 1287
                token = self.token()
                return token
            else:
                base_type = self.type
                # self.debug("before parseType", token)
                token = self.parseType(token)
                # self.debug("after parseType", token)
1288
                if token is not None and token[0] == "name":
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298
                    fname = token[1]
                    token = self.token()
                    if token[0] == "sep" and token[1] == ";":
                        self.comment = None
                        token = self.token()
                        self.cleanupComment()
                        fields.append((self.type, fname, self.comment))
                        self.comment = None
                    else:
                        self.error("parseUnion: expecting ;", token)
1299
                elif token is not None and token[0] == "sep" and token[1] == "{":
1300 1301
                    token = self.token()
                    token = self.parseTypeBlock(token)
1302
                    if token is not None and token[0] == "name":
1303
                        token = self.token()
1304
                    if token is not None and token[0] == "sep" and token[1] == ";":
1305 1306 1307 1308 1309 1310
                        token = self.token()
                    else:
                        self.error("parseUnion: expecting ;", token)
                else:
                    self.error("parseUnion: name", token)
                    token = self.token()
1311
                self.type = base_type
1312 1313
        self.union_fields = fields
        # self.debug("end parseUnion", token)
1314
        # print(fields)
1315 1316
        return token

1317 1318 1319
    #
    # Parse a C enum block, parse till the balancing }
    #
1320 1321
    def parseEnumBlock(self, token):
        self.enums = []
1322 1323
        name = None
        comment = ""
E
Eric Blake 已提交
1324
        value = "-1"
1325
        commentsBeforeVal = self.comment is not None
1326
        while token is not None:
1327 1328 1329 1330
            if token[0] == "sep" and token[1] == "{":
                token = self.token()
                token = self.parseTypeBlock(token)
            elif token[0] == "sep" and token[1] == "}":
1331
                if name is not None:
1332
                    self.cleanupComment()
1333
                    if self.comment is not None:
1334 1335 1336 1337 1338 1339
                        comment = self.comment
                        self.comment = None
                    self.enums.append((name, value, comment))
                token = self.token()
                return token
            elif token[0] == "name":
J
Jiri Denemark 已提交
1340 1341 1342
                self.cleanupComment()
                if name is not None:
                    if self.comment is not None:
1343
                        comment = self.comment.strip()
J
Jiri Denemark 已提交
1344 1345 1346 1347 1348 1349 1350 1351 1352
                        self.comment = None
                    self.enums.append((name, value, comment))
                name = token[1]
                comment = ""
                token = self.token()
                if token[0] == "op" and token[1][0] == "=":
                    value = ""
                    if len(token[1]) > 1:
                        value = token[1][1:]
1353
                    token = self.token()
J
Jiri Denemark 已提交
1354 1355
                    while token[0] != "sep" or (token[1] != ',' and
                          token[1] != '}'):
1356
                        # We might be dealing with '1U << 12' here
1357
                        value = value + re.sub("^(\d+)U$", "\\1", token[1])
1358
                        token = self.token()
J
Jiri Denemark 已提交
1359 1360 1361 1362
                else:
                    try:
                        value = "%d" % (int(value) + 1)
                    except:
1363
                        self.warning("Failed to compute value of enum %s" % name)
1364
                        value = ""
J
Jiri Denemark 已提交
1365
                if token[0] == "sep" and token[1] == ",":
1366 1367 1368 1369
                    if commentsBeforeVal:
                        self.cleanupComment()
                        self.enums.append((name, value, self.comment))
                        name = comment = self.comment = None
J
Jiri Denemark 已提交
1370
                    token = self.token()
1371 1372 1373
            else:
                token = self.token()
        return token
1374

1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463
    def parseVirEnumDecl(self, token):
        if token[0] != "name":
            self.error("parsing VIR_ENUM_DECL: expecting name", token)

        token = self.token()

        if token[0] != "sep":
            self.error("parsing VIR_ENUM_DECL: expecting ')'", token)

        if token[1] != ')':
            self.error("parsing VIR_ENUM_DECL: expecting ')'", token)

        token = self.token()
        if token[0] == "sep" and token[1] == ';':
            token = self.token()

        return token

    def parseVirEnumImpl(self, token):
        # First the type name
        if token[0] != "name":
            self.error("parsing VIR_ENUM_IMPL: expecting name", token)

        token = self.token()

        if token[0] != "sep":
            self.error("parsing VIR_ENUM_IMPL: expecting ','", token)

        if token[1] != ',':
            self.error("parsing VIR_ENUM_IMPL: expecting ','", token)
        token = self.token()

        # Now the sentinel name
        if token[0] != "name":
            self.error("parsing VIR_ENUM_IMPL: expecting name", token)

        token = self.token()

        if token[0] != "sep":
            self.error("parsing VIR_ENUM_IMPL: expecting ','", token)

        if token[1] != ',':
            self.error("parsing VIR_ENUM_IMPL: expecting ','", token)

        token = self.token()

        # Now a list of strings (optional comments)
        while token is not None:
            isGettext = False
            # First a string, optionally with N_(...)
            if token[0] == 'name':
                if token[1] != 'N_':
                    self.error("parsing VIR_ENUM_IMPL: expecting 'N_'", token)
                token = self.token()
                if token[0] != "sep" or token[1] != '(':
                    self.error("parsing VIR_ENUM_IMPL: expecting '('", token)
                token = self.token()
                isGettext = True

                if token[0] != "string":
                    self.error("parsing VIR_ENUM_IMPL: expecting a string", token)
                token = self.token()
            elif token[0] == "string":
                token = self.token()
            else:
                self.error("parsing VIR_ENUM_IMPL: expecting a string", token)

            # Then a separator
            if token[0] == "sep":
                if isGettext and token[1] == ')':
                    token = self.token()

                if token[1] == ',':
                    token = self.token()

                if token[1] == ')':
                    token = self.token()
                    break

            # Then an optional comment
            if token[0] == "comment":
                token = self.token()


        if token[0] == "sep" and token[1] == ';':
            token = self.token()

        return token

1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481
    def parseVirLogInit(self, token):
        if token[0] != "string":
            self.error("parsing VIR_LOG_INIT: expecting string", token)

        token = self.token()

        if token[0] != "sep":
            self.error("parsing VIR_LOG_INIT: expecting ')'", token)

        if token[1] != ')':
            self.error("parsing VIR_LOG_INIT: expecting ')'", token)

        token = self.token()
        if token[0] == "sep" and token[1] == ';':
            token = self.token()

        return token

1482 1483 1484 1485
    #
    # Parse a C definition block, used for structs or unions it parse till
    # the balancing }
    #
1486
    def parseTypeBlock(self, token):
1487
        while token is not None:
1488 1489 1490 1491 1492 1493 1494 1495 1496
            if token[0] == "sep" and token[1] == "{":
                token = self.token()
                token = self.parseTypeBlock(token)
            elif token[0] == "sep" and token[1] == "}":
                token = self.token()
                return token
            else:
                token = self.token()
        return token
1497

1498 1499 1500 1501 1502
    #
    # Parse a type: the fact that the type name can either occur after
    #    the definition or within the definition makes it a little harder
    #    if inside, the name token is pushed back before returning
    #
1503 1504
    def parseType(self, token):
        self.type = ""
1505
        self.struct_fields = []
1506
        self.union_fields = []
1507
        self.signature = None
1508
        if token is None:
1509 1510
            return token

1511 1512
        while (token[0] == "name" and
               token[1] in ["const", "unsigned", "signed"]):
1513 1514 1515 1516 1517
            if self.type == "":
                self.type = token[1]
            else:
                self.type = self.type + " " + token[1]
            token = self.token()
1518

1519
        if token[0] == "name" and token[1] == "long":
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533
            if self.type == "":
                self.type = token[1]
            else:
                self.type = self.type + " " + token[1]

            # some read ahead for long long
            oldtmp = token
            token = self.token()
            if token[0] == "name" and token[1] == "long":
                self.type = self.type + " " + token[1]
            else:
                self.push(token)
                token = oldtmp

1534 1535
            oldtmp = token
            token = self.token()
1536
            if token[0] == "name" and token[1] == "int":
1537 1538 1539 1540
                self.type = self.type + " " + token[1]
            else:
                self.push(token)
                token = oldtmp
1541 1542

        elif token[0] == "name" and token[1] == "short":
1543 1544 1545 1546
            if self.type == "":
                self.type = token[1]
            else:
                self.type = self.type + " " + token[1]
1547

1548
        elif token[0] == "name" and token[1] == "struct":
1549 1550 1551 1552 1553 1554 1555 1556 1557
            if self.type == "":
                self.type = token[1]
            else:
                self.type = self.type + " " + token[1]
            token = self.token()
            nametok = None
            if token[0] == "name":
                nametok = token
                token = self.token()
1558
            if token is not None and token[0] == "sep" and token[1] == "{":
1559 1560
                token = self.token()
                token = self.parseStruct(token)
1561
            elif token is not None and token[0] == "op" and token[1] == "*":
1562 1563
                self.type = self.type + " " + nametok[1] + " *"
                token = self.token()
1564
                while token is not None and token[0] == "op" and token[1] == "*":
1565 1566 1567 1568 1569 1570 1571 1572
                    self.type = self.type + " *"
                    token = self.token()
                if token[0] == "name":
                    nametok = token
                    token = self.token()
                else:
                    self.error("struct : expecting name", token)
                    return token
1573
            elif token is not None and token[0] == "name" and nametok is not None:
1574 1575 1576
                self.type = self.type + " " + nametok[1]
                return token

1577
            if nametok is not None:
1578 1579 1580
                self.lexer.push(token)
                token = nametok
            return token
1581

1582 1583 1584 1585 1586 1587 1588 1589 1590 1591
        elif token[0] == "name" and token[1] == "union":
            if self.type == "":
                self.type = token[1]
            else:
                self.type = self.type + " " + token[1]
            token = self.token()
            nametok = None
            if token[0] == "name":
                nametok = token
                token = self.token()
1592
            if token is not None and token[0] == "sep" and token[1] == "{":
1593 1594
                token = self.token()
                token = self.parseUnion(token)
1595
            elif token is not None and token[0] == "name" and nametok is not None:
1596 1597 1598
                self.type = self.type + " " + nametok[1]
                return token

1599
            if nametok is not None:
1600 1601 1602 1603
                self.lexer.push(token)
                token = nametok
            return token

1604
        elif token[0] == "name" and token[1] == "enum":
1605 1606 1607 1608 1609 1610
            if self.type == "":
                self.type = token[1]
            else:
                self.type = self.type + " " + token[1]
            self.enums = []
            token = self.token()
1611
            if token is not None and token[0] == "sep" and token[1] == "{":
1612 1613
                # drop comments before the enum block
                self.comment = None
1614 1615 1616 1617 1618
                token = self.token()
                token = self.parseEnumBlock(token)
            else:
                self.error("parsing enum: expecting '{'", token)
            enum_type = None
1619
            if token is not None and token[0] != "name":
1620 1621 1622 1623 1624 1625 1626 1627 1628
                self.lexer.push(token)
                token = ("name", "enum")
            else:
                enum_type = token[1]
            for enum in self.enums:
                self.index_add(enum[0], self.filename,
                               not self.is_header, "enum",
                               (enum[1], enum[2], enum_type))
            return token
1629 1630
        elif token[0] == "name" and token[1] == "VIR_ENUM_DECL":
            token = self.token()
1631
            if token is not None and token[0] == "sep" and token[1] == "(":
1632 1633 1634 1635
                token = self.token()
                token = self.parseVirEnumDecl(token)
            else:
                self.error("parsing VIR_ENUM_DECL: expecting '('", token)
1636
            if token is not None:
1637 1638 1639 1640 1641 1642
                self.lexer.push(token)
                token = ("name", "virenumdecl")
            return token

        elif token[0] == "name" and token[1] == "VIR_ENUM_IMPL":
            token = self.token()
1643
            if token is not None and token[0] == "sep" and token[1] == "(":
1644 1645 1646 1647
                token = self.token()
                token = self.parseVirEnumImpl(token)
            else:
                self.error("parsing VIR_ENUM_IMPL: expecting '('", token)
1648
            if token is not None:
1649 1650 1651
                self.lexer.push(token)
                token = ("name", "virenumimpl")
            return token
1652

1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664
        elif token[0] == "name" and token[1] == "VIR_LOG_INIT":
            token = self.token()
            if token is not None and token[0] == "sep" and token[1] == "(":
                token = self.token()
                token = self.parseVirLogInit(token)
            else:
                self.error("parsing VIR_LOG_INIT: expecting '('", token)
            if token is not None:
                self.lexer.push(token)
                token = ("name", "virloginit")
            return token

1665 1666 1667 1668 1669 1670 1671 1672 1673 1674
        elif token[0] == "name":
            if self.type == "":
                self.type = token[1]
            else:
                self.type = self.type + " " + token[1]
        else:
            self.error("parsing type %s: expecting a name" % (self.type),
                       token)
            return token
        token = self.token()
1675
        while token is not None and (token[0] == "op" or
1676 1677 1678
              token[0] == "name" and token[1] == "const"):
            self.type = self.type + " " + token[1]
            token = self.token()
1679

1680 1681 1682
        #
        # if there is a parenthesis here, this means a function type
        #
1683
        if token is not None and token[0] == "sep" and token[1] == '(':
1684 1685
            self.type = self.type + token[1]
            token = self.token()
1686
            while token is not None and token[0] == "op" and token[1] == '*':
1687 1688
                self.type = self.type + token[1]
                token = self.token()
1689
            if token is None or token[0] != "name":
1690
                self.error("parsing function type, name expected", token)
1691 1692 1693 1694
                return token
            self.type = self.type + token[1]
            nametok = token
            token = self.token()
1695
            if token is not None and token[0] == "sep" and token[1] == ')':
1696 1697
                self.type = self.type + token[1]
                token = self.token()
1698
                if token is not None and token[0] == "sep" and token[1] == '(':
1699
                    token = self.token()
1700 1701 1702
                    type = self.type
                    token = self.parseSignature(token)
                    self.type = type
1703
                else:
1704
                    self.error("parsing function type, '(' expected", token)
1705 1706
                    return token
            else:
1707
                self.error("parsing function type, ')' expected", token)
1708 1709 1710 1711 1712
                return token
            self.lexer.push(token)
            token = nametok
            return token

1713 1714 1715
        #
        # do some lookahead for arrays
        #
1716
        if token is not None and token[0] == "name":
1717 1718
            nametok = token
            token = self.token()
1719
            if token is not None and token[0] == "sep" and token[1] == '[':
1720
                self.type = self.type + " " + nametok[1]
1721
                while token is not None and token[0] == "sep" and token[1] == '[':
1722 1723
                    self.type = self.type + token[1]
                    token = self.token()
1724
                    while token is not None and token[0] != 'sep' and \
1725 1726 1727
                          token[1] != ']' and token[1] != ';':
                        self.type = self.type + token[1]
                        token = self.token()
1728
                if token is not None and token[0] == 'sep' and token[1] == ']':
1729 1730 1731
                    self.type = self.type + token[1]
                    token = self.token()
                else:
1732
                    self.error("parsing array type, ']' expected", token)
1733
                    return token
1734
            elif token is not None and token[0] == "sep" and token[1] == ':':
1735
                # remove :12 in case it's a limited int size
1736 1737 1738 1739 1740 1741
                token = self.token()
                token = self.token()
            self.lexer.push(token)
            token = nametok

        return token
1742

1743 1744 1745
    #
    # Parse a signature: '(' has been parsed and we scan the type definition
    #    up to the ')' included
1746 1747
    def parseSignature(self, token):
        signature = []
1748
        if token is not None and token[0] == "sep" and token[1] == ')':
1749 1750 1751
            self.signature = []
            token = self.token()
            return token
1752
        while token is not None:
1753
            token = self.parseType(token)
1754
            if token is not None and token[0] == "name":
1755 1756
                signature.append((self.type, token[1], None))
                token = self.token()
1757
            elif token is not None and token[0] == "sep" and token[1] == ',':
1758 1759
                token = self.token()
                continue
1760
            elif token is not None and token[0] == "sep" and token[1] == ')':
1761
                # only the type was provided
1762 1763 1764 1765
                if self.type == "...":
                    signature.append((self.type, "...", None))
                else:
                    signature.append((self.type, None, None))
1766
            if token is not None and token[0] == "sep":
1767 1768 1769 1770 1771 1772 1773 1774
                if token[1] == ',':
                    token = self.token()
                    continue
                elif token[1] == ')':
                    token = self.token()
                    break
        self.signature = signature
        return token
1775

1776 1777 1778
    # this dict contains the functions that are allowed to use [unsigned]
    # long for legacy reasons in their signature and return type. this list is
    # fixed. new procedures and public APIs have to use [unsigned] long long
1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814
    long_legacy_functions = {
        "virGetVersion": (False, ("libVer", "typeVer")),
        "virConnectGetLibVersion": (False, ("libVer")),
        "virConnectGetVersion": (False, ("hvVer")),
        "virDomainGetMaxMemory": (True, ()),
        "virDomainMigrate": (False, ("flags", "bandwidth")),
        "virDomainMigrate2": (False, ("flags", "bandwidth")),
        "virDomainMigrateBegin3": (False, ("flags", "bandwidth")),
        "virDomainMigrateConfirm3": (False, ("flags", "bandwidth")),
        "virDomainMigrateDirect": (False, ("flags", "bandwidth")),
        "virDomainMigrateFinish": (False, ("flags")),
        "virDomainMigrateFinish2": (False, ("flags")),
        "virDomainMigrateFinish3": (False, ("flags")),
        "virDomainMigratePeer2Peer": (False, ("flags", "bandwidth")),
        "virDomainMigratePerform": (False, ("flags", "bandwidth")),
        "virDomainMigratePerform3": (False, ("flags", "bandwidth")),
        "virDomainMigratePrepare": (False, ("flags", "bandwidth")),
        "virDomainMigratePrepare2": (False, ("flags", "bandwidth")),
        "virDomainMigratePrepare3": (False, ("flags", "bandwidth")),
        "virDomainMigratePrepareTunnel": (False, ("flags", "bandwidth")),
        "virDomainMigratePrepareTunnel3": (False, ("flags", "bandwidth")),
        "virDomainMigrateToURI": (False, ("flags", "bandwidth")),
        "virDomainMigrateToURI2": (False, ("flags", "bandwidth")),
        "virDomainMigrateVersion1": (False, ("flags", "bandwidth")),
        "virDomainMigrateVersion2": (False, ("flags", "bandwidth")),
        "virDomainMigrateVersion3": (False, ("flags", "bandwidth")),
        "virDomainMigrateSetMaxSpeed": (False, ("bandwidth")),
        "virDomainSetMaxMemory": (False, ("memory")),
        "virDomainSetMemory": (False, ("memory")),
        "virDomainSetMemoryFlags": (False, ("memory")),
        "virDomainBlockCommit": (False, ("bandwidth")),
        "virDomainBlockJobSetSpeed": (False, ("bandwidth")),
        "virDomainBlockPull": (False, ("bandwidth")),
        "virDomainBlockRebase": (False, ("bandwidth")),
        "virDomainMigrateGetMaxSpeed": (False, ("bandwidth"))
    }
1815 1816 1817 1818 1819 1820 1821 1822

    def checkLongLegacyFunction(self, name, return_type, signature):
        if "long" in return_type and "long long" not in return_type:
            try:
                if not CParser.long_legacy_functions[name][0]:
                    raise Exception()
            except:
                self.error(("function '%s' is not allowed to return long, "
1823
                            "use long long instead") % name)
1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837

        for param in signature:
            if "long" in param[0] and "long long" not in param[0]:
                try:
                    if param[1] not in CParser.long_legacy_functions[name][1]:
                        raise Exception()
                except:
                    self.error(("function '%s' is not allowed to take long "
                                "parameter '%s', use long long instead")
                               % (name, param[1]))

    # this dict contains the structs that are allowed to use [unsigned]
    # long for legacy reasons. this list is fixed. new structs have to use
    # [unsigned] long long
1838 1839 1840 1841 1842
    long_legacy_struct_fields = {
        "_virDomainInfo": ("maxMem", "memory"),
        "_virNodeInfo": ("memory"),
        "_virDomainBlockJobInfo": ("bandwidth")
    }
1843 1844 1845 1846 1847 1848 1849 1850 1851

    def checkLongLegacyStruct(self, name, fields):
        for field in fields:
            if "long" in field[0] and "long long" not in field[0]:
                try:
                    if field[1] not in CParser.long_legacy_struct_fields[name]:
                        raise Exception()
                except:
                    self.error(("struct '%s' is not allowed to contain long "
1852
                                "field '%s', use long long instead")
1853 1854
                               % (name, field[1]))

1855 1856 1857 1858
    #
    # Parse a global definition, be it a type, variable or function
    # the extern "C" blocks are a bit nasty and require it to recurse.
    #
1859 1860 1861
    def parseGlobal(self, token):
        static = 0
        if token[1] == 'extern':
1862
            token = self.token()
1863
            if token is None:
1864 1865 1866 1867
                return token
            if token[0] == 'string':
                if token[1] == 'C':
                    token = self.token()
1868
                    if token is None:
1869 1870 1871
                        return token
                    if token[0] == 'sep' and token[1] == "{":
                        token = self.token()
1872
#                        print('Entering extern "C line ', self.lineno())
1873
                        while token is not None and (token[0] != 'sep' or
1874 1875 1876 1877 1878 1879 1880 1881
                              token[1] != "}"):
                            if token[0] == 'name':
                                token = self.parseGlobal(token)
                            else:
                                self.error(
                                 "token %s %s unexpected at the top level" % (
                                        token[0], token[1]))
                                token = self.parseGlobal(token)
1882
#                        print('Exiting extern "C" line', self.lineno())
1883 1884 1885 1886 1887 1888 1889
                        token = self.token()
                        return token
                else:
                    return token
        elif token[1] == 'static':
            static = 1
            token = self.token()
1890
            if token is None or token[0] != 'name':
1891 1892 1893 1894 1895 1896 1897 1898
                return token

        if token[1] == 'typedef':
            token = self.token()
            return self.parseTypedef(token)
        else:
            token = self.parseType(token)
            type_orig = self.type
1899
        if token is None or token[0] != "name":
1900 1901 1902 1903
            return token
        type = type_orig
        self.name = token[1]
        token = self.token()
1904
        while token is not None and (token[0] == "sep" or token[0] == "op"):
1905 1906 1907 1908
            if token[0] == "sep":
                if token[1] == "[":
                    type = type + token[1]
                    token = self.token()
1909 1910
                    while token is not None and (token[0] != "sep" or
                                                 token[1] != ";"):
1911 1912 1913
                        type = type + token[1]
                        token = self.token()

1914
            if token is not None and token[0] == "op" and token[1] == "=":
1915 1916 1917
                #
                # Skip the initialization of the variable
                #
1918 1919 1920 1921 1922 1923
                token = self.token()
                if token[0] == 'sep' and token[1] == '{':
                    token = self.token()
                    token = self.parseBlock(token)
                else:
                    self.comment = None
1924 1925 1926
                    while token is not None and (token[0] != "sep" or
                                                 token[1] not in ',;'):
                        token = self.token()
1927
                self.comment = None
1928
                if token is None or token[0] != "sep" or (token[1] != ';' and
1929 1930 1931
                   token[1] != ','):
                    self.error("missing ';' or ',' after value")

1932
            if token is not None and token[0] == "sep":
1933 1934 1935 1936
                if token[1] == ";":
                    self.comment = None
                    token = self.token()
                    if type == "struct":
1937
                        self.checkLongLegacyStruct(self.name, self.struct_fields)
1938 1939 1940 1941 1942 1943 1944 1945 1946
                        self.index_add(self.name, self.filename,
                             not self.is_header, "struct", self.struct_fields)
                    else:
                        self.index_add(self.name, self.filename,
                             not self.is_header, "variable", type)
                    break
                elif token[1] == "(":
                    token = self.token()
                    token = self.parseSignature(token)
1947
                    if token is None:
1948 1949
                        return None
                    if token[0] == "sep" and token[1] == ";":
1950
                        self.checkLongLegacyFunction(self.name, type, self.signature)
1951 1952 1953 1954 1955 1956
                        d = self.mergeFunctionComment(self.name,
                                ((type, None), self.signature), 1)
                        self.index_add(self.name, self.filename, static,
                                        "function", d)
                        token = self.token()
                    elif token[0] == "sep" and token[1] == "{":
1957
                        self.checkLongLegacyFunction(self.name, type, self.signature)
1958 1959 1960 1961 1962
                        d = self.mergeFunctionComment(self.name,
                                ((type, None), self.signature), static)
                        self.index_add(self.name, self.filename, static,
                                        "function", d)
                        token = self.token()
1963
                        token = self.parseBlock(token)
1964 1965 1966 1967 1968 1969
                elif token[1] == ',':
                    self.comment = None
                    self.index_add(self.name, self.filename, static,
                                    "variable", type)
                    type = type_orig
                    token = self.token()
1970
                    while token is not None and token[0] == "sep":
1971 1972
                        type = type + token[1]
                        token = self.token()
1973
                    if token is not None and token[0] == "name":
1974 1975 1976 1977 1978 1979
                        self.name = token[1]
                        token = self.token()
                else:
                    break

        return token
1980 1981

    def parse(self):
1982
        if not quiet:
1983
            print("Parsing %s" % (self.filename))
1984
        token = self.token()
1985
        while token is not None:
1986
            if token[0] == 'name':
1987
                token = self.parseGlobal(token)
1988
            else:
1989 1990 1991 1992 1993
                self.error("token %s %s unexpected at the top level" % (
                       token[0], token[1]))
                token = self.parseGlobal(token)
                return
        self.parseTopComment(self.top_comment)
1994
        return self.index
1995

1996 1997 1998

class docBuilder:
    """A documentation builder"""
J
Jiri Denemark 已提交
1999
    def __init__(self, name, path='.', directories=['.'], includes=[]):
2000
        self.name = name
J
Jiri Denemark 已提交
2001
        self.path = path
2002
        self.directories = directories
2003
        if name == "libvirt":
2004
            self.includes = includes + list(included_files.keys())
2005
        elif name == "libvirt-qemu":
2006
            self.includes = includes + list(qemu_included_files.keys())
2007
        elif name == "libvirt-lxc":
2008
            self.includes = includes + list(lxc_included_files.keys())
2009
        elif name == "libvirt-admin":
2010
            self.includes = includes + list(admin_included_files.keys())
2011 2012 2013
        self.modules = {}
        self.headers = {}
        self.idx = index()
2014
        self.xref = {}
2015 2016
        self.index = {}
        self.basename = name
2017
        self.errors = 0
2018

2019 2020 2021
    def warning(self, msg):
        global warnings
        warnings = warnings + 1
2022
        print(msg)
2023

2024 2025
    def error(self, msg):
        self.errors += 1
2026
        print("Error:", msg, file=sys.stderr)
2027

2028
    def indexString(self, id, str):
2029
        if str is None:
2030
            return
2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046
        str = str.replace("'", ' ')
        str = str.replace('"', ' ')
        str = str.replace("/", ' ')
        str = str.replace('*', ' ')
        str = str.replace("[", ' ')
        str = str.replace("]", ' ')
        str = str.replace("(", ' ')
        str = str.replace(")", ' ')
        str = str.replace("<", ' ')
        str = str.replace('>', ' ')
        str = str.replace("&", ' ')
        str = str.replace('#', ' ')
        str = str.replace(",", ' ')
        str = str.replace('.', ' ')
        str = str.replace(';', ' ')
        tokens = str.split()
2047
        for token in tokens:
C
Cole Robinson 已提交
2048 2049 2050 2051 2052 2053 2054 2055 2056
            c = token[0]
            if not re.match(r"[a-zA-Z]", c):
                pass
            elif len(token) < 3:
                pass
            else:
                lower = token.lower()
                # TODO: generalize this a bit
                if lower == 'and' or lower == 'the':
2057
                    pass
C
Cole Robinson 已提交
2058 2059
                elif token in self.xref:
                    self.xref[token].append(id)
2060
                else:
C
Cole Robinson 已提交
2061
                    self.xref[token] = [id]
2062 2063

    def analyze(self):
2064
        if not quiet:
2065
            print("Project %s : %d headers, %d modules" % (self.name, len(self.headers.keys()), len(self.modules.keys())))
2066
        self.idx.analyze()
2067 2068

    def scanHeaders(self):
2069 2070 2071
        for header in self.headers.keys():
            parser = CParser(header)
            idx = parser.parse()
2072
            self.headers[header] = idx
2073
            self.idx.merge(idx)
2074 2075

    def scanModules(self):
2076 2077 2078 2079 2080 2081
        for module in self.modules.keys():
            parser = CParser(module)
            idx = parser.parse()
            # idx.analyze()
            self.modules[module] = idx
            self.idx.merge_public(idx)
2082 2083 2084

    def scan(self):
        for directory in self.directories:
2085 2086 2087 2088
            files = glob.glob(directory + "/*.c")
            for file in files:
                skip = 1
                for incl in self.includes:
2089
                    if file.find(incl) != -1:
2090
                        skip = 0
2091 2092
                        break
                if skip == 0:
2093
                    self.modules[file] = None
2094 2095 2096 2097
            files = glob.glob(directory + "/*.h")
            for file in files:
                skip = 1
                for incl in self.includes:
2098
                    if file.find(incl) != -1:
2099
                        skip = 0
2100 2101
                        break
                if skip == 0:
2102
                    self.headers[file] = None
2103 2104
        self.scanHeaders()
        self.scanModules()
2105

2106 2107
    def modulename_file(self, file):
        module = os.path.basename(file)
2108 2109 2110 2111 2112
        if module[-2:] == '.h':
            module = module[:-2]
        elif module[-2:] == '.c':
            module = module[:-2]
        return module
2113 2114 2115 2116

    def serialize_enum(self, output, name):
        id = self.idx.enums[name]
        output.write("    <enum name='%s' file='%s'" % (name,
2117
                     self.modulename_file(id.header)))
2118
        if id.info is not None:
2119
            info = id.info
2120
            if info[0] is not None and info[0] != '':
2121 2122 2123 2124
                try:
                    val = eval(info[0])
                except:
                    val = info[0]
2125
                output.write(" value='%s'" % (val))
2126
            if info[2] is not None and info[2] != '':
2127
                output.write(" type='%s'" % info[2])
2128
            if info[1] is not None and info[1] != '':
2129
                output.write(" info='%s'" % escape(info[1]))
2130 2131 2132 2133
        output.write("/>\n")

    def serialize_macro(self, output, name):
        id = self.idx.macros[name]
2134
        output.write("    <macro name='%s' file='%s'" % (name,
2135
                     self.modulename_file(id.header)))
2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156
        if id.info is None:
            args = []
            desc = None
            strValue = None
        else:
            (args, desc, strValue) = id.info

        if strValue is not None:
            output.write(" string='%s'" % strValue)
        output.write(">\n")

        if desc is not None and desc != "":
            output.write("      <info><![CDATA[%s]]></info>\n" % (desc))
            self.indexString(name, desc)
        for arg in args:
            (name, desc) = arg
            if desc is not None and desc != "":
                output.write("      <arg name='%s' info='%s'/>\n" % (
                             name, escape(desc)))
                self.indexString(name, desc)
            else:
2157
                output.write("      <arg name='%s'/>\n" % name)
2158 2159
        output.write("    </macro>\n")

2160
    def serialize_union(self, output, field, desc):
2161
        output.write("      <field name='%s' type='union' info='%s'>\n" % (field[1], desc))
2162 2163 2164
        output.write("        <union>\n")
        for f in field[3]:
            desc = f[2]
2165
            if desc is None:
2166 2167 2168
                desc = ''
            else:
                desc = escape(desc)
2169
            output.write("          <field name='%s' type='%s' info='%s'/>\n" % (f[1], f[0], desc))
2170 2171 2172 2173

        output.write("        </union>\n")
        output.write("      </field>\n")

2174 2175
    def serialize_typedef(self, output, name):
        id = self.idx.typedefs[name]
2176 2177 2178 2179
        if id.info[0:7] == 'struct ':
            output.write("    <struct name='%s' file='%s' type='%s'" % (
                     name, self.modulename_file(id.header), id.info))
            name = id.info[7:]
2180 2181
            if (name in self.idx.structs and
                    isinstance(self.idx.structs[name].info, (list, tuple))):
2182
                output.write(">\n")
2183 2184 2185 2186
                try:
                    for field in self.idx.structs[name].info:
                        desc = field[2]
                        self.indexString(name, desc)
2187
                        if desc is None:
2188 2189 2190
                            desc = ''
                        else:
                            desc = escape(desc)
2191 2192 2193
                        if field[0] == "union":
                            self.serialize_union(output, field, desc)
                        else:
2194
                            output.write("      <field name='%s' type='%s' info='%s'/>\n" % (field[1], field[0], desc))
2195
                except:
2196
                    self.warning("Failed to serialize struct %s" % name)
2197 2198
                output.write("    </struct>\n")
            else:
2199
                output.write("/>\n")
2200
        else:
2201 2202
            output.write("    <typedef name='%s' file='%s' type='%s'" % (
                         name, self.modulename_file(id.header), id.info))
2203
            try:
2204
                desc = id.extra
2205
                if desc is not None and desc != "":
2206 2207 2208 2209 2210 2211
                    output.write(">\n      <info><![CDATA[%s]]></info>\n" % (desc))
                    output.write("    </typedef>\n")
                else:
                    output.write("/>\n")
            except:
                output.write("/>\n")
2212 2213 2214

    def serialize_variable(self, output, name):
        id = self.idx.variables[name]
2215
        if id.info is not None:
2216 2217 2218 2219 2220
            output.write("    <variable name='%s' file='%s' type='%s'/>\n" % (
                    name, self.modulename_file(id.header), id.info))
        else:
            output.write("    <variable name='%s' file='%s'/>\n" % (
                    name, self.modulename_file(id.header)))
2221

2222 2223
    def serialize_function(self, output, name):
        id = self.idx.functions[name]
2224
        if name == debugsym and not quiet:
2225
            print("=>", id)
2226

2227
        # NB: this is consumed by a regex in 'getAPIFilenames' in hvsupport.pl
2228
        output.write("    <%s name='%s' file='%s' module='%s'>\n" % (id.type,
2229 2230 2231 2232 2233
                     name, self.modulename_file(id.header),
                     self.modulename_file(id.module)))
        #
        # Processing of conditionals modified by Bill 1/1/05
        #
2234
        if id.conditionals is not None:
2235 2236 2237 2238 2239
            apstr = ""
            for cond in id.conditionals:
                if apstr != "":
                    apstr = apstr + " &amp;&amp; "
                apstr = apstr + cond
2240
            output.write("      <cond>%s</cond>\n" % (apstr))
2241 2242 2243 2244
        try:
            (ret, params, desc) = id.info
            output.write("      <info><![CDATA[%s]]></info>\n" % (desc))
            self.indexString(name, desc)
2245
            if ret[0] is not None:
2246 2247
                if ret[0] == "void":
                    output.write("      <return type='void'/>\n")
A
Andrea Bolognani 已提交
2248
                elif (ret[1] is None or ret[1] == '') and name not in ignored_functions:
2249
                    self.error("Missing documentation for return of function `%s'" % name)
2250 2251 2252 2253 2254 2255 2256
                else:
                    output.write("      <return type='%s' info='%s'/>\n" % (
                             ret[0], escape(ret[1])))
                    self.indexString(name, ret[1])
            for param in params:
                if param[0] == 'void':
                    continue
2257
                if (param[2] is None or param[2] == ''):
A
Andrea Bolognani 已提交
2258
                    if name in ignored_functions:
2259 2260 2261
                        output.write("      <arg name='%s' type='%s' info=''/>\n" % (param[1], param[0]))
                    else:
                        self.error("Missing documentation for arg `%s' of function `%s'" % (param[1], name))
2262 2263 2264 2265
                else:
                    output.write("      <arg name='%s' type='%s' info='%s'/>\n" % (param[1], param[0], escape(param[2])))
                    self.indexString(name, param[2])
        except:
2266
            print("Exception:", sys.exc_info()[1], file=sys.stderr)
2267
            self.warning("Failed to save function %s info: %s" % (name, repr(id.info)))
2268 2269 2270 2271
        output.write("    </%s>\n" % (id.type))

    def serialize_exports(self, output, file):
        module = self.modulename_file(file)
2272 2273
        output.write("    <file name='%s'>\n" % (module))
        dict = self.headers[file]
2274
        if dict.info is not None:
2275
            for data in ('Summary', 'Description'):
2276 2277
                try:
                    output.write("     <%s>%s</%s>\n" % (
C
Cole Robinson 已提交
2278
                                 data.lower(),
2279
                                 escape(dict.info[data]),
C
Cole Robinson 已提交
2280 2281
                                 data.lower()))
                except KeyError:
2282
                    self.warning("Header %s lacks a %s description" % (module, data))
A
Andrea Bolognani 已提交
2283
            if 'Description' in dict.info:
2284
                desc = dict.info['Description']
2285
                if desc.find("DEPRECATED") != -1:
2286
                    output.write("     <deprecated/>\n")
2287

2288
        for id in uniq(dict.macros.keys()):
2289
            # Macros are sometime used to masquerade other types.
A
Andrea Bolognani 已提交
2290
            if id in dict.functions:
2291
                continue
A
Andrea Bolognani 已提交
2292
            if id in dict.variables:
2293
                continue
A
Andrea Bolognani 已提交
2294
            if id in dict.typedefs:
2295
                continue
A
Andrea Bolognani 已提交
2296
            if id in dict.structs:
2297
                continue
A
Andrea Bolognani 已提交
2298
            if id in dict.unions:
2299
                continue
A
Andrea Bolognani 已提交
2300
            if id in dict.enums:
2301 2302
                continue
            output.write("     <exports symbol='%s' type='macro'/>\n" % (id))
2303
        for id in uniq(dict.enums.keys()):
2304
            output.write("     <exports symbol='%s' type='enum'/>\n" % (id))
2305
        for id in uniq(dict.typedefs.keys()):
2306
            output.write("     <exports symbol='%s' type='typedef'/>\n" % (id))
2307
        for id in uniq(dict.structs.keys()):
2308
            output.write("     <exports symbol='%s' type='struct'/>\n" % (id))
2309
        for id in uniq(dict.variables.keys()):
2310
            output.write("     <exports symbol='%s' type='variable'/>\n" % (id))
2311
        for id in uniq(dict.functions.keys()):
2312 2313
            output.write("     <exports symbol='%s' type='function'/>\n" % (id))
        output.write("    </file>\n")
2314 2315

    def serialize_xrefs_files(self, output):
2316
        headers = sorted(self.headers.keys())
2317
        for file in headers:
2318 2319 2320
            module = self.modulename_file(file)
            output.write("    <file name='%s'>\n" % (module))
            dict = self.headers[file]
2321 2322 2323 2324 2325
            ids = uniq(list(dict.functions.keys()) +
                       list(dict.variables.keys()) +
                       list(dict.macros.keys()) +
                       list(dict.typedefs.keys()) +
                       list(dict.structs.keys()) +
2326
                       list(dict.enums.keys()))
2327 2328 2329
            for id in ids:
                output.write("      <ref name='%s'/>\n" % (id))
            output.write("    </file>\n")
2330 2331 2332 2333
        pass

    def serialize_xrefs_functions(self, output):
        funcs = {}
2334 2335 2336 2337 2338 2339 2340
        for name in self.idx.functions.keys():
            id = self.idx.functions[name]
            try:
                (ret, params, desc) = id.info
                for param in params:
                    if param[0] == 'void':
                        continue
A
Andrea Bolognani 已提交
2341
                    if param[0] in funcs:
2342 2343 2344 2345 2346
                        funcs[param[0]].append(name)
                    else:
                        funcs[param[0]] = [name]
            except:
                pass
2347
        typ = sorted(funcs.keys())
2348
        for type in typ:
2349
            if type in ['', "void", "int", "char *", "const char *"]:
2350 2351 2352 2353 2354 2355 2356 2357 2358 2359
                continue
            output.write("    <type name='%s'>\n" % (type))
            ids = funcs[type]
            ids.sort()
            pid = ''    # not sure why we have dups, but get rid of them!
            for id in ids:
                if id != pid:
                    output.write("      <ref name='%s'/>\n" % (id))
                    pid = id
            output.write("    </type>\n")
2360 2361 2362

    def serialize_xrefs_constructors(self, output):
        funcs = {}
2363 2364 2365 2366 2367 2368
        for name in self.idx.functions.keys():
            id = self.idx.functions[name]
            try:
                (ret, params, desc) = id.info
                if ret[0] == "void":
                    continue
A
Andrea Bolognani 已提交
2369
                if ret[0] in funcs:
2370 2371 2372 2373 2374
                    funcs[ret[0]].append(name)
                else:
                    funcs[ret[0]] = [name]
            except:
                pass
2375
        typ = sorted(funcs.keys())
2376
        for type in typ:
2377
            if type in ['', "void", "int", "char *", "const char *"]:
2378 2379
                continue
            output.write("    <type name='%s'>\n" % (type))
2380
            ids = sorted(funcs[type])
2381 2382 2383
            for id in ids:
                output.write("      <ref name='%s'/>\n" % (id))
            output.write("    </type>\n")
2384 2385

    def serialize_xrefs_alpha(self, output):
2386
        letter = None
2387
        ids = sorted(self.idx.identifiers.keys())
2388 2389
        for id in ids:
            if id[0] != letter:
2390
                if letter is not None:
2391 2392 2393 2394
                    output.write("    </letter>\n")
                letter = id[0]
                output.write("    <letter name='%s'>\n" % (letter))
            output.write("      <ref name='%s'/>\n" % (id))
2395
        if letter is not None:
2396
            output.write("    </letter>\n")
2397 2398

    def serialize_xrefs_references(self, output):
2399
        typ = sorted(self.idx.identifiers.keys())
2400 2401 2402 2403 2404 2405 2406
        for id in typ:
            idf = self.idx.identifiers[id]
            module = idf.header
            output.write("    <reference name='%s' href='%s'/>\n" % (id,
                         'html/' + self.basename + '-' +
                         self.modulename_file(module) + '.html#' +
                         id))
2407 2408 2409

    def serialize_xrefs_index(self, output):
        index = self.xref
2410
        typ = sorted(index.keys())
2411 2412 2413 2414 2415 2416 2417 2418
        letter = None
        count = 0
        chunk = 0
        chunks = []
        for id in typ:
            if len(index[id]) > 30:
                continue
            if id[0] != letter:
2419 2420
                if letter is None or count > 200:
                    if letter is not None:
2421 2422 2423
                        output.write("      </letter>\n")
                        output.write("    </chunk>\n")
                        count = 0
2424
                        chunks.append(["chunk%s" % (chunk - 1), first_letter, letter])
2425 2426 2427
                    output.write("    <chunk name='chunk%s'>\n" % (chunk))
                    first_letter = id[0]
                    chunk = chunk + 1
2428
                elif letter is not None:
2429 2430 2431 2432
                    output.write("      </letter>\n")
                letter = id[0]
                output.write("      <letter name='%s'>\n" % (letter))
            output.write("        <word name='%s'>\n" % (id))
2433
            tokens = index[id]
2434 2435 2436 2437 2438 2439 2440 2441 2442
            tokens.sort()
            tok = None
            for token in tokens:
                if tok == token:
                    continue
                tok = token
                output.write("          <ref name='%s'/>\n" % (token))
                count = count + 1
            output.write("        </word>\n")
2443
        if letter is not None:
2444 2445 2446
            output.write("      </letter>\n")
            output.write("    </chunk>\n")
            if count != 0:
2447
                chunks.append(["chunk%s" % (chunk - 1), first_letter, letter])
2448 2449 2450 2451 2452
            output.write("    <chunks>\n")
            for ch in chunks:
                output.write("      <chunk name='%s' start='%s' end='%s'/>\n" % (
                             ch[0], ch[1], ch[2]))
            output.write("    </chunks>\n")
2453 2454

    def serialize_xrefs(self, output):
2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472
        output.write("  <references>\n")
        self.serialize_xrefs_references(output)
        output.write("  </references>\n")
        output.write("  <alpha>\n")
        self.serialize_xrefs_alpha(output)
        output.write("  </alpha>\n")
        output.write("  <constructors>\n")
        self.serialize_xrefs_constructors(output)
        output.write("  </constructors>\n")
        output.write("  <functions>\n")
        self.serialize_xrefs_functions(output)
        output.write("  </functions>\n")
        output.write("  <files>\n")
        self.serialize_xrefs_files(output)
        output.write("  </files>\n")
        output.write("  <index>\n")
        self.serialize_xrefs_index(output)
        output.write("  </index>\n")
2473 2474

    def serialize(self):
J
Jiri Denemark 已提交
2475
        filename = "%s/%s-api.xml" % (self.path, self.name)
2476
        if not quiet:
2477
            print("Saving XML description %s" % (filename))
2478 2479 2480 2481
        output = open(filename, "w")
        output.write('<?xml version="1.0" encoding="ISO-8859-1"?>\n')
        output.write("<api name='%s'>\n" % self.name)
        output.write("  <files>\n")
2482
        headers = sorted(self.headers.keys())
2483 2484 2485 2486
        for file in headers:
            self.serialize_exports(output, file)
        output.write("  </files>\n")
        output.write("  <symbols>\n")
2487
        macros = sorted(self.idx.macros.keys())
2488 2489
        for macro in macros:
            self.serialize_macro(output, macro)
2490
        enums = sorted(self.idx.enums.keys())
2491 2492
        for enum in enums:
            self.serialize_enum(output, enum)
2493
        typedefs = sorted(self.idx.typedefs.keys())
2494 2495
        for typedef in typedefs:
            self.serialize_typedef(output, typedef)
2496
        variables = sorted(self.idx.variables.keys())
2497 2498
        for variable in variables:
            self.serialize_variable(output, variable)
2499
        functions = sorted(self.idx.functions.keys())
2500 2501 2502 2503 2504 2505
        for function in functions:
            self.serialize_function(output, function)
        output.write("  </symbols>\n")
        output.write("</api>\n")
        output.close()

2506
        if self.errors > 0:
2507
            print("apibuild.py: %d error(s) encountered during generation" % self.errors, file=sys.stderr)
2508 2509
            sys.exit(3)

J
Jiri Denemark 已提交
2510
        filename = "%s/%s-refs.xml" % (self.path, self.name)
2511
        if not quiet:
2512
            print("Saving XML Cross References %s" % (filename))
2513 2514 2515 2516 2517 2518 2519 2520
        output = open(filename, "w")
        output.write('<?xml version="1.0" encoding="ISO-8859-1"?>\n')
        output.write("<apirefs name='%s'>\n" % self.name)
        self.serialize_xrefs(output)
        output.write("</apirefs>\n")
        output.close()


A
Andrea Bolognani 已提交
2521 2522 2523 2524
class app:
    def warning(self, msg):
        global warnings
        warnings = warnings + 1
2525
        print(msg)
A
Andrea Bolognani 已提交
2526 2527 2528

    def rebuild(self, name):
        if name not in ["libvirt", "libvirt-qemu", "libvirt-lxc", "libvirt-admin"]:
A
Andrea Bolognani 已提交
2529
            self.warning("rebuild() failed, unknown module %s" % name)
A
Andrea Bolognani 已提交
2530 2531 2532 2533 2534 2535
            return None
        builder = None
        srcdir = os.path.abspath((os.environ["srcdir"]))
        builddir = os.path.abspath((os.environ["builddir"]))
        if srcdir == builddir:
            builddir = None
2536
        if glob.glob(srcdir + "/../src/libvirt.c") != []:
A
Andrea Bolognani 已提交
2537
            if not quiet:
2538
                print("Rebuilding API description for %s" % name)
A
Andrea Bolognani 已提交
2539 2540 2541
            dirs = [srcdir + "/../src",
                    srcdir + "/../src/util",
                    srcdir + "/../include/libvirt"]
2542 2543
            if (builddir and
                not os.path.exists(srcdir + "/../include/libvirt/libvirt-common.h")):
A
Andrea Bolognani 已提交
2544 2545
                dirs.append(builddir + "/../include/libvirt")
            builder = docBuilder(name, srcdir, dirs, [])
2546
        elif glob.glob("src/libvirt.c") != []:
A
Andrea Bolognani 已提交
2547
            if not quiet:
2548
                print("Rebuilding API description for %s" % name)
A
Andrea Bolognani 已提交
2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566
            builder = docBuilder(name, srcdir,
                                 ["src", "src/util", "include/libvirt"],
                                 [])
        else:
            self.warning("rebuild() failed, unable to guess the module")
            return None
        builder.scan()
        builder.analyze()
        builder.serialize()
        return builder

    #
    # for debugging the parser
    #
    def parse(self, filename):
        parser = CParser(filename)
        idx = parser.parse()
        return idx
2567 2568 2569


if __name__ == "__main__":
A
Andrea Bolognani 已提交
2570
    app = app()
2571 2572
    if len(sys.argv) > 1:
        debug = 1
A
Andrea Bolognani 已提交
2573
        app.parse(sys.argv[1])
2574
    else:
A
Andrea Bolognani 已提交
2575 2576 2577 2578
        app.rebuild("libvirt")
        app.rebuild("libvirt-qemu")
        app.rebuild("libvirt-lxc")
        app.rebuild("libvirt-admin")
2579 2580 2581 2582
    if warnings > 0:
        sys.exit(2)
    else:
        sys.exit(0)