# Check Protocol-A.texi. # Copyright (C) 1995-2003, 2005 Lysator Academic Computer Association. # # This file is part of the LysKOM server. # # LysKOM is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 1, or (at your option) # any later version. # # LysKOM is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # # You should have received a copy of the GNU General Public License # along with LysKOM; see the file COPYING. If not, write to # Lysator, c/o ISY, Linkoping University, S-581 83 Linkoping, SWEDEN, # or the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, # MA 02139, USA. # # Please report bugs at http://bugzilla.lysator.liu.se/. # # Check @aarg{} and @rarg{} usage in Protocol-A.texi. # ...and a lot of other stuff... # ...and generate a stable machine-readable protocol specification. import sys import types import string import re import os # A mapping from type names (as strings) to prot_a_* objects. defined_types = {} # A mapping from request names (as strings) to prot_a_request objects. defined_request_names = {} # A mapping from request numbers (as ints) to prot_a_request objects. defined_request_numbers = {} # A mapping from async names (as strings) to prot_a_async objects. defined_async_names = {} # A mapping containing values set with @set. (@ifinfo et c # are not understood; the latest @set found is used.) set_values = {} # Translation tables between the stable names and the names used by # Protocol-A.texi. The first letter is the domain: # t : types # r : requests # a : asyncs # # The second letter is the direction: # t : table (from Protocol-A.texi to stable name) # r : reverse (from stable name to current Protocol-A.texi alias) tt = {} tr = {} rt = {} rr = {} at = {} ar = {} # All fields that have been seen so far. Initialized with some # built-in fields. defined_fields = { 'error-code': None, 'error-status': None, 'ref-no': None, 'reply-data': None, } # Fields seen in @field{} but not yet defined. undefined_fields = {} def number_suffixed(s, base): """Return true if S is BASE followed by a decimal number. """ if s[:len(base)] != base: return 0 s = s[len(base):] if len(s) < 1: return 0 try: int(s) return 1 except: return 0 def has_suffix(s, suffix): """Return true if S has the specified SUFFIX. """ if len(s) < len(suffix): return 0 return s[len(s) - len(suffix):] == suffix def remove_suffix(s, suffix): """Return S, but with SUFFIX removed. Return S unchanged if it doesn't end with SUFFIX. """ if has_suffix(s, suffix): return s[:len(s) - len(suffix)] else: return s # We define our own isalpha et c to avoid dependencies on the current # locale, and to be portable across old Python versions. def isalpha(s): for c in s: if c not in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ": return 0 return 1 def isupper(s): for c in s: if c not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ": return 0 return 1 def islower(s): for c in s: if c not in "abcdefghijklmnopqrstuvwxyz": return 0 return 1 def isdigit(s): for c in s: if c not in "0123456789": return 0 return 1 def isspace(s): for c in s: if c not in " \n": # There should be no \t or \r in this file... return 0 return 1 class reader_eof(Exception): pass class prot_a_type: """Base class used for all Protocol A types. """ __usage_count = 0 def __init__(self, line): """Create a type. LINE should be the line number in Protocol-A.texi where this type was defined. It is used to create error messages. """ self.__line = line self.__protover = None self.__recommended = 0 def line_no(self): """Return the line number that defined this type. """ return self.__line def use(self, protover, recommended): """Mark this type as used by something else. This will increase the usage count (returned by usage()) and call use_recurse(), which in turn will call use() on all types referenced by this type. PROTOVER is the version of the request or async message that uses this type. The type will remember the lowest PROTOVER; this is the protocol version where the type was introduced. RECOMMENDED is true if this is used by a Recommended request or async message. """ self.__usage_count = self.__usage_count + 1 if self.__protover == None or protover < self.__protover: self.__protover = protover if self.__usage_count == 1: self.use_recurse(protover, recommended) if recommended: self.__recommended = 1 def usage(self): """Return the usage count. """ return self.__usage_count def protover(self): """Return the lowest protocol version that used this type. """ return self.__protover def recommended(self): """Return true if this type is used by a recommended request or async. """ return self.__recommended class prot_a_builtin(prot_a_type): """Basic types defined by the protocol, such as INT32. """ def use_recurse(self, protover, recommended): pass class prot_a_simple(prot_a_type): """Simple alias types, such as Conf-No or ARRAY Text-Stat. """ def __init__(self, line, typ, is_array): self.__type = typ self.__array = is_array prot_a_type.__init__(self, line) def use_recurse(self, protover, recommended): defined_types[self.__type].use(protover, recommended) def base_type(self): return self.__type def array(self): return self.__array class prot_a_alternate(prot_a_type): """A type that is an alternative of two other types. The only example of this is currently Any-Conf-Stat. """ def __init__(self, line, type_a, type_b): self.__type_a = type_a self.__type_b = type_b prot_a_type.__init__(self, line) def use_recurse(self, protover, recommended): defined_types[self.__type_a].use(protover, recommended) defined_types[self.__type_b].use(protover, recommended) def type_a(self): return self.__type_a def type_b(self): return self.__type_b class prot_a_struct(prot_a_type): """A structure, such as Conference. """ def __init__(self, line): prot_a_type.__init__(self, line) self.__fields = [] self.__used_names = {} def add_field(self, field_name, type_name, is_array): """Add a field to the structure. """ if self.__used_names.has_key(field_name): return "field name ``%s'' used twice" % field_name o = (field_name, type_name, is_array) self.__used_names[field_name] = o self.__fields.append(o) defined_fields[field_name] = None if undefined_fields.has_key(field_name): del undefined_fields[field_name] return None def use_recurse(self, protover, recommended): for (fn, tn, ar) in self.__fields: defined_types[tn].use(protover, recommended) def fields(self): return self.__fields class prot_a_bitstring(prot_a_type): """A bitstring, such Aux-Item-Flags. """ def __init__(self, line): prot_a_type.__init__(self, line) self.__bits = {} self.__ordered_bits = [] def add_field(self, field_name): if self.__bits.has_key(field_name): return "bit ``%s'' used twice" % field_name self.__bits[field_name] = None defined_fields[field_name] = None if undefined_fields.has_key(field_name): del undefined_fields[field_name] self.__ordered_bits.append(field_name) def check_implemented(self, name, implemented_fields): res = [] i = {} for f in implemented_fields.keys(): if not self.__bits.has_key(f): res.append("%s: bit ``%s'' not documented" % (name, f)) i[f] = None for f in self.__bits.keys(): if not i.has_key(f): res.append("%s: bit ``%s'' not implemented" % (name, f)) return res def use_recurse(self, protover, recommended): pass def bits(self): return self.__ordered_bits class prot_a_selection(prot_a_type): """A selection, such as Misc-Info or Local-To-Global-Block. """ def __init__(self, line): prot_a_type.__init__(self, line) self.__fields = [] self.__used_nums = {} self.__used_names = {} self.__used_tailnames = {} def add_variant(self, number, name, tailname, tail_type, is_array): if self.__used_nums.has_key(number): return "selection number ``%s'' used twice" % number if self.__used_names.has_key(name): return "selection name ``%s'' used twice" % name if self.__used_tailnames.has_key(tailname): return "selection tailname ``%s'' used twice" % tailname o = (number, name, tailname, tail_type, is_array) self.__used_nums[number] = o self.__used_names[name] = o self.__used_tailnames[tailname] = o self.__fields.append(o) def check_implemented(self, imp_nr, imp_name): if not self.__used_names.has_key(imp_name): return "``%s'' implemented but not documented" % imp_name if self.__used_names[imp_name][0] != imp_nr: return "``%s'' implemented as %s but documented as %s" % ( imp_name, repr(imp_nr), repr(self.__used_names[imp_name][0])) return None def all_names(self): return self.__used_names.keys() def use_recurse(self, protover, recommended): for (number, name, tailname, tail_type, is_array) in self.__fields: defined_types[tail_type].use(protover, recommended) def fields(self): return self.__fields class prot_a_enumeration_of(prot_a_type): """An ENUMERATION-OF, such as Info-Type. """ def __init__(self, line, base): prot_a_type.__init__(self, line) self.__base = base def use_recurse(self, protover, recommended): defined_types[self.__base].use(protover, recommended) def base_type(self): return self.__base class prot_a_msg: """Base class for requests and asynchronous messages. """ def __init__(self, line, reqnr, reqname, rec, exp, obs, protover, obsver): """Construct a request. LINE: Line number that defines the message. REQNR: The number of this request. REQNAME: The name, as it appears in Protocol-A.texi. REC: True if recommended. EXP: True if experimental. OBS: True if obsolete. PROTOVER: Protocol version when it was introduced. OBSVER: Protocol version when it became obsolete, or None. """ self.__line_no = line self.__reqnr = reqnr self.__reqname = reqname self.__rec = rec self.__exp = exp self.__obs = obs self.__protover = protover self.__obsver = obsver self.__args = [] self.__retval = None def line_no(self): return self.__line_no def request_nr(self): return self.__reqnr def request_name(self): return self.__reqname def recommended(self): return self.__rec def experimental(self): return self.__exp def obsolete(self): return self.__obs def protover(self): return self.__protover def obsver(self): return self.__obsver def append_arg(self, request_arg): self.__args.append(request_arg) def arguments(self): return self.__args class prot_a_request(prot_a_msg): """Hold all information about a request. """ __retval = None def set_return_type(self, ret_type, is_array): # FIXME: cleanup possible? Use anonymous prot_a_simple? self.__retval = (ret_type, is_array) def return_type(self): if self.__retval == None: return None return self.__retval[0] def array(self): return self.__retval[1] class prot_a_async(prot_a_msg): """Hold all information about an async message. """ pass class menu: def __init__(self, line_no, name, nr, state): self.__line_no = line_no self.__name = name self.__nr = int(nr) self.__state = state def line_no(self): return self.__line_no def name(self): return self.__name def nr(self): return self.__nr def state(self): return self.__state def same_state(self, req): if req.recommended() and self.__state == "r": return 1 if req.experimental() and self.__state == "e": return 1 if req.obsolete() and self.__state == "O": return 1 return 0 class reader: """Read a character at a time. The reader checks that unqoted occurances of (), [] and {} are properly matched, and gives an error message otherwise. The reader also keeps track of the line number. The reader recognizes the info menu lines for the request and async menues, and saves them. """ parens = {'(': ')', '[': ']', '{': '}'} rev_parens = {} for k, v in parens.items(): rev_parens[v] = k del v del k menu_re = re.compile('\\* (?P[-a-z0-9]*):: ' '*(?P[Oer]) .*' '\\((?P[0-9][0-9]*)\\)\n') def __init__(self, file): self.__filename = file self.__file = open(file, "r") self.__line_no = 0 self.__line = [] self.__eof = 0 self.__parenstack = [] self.__errfound = 0 self.__quoted = 0 self.__menu = {} def filename(self): return self.__filename def error(self, line_no, errmsg): sys.stderr.write("%s:%s:%s\n" % (self.filename(), line_no, errmsg)) self.__errfound = 1 def errfound(self): return self.__errfound def ungetc(self, c): if self.rev_parens.has_key(c): self.__parenstack.append((self.rev_parens[c], 0)) if self.parens.has_key(c): assert len(self.__parenstack) > 0 assert self.__parenstack[-1][0] == c del self.__parenstack[-1] if c == '@': self.__quoted = not self.__quoted self.__line.insert(0, c) def getc_eofok(self): if self.__line == []: if self.__eof: return None line = self.__file.readline() if line == '': self.__eof = 1 return None self.__line = list(line) self.__line_no = self.__line_no + 1 if len(line) == 80: m = self.menu_re.match(line) if m != None: self.__menu[m.group("name")] = menu( self.__line_no, m.group("name"), m.group("nr"), m.group("state")) ret = self.__line[0] del self.__line[0] if not self.__quoted: if ret in '([{': self.__parenstack.append((ret, self.line_no())) if ret in '}])': if len(self.__parenstack) == 0: self.error(self.line_no(), "unmatched paren ``%s''" % ret) else: if self.__parenstack[-1][0] != self.rev_parens[ret]: self.error(self.line_no(), "badly matched parens ``%s'' ``%s''" % (self.__parenstack[-1][0], ret)) del self.__parenstack[-1] if ret == '@': self.__quoted = not self.__quoted else: self.__quoted = 0 return ret def getc(self): c = self.getc_eofok() if c == None: raise reader_eof return c def line_no(self): return self.__line_no def check_paren_null(self): for par, line in self.__parenstack: self.error(line, "unclosed ``%s''" % par) self.__parenstack = [] def menu(self, name): """Return the menu line for the request or async named NAME. """ if self.__menu.has_key(name): return self.__menu[name] else: return None class lexer: section_re = re.compile( '(?P[a-z][-a-z0-9]*) ' '\\[(?P[0-9][0-9]*)\\] ' '\\((?P[1-9][0-9]*)\\) ' '(?:(?PRecommended)' '|(?PExperimental)' '|(?PObsolete(?: \\((?P[0-9]*)\\))?))' '$') def __init__(self, file): self.__reader = reader(file) self.__findex = None self.__amindex = None self.__linkhere = None self.__builtin_types = ["BOOL", "INT8", "INT16", "INT32", "FLOAT", "HOLLERITH"] self.__builtin_aggregates = ["BITSTRING", "ENUMERATION", "ENUMERATION-OF", "ARRAY", "SELECTION", "RPC"] self.__builtin_typelike = (self.__builtin_types + self.__builtin_aggregates) self.__tindex_seen = {} for t in self.__builtin_types: defined_types[t] = prot_a_builtin('*builtin*') # A mapping from request name to line number where it is defined. self.__defined_requests = {} # A mapping from async name to line number where it is defined. self.__defined_asyncs = {} # A mapping from request name to request number, extracted # from the lyskomd source code. self.__implemented_reqs = {} f = open("requests-numbered.tmp", "r") for line in f.readlines(): [nr, name] = string.split(line, " ") self.__implemented_reqs[string.strip(name)] = nr f.close() # A mapping from async name to async number, extracted # from the lyskomd source code. self.__implemented_asyncs = {} n = {} f = open("asyncs-numbered.tmp", "r") for line in f.readlines(): [nr, name] = string.split(line, " ") name = string.strip(name) if n.has_key(nr): sys.stderr.write("../src/server/async.h:1:enum async:" " number %s used for both %s and %s\n" % ( nr, n[nr], name)) sys.exit(1) n[nr] = name self.__implemented_asyncs[name] = nr f.close() # A mapping from misc-item name to number, extracted # from the lyskomd source code. self.__implemented_miscs = {} n = {} f = open("miscs-numbered.tmp", "r") for line in f.readlines(): [nr, name] = string.split(line, " ") nr = int(nr) name = string.strip(name) if n.has_key(nr): sys.stderr.write("../src/include/kom-types.h:1:enum info_type:" " number %s used for both %s and %s\n" % ( nr, n[nr], name)) sys.exit(1) n[nr] = name self.__implemented_miscs[name] = nr f.close() # A mapping from conftype bit name to None, extracted # from the lyskomd source code. self.__implemented_conftypes = {} f = open("conftypes.tmp", "r") for line in f.readlines(): name = string.strip(line) self.__implemented_conftypes[name] = None f.close() # A mapping from privilege bit name to None, extracted # from the lyskomd source code. self.__implemented_privbits = {} f = open("privbits.tmp", "r") for line in f.readlines(): name = string.strip(line) self.__implemented_privbits[name] = None f.close() def run(self): while 1: c = self.__reader.getc() if c == '@': ret = self.__toplevel_at() if ret != None: return ret def __toplevel_at(self): line_no = self.__reader.line_no() c = self.__reader.getc() if c in '{}@-"*': return cmd = "" while 1: if isalpha(c) or c in '_"': cmd = cmd + c c = self.__reader.getc() elif c in ' \t\n{@=-': assert cmd != '' if c == '{': arg = self.__read_arg() elif c == ' ' and cmd not in ['tab']: arg = self.__read_line() else: arg = None if c == '@': self.__reader.ungetc(c) if hasattr(self, 'toplevel_' + cmd): return getattr(self, 'toplevel_' + cmd)(arg, line_no) else: self.error(line_no, "unknown command @%s{}" % cmd) return else: self.error(line_no, "bad command ``@%s%s''" % (cmd, c)) return def __read_line(self): line = "" while 1: c = self.__reader.getc() if c == '\n': return line line = line + c def ignore(self, arg, line_no): pass toplevel_setfilename = ignore toplevel_settitle = ignore toplevel_setchapternewpage = ignore toplevel_macro = ignore toplevel_end = ignore toplevel_ifinfo = ignore toplevel_value = ignore toplevel_copyright = ignore toplevel_iftex = ignore toplevel_parindent = ignore toplevel_ifinfo = ignore toplevel_begin = ignore toplevel_titlepage = ignore toplevel_title = ignore toplevel_subtitle = ignore toplevel_author = ignore toplevel_font = ignore toplevel_ignore = ignore toplevel_tensltt = ignore toplevel_page = ignore toplevel_vskip = ignore toplevel_ifnothtml = ignore toplevel_contents = ignore toplevel_dircategory = ignore toplevel_direntry = ignore toplevel_ifhtml = ignore toplevel_html = ignore toplevel_ifnottex = ignore toplevel_top = ignore toplevel_menu = ignore toplevel_chapter = ignore toplevel_penalty = ignore toplevel_table = ignore toplevel_item = ignore toplevel_req = ignore toplevel_reqlink = ignore toplevel_asynclink = ignore toplevel_subsection = ignore toplevel_itemize = ignore toplevel_bullet = ignore toplevel_multitable = ignore toplevel_tab = ignore toplevel_aux = ignore toplevel_errorcode = ignore toplevel_misc = ignore toplevel_type = ignore toplevel_dfn = ignore toplevel_conftype = ignore toplevel_command = ignore toplevel_ref = ignore toplevel_subsubsection = ignore toplevel_priv = ignore toplevel_enumerate = ignore toplevel_itemx = ignore toplevel_ae = ignore toplevel_i = ignore toplevel_subheading = ignore toplevel_c = ignore toplevel_t = ignore toplevel_aa = ignore toplevel_async = ignore toplevel_unnumbered = ignore toplevel_printindex = ignore toplevel_example = ignore toplevel_unmacro = ignore toplevel_dots = ignore toplevel_tex = ignore toplevel_defcodeindex = ignore toplevel_syncodeindex = ignore toplevel_need = ignore toplevel_reqexample = ignore toplevel_anchor = ignore toplevel_appendix = ignore toplevel_display = ignore toplevel_daemon = ignore toplevel_TeX = ignore toplevel_copying = ignore toplevel_quotation = ignore toplevel_insertcopying = ignore def pushback(self, arg, line_no): lst = list(arg) lst.reverse() for char in lst: self.__reader.ungetc(char) toplevel_w = pushback toplevel_code = pushback toplevel_uref = pushback toplevel_footnote = pushback toplevel_email = pushback toplevel_asis = pushback toplevel_samp = pushback toplevel_pxref = pushback toplevel_var = pushback toplevel_emph = pushback toplevel_xref = pushback toplevel_badspell = pushback toplevel_holl = pushback toplevel_file = pushback toplevel_cindex = pushback toplevel_cite = pushback def toplevel_set(self, arg, line_no): [k, v] = string.split(arg, None, 1) set_values[k] = v def toplevel_section(self, arg, line_no): self.__section_name = arg self.__section_start = line_no def toplevel_node(self, arg, line_no): if self.__findex != None: self.__findex = None for (argname, [lineno, usage]) in self.__args.items(): if usage == 0: self.error(lineno, "Undocumented argument ``%s''" % (argname, )) if self.__amindex != None: self.__amindex = None for (argname, [lineno, usage]) in self.__args.items(): if usage == 0: self.error(lineno, "Undocumented argument ``%s''" % (argname, )) self.__node_name = arg self.__node_start = line_no self.__assert_no_linkhere() self.__reader.check_paren_null() def toplevel_findex(self, arg, line_no): if self.__node_name != arg: self.error(line_no, "@node/@findex mismatch: %s..." % arg) self.error(line_no, "...inside node %s" % self.__node_name) return if self.__amindex != None: self.error(line_no, "@findex and @amindex used in the same node") return if self.__findex != None: self.error(line_no, "multiple @findex in single @node") return self.__findex = arg m = self.section_re.match(self.__section_name) if m == None: self.error(self.__section_start, "bad section heading for request") return if m.group("req") != arg: self.error(line_no, "@section/@findex mismatch: %s..." % arg) self.error(self.__section_start, "...inside section %s" % m.group("req")) return reqnr = int(m.group("reqnr")) protover = int(m.group("protover")) if m.group("obsolete") != None and m.group("obsver") == None: self.error(self.__section_start, "when was this obsoleted?") rec = m.group("recommended") != None exp = m.group("experimental") != None obs = m.group("obsolete") != None # self.__args holds a mapping from the argument name # to a list of two elements: # 0: the line number where it is defined # 1: a usage count self.__args = {} if self.__defined_requests.has_key(arg): self.error(line_no, "request ``%s'' redefined" % arg) self.error(self.__defined_requests[arg], "previous definition") self.__defined_requests[arg] = line_no if self.__get_token() != '@example': self.error(self.__reader.line_no(), "missing @example") return req = prot_a_request(line_no, reqnr, arg, rec, exp, obs, protover, m.group("obsver")) self.__parse_request(req) defined_request_names[arg] = req defined_request_numbers[reqnr] = req menu = self.__reader.menu(req.request_name()) if menu == None: self.error(line_no, "no @menu item found") else: if not menu.same_state(req): self.error(menu.line_no(), "state clash for %s" % menu.name()) self.error(line_no, "...clashing") if menu.nr() != req.request_nr(): self.error(menu.line_no(), "number clash for %s" % menu.name()) self.error(line_no, "...clashing") def toplevel_rarg(self, arg, line_no): if self.__findex == None: self.error(line_no, "@rarg outside @findex node") return if not self.__args.has_key(arg): self.error(line_no, "undefined argument ``%s''" % (arg, )) return self.__args[arg][1] = self.__args[arg][1] + 1 def toplevel_amindex(self, arg, line_no): if self.__node_name != arg: self.error(line_no, "@node/@amindex mismatch: %s..." % arg) self.error(line_no, "...inside node %s" % self.__node_name) return if self.__findex != None: self.error(line_no, "@findex and @amindex used in the same node") return if self.__amindex != None: self.error(line_no, "multiple @amindex in single @node") return self.__amindex = arg self.__args = {} if self.__defined_asyncs.has_key(arg): self.error(line_no, "async message ``%s'' redefined" % arg) self.error(self.__defined_asyncs[arg], "previous definition") self.__defined_asyncs[arg] = line_no token = self.__get_token() obsolete = 0 if token == '@c': token = self.__get_token() if token == 'obsolete': obsolete = 1 else: self.error(self.__reader.line_no(), "broken comment within @amindex block") return token = self.__get_token() if token != '@example': self.error(self.__reader.line_no(), "missing @example") return m = self.section_re.match(self.__section_name) if m == None: self.error(self.__section_start, "bad section heading for message") return if m.group("req") != arg: self.error(line_no, "@section/@amindex mismatch: %s..." % arg) self.error(self.__section_start, "...inside section %s" % m.group("req")) return reqnr = int(m.group("reqnr")) protover = int(m.group("protover")) if m.group("obsolete") != None and m.group("obsver") == None: self.error(self.__section_start, "when was this obsoleted?") rec = m.group("recommended") != None exp = m.group("experimental") != None obs = m.group("obsolete") != None req = prot_a_async(line_no, reqnr, arg, rec, exp, obs, protover, m.group("obsver")) self.__parse_async(req) defined_async_names[arg] = req menu = self.__reader.menu(req.request_name()) if menu == None: self.error(line_no, "no @menu item found") else: if not menu.same_state(req): self.error(menu.line_no(), "state clash for %s" % menu.name()) self.error(line_no, "...clashing") if menu.nr() != req.request_nr(): self.error(menu.line_no(), "number clash for %s" % menu.name()) self.error(line_no, "...clashing") def __parse_async(self, req): self.__tokens = [] async = self.__get_token() if async != self.__amindex: self.error(self.__reader.line_no(), "wrong async name ``%s''" % async) return if self.__get_token() != '[': self.error(self.__reader.line_no(), "missing ``[''") return nr = self.__get_token() if type(nr) != types.IntType: self.error(self.__reader.line_no(), "bad async number") if self.__implemented_asyncs.has_key(async): if self.__implemented_asyncs[async] != str(nr): self.error(self.__reader.line_no(), "``%s'' is implemented as asynchronous message %s, " "not %s" % (async, self.__implemented_asyncs[async], nr)) elif not req.obsolete(): self.error(self.__reader.line_no(), "asynchronous message ``%s'' not implemented" % async) if req.request_nr() != nr: self.error(self.__reader.line_no(), "async number mismatch") if self.__get_token() != ']': self.error(self.__reader.line_no(), "missing ``]''") return paren = self.__get_token() if paren == '(': next = self.__get_token() if next != ')': self.__unget_token(next) req.append_arg(self.__parse_request_arg(req)) next = self.__get_token() if next != ')': self.error(self.__reader.line_no(), "missing close parenthesis after arguments") return elif paren == '((': req.append_arg(self.__parse_request_arg(req)) next = self.__get_token() while next == ';': req.append_arg(self.__parse_request_arg(req)) next = self.__get_token() if next != '))': self.error(self.__reader.line_no(), "missing double close parenthesis after arguments") return else: self.error(self.__reader.line_no(), "missing argument list") return if self.__get_token() != ';': self.error(self.__reader.line_no(), "missing final ``;''") return if self.__get_token() != '@end': self.error(self.__reader.line_no(), "extra garbage found") return return def toplevel_aarg(self, arg, line_no): if self.__amindex == None: self.error(line_no, "@aarg outside @amindex node") return if not self.__args.has_key(arg): self.error(line_no, "undefined argument ``%s''" % (arg, )) return self.__args[arg][1] = self.__args[arg][1] + 1 def toplevel_bye(self, arg, line_no): if self.__findex != None: self.error(self.__reader.line_no(), "unterminated @findex node") if self.__amindex != None: self.error(self.__reader.line_no(), "unterminated @amindex node") # Check types. for (n, o) in defined_types.items(): if o.usage() < 1: self.error(o.line_no(), "unused type ``%s''" % n) # Check requests. for req in self.__implemented_reqs.keys(): if not self.__defined_requests.has_key(req): self.error(self.__reader.line_no(), "request ``%s'' not documented" % req) # Check async messages. for req in self.__implemented_asyncs.keys(): if not self.__defined_asyncs.has_key(req): self.error(self.__reader.line_no(), "asynchronous message ``%s'' not documented" % req) # Check misc-info stuff. for name, nr in self.__implemented_miscs.items(): err = defined_types['Misc-Info'].check_implemented(nr, name) if err: self.error(self.__reader.line_no(), "Misc-info " + err) for name in defined_types['Misc-Info'].all_names(): if not self.__implemented_miscs.has_key(name): self.error(self.__reader.line_no(), "Misc-info ``%s'' not implemented." % name) # Check bitfields. for emsg in defined_types['Extended-Conf-Type'].check_implemented( "conference type", self.__implemented_conftypes): self.error(self.__reader.line_no(), emsg) for emsg in defined_types['Priv-Bits'].check_implemented( "conference type", self.__implemented_privbits): self.error(self.__reader.line_no(), emsg) # Check fields. for (field, lines) in undefined_fields.items(): for lin in lines: self.error(lin, "undefined field ``%s'' referenced" % field) self.__reader.check_paren_null() # Some additional checks. self.generate_stable_names() return self.__reader.errfound() def toplevel_reqdlink(self, arg, line_no): self.__assert_no_linkhere() self.__linkhere = ("@reqdlink{%s}" % arg, line_no) def toplevel_asyncdlink(self, arg, line_no): self.__assert_no_linkhere() self.__linkhere = ("@asyncdlink{%s}" % arg, line_no) def __assert_no_linkhere(self): if self.__linkhere != None: self.error(self.__linkhere[1], "@linkhere{} after %s missing" % (self.__linkhere[0])) def toplevel_linkhere(self, arg, line_no): if self.__linkhere == None: self.error(line_no, "spurious @linkhere{}") self.__linkhere = None def __seen_type(self, arg, line_no): if self.__tindex_seen.has_key(arg): self.error(line_no, "multiple @tindex entries for %s" % arg) self.error(self.__tindex_seen[arg], "previous location") else: self.__tindex_seen[arg] = line_no def toplevel_tindex(self, arg, line_no): self.__seen_type(arg, line_no) if arg in self.__builtin_typelike: return self.__bad_type(arg) tindexed_types = [arg] self.__tokens = [] t = self.__get_token() while t == '@tindex': t = self.__get_token() self.__seen_type(t, self.__reader.line_no()) self.__bad_type(t) tindexed_types.append(t) t = self.__get_token() if t != '@example': self.error(self.__reader.line_no(), "missing @example after @tindex for defined type") return self.__parse_userdefined_types(tindexed_types) def __parse_userdefined_types(self, tindexed_types): """Parse type definitions for TINDEXED_TYPES. TINDEXED_TYPES is a list of type names, as strings, that originates from the @tindex statements above this @example section. An error message is given if TINDEXED_TYPES doesn't correspond to the types actually defined in this @example section. """ seen_types = [] while 1: newtype = self.__get_token() line = self.__reader.line_no() if newtype == '@end': token = self.__get_token() if token != 'example': self.error(self.__reader.line_no(), "``@end example'' expected") break if newtype == '@need': self.__get_token() # Skip the argument to @need. newtype = self.__get_token() if newtype == '@anchor': if self.__get_token() != '{': self.error(self.__reader.line_no(), "@anchor must be followed by ``{''") newtype = self.__get_token() if self.__get_token() != '}': self.error(self.__reader.line_no(), "@anchor missing ``}''") token = self.__get_token() if token != newtype: self.error(self.__reader.line_no(), "@anchor mismatch -- ``%s'' or ``%s''?" % ( newtype, token)) token = self.__get_token() if token != '::=': self.error(self.__reader.line_no(), "``::='' expected") return token = self.__get_token() if token == '(': typedef = self.__parse_userdefined_struct(line) elif token == 'SELECTION': typedef = self.__parse_userdefined_selection(line) elif token == 'BITSTRING': typedef = self.__parse_userdefined_bitstring(line) elif token == 'ENUMERATION-OF': typedef = self.__parse_userdefined_enumeration_of(line) else: self.__unget_token(token) token = self.__get_lt_token() if token == 'ARRAY': name = self.__get_lt_token() array = 1 else: array = 0 name = token token = self.__get_token() if token == '|': token = self.__get_lt_token() if array or token == 'ARRAY': self.error(self.__reader.line_no(), "cannot mix ARRAY and ``|''") return typedef = prot_a_alternate(line, name, token) token = self.__get_token() else: typedef = prot_a_simple(line, name, array) if token != ';': # This is not a fatal error; we try to recover # from it. self.error(self.__reader.line_no(), "missing ;") if typedef != None: if newtype not in tindexed_types: self.error(self.__reader.line_no(), "missing @tindex entry for %s" % newtype) seen_types.append(newtype) if defined_types.has_key(newtype): self.error(self.__reader.line_no(), "redefinition of ``%s''" % newtype) else: defined_types[newtype] = typedef # Check that all types in the @tindex were defined. for typename in tindexed_types: if typename not in seen_types: self.error(self.__reader.line_no(), "type %s not defined but @tindex entry exists" % typename) def __parse_userdefined_struct(self, line): res = prot_a_struct(line) while 1: token = self.__get_token() if token == ')': return res name = token token = self.__get_token() if token != ':': self.error(self.__reader.line_no(), "missing ``:'' near %s" % token) token = self.__get_lt_token() if token == 'ARRAY': array = 1 token = self.__get_lt_token() else: array = 0 if not self.__bad_type(token) and not self.__bad_arg(name): ret = res.add_field(name, token, array) if ret: self.error(self.__reader.line_no(), ret) token = self.__get_token() if token != ';': self.error(self.__reader.line_no(), "missing ``;''") def __parse_userdefined_bitstring(self, line): res = prot_a_bitstring(line) token = self.__get_token() if token != '(': self.error(self.__reader.line_no(), "expected ``('' after BITSTRING, not ``%s''" % token) return None while 1: token = self.__get_token() if token == ')': return res name = token if not self.__bad_arg(name): ret = res.add_field(name) if ret: self.error(self.__reader.line_no(), ret) token = self.__get_token() if token != ';': self.error(self.__reader.line_no(), "missing ``;''") def __parse_userdefined_selection(self, line): res = prot_a_selection(line) token = self.__get_token() if token != '(': self.error(self.__reader.line_no(), "expected ``('' after SELECTION, not ``%s''" % token) return None while 1: token = self.__get_token() if token == ')': return res number = token try: int(number) except: self.error(self.__reader.line_no(), "bad number ``%s''" % number) return None token = self.__get_token() if token != '=': self.error(self.__reader.line_no(), "missing ``:'' near %s" % token) name = self.__get_token() tail_name = self.__get_token() token = self.__get_token() if token != ':': self.error(self.__reader.line_no(), "missing ``:'' near %s" % token) token = self.__get_lt_token() if token == 'ARRAY': array = 1 token = self.__get_lt_token() else: array = 0 if (not self.__bad_type(token) and not self.__bad_arg(name) and not self.__bad_arg(tail_name)): ret = res.add_variant(number, name, tail_name, token, array) if ret: self.error(self.__reader.line_no(), ret) token = self.__get_token() if token != ';': self.error(self.__reader.line_no(), "missing ``;''") def __parse_userdefined_enumeration_of(self, line): token = self.__get_token() if token != '(': self.error(self.__reader.line_no(), "expected ``('' after ENUMERATION-OF, not ``%s''" % token) return None name = self.__get_lt_token() token = self.__get_token() if token != ')': self.error(self.__reader.line_no(), "missing close ``)'', got ``%s''" % token) return None if self.__bad_type(name): return None return prot_a_enumeration_of(line, name) def __parse_request(self, req_obj): self.__tokens = [] req = self.__get_token() if req != self.__findex: self.error(self.__reader.line_no(), "wrong request name ``%s''" % req) return if self.__get_token() != '[': self.error(self.__reader.line_no(), "missing ``[''") return nr = self.__get_token() if type(nr) != types.IntType: self.error(self.__reader.line_no(), "bad request number") if self.__implemented_reqs.has_key(req): if self.__implemented_reqs[req] != str(nr): self.error(self.__reader.line_no(), "``%s'' is implemented as request number %s, not %s" % (req, self.__implemented_reqs[req], nr)) else: self.error(self.__reader.line_no(), "request ``%s'' not implemented" % req) if req_obj.request_nr() != nr: self.error(self.__reader.line_no(), "request number mismatch vs. @section") if self.__get_token() != ']': self.error(self.__reader.line_no(), "missing ``]''") return paren = self.__get_token() if paren == '(': next = self.__get_token() if next != ')': self.__unget_token(next) req_obj.append_arg(self.__parse_request_arg(req_obj)) next = self.__get_token() if next != ')': self.error(self.__reader.line_no(), "missing close parenthesis after arguments") return elif paren == '((': req_obj.append_arg(self.__parse_request_arg(req_obj)) next = self.__get_token() while next == ';': req_obj.append_arg(self.__parse_request_arg(req_obj)) next = self.__get_token() if next != '))': self.error(self.__reader.line_no(), "missing double close parenthesis after arguments") return else: self.error(self.__reader.line_no(), "missing argument list") return if self.__get_token() != '->': self.error(self.__reader.line_no(), "missing ``->''") return if self.__get_token() != '(': self.error(self.__reader.line_no(), "missing ``('' for result") return next = self.__get_token() if next != ')': self.__unget_token(next) (ret_type, ret_array) = self.__parse_type(req_obj) req_obj.set_return_type(ret_type, ret_array) next = self.__get_token() if next != ')': self.error(self.__reader.line_no(), "missing ``)'' for result") return if self.__get_token() != ';': self.error(self.__reader.line_no(), "missing final ``;''") return if self.__get_token() != '@end': self.error(self.__reader.line_no(), "extra garbage found") return return def __parse_type(self, req_obj): token = self.__get_lt_token() if token == 'ARRAY': token = self.__get_lt_token() array = 1 else: array = 0 if self.__bad_type(token): return (None, 0) if not defined_types.has_key(token): self.error(self.__reader.line_no(), "undefined type ``%s''" % token) defined_types[token].use(req_obj.protover(), req_obj.recommended()) return (token, array) def __get_lt_token(self): token = self.__get_token() if token != '@lt': self.error(self.__reader.line_no(), 'expected @lt{}-enclosed token') return token token = self.__get_token() if token != '{': self.error(self.__reader.line_no(), "expected ``{'', not ``%s''" % token) return token result = self.__get_token() token = self.__get_token() if token != '}': self.error(self.__reader.line_no(), "expected ``}'', not ``%s''" % token) return result def __bad_type(self, tp): if tp in self.__builtin_types: return 0 # This is an ugly special case, that is a good type name. # More or less. if tp == "UConference": return 0 ok = 1 if len(tp) < 0: ok = 0 if ok and not isupper(tp[0]): ok = 0 upper = 0 num_seen = 0 for c in tp[1:]: if num_seen: if not isdigit(c): ok = 0 elif upper: if isdigit(c): num_seen = 1 elif c == '-' or not isupper(c): ok = 0 upper = 0 else: if c == '-': upper = 1 elif not islower(c): ok = 0 if ok and tp[-1] == '-': ok = 0 if not ok: self.error(self.__reader.line_no(), "bad type name ``%s''" % (tp,)) return not ok def __bad_arg(self, arg): if number_suffixed(arg, 'reserved'): return 0 if number_suffixed(arg, 'flg'): return 0 ok = 1 if len(arg) < 1: ok = 0 if ok and not islower(arg[0]): ok = 0 for c in arg[1:]: if not islower(c) and c != '-': ok = 0 if ok and arg[-1] == '-': ok = 0 if not ok: if arg == '))': self.__unget_token(arg) self.error(self.__reader.line_no(), "extra semicolon after last arg") else: self.error(self.__reader.line_no(), "bad argument ``%s''" % (arg,)) return not ok def __parse_request_arg(self, req_obj): argname = self.__get_token() if self.__bad_arg(argname): return if self.__get_token() != ':': self.error(self.__reader.line_no(), "missing ``:'' after argument") return (tp, array) = self.__parse_type(req_obj) if tp == None: return if self.__args.has_key(argname): self.error(self.__reader.line_no(), "argument name ``%s'' used twice" % (argname, )) return self.__args[argname] = [self.__reader.line_no(), 0] return (argname, tp, array) def toplevel_field(self, arg, line_no): if not defined_fields.has_key(arg): if not undefined_fields.has_key(arg): undefined_fields[arg] = [] undefined_fields[arg].append(line_no) def __unget_token(self, token): self.__tokens.insert(0, token) def __get_token(self): if len(self.__tokens) > 0: res = self.__tokens[0] del self.__tokens[0] return res c = self.__reader.getc() while isspace(c): c = self.__reader.getc() if isalpha(c): res = c while 1: c = self.__reader.getc() if not isalpha(c) and c not in "-" and not isdigit(c): if isdigit(c): self.error(self.__reader.line_no(), "bad token ``%s''" % (res + c)) self.__reader.ungetc(c) return res res = res + c elif c == ':': d = self.__reader.getc() if d != ':': self.__reader.ungetc(d) return c d = self.__reader.getc() if d != '=': self.__reader.ungetc(d) self.__reader.ungetc(':') return c return '::=' elif c in '[];|={}': return c elif c in '()': d = self.__reader.getc() if c != d: self.__reader.ungetc(d) return c else: return c + d elif isdigit(c): res = c while 1: c = self.__reader.getc() if not isdigit(c): if isalpha(c): self.error(self.__reader.line_no(), "bad token ``%s''" % (res + c)) self.__reader.ungetc(c) if res[0] == '0' and len(res) > 1: self.error(self.__reader.line_no(), "bad number ``%s''" % (res,)) return int(res) res = res + c elif c == '-': d = self.__reader.getc() if d == '>': return '->' else: self.error(self.__reader.line_no(), "bad token ``%s%s''" % (c, d)) self.__reader.ungetc(d) return c elif c == '@': res = c while 1: c = self.__reader.getc() if not isalpha(c): self.__reader.ungetc(c) return res res = res + c elif c == '!': # This is a comment. Skip it. We *should* parse it like # toplevel Texinfo code until the next end of line, but # doing so is too hard -- there is only a single comment # in the entire document. while 1: c = self.__reader.getc() if c == '\n': return self.__get_token() else: self.error(self.__reader.line_no(), "bad character ``%s''" % (c,)) return c def __read_arg(self): arg = "" level = 0 while 1: c = self.__reader.getc() if c == '@': # Handle '@{', '@}', and '@@' (and everything else # that starts with `@'). arg = arg + c c = self.__reader.getc() elif c == '{': level = level + 1 elif c == '}': if level == 0: return arg level = level - 1 arg = arg + c def error(self, line_no, errmsg): self.__reader.error(line_no, errmsg) def generate_stable_names(self): number_suffix_re = re.compile("(.*)-([1-9][0-9]*)$") # Create a translation table for type names. for type_name, type_obj in defined_types.items(): if isinstance(type_obj, prot_a_builtin): tt[type_name] = type_name else: pretty = type_name if pretty not in ["Who-Info-Old"]: pretty = remove_suffix(pretty, "-Old") m = number_suffix_re.match(pretty) if m != None: pretty = m.group(1) if type_obj.protover() != int(m.group(2)): self.error(type_obj.line_no(), "protocol version mismatch") stable = "%s-%d" % (pretty, type_obj.protover()) if tr.has_key(stable): self.error(type_obj.line_no(), "stable type name clash: both %s and %s " "becomes %s" % (tr[stable], type_name, stable)) tt[type_name] = stable tr[stable] = type_name tlist = tt.keys() tlist.sort() # Create a translation table for request names. for req_name, req_obj in defined_request_names.items(): pretty = req_name if pretty not in ['who-is-on-old', 'get-person-stat-old', 'get-conf-stat-older']: pretty = remove_suffix(pretty, "-old") pretty = remove_suffix(pretty, "-older") m = number_suffix_re.match(pretty) if m != None: pretty = m.group(1) if req_obj.protover() != int(m.group(2)): self.error(req_obj.line_no(), "protocol version mismatch") stable = "%s-%d" % (pretty, req_obj.protover()) if rr.has_key(stable): self.error(req_obj.line_no(), "stable request name clash: both %s and %s " "becomes %s" % (rr[stable], req_name, stable)) rt[req_name] = stable rr[stable] = req_name # Create a translation table for async names. for async_name, req_obj in defined_async_names.items(): pretty = async_name if pretty not in []: pretty = remove_suffix(pretty, "-old") m = number_suffix_re.match(pretty) if m != None: pretty = m.group(1) if req_obj.protover() != int(m.group(2)): self.error(req_obj.line_no(), "protocol version mismatch") stable = "%s-%d" % (pretty, req_obj.protover()) if ar.has_key(stable): self.error(req_obj.line_no(), "stable async name clash: both %s and %s " "becomes %s" % (ar[stable], async_name, stable)) at[async_name] = stable ar[stable] = async_name def generate_stable_output(filename, only_recommended): fp = open(filename + ".tmp", "w") fp.write("# This protocol-a.txt, generated from Protocol-A.texi\n") fp.write("# by checkargs.py. This file contains the definitions\n") fp.write("# of the types, requests and asynchronous messages in a\n") fp.write("# format that is intended to be machine-readable and\n") fp.write("# stable. All names have a numbered suffix which is\n") fp.write("# the protocol version in which the entity was first\n") fp.write("# introduced.\n") fp.write("\n") fp.write("# This file is Copyright (C) 1995-2003, 2005 Lysator ACS.\n") fp.write("# All rights reserved. This file may only be used\n") fp.write("# for testing purposes. Once the file format has\n") fp.write("# stabilized a future version of the file will probably\n") fp.write("# be placed in the public domain.\n") fp.write("\n") if only_recommended: fp.write("# This file only contains the recommended stuff.\n") else: fp.write("# Note: this file does not describe request 12,\n") fp.write("# lookup-name, which returns Conf-List-Archaic.\n") fp.write("# That is because that data type is so irregular.\n") fp.write("\n") fp.write("# Part 1: Define the alias names that Protocol-A.texi\n") fp.write("# uses. These aliases will change from time to time, as\n") fp.write("# new versions which are believed to be better are\n") fp.write("# introduced. It might be best not to use the aliases\n") fp.write("# in generated code.\n") fp.write("\n") fp.write("%%PROTOEDITION %s\n" % set_values["PROTOEDITION"]) fp.write("%%PROTOVER %s\n" % set_values["PROTOVER"]) fp.write("%%LYSKOMDVERSION %s\n\n" % set_values["VERSION"]) fp.write("\n") fp.write("# Types.\n") tlist = tt.keys() tlist.sort() for tn in tlist: if only_recommended and not defined_types[tn].recommended(): continue if tn != tt[tn] and tn not in ["Conf-List-Archaic"]: fp.write("%%type-alias %-30s %s\n" % (tt[tn], tn)) fp.write("\n") fp.write("# Requests.\n") rlist = rt.keys() rlist.sort() for rn in rlist: if (only_recommended and not defined_request_names[rn].recommended()): continue if rn != rt[rn] and rn not in ["lookup-name-1"]: fp.write("%%request-alias %-30s %s\n" % (rt[rn], rn)) fp.write("\n") fp.write("# Asynchronous messages.\n") alist = at.keys() alist.sort() for an in alist: if (only_recommended and not defined_async_names[an].recommended()): continue if an != at[an]: fp.write("%%async-alias %-30s %s\n" % (at[an], an)) fp.write("\n") fp.write("# Part two: all derived types.\n") fp.write("\n") tlist = tr.keys() tlist.sort() for tn in tlist: if tn in ["Conf-List-Archaic-1"]: continue t = defined_types[tr[tn]] if only_recommended and not t.recommended(): continue if isinstance(t, prot_a_builtin): fp.write("%%builtin %s\n" % tn) elif isinstance(t, prot_a_simple): if t.array(): fp.write("%s ::= ARRAY %s\n" % (tn, tt[t.base_type()])) else: fp.write("%s ::= %s\n" % (tn, tt[t.base_type()])) elif isinstance(t, prot_a_alternate): fp.write("%s ::= %s | %s\n" % (tn, tt[t.type_a()], tt[t.type_b()])) elif isinstance(t, prot_a_struct): fp.write("%s ::=\n" % tn) first = 1 for field_name, field_type, is_array in t.fields(): if first: fp.write(" ( ") else: fp.write(" ") first = 0 if is_array: fp.write("%-20s : ARRAY %s;\n" % (field_name, tt[field_type])) else: fp.write("%-20s : %s;\n" % (field_name, tt[field_type])) fp.write(" )\n") elif isinstance(t, prot_a_bitstring): fp.write("%s ::= BITSTRING\n" % tn) first = 1 for bit in t.bits(): if first: fp.write(" ( ") else: fp.write(" ") first = 0 fp.write("%s;\n" % bit) fp.write(" )\n") elif isinstance(t, prot_a_selection): fp.write("%s ::= SELECTION\n" % tn) first = 1 for (nr, name, tailname, tailtype, array) in t.fields(): if first: fp.write(" ( ") else: fp.write(" ") first = 0 fp.write("%2d=%-10s %-20s : " % (nr, name, tailname)) if array: fp.write("ARRAY ") fp.write("%s;\n" % tt[tailtype]) fp.write(" )\n") elif isinstance(t, prot_a_enumeration_of): fp.write("%s ::= ENUMERATION-OF(%s)\n" % ( tn, tt[t.base_type()])) else: sys.stderr.write("bad type %s" % repr(t)) sys.exit(1) fp.write("\n") fp.write("# Part three: all requests types.\n") fp.write("\n") rlist = rr.keys() rlist.sort() for rn in rlist: if rn in ["lookup-name-1"]: continue r = defined_request_names[rr[rn]] if only_recommended and not r.recommended(): continue fp.write("%%Request: %d\n" % r.request_nr()) fp.write(" %%Name: %s\n" % rt[r.request_name()]) fp.write(" %%Protocol version: %s\n" % r.protover()) if r.recommended(): fp.write(" %Status: Recommended\n") elif r.experimental(): fp.write(" %Status: Experimental\n") elif r.obsolete(): fp.write(" %Status: Obsolete\n") fp.write(" %%Obsoleted by: %s\n" % r.obsver()) else: sys.stderr.write("No status found\n") sys.exit(1) fp.write("%End Request\n\n") leader = "%s [%d]" % (rt[r.request_name()], r.request_nr()) if len(r.arguments()) == 0: fp.write("%s ( )" % (leader)) elif len(r.arguments()) == 1 and r.arguments()[0] != None: argname, argtype, array = r.arguments()[0] if array: fp.write("%s ( %s : ARRAY %s )" % (leader, argname, tt[argtype])) else: fp.write("%s ( %s : %s )" % (leader, argname, tt[argtype])) else: leader = "%s (( " % leader fp.write(leader) first = 1 for a in r.arguments(): if a == None: continue argname, argtype, array = a if not first: fp.write(";\n" + " " * len(leader)) if array: fp.write("%-10s : ARRAY %s" % (argname, tt[argtype])) else: fp.write("%-10s : %s" % (argname, tt[argtype])) first = 0 fp.write(" ))") fp.write("\n -> ( ") if r.return_type() != None: if r.array(): fp.write("ARRAY ") fp.write(tt[r.return_type()]) fp.write(" );\n\n") fp.write("\n") fp.write("# Part four: all asynchronous messages.\n") fp.write("\n") alist = ar.keys() alist.sort() for an in alist: r = defined_async_names[ar[an]] if only_recommended and not r.recommended(): continue fp.write("%%Async: %d\n" % r.request_nr()) fp.write(" %%Async name: %s\n" % at[r.request_name()]) fp.write(" %%Protocol version: %s\n" % r.protover()) if r.recommended(): fp.write(" %Status: Recommended\n") elif r.experimental(): fp.write(" %Status: Experimental\n") elif r.obsolete(): fp.write(" %Status: Obsolete\n") fp.write(" %%Obsoleted by: %s\n" % r.obsver()) else: sys.stderr.write("No status found\n") sys.exit(1) fp.write("%End Async\n\n") leader = "%s [%d]" % (at[r.request_name()], r.request_nr()) if len(r.arguments()) == 0: fp.write("%s ( )" % (leader)) elif len(r.arguments()) == 1 and r.arguments()[0] != None: argname, argtype, array = r.arguments()[0] if array: fp.write("%s ( %s : ARRAY %s )" % (leader, argname, tt[argtype])) else: fp.write("%s ( %s : %s )" % (leader, argname, tt[argtype])) else: leader = "%s (( " % leader fp.write(leader) first = 1 for a in r.arguments(): if a == None: continue argname, argtype, array = a if not first: fp.write(";\n" + " " * len(leader)) if array: fp.write("%-10s : ARRAY %s" % (argname, tt[argtype])) else: fp.write("%-10s : %s" % (argname, tt[argtype])) first = 0 fp.write(" ))") fp.write("\n\n") fp.close() os.rename(filename + ".tmp", filename) def generate_summary_output(filename): fp = open(filename + ".tmp", "w") fp.write("# This is %s, generated from Protocol-A.texi\n" % filename) fp.write("# by checkargs.py. This file contains the definitions\n") fp.write("# of the types, requests and asynchronous messages in a\n") fp.write("# format that is intended to be both machine-readable and\n") fp.write("# human-readable. The requests may be renamed in the future.\n") fp.write("\n") fp.write("# This file is Copyright (C) 1995-2003, 2005 Lysator ACS.\n") fp.write("# All rights reserved. This file may only be used\n") fp.write("# for testing purposes. Once the file format has\n") fp.write("# stabilized a future version of the file will probably\n") fp.write("# be placed in the public domain.\n") fp.write("\n") fp.write("# This file only contains the recommended stuff.\n") fp.write("\n") fp.write("%%PROTOEDITION %s\n" % set_values["PROTOEDITION"]) fp.write("%%PROTOVER %s\n" % set_values["PROTOVER"]) fp.write("%%LYSKOMDVERSION %s\n\n" % set_values["VERSION"]) tlist = tt.keys() tlist.sort() for tn in tlist: t = defined_types[tn] if not t.recommended(): continue if isinstance(t, prot_a_builtin): fp.write("%%builtin %s\n" % tn) elif isinstance(t, prot_a_simple): if t.array(): fp.write("%s ::= ARRAY %s\n" % (tn, t.base_type())) else: fp.write("%s ::= %s\n" % (tn, t.base_type())) elif isinstance(t, prot_a_alternate): fp.write("%s ::= %s | %s\n" % (tn, t.type_a(), t.type_b())) elif isinstance(t, prot_a_struct): fp.write("%s ::=\n" % tn) first = 1 for field_name, field_type, is_array in t.fields(): if first: fp.write(" ( ") else: fp.write(" ") first = 0 if is_array: fp.write("%-20s : ARRAY %s;\n" % (field_name, field_type)) else: fp.write("%-20s : %s;\n" % (field_name, field_type)) fp.write(" )\n") elif isinstance(t, prot_a_bitstring): fp.write("%s ::= BITSTRING\n" % tn) first = 1 for bit in t.bits(): if first: fp.write(" ( ") else: fp.write(" ") first = 0 fp.write("%s;\n" % bit) fp.write(" )\n") elif isinstance(t, prot_a_selection): fp.write("%s ::= SELECTION\n" % tn) first = 1 for (nr, name, tailname, tailtype, array) in t.fields(): if first: fp.write(" ( ") else: fp.write(" ") first = 0 fp.write("%2d=%-10s %-20s : " % (nr, name, tailname)) if array: fp.write("ARRAY ") fp.write("%s;\n" % tailtype) fp.write(" )\n") elif isinstance(t, prot_a_enumeration_of): fp.write("%s ::= ENUMERATION-OF(%s)\n" % ( tn, t.base_type())) else: sys.stderr.write("bad type %s" % repr(t)) sys.exit(1) rlist = rt.keys() rlist.sort() for rn in rlist: r = defined_request_names[rn] if not r.recommended(): continue fp.write("%%Request: %d\n" % r.request_nr()) fp.write(" %%Name: %s\n" % r.request_name()) fp.write(" %%Stable-Name: %s\n" % rt[r.request_name()]) fp.write(" %%Protocol version: %s\n" % r.protover()) if r.recommended(): fp.write(" %Status: Recommended\n") elif r.experimental(): fp.write(" %Status: Experimental\n") elif r.obsolete(): fp.write(" %Status: Obsolete\n") fp.write(" %%Obsoleted by: %s\n" % r.obsver()) else: sys.stderr.write("No status found\n") sys.exit(1) fp.write("%End Request\n\n") leader = "%s [%d]" % (r.request_name(), r.request_nr()) if len(r.arguments()) == 0: fp.write("%s ( )" % (leader)) elif len(r.arguments()) == 1 and r.arguments()[0] != None: argname, argtype, array = r.arguments()[0] if array: fp.write("%s ( %s : ARRAY %s )" % (leader, argname, argtype)) else: fp.write("%s ( %s : %s )" % (leader, argname, argtype)) else: leader = "%s (( " % leader fp.write(leader) first = 1 for a in r.arguments(): if a == None: continue argname, argtype, array = a if not first: fp.write(";\n" + " " * len(leader)) if array: fp.write("%-10s : ARRAY %s" % (argname, argtype)) else: fp.write("%-10s : %s" % (argname, argtype)) first = 0 fp.write(" ))") fp.write("\n -> ( ") if r.return_type() != None: if r.array(): fp.write("ARRAY ") fp.write(r.return_type()) fp.write(" );\n\n") alist = at.keys() alist.sort() for an in alist: r = defined_async_names[an] if not r.recommended(): continue fp.write("%%Async: %d\n" % r.request_nr()) fp.write(" %%Async name: %s\n" % r.request_name()) fp.write(" %%Async-Stable-Name: %s\n" % at[r.request_name()]) fp.write(" %%Protocol version: %s\n" % r.protover()) if r.recommended(): fp.write(" %Status: Recommended\n") elif r.experimental(): fp.write(" %Status: Experimental\n") elif r.obsolete(): fp.write(" %Status: Obsolete\n") fp.write(" %%Obsoleted by: %s\n" % r.obsver()) else: sys.stderr.write("No status found\n") sys.exit(1) fp.write("%End Async\n\n") leader = "%s [%d]" % (r.request_name(), r.request_nr()) if len(r.arguments()) == 0: fp.write("%s ( )" % (leader)) elif len(r.arguments()) == 1 and r.arguments()[0] != None: argname, argtype, array = r.arguments()[0] if array: fp.write("%s ( %s : ARRAY %s )" % (leader, argname, argtype)) else: fp.write("%s ( %s : %s )" % (leader, argname, argtype)) else: leader = "%s (( " % leader fp.write(leader) first = 1 for a in r.arguments(): if a == None: continue argname, argtype, array = a if not first: fp.write(";\n" + " " * len(leader)) if array: fp.write("%-10s : ARRAY %s" % (argname, argtype)) else: fp.write("%-10s : %s" % (argname, argtype)) first = 0 fp.write(" ))") fp.write("\n\n") fp.close() os.rename(filename + ".tmp", filename) if __name__ == '__main__': l = lexer(sys.argv[1]) ret = l.run() if ret == 0: generate_stable_output("protocol-a-full.txt", 0) generate_stable_output("protocol-a-recommended.txt", 1) generate_summary_output("protocol-a-current.txt") elif ret == 1: sys.exit(1) else: print "ERROR: bad return from run", `ret` sys.exit(2)