xref: /openbmc/qemu/scripts/qapi/common.py (revision 967c885108f18e5065744719f7959ba5ea0a5b0d)
1#
2# QAPI helper library
3#
4# Copyright IBM, Corp. 2011
5# Copyright (c) 2013-2018 Red Hat Inc.
6#
7# Authors:
8#  Anthony Liguori <aliguori@us.ibm.com>
9#  Markus Armbruster <armbru@redhat.com>
10#
11# This work is licensed under the terms of the GNU GPL, version 2.
12# See the COPYING file in the top-level directory.
13
14from __future__ import print_function
15import errno
16import os
17import re
18import string
19import sys
20from collections import OrderedDict
21
22builtin_types = {
23    'null':     'QTYPE_QNULL',
24    'str':      'QTYPE_QSTRING',
25    'int':      'QTYPE_QNUM',
26    'number':   'QTYPE_QNUM',
27    'bool':     'QTYPE_QBOOL',
28    'int8':     'QTYPE_QNUM',
29    'int16':    'QTYPE_QNUM',
30    'int32':    'QTYPE_QNUM',
31    'int64':    'QTYPE_QNUM',
32    'uint8':    'QTYPE_QNUM',
33    'uint16':   'QTYPE_QNUM',
34    'uint32':   'QTYPE_QNUM',
35    'uint64':   'QTYPE_QNUM',
36    'size':     'QTYPE_QNUM',
37    'any':      None,           # any QType possible, actually
38    'QType':    'QTYPE_QSTRING',
39}
40
41# Are documentation comments required?
42doc_required = False
43
44# Whitelist of commands allowed to return a non-dictionary
45returns_whitelist = []
46
47# Whitelist of entities allowed to violate case conventions
48name_case_whitelist = []
49
50enum_types = {}
51struct_types = {}
52union_types = {}
53all_names = {}
54
55#
56# Parsing the schema into expressions
57#
58
59
60def error_path(parent):
61    res = ''
62    while parent:
63        res = ('In file included from %s:%d:\n' % (parent['file'],
64                                                   parent['line'])) + res
65        parent = parent['parent']
66    return res
67
68
69class QAPIError(Exception):
70    def __init__(self, fname, line, col, incl_info, msg):
71        Exception.__init__(self)
72        self.fname = fname
73        self.line = line
74        self.col = col
75        self.info = incl_info
76        self.msg = msg
77
78    def __str__(self):
79        loc = '%s:%d' % (self.fname, self.line)
80        if self.col is not None:
81            loc += ':%s' % self.col
82        return error_path(self.info) + '%s: %s' % (loc, self.msg)
83
84
85class QAPIParseError(QAPIError):
86    def __init__(self, parser, msg):
87        col = 1
88        for ch in parser.src[parser.line_pos:parser.pos]:
89            if ch == '\t':
90                col = (col + 7) % 8 + 1
91            else:
92                col += 1
93        QAPIError.__init__(self, parser.fname, parser.line, col,
94                           parser.incl_info, msg)
95
96
97class QAPISemError(QAPIError):
98    def __init__(self, info, msg):
99        QAPIError.__init__(self, info['file'], info['line'], None,
100                           info['parent'], msg)
101
102
103class QAPIDoc(object):
104    class Section(object):
105        def __init__(self, name=None):
106            # optional section name (argument/member or section name)
107            self.name = name
108            # the list of lines for this section
109            self.text = ''
110
111        def append(self, line):
112            self.text += line.rstrip() + '\n'
113
114    class ArgSection(Section):
115        def __init__(self, name):
116            QAPIDoc.Section.__init__(self, name)
117            self.member = None
118
119        def connect(self, member):
120            self.member = member
121
122    def __init__(self, parser, info):
123        # self._parser is used to report errors with QAPIParseError.  The
124        # resulting error position depends on the state of the parser.
125        # It happens to be the beginning of the comment.  More or less
126        # servicable, but action at a distance.
127        self._parser = parser
128        self.info = info
129        self.symbol = None
130        self.body = QAPIDoc.Section()
131        # dict mapping parameter name to ArgSection
132        self.args = OrderedDict()
133        # a list of Section
134        self.sections = []
135        # the current section
136        self._section = self.body
137
138    def has_section(self, name):
139        """Return True if we have a section with this name."""
140        for i in self.sections:
141            if i.name == name:
142                return True
143        return False
144
145    def append(self, line):
146        """Parse a comment line and add it to the documentation."""
147        line = line[1:]
148        if not line:
149            self._append_freeform(line)
150            return
151
152        if line[0] != ' ':
153            raise QAPIParseError(self._parser, "Missing space after #")
154        line = line[1:]
155
156        # FIXME not nice: things like '#  @foo:' and '# @foo: ' aren't
157        # recognized, and get silently treated as ordinary text
158        if self.symbol:
159            self._append_symbol_line(line)
160        elif not self.body.text and line.startswith('@'):
161            if not line.endswith(':'):
162                raise QAPIParseError(self._parser, "Line should end with :")
163            self.symbol = line[1:-1]
164            # FIXME invalid names other than the empty string aren't flagged
165            if not self.symbol:
166                raise QAPIParseError(self._parser, "Invalid name")
167        else:
168            self._append_freeform(line)
169
170    def end_comment(self):
171        self._end_section()
172
173    def _append_symbol_line(self, line):
174        name = line.split(' ', 1)[0]
175
176        if name.startswith('@') and name.endswith(':'):
177            line = line[len(name)+1:]
178            self._start_args_section(name[1:-1])
179        elif name in ('Returns:', 'Since:',
180                      # those are often singular or plural
181                      'Note:', 'Notes:',
182                      'Example:', 'Examples:',
183                      'TODO:'):
184            line = line[len(name)+1:]
185            self._start_section(name[:-1])
186
187        self._append_freeform(line)
188
189    def _start_args_section(self, name):
190        # FIXME invalid names other than the empty string aren't flagged
191        if not name:
192            raise QAPIParseError(self._parser, "Invalid parameter name")
193        if name in self.args:
194            raise QAPIParseError(self._parser,
195                                 "'%s' parameter name duplicated" % name)
196        if self.sections:
197            raise QAPIParseError(self._parser,
198                                 "'@%s:' can't follow '%s' section"
199                                 % (name, self.sections[0].name))
200        self._end_section()
201        self._section = QAPIDoc.ArgSection(name)
202        self.args[name] = self._section
203
204    def _start_section(self, name=None):
205        if name in ('Returns', 'Since') and self.has_section(name):
206            raise QAPIParseError(self._parser,
207                                 "Duplicated '%s' section" % name)
208        self._end_section()
209        self._section = QAPIDoc.Section(name)
210        self.sections.append(self._section)
211
212    def _end_section(self):
213        if self._section:
214            text = self._section.text = self._section.text.strip()
215            if self._section.name and (not text or text.isspace()):
216                raise QAPIParseError(self._parser, "Empty doc section '%s'"
217                                     % self._section.name)
218            self._section = None
219
220    def _append_freeform(self, line):
221        in_arg = isinstance(self._section, QAPIDoc.ArgSection)
222        if (in_arg and self._section.text.endswith('\n\n')
223                and line and not line[0].isspace()):
224            self._start_section()
225        if (in_arg or not self._section.name
226                or not self._section.name.startswith('Example')):
227            line = line.strip()
228        match = re.match(r'(@\S+:)', line)
229        if match:
230            raise QAPIParseError(self._parser,
231                                 "'%s' not allowed in free-form documentation"
232                                 % match.group(1))
233        self._section.append(line)
234
235    def connect_member(self, member):
236        if member.name not in self.args:
237            # Undocumented TODO outlaw
238            self.args[member.name] = QAPIDoc.ArgSection(member.name)
239        self.args[member.name].connect(member)
240
241    def check_expr(self, expr):
242        if self.has_section('Returns') and 'command' not in expr:
243            raise QAPISemError(self.info,
244                               "'Returns:' is only valid for commands")
245
246    def check(self):
247        bogus = [name for name, section in self.args.items()
248                 if not section.member]
249        if bogus:
250            raise QAPISemError(
251                self.info,
252                "The following documented members are not in "
253                "the declaration: %s" % ", ".join(bogus))
254
255
256class QAPISchemaParser(object):
257
258    def __init__(self, fp, previously_included=[], incl_info=None):
259        self.fname = fp.name
260        previously_included.append(os.path.abspath(fp.name))
261        self.incl_info = incl_info
262        self.src = fp.read()
263        if self.src == '' or self.src[-1] != '\n':
264            self.src += '\n'
265        self.cursor = 0
266        self.line = 1
267        self.line_pos = 0
268        self.exprs = []
269        self.docs = []
270        self.accept()
271        cur_doc = None
272
273        while self.tok is not None:
274            info = {'file': self.fname, 'line': self.line,
275                    'parent': self.incl_info}
276            if self.tok == '#':
277                self.reject_expr_doc(cur_doc)
278                cur_doc = self.get_doc(info)
279                self.docs.append(cur_doc)
280                continue
281
282            expr = self.get_expr(False)
283            if 'include' in expr:
284                self.reject_expr_doc(cur_doc)
285                if len(expr) != 1:
286                    raise QAPISemError(info, "Invalid 'include' directive")
287                include = expr['include']
288                if not isinstance(include, str):
289                    raise QAPISemError(info,
290                                       "Value of 'include' must be a string")
291                incl_fname = os.path.join(os.path.dirname(self.fname),
292                                          include)
293                self.exprs.append({'expr': {'include': incl_fname},
294                                   'info': info})
295                exprs_include = self._include(include, info, incl_fname,
296                                              previously_included)
297                if exprs_include:
298                    self.exprs.extend(exprs_include.exprs)
299                    self.docs.extend(exprs_include.docs)
300            elif "pragma" in expr:
301                self.reject_expr_doc(cur_doc)
302                if len(expr) != 1:
303                    raise QAPISemError(info, "Invalid 'pragma' directive")
304                pragma = expr['pragma']
305                if not isinstance(pragma, dict):
306                    raise QAPISemError(
307                        info, "Value of 'pragma' must be a dictionary")
308                for name, value in pragma.items():
309                    self._pragma(name, value, info)
310            else:
311                expr_elem = {'expr': expr,
312                             'info': info}
313                if cur_doc:
314                    if not cur_doc.symbol:
315                        raise QAPISemError(
316                            cur_doc.info, "Expression documentation required")
317                    expr_elem['doc'] = cur_doc
318                self.exprs.append(expr_elem)
319            cur_doc = None
320        self.reject_expr_doc(cur_doc)
321
322    @staticmethod
323    def reject_expr_doc(doc):
324        if doc and doc.symbol:
325            raise QAPISemError(
326                doc.info,
327                "Documentation for '%s' is not followed by the definition"
328                % doc.symbol)
329
330    def _include(self, include, info, incl_fname, previously_included):
331        incl_abs_fname = os.path.abspath(incl_fname)
332        # catch inclusion cycle
333        inf = info
334        while inf:
335            if incl_abs_fname == os.path.abspath(inf['file']):
336                raise QAPISemError(info, "Inclusion loop for %s" % include)
337            inf = inf['parent']
338
339        # skip multiple include of the same file
340        if incl_abs_fname in previously_included:
341            return None
342
343        try:
344            if sys.version_info[0] >= 3:
345                fobj = open(incl_fname, 'r', encoding='utf-8')
346            else:
347                fobj = open(incl_fname, 'r')
348        except IOError as e:
349            raise QAPISemError(info, '%s: %s' % (e.strerror, incl_fname))
350        return QAPISchemaParser(fobj, previously_included, info)
351
352    def _pragma(self, name, value, info):
353        global doc_required, returns_whitelist, name_case_whitelist
354        if name == 'doc-required':
355            if not isinstance(value, bool):
356                raise QAPISemError(info,
357                                   "Pragma 'doc-required' must be boolean")
358            doc_required = value
359        elif name == 'returns-whitelist':
360            if (not isinstance(value, list)
361                    or any([not isinstance(elt, str) for elt in value])):
362                raise QAPISemError(info,
363                                   "Pragma returns-whitelist must be"
364                                   " a list of strings")
365            returns_whitelist = value
366        elif name == 'name-case-whitelist':
367            if (not isinstance(value, list)
368                    or any([not isinstance(elt, str) for elt in value])):
369                raise QAPISemError(info,
370                                   "Pragma name-case-whitelist must be"
371                                   " a list of strings")
372            name_case_whitelist = value
373        else:
374            raise QAPISemError(info, "Unknown pragma '%s'" % name)
375
376    def accept(self, skip_comment=True):
377        while True:
378            self.tok = self.src[self.cursor]
379            self.pos = self.cursor
380            self.cursor += 1
381            self.val = None
382
383            if self.tok == '#':
384                if self.src[self.cursor] == '#':
385                    # Start of doc comment
386                    skip_comment = False
387                self.cursor = self.src.find('\n', self.cursor)
388                if not skip_comment:
389                    self.val = self.src[self.pos:self.cursor]
390                    return
391            elif self.tok in '{}:,[]':
392                return
393            elif self.tok == "'":
394                string = ''
395                esc = False
396                while True:
397                    ch = self.src[self.cursor]
398                    self.cursor += 1
399                    if ch == '\n':
400                        raise QAPIParseError(self, 'Missing terminating "\'"')
401                    if esc:
402                        if ch == 'b':
403                            string += '\b'
404                        elif ch == 'f':
405                            string += '\f'
406                        elif ch == 'n':
407                            string += '\n'
408                        elif ch == 'r':
409                            string += '\r'
410                        elif ch == 't':
411                            string += '\t'
412                        elif ch == 'u':
413                            value = 0
414                            for _ in range(0, 4):
415                                ch = self.src[self.cursor]
416                                self.cursor += 1
417                                if ch not in '0123456789abcdefABCDEF':
418                                    raise QAPIParseError(self,
419                                                         '\\u escape needs 4 '
420                                                         'hex digits')
421                                value = (value << 4) + int(ch, 16)
422                            # If Python 2 and 3 didn't disagree so much on
423                            # how to handle Unicode, then we could allow
424                            # Unicode string defaults.  But most of QAPI is
425                            # ASCII-only, so we aren't losing much for now.
426                            if not value or value > 0x7f:
427                                raise QAPIParseError(self,
428                                                     'For now, \\u escape '
429                                                     'only supports non-zero '
430                                                     'values up to \\u007f')
431                            string += chr(value)
432                        elif ch in '\\/\'"':
433                            string += ch
434                        else:
435                            raise QAPIParseError(self,
436                                                 "Unknown escape \\%s" % ch)
437                        esc = False
438                    elif ch == '\\':
439                        esc = True
440                    elif ch == "'":
441                        self.val = string
442                        return
443                    else:
444                        string += ch
445            elif self.src.startswith('true', self.pos):
446                self.val = True
447                self.cursor += 3
448                return
449            elif self.src.startswith('false', self.pos):
450                self.val = False
451                self.cursor += 4
452                return
453            elif self.src.startswith('null', self.pos):
454                self.val = None
455                self.cursor += 3
456                return
457            elif self.tok == '\n':
458                if self.cursor == len(self.src):
459                    self.tok = None
460                    return
461                self.line += 1
462                self.line_pos = self.cursor
463            elif not self.tok.isspace():
464                raise QAPIParseError(self, 'Stray "%s"' % self.tok)
465
466    def get_members(self):
467        expr = OrderedDict()
468        if self.tok == '}':
469            self.accept()
470            return expr
471        if self.tok != "'":
472            raise QAPIParseError(self, 'Expected string or "}"')
473        while True:
474            key = self.val
475            self.accept()
476            if self.tok != ':':
477                raise QAPIParseError(self, 'Expected ":"')
478            self.accept()
479            if key in expr:
480                raise QAPIParseError(self, 'Duplicate key "%s"' % key)
481            expr[key] = self.get_expr(True)
482            if self.tok == '}':
483                self.accept()
484                return expr
485            if self.tok != ',':
486                raise QAPIParseError(self, 'Expected "," or "}"')
487            self.accept()
488            if self.tok != "'":
489                raise QAPIParseError(self, 'Expected string')
490
491    def get_values(self):
492        expr = []
493        if self.tok == ']':
494            self.accept()
495            return expr
496        if self.tok not in "{['tfn":
497            raise QAPIParseError(self, 'Expected "{", "[", "]", string, '
498                                 'boolean or "null"')
499        while True:
500            expr.append(self.get_expr(True))
501            if self.tok == ']':
502                self.accept()
503                return expr
504            if self.tok != ',':
505                raise QAPIParseError(self, 'Expected "," or "]"')
506            self.accept()
507
508    def get_expr(self, nested):
509        if self.tok != '{' and not nested:
510            raise QAPIParseError(self, 'Expected "{"')
511        if self.tok == '{':
512            self.accept()
513            expr = self.get_members()
514        elif self.tok == '[':
515            self.accept()
516            expr = self.get_values()
517        elif self.tok in "'tfn":
518            expr = self.val
519            self.accept()
520        else:
521            raise QAPIParseError(self, 'Expected "{", "[", string, '
522                                 'boolean or "null"')
523        return expr
524
525    def get_doc(self, info):
526        if self.val != '##':
527            raise QAPIParseError(self, "Junk after '##' at start of "
528                                 "documentation comment")
529
530        doc = QAPIDoc(self, info)
531        self.accept(False)
532        while self.tok == '#':
533            if self.val.startswith('##'):
534                # End of doc comment
535                if self.val != '##':
536                    raise QAPIParseError(self, "Junk after '##' at end of "
537                                         "documentation comment")
538                doc.end_comment()
539                self.accept()
540                return doc
541            else:
542                doc.append(self.val)
543            self.accept(False)
544
545        raise QAPIParseError(self, "Documentation comment must end with '##'")
546
547
548#
549# Semantic analysis of schema expressions
550# TODO fold into QAPISchema
551# TODO catching name collisions in generated code would be nice
552#
553
554
555def find_base_members(base):
556    if isinstance(base, dict):
557        return base
558    base_struct_define = struct_types.get(base)
559    if not base_struct_define:
560        return None
561    return base_struct_define['data']
562
563
564# Return the qtype of an alternate branch, or None on error.
565def find_alternate_member_qtype(qapi_type):
566    if qapi_type in builtin_types:
567        return builtin_types[qapi_type]
568    elif qapi_type in struct_types:
569        return 'QTYPE_QDICT'
570    elif qapi_type in enum_types:
571        return 'QTYPE_QSTRING'
572    elif qapi_type in union_types:
573        return 'QTYPE_QDICT'
574    return None
575
576
577# Return the discriminator enum define if discriminator is specified as an
578# enum type, otherwise return None.
579def discriminator_find_enum_define(expr):
580    base = expr.get('base')
581    discriminator = expr.get('discriminator')
582
583    if not (discriminator and base):
584        return None
585
586    base_members = find_base_members(base)
587    if not base_members:
588        return None
589
590    discriminator_type = base_members.get(discriminator)
591    if not discriminator_type:
592        return None
593
594    return enum_types.get(discriminator_type)
595
596
597# Names must be letters, numbers, -, and _.  They must start with letter,
598# except for downstream extensions which must start with __RFQDN_.
599# Dots are only valid in the downstream extension prefix.
600valid_name = re.compile(r'^(__[a-zA-Z0-9.-]+_)?'
601                        '[a-zA-Z][a-zA-Z0-9_-]*$')
602
603
604def check_name(info, source, name, allow_optional=False,
605               enum_member=False):
606    global valid_name
607    membername = name
608
609    if not isinstance(name, str):
610        raise QAPISemError(info, "%s requires a string name" % source)
611    if name.startswith('*'):
612        membername = name[1:]
613        if not allow_optional:
614            raise QAPISemError(info, "%s does not allow optional name '%s'"
615                               % (source, name))
616    # Enum members can start with a digit, because the generated C
617    # code always prefixes it with the enum name
618    if enum_member and membername[0].isdigit():
619        membername = 'D' + membername
620    # Reserve the entire 'q_' namespace for c_name(), and for 'q_empty'
621    # and 'q_obj_*' implicit type names.
622    if not valid_name.match(membername) or \
623       c_name(membername, False).startswith('q_'):
624        raise QAPISemError(info, "%s uses invalid name '%s'" % (source, name))
625
626
627def add_name(name, info, meta, implicit=False):
628    global all_names
629    check_name(info, "'%s'" % meta, name)
630    # FIXME should reject names that differ only in '_' vs. '.'
631    # vs. '-', because they're liable to clash in generated C.
632    if name in all_names:
633        raise QAPISemError(info, "%s '%s' is already defined"
634                           % (all_names[name], name))
635    if not implicit and (name.endswith('Kind') or name.endswith('List')):
636        raise QAPISemError(info, "%s '%s' should not end in '%s'"
637                           % (meta, name, name[-4:]))
638    all_names[name] = meta
639
640
641def check_if(expr, info):
642
643    def check_if_str(ifcond, info):
644        if not isinstance(ifcond, str):
645            raise QAPISemError(
646                info, "'if' condition must be a string or a list of strings")
647        if ifcond == '':
648            raise QAPISemError(info, "'if' condition '' makes no sense")
649
650    ifcond = expr.get('if')
651    if ifcond is None:
652        return
653    if isinstance(ifcond, list):
654        if ifcond == []:
655            raise QAPISemError(info, "'if' condition [] is useless")
656        for elt in ifcond:
657            check_if_str(elt, info)
658    else:
659        check_if_str(ifcond, info)
660
661
662def check_type(info, source, value, allow_array=False,
663               allow_dict=False, allow_optional=False,
664               allow_metas=[]):
665    global all_names
666
667    if value is None:
668        return
669
670    # Check if array type for value is okay
671    if isinstance(value, list):
672        if not allow_array:
673            raise QAPISemError(info, "%s cannot be an array" % source)
674        if len(value) != 1 or not isinstance(value[0], str):
675            raise QAPISemError(info,
676                               "%s: array type must contain single type name" %
677                               source)
678        value = value[0]
679
680    # Check if type name for value is okay
681    if isinstance(value, str):
682        if value not in all_names:
683            raise QAPISemError(info, "%s uses unknown type '%s'"
684                               % (source, value))
685        if not all_names[value] in allow_metas:
686            raise QAPISemError(info, "%s cannot use %s type '%s'" %
687                               (source, all_names[value], value))
688        return
689
690    if not allow_dict:
691        raise QAPISemError(info, "%s should be a type name" % source)
692
693    if not isinstance(value, OrderedDict):
694        raise QAPISemError(info,
695                           "%s should be a dictionary or type name" % source)
696
697    # value is a dictionary, check that each member is okay
698    for (key, arg) in value.items():
699        check_name(info, "Member of %s" % source, key,
700                   allow_optional=allow_optional)
701        if c_name(key, False) == 'u' or c_name(key, False).startswith('has_'):
702            raise QAPISemError(info, "Member of %s uses reserved name '%s'"
703                               % (source, key))
704        # Todo: allow dictionaries to represent default values of
705        # an optional argument.
706        check_type(info, "Member '%s' of %s" % (key, source), arg,
707                   allow_array=True,
708                   allow_metas=['built-in', 'union', 'alternate', 'struct',
709                                'enum'])
710
711
712def check_command(expr, info):
713    name = expr['command']
714    boxed = expr.get('boxed', False)
715
716    args_meta = ['struct']
717    if boxed:
718        args_meta += ['union', 'alternate']
719    check_type(info, "'data' for command '%s'" % name,
720               expr.get('data'), allow_dict=not boxed, allow_optional=True,
721               allow_metas=args_meta)
722    returns_meta = ['union', 'struct']
723    if name in returns_whitelist:
724        returns_meta += ['built-in', 'alternate', 'enum']
725    check_type(info, "'returns' for command '%s'" % name,
726               expr.get('returns'), allow_array=True,
727               allow_optional=True, allow_metas=returns_meta)
728
729
730def check_event(expr, info):
731    name = expr['event']
732    boxed = expr.get('boxed', False)
733
734    meta = ['struct']
735    if boxed:
736        meta += ['union', 'alternate']
737    check_type(info, "'data' for event '%s'" % name,
738               expr.get('data'), allow_dict=not boxed, allow_optional=True,
739               allow_metas=meta)
740
741
742def check_union(expr, info):
743    name = expr['union']
744    base = expr.get('base')
745    discriminator = expr.get('discriminator')
746    members = expr['data']
747
748    # Two types of unions, determined by discriminator.
749
750    # With no discriminator it is a simple union.
751    if discriminator is None:
752        enum_define = None
753        allow_metas = ['built-in', 'union', 'alternate', 'struct', 'enum']
754        if base is not None:
755            raise QAPISemError(info, "Simple union '%s' must not have a base" %
756                               name)
757
758    # Else, it's a flat union.
759    else:
760        # The object must have a string or dictionary 'base'.
761        check_type(info, "'base' for union '%s'" % name,
762                   base, allow_dict=True, allow_optional=True,
763                   allow_metas=['struct'])
764        if not base:
765            raise QAPISemError(info, "Flat union '%s' must have a base"
766                               % name)
767        base_members = find_base_members(base)
768        assert base_members is not None
769
770        # The value of member 'discriminator' must name a non-optional
771        # member of the base struct.
772        check_name(info, "Discriminator of flat union '%s'" % name,
773                   discriminator)
774        discriminator_type = base_members.get(discriminator)
775        if not discriminator_type:
776            raise QAPISemError(info,
777                               "Discriminator '%s' is not a member of base "
778                               "struct '%s'"
779                               % (discriminator, base))
780        enum_define = enum_types.get(discriminator_type)
781        allow_metas = ['struct']
782        # Do not allow string discriminator
783        if not enum_define:
784            raise QAPISemError(info,
785                               "Discriminator '%s' must be of enumeration "
786                               "type" % discriminator)
787
788    # Check every branch; don't allow an empty union
789    if len(members) == 0:
790        raise QAPISemError(info, "Union '%s' cannot have empty 'data'" % name)
791    for (key, value) in members.items():
792        check_name(info, "Member of union '%s'" % name, key)
793
794        # Each value must name a known type
795        check_type(info, "Member '%s' of union '%s'" % (key, name),
796                   value, allow_array=not base, allow_metas=allow_metas)
797
798        # If the discriminator names an enum type, then all members
799        # of 'data' must also be members of the enum type.
800        if enum_define:
801            if key not in enum_define['data']:
802                raise QAPISemError(info,
803                                   "Discriminator value '%s' is not found in "
804                                   "enum '%s'"
805                                   % (key, enum_define['enum']))
806
807
808def check_alternate(expr, info):
809    name = expr['alternate']
810    members = expr['data']
811    types_seen = {}
812
813    # Check every branch; require at least two branches
814    if len(members) < 2:
815        raise QAPISemError(info,
816                           "Alternate '%s' should have at least two branches "
817                           "in 'data'" % name)
818    for (key, value) in members.items():
819        check_name(info, "Member of alternate '%s'" % name, key)
820
821        # Ensure alternates have no type conflicts.
822        check_type(info, "Member '%s' of alternate '%s'" % (key, name),
823                   value,
824                   allow_metas=['built-in', 'union', 'struct', 'enum'])
825        qtype = find_alternate_member_qtype(value)
826        if not qtype:
827            raise QAPISemError(info, "Alternate '%s' member '%s' cannot use "
828                               "type '%s'" % (name, key, value))
829        conflicting = set([qtype])
830        if qtype == 'QTYPE_QSTRING':
831            enum_expr = enum_types.get(value)
832            if enum_expr:
833                for v in enum_expr['data']:
834                    if v in ['on', 'off']:
835                        conflicting.add('QTYPE_QBOOL')
836                    if re.match(r'[-+0-9.]', v): # lazy, could be tightened
837                        conflicting.add('QTYPE_QNUM')
838            else:
839                conflicting.add('QTYPE_QNUM')
840                conflicting.add('QTYPE_QBOOL')
841        for qt in conflicting:
842            if qt in types_seen:
843                raise QAPISemError(info, "Alternate '%s' member '%s' can't "
844                                   "be distinguished from member '%s'"
845                                   % (name, key, types_seen[qt]))
846            types_seen[qt] = key
847
848
849def check_enum(expr, info):
850    name = expr['enum']
851    members = expr.get('data')
852    prefix = expr.get('prefix')
853
854    if not isinstance(members, list):
855        raise QAPISemError(info,
856                           "Enum '%s' requires an array for 'data'" % name)
857    if prefix is not None and not isinstance(prefix, str):
858        raise QAPISemError(info,
859                           "Enum '%s' requires a string for 'prefix'" % name)
860    for member in members:
861        check_name(info, "Member of enum '%s'" % name, member,
862                   enum_member=True)
863
864
865def check_struct(expr, info):
866    name = expr['struct']
867    members = expr['data']
868
869    check_type(info, "'data' for struct '%s'" % name, members,
870               allow_dict=True, allow_optional=True)
871    check_type(info, "'base' for struct '%s'" % name, expr.get('base'),
872               allow_metas=['struct'])
873
874
875def check_keys(expr_elem, meta, required, optional=[]):
876    expr = expr_elem['expr']
877    info = expr_elem['info']
878    name = expr[meta]
879    if not isinstance(name, str):
880        raise QAPISemError(info, "'%s' key must have a string value" % meta)
881    required = required + [meta]
882    for (key, value) in expr.items():
883        if key not in required and key not in optional:
884            raise QAPISemError(info, "Unknown key '%s' in %s '%s'"
885                               % (key, meta, name))
886        if (key == 'gen' or key == 'success-response') and value is not False:
887            raise QAPISemError(info,
888                               "'%s' of %s '%s' should only use false value"
889                               % (key, meta, name))
890        if (key == 'boxed' or key == 'allow-oob' or
891            key == 'allow-preconfig') and value is not True:
892            raise QAPISemError(info,
893                               "'%s' of %s '%s' should only use true value"
894                               % (key, meta, name))
895        if key == 'if':
896            check_if(expr, info)
897    for key in required:
898        if key not in expr:
899            raise QAPISemError(info, "Key '%s' is missing from %s '%s'"
900                               % (key, meta, name))
901
902
903def check_exprs(exprs):
904    global all_names
905
906    # Populate name table with names of built-in types
907    for builtin in builtin_types.keys():
908        all_names[builtin] = 'built-in'
909
910    # Learn the types and check for valid expression keys
911    for expr_elem in exprs:
912        expr = expr_elem['expr']
913        info = expr_elem['info']
914        doc = expr_elem.get('doc')
915
916        if 'include' in expr:
917            continue
918
919        if not doc and doc_required:
920            raise QAPISemError(info,
921                               "Expression missing documentation comment")
922
923        if 'enum' in expr:
924            meta = 'enum'
925            check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix'])
926            enum_types[expr[meta]] = expr
927        elif 'union' in expr:
928            meta = 'union'
929            check_keys(expr_elem, 'union', ['data'],
930                       ['base', 'discriminator', 'if'])
931            union_types[expr[meta]] = expr
932        elif 'alternate' in expr:
933            meta = 'alternate'
934            check_keys(expr_elem, 'alternate', ['data'], ['if'])
935        elif 'struct' in expr:
936            meta = 'struct'
937            check_keys(expr_elem, 'struct', ['data'], ['base', 'if'])
938            struct_types[expr[meta]] = expr
939        elif 'command' in expr:
940            meta = 'command'
941            check_keys(expr_elem, 'command', [],
942                       ['data', 'returns', 'gen', 'success-response',
943                        'boxed', 'allow-oob', 'allow-preconfig', 'if'])
944        elif 'event' in expr:
945            meta = 'event'
946            check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if'])
947        else:
948            raise QAPISemError(expr_elem['info'],
949                               "Expression is missing metatype")
950        name = expr[meta]
951        add_name(name, info, meta)
952        if doc and doc.symbol != name:
953            raise QAPISemError(info, "Definition of '%s' follows documentation"
954                               " for '%s'" % (name, doc.symbol))
955
956    # Try again for hidden UnionKind enum
957    for expr_elem in exprs:
958        expr = expr_elem['expr']
959
960        if 'include' in expr:
961            continue
962        if 'union' in expr and not discriminator_find_enum_define(expr):
963            name = '%sKind' % expr['union']
964        elif 'alternate' in expr:
965            name = '%sKind' % expr['alternate']
966        else:
967            continue
968        enum_types[name] = {'enum': name}
969        add_name(name, info, 'enum', implicit=True)
970
971    # Validate that exprs make sense
972    for expr_elem in exprs:
973        expr = expr_elem['expr']
974        info = expr_elem['info']
975        doc = expr_elem.get('doc')
976
977        if 'include' in expr:
978            continue
979        if 'enum' in expr:
980            check_enum(expr, info)
981        elif 'union' in expr:
982            check_union(expr, info)
983        elif 'alternate' in expr:
984            check_alternate(expr, info)
985        elif 'struct' in expr:
986            check_struct(expr, info)
987        elif 'command' in expr:
988            check_command(expr, info)
989        elif 'event' in expr:
990            check_event(expr, info)
991        else:
992            assert False, 'unexpected meta type'
993
994        if doc:
995            doc.check_expr(expr)
996
997    return exprs
998
999
1000#
1001# Schema compiler frontend
1002#
1003
1004class QAPISchemaEntity(object):
1005    def __init__(self, name, info, doc):
1006        assert name is None or isinstance(name, str)
1007        self.name = name
1008        self.module = None
1009        # For explicitly defined entities, info points to the (explicit)
1010        # definition.  For builtins (and their arrays), info is None.
1011        # For implicitly defined entities, info points to a place that
1012        # triggered the implicit definition (there may be more than one
1013        # such place).
1014        self.info = info
1015        self.doc = doc
1016
1017    def c_name(self):
1018        return c_name(self.name)
1019
1020    def check(self, schema):
1021        pass
1022
1023    def is_implicit(self):
1024        return not self.info
1025
1026    def visit(self, visitor):
1027        pass
1028
1029
1030class QAPISchemaVisitor(object):
1031    def visit_begin(self, schema):
1032        pass
1033
1034    def visit_end(self):
1035        pass
1036
1037    def visit_module(self, fname):
1038        pass
1039
1040    def visit_needed(self, entity):
1041        # Default to visiting everything
1042        return True
1043
1044    def visit_include(self, fname, info):
1045        pass
1046
1047    def visit_builtin_type(self, name, info, json_type):
1048        pass
1049
1050    def visit_enum_type(self, name, info, values, prefix):
1051        pass
1052
1053    def visit_array_type(self, name, info, element_type):
1054        pass
1055
1056    def visit_object_type(self, name, info, base, members, variants):
1057        pass
1058
1059    def visit_object_type_flat(self, name, info, members, variants):
1060        pass
1061
1062    def visit_alternate_type(self, name, info, variants):
1063        pass
1064
1065    def visit_command(self, name, info, arg_type, ret_type, gen,
1066                      success_response, boxed, allow_oob, allow_preconfig):
1067        pass
1068
1069    def visit_event(self, name, info, arg_type, boxed):
1070        pass
1071
1072
1073class QAPISchemaInclude(QAPISchemaEntity):
1074
1075    def __init__(self, fname, info):
1076        QAPISchemaEntity.__init__(self, None, info, None)
1077        self.fname = fname
1078
1079    def visit(self, visitor):
1080        visitor.visit_include(self.fname, self.info)
1081
1082
1083class QAPISchemaType(QAPISchemaEntity):
1084    # Return the C type for common use.
1085    # For the types we commonly box, this is a pointer type.
1086    def c_type(self):
1087        pass
1088
1089    # Return the C type to be used in a parameter list.
1090    def c_param_type(self):
1091        return self.c_type()
1092
1093    # Return the C type to be used where we suppress boxing.
1094    def c_unboxed_type(self):
1095        return self.c_type()
1096
1097    def json_type(self):
1098        pass
1099
1100    def alternate_qtype(self):
1101        json2qtype = {
1102            'null':    'QTYPE_QNULL',
1103            'string':  'QTYPE_QSTRING',
1104            'number':  'QTYPE_QNUM',
1105            'int':     'QTYPE_QNUM',
1106            'boolean': 'QTYPE_QBOOL',
1107            'object':  'QTYPE_QDICT'
1108        }
1109        return json2qtype.get(self.json_type())
1110
1111    def doc_type(self):
1112        if self.is_implicit():
1113            return None
1114        return self.name
1115
1116
1117class QAPISchemaBuiltinType(QAPISchemaType):
1118    def __init__(self, name, json_type, c_type):
1119        QAPISchemaType.__init__(self, name, None, None)
1120        assert not c_type or isinstance(c_type, str)
1121        assert json_type in ('string', 'number', 'int', 'boolean', 'null',
1122                             'value')
1123        self._json_type_name = json_type
1124        self._c_type_name = c_type
1125
1126    def c_name(self):
1127        return self.name
1128
1129    def c_type(self):
1130        return self._c_type_name
1131
1132    def c_param_type(self):
1133        if self.name == 'str':
1134            return 'const ' + self._c_type_name
1135        return self._c_type_name
1136
1137    def json_type(self):
1138        return self._json_type_name
1139
1140    def doc_type(self):
1141        return self.json_type()
1142
1143    def visit(self, visitor):
1144        visitor.visit_builtin_type(self.name, self.info, self.json_type())
1145
1146
1147class QAPISchemaEnumType(QAPISchemaType):
1148    def __init__(self, name, info, doc, values, prefix):
1149        QAPISchemaType.__init__(self, name, info, doc)
1150        for v in values:
1151            assert isinstance(v, QAPISchemaMember)
1152            v.set_owner(name)
1153        assert prefix is None or isinstance(prefix, str)
1154        self.values = values
1155        self.prefix = prefix
1156
1157    def check(self, schema):
1158        seen = {}
1159        for v in self.values:
1160            v.check_clash(self.info, seen)
1161            if self.doc:
1162                self.doc.connect_member(v)
1163
1164    def is_implicit(self):
1165        # See QAPISchema._make_implicit_enum_type() and ._def_predefineds()
1166        return self.name.endswith('Kind') or self.name == 'QType'
1167
1168    def c_type(self):
1169        return c_name(self.name)
1170
1171    def member_names(self):
1172        return [v.name for v in self.values]
1173
1174    def json_type(self):
1175        return 'string'
1176
1177    def visit(self, visitor):
1178        visitor.visit_enum_type(self.name, self.info,
1179                                self.member_names(), self.prefix)
1180
1181
1182class QAPISchemaArrayType(QAPISchemaType):
1183    def __init__(self, name, info, element_type):
1184        QAPISchemaType.__init__(self, name, info, None)
1185        assert isinstance(element_type, str)
1186        self._element_type_name = element_type
1187        self.element_type = None
1188
1189    def check(self, schema):
1190        self.element_type = schema.lookup_type(self._element_type_name)
1191        assert self.element_type
1192
1193    def is_implicit(self):
1194        return True
1195
1196    def c_type(self):
1197        return c_name(self.name) + pointer_suffix
1198
1199    def json_type(self):
1200        return 'array'
1201
1202    def doc_type(self):
1203        elt_doc_type = self.element_type.doc_type()
1204        if not elt_doc_type:
1205            return None
1206        return 'array of ' + elt_doc_type
1207
1208    def visit(self, visitor):
1209        visitor.visit_array_type(self.name, self.info, self.element_type)
1210
1211
1212class QAPISchemaObjectType(QAPISchemaType):
1213    def __init__(self, name, info, doc, base, local_members, variants):
1214        # struct has local_members, optional base, and no variants
1215        # flat union has base, variants, and no local_members
1216        # simple union has local_members, variants, and no base
1217        QAPISchemaType.__init__(self, name, info, doc)
1218        assert base is None or isinstance(base, str)
1219        for m in local_members:
1220            assert isinstance(m, QAPISchemaObjectTypeMember)
1221            m.set_owner(name)
1222        if variants is not None:
1223            assert isinstance(variants, QAPISchemaObjectTypeVariants)
1224            variants.set_owner(name)
1225        self._base_name = base
1226        self.base = None
1227        self.local_members = local_members
1228        self.variants = variants
1229        self.members = None
1230
1231    def check(self, schema):
1232        if self.members is False:               # check for cycles
1233            raise QAPISemError(self.info,
1234                               "Object %s contains itself" % self.name)
1235        if self.members:
1236            return
1237        self.members = False                    # mark as being checked
1238        seen = OrderedDict()
1239        if self._base_name:
1240            self.base = schema.lookup_type(self._base_name)
1241            assert isinstance(self.base, QAPISchemaObjectType)
1242            self.base.check(schema)
1243            self.base.check_clash(self.info, seen)
1244        for m in self.local_members:
1245            m.check(schema)
1246            m.check_clash(self.info, seen)
1247            if self.doc:
1248                self.doc.connect_member(m)
1249        self.members = seen.values()
1250        if self.variants:
1251            self.variants.check(schema, seen)
1252            assert self.variants.tag_member in self.members
1253            self.variants.check_clash(self.info, seen)
1254        if self.doc:
1255            self.doc.check()
1256
1257    # Check that the members of this type do not cause duplicate JSON members,
1258    # and update seen to track the members seen so far. Report any errors
1259    # on behalf of info, which is not necessarily self.info
1260    def check_clash(self, info, seen):
1261        assert not self.variants       # not implemented
1262        for m in self.members:
1263            m.check_clash(info, seen)
1264
1265    def is_implicit(self):
1266        # See QAPISchema._make_implicit_object_type(), as well as
1267        # _def_predefineds()
1268        return self.name.startswith('q_')
1269
1270    def is_empty(self):
1271        assert self.members is not None
1272        return not self.members and not self.variants
1273
1274    def c_name(self):
1275        assert self.name != 'q_empty'
1276        return QAPISchemaType.c_name(self)
1277
1278    def c_type(self):
1279        assert not self.is_implicit()
1280        return c_name(self.name) + pointer_suffix
1281
1282    def c_unboxed_type(self):
1283        return c_name(self.name)
1284
1285    def json_type(self):
1286        return 'object'
1287
1288    def visit(self, visitor):
1289        visitor.visit_object_type(self.name, self.info,
1290                                  self.base, self.local_members, self.variants)
1291        visitor.visit_object_type_flat(self.name, self.info,
1292                                       self.members, self.variants)
1293
1294
1295class QAPISchemaMember(object):
1296    role = 'member'
1297
1298    def __init__(self, name):
1299        assert isinstance(name, str)
1300        self.name = name
1301        self.owner = None
1302
1303    def set_owner(self, name):
1304        assert not self.owner
1305        self.owner = name
1306
1307    def check_clash(self, info, seen):
1308        cname = c_name(self.name)
1309        if cname.lower() != cname and self.owner not in name_case_whitelist:
1310            raise QAPISemError(info,
1311                               "%s should not use uppercase" % self.describe())
1312        if cname in seen:
1313            raise QAPISemError(info, "%s collides with %s" %
1314                               (self.describe(), seen[cname].describe()))
1315        seen[cname] = self
1316
1317    def _pretty_owner(self):
1318        owner = self.owner
1319        if owner.startswith('q_obj_'):
1320            # See QAPISchema._make_implicit_object_type() - reverse the
1321            # mapping there to create a nice human-readable description
1322            owner = owner[6:]
1323            if owner.endswith('-arg'):
1324                return '(parameter of %s)' % owner[:-4]
1325            elif owner.endswith('-base'):
1326                return '(base of %s)' % owner[:-5]
1327            else:
1328                assert owner.endswith('-wrapper')
1329                # Unreachable and not implemented
1330                assert False
1331        if owner.endswith('Kind'):
1332            # See QAPISchema._make_implicit_enum_type()
1333            return '(branch of %s)' % owner[:-4]
1334        return '(%s of %s)' % (self.role, owner)
1335
1336    def describe(self):
1337        return "'%s' %s" % (self.name, self._pretty_owner())
1338
1339
1340class QAPISchemaObjectTypeMember(QAPISchemaMember):
1341    def __init__(self, name, typ, optional):
1342        QAPISchemaMember.__init__(self, name)
1343        assert isinstance(typ, str)
1344        assert isinstance(optional, bool)
1345        self._type_name = typ
1346        self.type = None
1347        self.optional = optional
1348
1349    def check(self, schema):
1350        assert self.owner
1351        self.type = schema.lookup_type(self._type_name)
1352        assert self.type
1353
1354
1355class QAPISchemaObjectTypeVariants(object):
1356    def __init__(self, tag_name, tag_member, variants):
1357        # Flat unions pass tag_name but not tag_member.
1358        # Simple unions and alternates pass tag_member but not tag_name.
1359        # After check(), tag_member is always set, and tag_name remains
1360        # a reliable witness of being used by a flat union.
1361        assert bool(tag_member) != bool(tag_name)
1362        assert (isinstance(tag_name, str) or
1363                isinstance(tag_member, QAPISchemaObjectTypeMember))
1364        assert len(variants) > 0
1365        for v in variants:
1366            assert isinstance(v, QAPISchemaObjectTypeVariant)
1367        self._tag_name = tag_name
1368        self.tag_member = tag_member
1369        self.variants = variants
1370
1371    def set_owner(self, name):
1372        for v in self.variants:
1373            v.set_owner(name)
1374
1375    def check(self, schema, seen):
1376        if not self.tag_member:    # flat union
1377            self.tag_member = seen[c_name(self._tag_name)]
1378            assert self._tag_name == self.tag_member.name
1379        assert isinstance(self.tag_member.type, QAPISchemaEnumType)
1380        if self._tag_name:    # flat union
1381            # branches that are not explicitly covered get an empty type
1382            cases = set([v.name for v in self.variants])
1383            for val in self.tag_member.type.values:
1384                if val.name not in cases:
1385                    v = QAPISchemaObjectTypeVariant(val.name, 'q_empty')
1386                    v.set_owner(self.tag_member.owner)
1387                    self.variants.append(v)
1388        for v in self.variants:
1389            v.check(schema)
1390            # Union names must match enum values; alternate names are
1391            # checked separately. Use 'seen' to tell the two apart.
1392            if seen:
1393                assert v.name in self.tag_member.type.member_names()
1394                assert isinstance(v.type, QAPISchemaObjectType)
1395                v.type.check(schema)
1396
1397    def check_clash(self, info, seen):
1398        for v in self.variants:
1399            # Reset seen map for each variant, since qapi names from one
1400            # branch do not affect another branch
1401            assert isinstance(v.type, QAPISchemaObjectType)
1402            v.type.check_clash(info, dict(seen))
1403
1404
1405class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember):
1406    role = 'branch'
1407
1408    def __init__(self, name, typ):
1409        QAPISchemaObjectTypeMember.__init__(self, name, typ, False)
1410
1411
1412class QAPISchemaAlternateType(QAPISchemaType):
1413    def __init__(self, name, info, doc, variants):
1414        QAPISchemaType.__init__(self, name, info, doc)
1415        assert isinstance(variants, QAPISchemaObjectTypeVariants)
1416        assert variants.tag_member
1417        variants.set_owner(name)
1418        variants.tag_member.set_owner(self.name)
1419        self.variants = variants
1420
1421    def check(self, schema):
1422        self.variants.tag_member.check(schema)
1423        # Not calling self.variants.check_clash(), because there's nothing
1424        # to clash with
1425        self.variants.check(schema, {})
1426        # Alternate branch names have no relation to the tag enum values;
1427        # so we have to check for potential name collisions ourselves.
1428        seen = {}
1429        for v in self.variants.variants:
1430            v.check_clash(self.info, seen)
1431            if self.doc:
1432                self.doc.connect_member(v)
1433        if self.doc:
1434            self.doc.check()
1435
1436    def c_type(self):
1437        return c_name(self.name) + pointer_suffix
1438
1439    def json_type(self):
1440        return 'value'
1441
1442    def visit(self, visitor):
1443        visitor.visit_alternate_type(self.name, self.info, self.variants)
1444
1445    def is_empty(self):
1446        return False
1447
1448
1449class QAPISchemaCommand(QAPISchemaEntity):
1450    def __init__(self, name, info, doc, arg_type, ret_type,
1451                 gen, success_response, boxed, allow_oob, allow_preconfig):
1452        QAPISchemaEntity.__init__(self, name, info, doc)
1453        assert not arg_type or isinstance(arg_type, str)
1454        assert not ret_type or isinstance(ret_type, str)
1455        self._arg_type_name = arg_type
1456        self.arg_type = None
1457        self._ret_type_name = ret_type
1458        self.ret_type = None
1459        self.gen = gen
1460        self.success_response = success_response
1461        self.boxed = boxed
1462        self.allow_oob = allow_oob
1463        self.allow_preconfig = allow_preconfig
1464
1465    def check(self, schema):
1466        if self._arg_type_name:
1467            self.arg_type = schema.lookup_type(self._arg_type_name)
1468            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1469                    isinstance(self.arg_type, QAPISchemaAlternateType))
1470            self.arg_type.check(schema)
1471            if self.boxed:
1472                if self.arg_type.is_empty():
1473                    raise QAPISemError(self.info,
1474                                       "Cannot use 'boxed' with empty type")
1475            else:
1476                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1477                assert not self.arg_type.variants
1478        elif self.boxed:
1479            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1480        if self._ret_type_name:
1481            self.ret_type = schema.lookup_type(self._ret_type_name)
1482            assert isinstance(self.ret_type, QAPISchemaType)
1483
1484    def visit(self, visitor):
1485        visitor.visit_command(self.name, self.info,
1486                              self.arg_type, self.ret_type,
1487                              self.gen, self.success_response,
1488                              self.boxed, self.allow_oob,
1489                              self.allow_preconfig)
1490
1491
1492class QAPISchemaEvent(QAPISchemaEntity):
1493    def __init__(self, name, info, doc, arg_type, boxed):
1494        QAPISchemaEntity.__init__(self, name, info, doc)
1495        assert not arg_type or isinstance(arg_type, str)
1496        self._arg_type_name = arg_type
1497        self.arg_type = None
1498        self.boxed = boxed
1499
1500    def check(self, schema):
1501        if self._arg_type_name:
1502            self.arg_type = schema.lookup_type(self._arg_type_name)
1503            assert (isinstance(self.arg_type, QAPISchemaObjectType) or
1504                    isinstance(self.arg_type, QAPISchemaAlternateType))
1505            self.arg_type.check(schema)
1506            if self.boxed:
1507                if self.arg_type.is_empty():
1508                    raise QAPISemError(self.info,
1509                                       "Cannot use 'boxed' with empty type")
1510            else:
1511                assert not isinstance(self.arg_type, QAPISchemaAlternateType)
1512                assert not self.arg_type.variants
1513        elif self.boxed:
1514            raise QAPISemError(self.info, "Use of 'boxed' requires 'data'")
1515
1516    def visit(self, visitor):
1517        visitor.visit_event(self.name, self.info, self.arg_type, self.boxed)
1518
1519
1520class QAPISchema(object):
1521    def __init__(self, fname):
1522        self._fname = fname
1523        if sys.version_info[0] >= 3:
1524            f = open(fname, 'r', encoding='utf-8')
1525        else:
1526            f = open(fname, 'r')
1527        parser = QAPISchemaParser(f)
1528        exprs = check_exprs(parser.exprs)
1529        self.docs = parser.docs
1530        self._entity_list = []
1531        self._entity_dict = {}
1532        self._predefining = True
1533        self._def_predefineds()
1534        self._predefining = False
1535        self._def_exprs(exprs)
1536        self.check()
1537
1538    def _def_entity(self, ent):
1539        # Only the predefined types are allowed to not have info
1540        assert ent.info or self._predefining
1541        assert ent.name is None or ent.name not in self._entity_dict
1542        self._entity_list.append(ent)
1543        if ent.name is not None:
1544            self._entity_dict[ent.name] = ent
1545        if ent.info:
1546            ent.module = os.path.relpath(ent.info['file'],
1547                                         os.path.dirname(self._fname))
1548
1549    def lookup_entity(self, name, typ=None):
1550        ent = self._entity_dict.get(name)
1551        if typ and not isinstance(ent, typ):
1552            return None
1553        return ent
1554
1555    def lookup_type(self, name):
1556        return self.lookup_entity(name, QAPISchemaType)
1557
1558    def _def_include(self, expr, info, doc):
1559        include = expr['include']
1560        assert doc is None
1561        main_info = info
1562        while main_info['parent']:
1563            main_info = main_info['parent']
1564        fname = os.path.relpath(include, os.path.dirname(main_info['file']))
1565        self._def_entity(QAPISchemaInclude(fname, info))
1566
1567    def _def_builtin_type(self, name, json_type, c_type):
1568        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
1569        # Instantiating only the arrays that are actually used would
1570        # be nice, but we can't as long as their generated code
1571        # (qapi-builtin-types.[ch]) may be shared by some other
1572        # schema.
1573        self._make_array_type(name, None)
1574
1575    def _def_predefineds(self):
1576        for t in [('str',    'string',  'char' + pointer_suffix),
1577                  ('number', 'number',  'double'),
1578                  ('int',    'int',     'int64_t'),
1579                  ('int8',   'int',     'int8_t'),
1580                  ('int16',  'int',     'int16_t'),
1581                  ('int32',  'int',     'int32_t'),
1582                  ('int64',  'int',     'int64_t'),
1583                  ('uint8',  'int',     'uint8_t'),
1584                  ('uint16', 'int',     'uint16_t'),
1585                  ('uint32', 'int',     'uint32_t'),
1586                  ('uint64', 'int',     'uint64_t'),
1587                  ('size',   'int',     'uint64_t'),
1588                  ('bool',   'boolean', 'bool'),
1589                  ('any',    'value',   'QObject' + pointer_suffix),
1590                  ('null',   'null',    'QNull' + pointer_suffix)]:
1591            self._def_builtin_type(*t)
1592        self.the_empty_object_type = QAPISchemaObjectType(
1593            'q_empty', None, None, None, [], None)
1594        self._def_entity(self.the_empty_object_type)
1595        qtype_values = self._make_enum_members(['none', 'qnull', 'qnum',
1596                                                'qstring', 'qdict', 'qlist',
1597                                                'qbool'])
1598        self._def_entity(QAPISchemaEnumType('QType', None, None,
1599                                            qtype_values, 'QTYPE'))
1600
1601    def _make_enum_members(self, values):
1602        return [QAPISchemaMember(v) for v in values]
1603
1604    def _make_implicit_enum_type(self, name, info, values):
1605        # See also QAPISchemaObjectTypeMember._pretty_owner()
1606        name = name + 'Kind'   # Use namespace reserved by add_name()
1607        self._def_entity(QAPISchemaEnumType(
1608            name, info, None, self._make_enum_members(values), None))
1609        return name
1610
1611    def _make_array_type(self, element_type, info):
1612        name = element_type + 'List'   # Use namespace reserved by add_name()
1613        if not self.lookup_type(name):
1614            self._def_entity(QAPISchemaArrayType(name, info, element_type))
1615        return name
1616
1617    def _make_implicit_object_type(self, name, info, doc, role, members):
1618        if not members:
1619            return None
1620        # See also QAPISchemaObjectTypeMember._pretty_owner()
1621        name = 'q_obj_%s-%s' % (name, role)
1622        if not self.lookup_entity(name, QAPISchemaObjectType):
1623            self._def_entity(QAPISchemaObjectType(name, info, doc, None,
1624                                                  members, None))
1625        return name
1626
1627    def _def_enum_type(self, expr, info, doc):
1628        name = expr['enum']
1629        data = expr['data']
1630        prefix = expr.get('prefix')
1631        self._def_entity(QAPISchemaEnumType(
1632            name, info, doc, self._make_enum_members(data), prefix))
1633
1634    def _make_member(self, name, typ, info):
1635        optional = False
1636        if name.startswith('*'):
1637            name = name[1:]
1638            optional = True
1639        if isinstance(typ, list):
1640            assert len(typ) == 1
1641            typ = self._make_array_type(typ[0], info)
1642        return QAPISchemaObjectTypeMember(name, typ, optional)
1643
1644    def _make_members(self, data, info):
1645        return [self._make_member(key, value, info)
1646                for (key, value) in data.items()]
1647
1648    def _def_struct_type(self, expr, info, doc):
1649        name = expr['struct']
1650        base = expr.get('base')
1651        data = expr['data']
1652        self._def_entity(QAPISchemaObjectType(name, info, doc, base,
1653                                              self._make_members(data, info),
1654                                              None))
1655
1656    def _make_variant(self, case, typ):
1657        return QAPISchemaObjectTypeVariant(case, typ)
1658
1659    def _make_simple_variant(self, case, typ, info):
1660        if isinstance(typ, list):
1661            assert len(typ) == 1
1662            typ = self._make_array_type(typ[0], info)
1663        typ = self._make_implicit_object_type(
1664            typ, info, None, 'wrapper', [self._make_member('data', typ, info)])
1665        return QAPISchemaObjectTypeVariant(case, typ)
1666
1667    def _def_union_type(self, expr, info, doc):
1668        name = expr['union']
1669        data = expr['data']
1670        base = expr.get('base')
1671        tag_name = expr.get('discriminator')
1672        tag_member = None
1673        if isinstance(base, dict):
1674            base = (self._make_implicit_object_type(
1675                name, info, doc, 'base', self._make_members(base, info)))
1676        if tag_name:
1677            variants = [self._make_variant(key, value)
1678                        for (key, value) in data.items()]
1679            members = []
1680        else:
1681            variants = [self._make_simple_variant(key, value, info)
1682                        for (key, value) in data.items()]
1683            typ = self._make_implicit_enum_type(name, info,
1684                                                [v.name for v in variants])
1685            tag_member = QAPISchemaObjectTypeMember('type', typ, False)
1686            members = [tag_member]
1687        self._def_entity(
1688            QAPISchemaObjectType(name, info, doc, base, members,
1689                                 QAPISchemaObjectTypeVariants(tag_name,
1690                                                              tag_member,
1691                                                              variants)))
1692
1693    def _def_alternate_type(self, expr, info, doc):
1694        name = expr['alternate']
1695        data = expr['data']
1696        variants = [self._make_variant(key, value)
1697                    for (key, value) in data.items()]
1698        tag_member = QAPISchemaObjectTypeMember('type', 'QType', False)
1699        self._def_entity(
1700            QAPISchemaAlternateType(name, info, doc,
1701                                    QAPISchemaObjectTypeVariants(None,
1702                                                                 tag_member,
1703                                                                 variants)))
1704
1705    def _def_command(self, expr, info, doc):
1706        name = expr['command']
1707        data = expr.get('data')
1708        rets = expr.get('returns')
1709        gen = expr.get('gen', True)
1710        success_response = expr.get('success-response', True)
1711        boxed = expr.get('boxed', False)
1712        allow_oob = expr.get('allow-oob', False)
1713        allow_preconfig = expr.get('allow-preconfig', False)
1714        if isinstance(data, OrderedDict):
1715            data = self._make_implicit_object_type(
1716                name, info, doc, 'arg', self._make_members(data, info))
1717        if isinstance(rets, list):
1718            assert len(rets) == 1
1719            rets = self._make_array_type(rets[0], info)
1720        self._def_entity(QAPISchemaCommand(name, info, doc, data, rets,
1721                                           gen, success_response,
1722                                           boxed, allow_oob, allow_preconfig))
1723
1724    def _def_event(self, expr, info, doc):
1725        name = expr['event']
1726        data = expr.get('data')
1727        boxed = expr.get('boxed', False)
1728        if isinstance(data, OrderedDict):
1729            data = self._make_implicit_object_type(
1730                name, info, doc, 'arg', self._make_members(data, info))
1731        self._def_entity(QAPISchemaEvent(name, info, doc, data, boxed))
1732
1733    def _def_exprs(self, exprs):
1734        for expr_elem in exprs:
1735            expr = expr_elem['expr']
1736            info = expr_elem['info']
1737            doc = expr_elem.get('doc')
1738            if 'enum' in expr:
1739                self._def_enum_type(expr, info, doc)
1740            elif 'struct' in expr:
1741                self._def_struct_type(expr, info, doc)
1742            elif 'union' in expr:
1743                self._def_union_type(expr, info, doc)
1744            elif 'alternate' in expr:
1745                self._def_alternate_type(expr, info, doc)
1746            elif 'command' in expr:
1747                self._def_command(expr, info, doc)
1748            elif 'event' in expr:
1749                self._def_event(expr, info, doc)
1750            elif 'include' in expr:
1751                self._def_include(expr, info, doc)
1752            else:
1753                assert False
1754
1755    def check(self):
1756        for ent in self._entity_list:
1757            ent.check(self)
1758
1759    def visit(self, visitor):
1760        visitor.visit_begin(self)
1761        module = None
1762        for entity in self._entity_list:
1763            if visitor.visit_needed(entity):
1764                if entity.module != module:
1765                    module = entity.module
1766                    visitor.visit_module(module)
1767                entity.visit(visitor)
1768        visitor.visit_end()
1769
1770
1771#
1772# Code generation helpers
1773#
1774
1775def camel_case(name):
1776    new_name = ''
1777    first = True
1778    for ch in name:
1779        if ch in ['_', '-']:
1780            first = True
1781        elif first:
1782            new_name += ch.upper()
1783            first = False
1784        else:
1785            new_name += ch.lower()
1786    return new_name
1787
1788
1789# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1790# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1791# ENUM24_Name -> ENUM24_NAME
1792def camel_to_upper(value):
1793    c_fun_str = c_name(value, False)
1794    if value.isupper():
1795        return c_fun_str
1796
1797    new_name = ''
1798    l = len(c_fun_str)
1799    for i in range(l):
1800        c = c_fun_str[i]
1801        # When c is upper and no '_' appears before, do more checks
1802        if c.isupper() and (i > 0) and c_fun_str[i - 1] != '_':
1803            if i < l - 1 and c_fun_str[i + 1].islower():
1804                new_name += '_'
1805            elif c_fun_str[i - 1].isdigit():
1806                new_name += '_'
1807        new_name += c
1808    return new_name.lstrip('_').upper()
1809
1810
1811def c_enum_const(type_name, const_name, prefix=None):
1812    if prefix is not None:
1813        type_name = prefix
1814    return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper()
1815
1816if hasattr(str, 'maketrans'):
1817    c_name_trans = str.maketrans('.-', '__')
1818else:
1819    c_name_trans = string.maketrans('.-', '__')
1820
1821
1822# Map @name to a valid C identifier.
1823# If @protect, avoid returning certain ticklish identifiers (like
1824# C keywords) by prepending 'q_'.
1825#
1826# Used for converting 'name' from a 'name':'type' qapi definition
1827# into a generated struct member, as well as converting type names
1828# into substrings of a generated C function name.
1829# '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1830# protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1831def c_name(name, protect=True):
1832    # ANSI X3J11/88-090, 3.1.1
1833    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
1834                     'default', 'do', 'double', 'else', 'enum', 'extern',
1835                     'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1836                     'return', 'short', 'signed', 'sizeof', 'static',
1837                     'struct', 'switch', 'typedef', 'union', 'unsigned',
1838                     'void', 'volatile', 'while'])
1839    # ISO/IEC 9899:1999, 6.4.1
1840    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1841    # ISO/IEC 9899:2011, 6.4.1
1842    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1843                     '_Noreturn', '_Static_assert', '_Thread_local'])
1844    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1845    # excluding _.*
1846    gcc_words = set(['asm', 'typeof'])
1847    # C++ ISO/IEC 14882:2003 2.11
1848    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
1849                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1850                     'namespace', 'new', 'operator', 'private', 'protected',
1851                     'public', 'reinterpret_cast', 'static_cast', 'template',
1852                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
1853                     'using', 'virtual', 'wchar_t',
1854                     # alternative representations
1855                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1856                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1857    # namespace pollution:
1858    polluted_words = set(['unix', 'errno', 'mips', 'sparc', 'i386'])
1859    name = name.translate(c_name_trans)
1860    if protect and (name in c89_words | c99_words | c11_words | gcc_words
1861                    | cpp_words | polluted_words):
1862        return 'q_' + name
1863    return name
1864
1865eatspace = '\033EATSPACE.'
1866pointer_suffix = ' *' + eatspace
1867
1868
1869def genindent(count):
1870    ret = ''
1871    for _ in range(count):
1872        ret += ' '
1873    return ret
1874
1875indent_level = 0
1876
1877
1878def push_indent(indent_amount=4):
1879    global indent_level
1880    indent_level += indent_amount
1881
1882
1883def pop_indent(indent_amount=4):
1884    global indent_level
1885    indent_level -= indent_amount
1886
1887
1888# Generate @code with @kwds interpolated.
1889# Obey indent_level, and strip eatspace.
1890def cgen(code, **kwds):
1891    raw = code % kwds
1892    if indent_level:
1893        indent = genindent(indent_level)
1894        # re.subn() lacks flags support before Python 2.7, use re.compile()
1895        raw = re.subn(re.compile(r'^.', re.MULTILINE),
1896                      indent + r'\g<0>', raw)
1897        raw = raw[0]
1898    return re.sub(re.escape(eatspace) + r' *', '', raw)
1899
1900
1901def mcgen(code, **kwds):
1902    if code[0] == '\n':
1903        code = code[1:]
1904    return cgen(code, **kwds)
1905
1906
1907def guardname(filename):
1908    return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper()
1909
1910
1911def guardstart(name):
1912    return mcgen('''
1913#ifndef %(name)s
1914#define %(name)s
1915
1916''',
1917                 name=guardname(name))
1918
1919
1920def guardend(name):
1921    return mcgen('''
1922
1923#endif /* %(name)s */
1924''',
1925                 name=guardname(name))
1926
1927
1928def gen_enum_lookup(name, values, prefix=None):
1929    ret = mcgen('''
1930
1931const QEnumLookup %(c_name)s_lookup = {
1932    .array = (const char *const[]) {
1933''',
1934                c_name=c_name(name))
1935    for value in values:
1936        index = c_enum_const(name, value, prefix)
1937        ret += mcgen('''
1938        [%(index)s] = "%(value)s",
1939''',
1940                     index=index, value=value)
1941
1942    ret += mcgen('''
1943    },
1944    .size = %(max_index)s
1945};
1946''',
1947                 max_index=c_enum_const(name, '_MAX', prefix))
1948    return ret
1949
1950
1951def gen_enum(name, values, prefix=None):
1952    # append automatically generated _MAX value
1953    enum_values = values + ['_MAX']
1954
1955    ret = mcgen('''
1956
1957typedef enum %(c_name)s {
1958''',
1959                c_name=c_name(name))
1960
1961    i = 0
1962    for value in enum_values:
1963        ret += mcgen('''
1964    %(c_enum)s = %(i)d,
1965''',
1966                     c_enum=c_enum_const(name, value, prefix),
1967                     i=i)
1968        i += 1
1969
1970    ret += mcgen('''
1971} %(c_name)s;
1972''',
1973                 c_name=c_name(name))
1974
1975    ret += mcgen('''
1976
1977#define %(c_name)s_str(val) \\
1978    qapi_enum_lookup(&%(c_name)s_lookup, (val))
1979
1980extern const QEnumLookup %(c_name)s_lookup;
1981''',
1982                 c_name=c_name(name))
1983    return ret
1984
1985
1986def build_params(arg_type, boxed, extra):
1987    if not arg_type:
1988        assert not boxed
1989        return extra
1990    ret = ''
1991    sep = ''
1992    if boxed:
1993        ret += '%s arg' % arg_type.c_param_type()
1994        sep = ', '
1995    else:
1996        assert not arg_type.variants
1997        for memb in arg_type.members:
1998            ret += sep
1999            sep = ', '
2000            if memb.optional:
2001                ret += 'bool has_%s, ' % c_name(memb.name)
2002            ret += '%s %s' % (memb.type.c_param_type(),
2003                              c_name(memb.name))
2004    if extra:
2005        ret += sep + extra
2006    return ret
2007
2008
2009#
2010# Accumulate and write output
2011#
2012
2013class QAPIGen(object):
2014
2015    def __init__(self):
2016        self._preamble = ''
2017        self._body = ''
2018
2019    def preamble_add(self, text):
2020        self._preamble += text
2021
2022    def add(self, text):
2023        self._body += text
2024
2025    def _top(self, fname):
2026        return ''
2027
2028    def _bottom(self, fname):
2029        return ''
2030
2031    def write(self, output_dir, fname):
2032        pathname = os.path.join(output_dir, fname)
2033        dir = os.path.dirname(pathname)
2034        if dir:
2035            try:
2036                os.makedirs(dir)
2037            except os.error as e:
2038                if e.errno != errno.EEXIST:
2039                    raise
2040        fd = os.open(pathname, os.O_RDWR | os.O_CREAT, 0o666)
2041        if sys.version_info[0] >= 3:
2042            f = open(fd, 'r+', encoding='utf-8')
2043        else:
2044            f = os.fdopen(fd, 'r+')
2045        text = (self._top(fname) + self._preamble + self._body
2046                + self._bottom(fname))
2047        oldtext = f.read(len(text) + 1)
2048        if text != oldtext:
2049            f.seek(0)
2050            f.truncate(0)
2051            f.write(text)
2052        f.close()
2053
2054
2055class QAPIGenC(QAPIGen):
2056
2057    def __init__(self, blurb, pydoc):
2058        QAPIGen.__init__(self)
2059        self._blurb = blurb
2060        self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc,
2061                                                  re.MULTILINE))
2062
2063    def _top(self, fname):
2064        return mcgen('''
2065/* AUTOMATICALLY GENERATED, DO NOT MODIFY */
2066
2067/*
2068%(blurb)s
2069 *
2070 * %(copyright)s
2071 *
2072 * This work is licensed under the terms of the GNU LGPL, version 2.1 or later.
2073 * See the COPYING.LIB file in the top-level directory.
2074 */
2075
2076''',
2077                     blurb=self._blurb, copyright=self._copyright)
2078
2079    def _bottom(self, fname):
2080        return mcgen('''
2081/* Dummy declaration to prevent empty .o file */
2082char dummy_%(name)s;
2083''',
2084                     name=c_name(fname))
2085
2086
2087class QAPIGenH(QAPIGenC):
2088
2089    def _top(self, fname):
2090        return QAPIGenC._top(self, fname) + guardstart(fname)
2091
2092    def _bottom(self, fname):
2093        return guardend(fname)
2094
2095
2096class QAPIGenDoc(QAPIGen):
2097
2098    def _top(self, fname):
2099        return (QAPIGen._top(self, fname)
2100                + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n')
2101
2102
2103class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor):
2104
2105    def __init__(self, prefix, what, blurb, pydoc):
2106        self._prefix = prefix
2107        self._what = what
2108        self._genc = QAPIGenC(blurb, pydoc)
2109        self._genh = QAPIGenH(blurb, pydoc)
2110
2111    def write(self, output_dir):
2112        self._genc.write(output_dir, self._prefix + self._what + '.c')
2113        self._genh.write(output_dir, self._prefix + self._what + '.h')
2114
2115
2116class QAPISchemaModularCVisitor(QAPISchemaVisitor):
2117
2118    def __init__(self, prefix, what, blurb, pydoc):
2119        self._prefix = prefix
2120        self._what = what
2121        self._blurb = blurb
2122        self._pydoc = pydoc
2123        self._module = {}
2124        self._main_module = None
2125
2126    def _module_basename(self, what, name):
2127        if name is None:
2128            return re.sub(r'-', '-builtin-', what)
2129        basename = os.path.join(os.path.dirname(name),
2130                                self._prefix + what)
2131        if name == self._main_module:
2132            return basename
2133        return basename + '-' + os.path.splitext(os.path.basename(name))[0]
2134
2135    def _add_module(self, name, blurb):
2136        if self._main_module is None and name is not None:
2137            self._main_module = name
2138        genc = QAPIGenC(blurb, self._pydoc)
2139        genh = QAPIGenH(blurb, self._pydoc)
2140        self._module[name] = (genc, genh)
2141        self._set_module(name)
2142
2143    def _set_module(self, name):
2144        self._genc, self._genh = self._module[name]
2145
2146    def write(self, output_dir, opt_builtins=False):
2147        for name in self._module:
2148            if name is None and not opt_builtins:
2149                continue
2150            basename = self._module_basename(self._what, name)
2151            (genc, genh) = self._module[name]
2152            genc.write(output_dir, basename + '.c')
2153            genh.write(output_dir, basename + '.h')
2154
2155    def _begin_module(self, name):
2156        pass
2157
2158    def visit_module(self, name):
2159        if name in self._module:
2160            self._set_module(name)
2161            return
2162        self._add_module(name, self._blurb)
2163        self._begin_module(name)
2164
2165    def visit_include(self, name, info):
2166        basename = self._module_basename(self._what, name)
2167        self._genh.preamble_add(mcgen('''
2168#include "%(basename)s.h"
2169''',
2170                                      basename=basename))
2171