tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

event_rpcgen.py (54938B)


      1 #!/usr/bin/env python
      2 #
      3 # Copyright (c) 2005-2007 Niels Provos <provos@citi.umich.edu>
      4 # Copyright (c) 2007-2012 Niels Provos and Nick Mathewson
      5 # All rights reserved.
      6 #
      7 # Generates marshaling code based on libevent.
      8 
      9 # pylint: disable=too-many-lines
     10 # pylint: disable=too-many-branches
     11 # pylint: disable=too-many-public-methods
     12 # pylint: disable=too-many-statements
     13 # pylint: disable=global-statement
     14 
     15 # TODO:
     16 # 1) propagate the arguments/options parsed by argparse down to the
     17 #    instantiated factory objects.
     18 # 2) move the globals into a class that manages execution, including the
     19 #    progress outputs that go to stderr at the moment.
     20 # 3) emit other languages.
     21 
     22 import argparse
     23 import re
     24 import sys
     25 
     26 _NAME = "event_rpcgen.py"
     27 _VERSION = "0.1"
     28 
     29 # Globals
     30 LINE_COUNT = 0
     31 
     32 CPPCOMMENT_RE = re.compile(r"\/\/.*$")
     33 NONIDENT_RE = re.compile(r"\W")
     34 PREPROCESSOR_DEF_RE = re.compile(r"^#define")
     35 STRUCT_REF_RE = re.compile(r"^struct\[(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)\]$")
     36 STRUCT_DEF_RE = re.compile(r"^struct +[a-zA-Z_][a-zA-Z0-9_]* *{$")
     37 WHITESPACE_RE = re.compile(r"\s+")
     38 
     39 HEADER_DIRECT = []
     40 CPP_DIRECT = []
     41 
     42 QUIETLY = False
     43 
     44 
     45 def declare(s):
     46    if not QUIETLY:
     47        print(s)
     48 
     49 
     50 def TranslateList(mylist, mydict):
     51    return [x % mydict for x in mylist]
     52 
     53 
     54 class RpcGenError(Exception):
     55    """An Exception class for parse errors."""
     56 
     57    def __init__(self, why): # pylint: disable=super-init-not-called
     58        self.why = why
     59 
     60    def __str__(self):
     61        return str(self.why)
     62 
     63 
     64 # Holds everything that makes a struct
     65 class Struct(object):
     66    def __init__(self, name):
     67        self._name = name
     68        self._entries = []
     69        self._tags = {}
     70        declare("  Created struct: %s" % name)
     71 
     72    def AddEntry(self, entry):
     73        if entry.Tag() in self._tags:
     74            raise RpcGenError(
     75                'Entry "%s" duplicates tag number %d from "%s" '
     76                "around line %d"
     77                % (entry.Name(), entry.Tag(), self._tags[entry.Tag()], LINE_COUNT)
     78            )
     79        self._entries.append(entry)
     80        self._tags[entry.Tag()] = entry.Name()
     81        declare("    Added entry: %s" % entry.Name())
     82 
     83    def Name(self):
     84        return self._name
     85 
     86    def EntryTagName(self, entry):
     87        """Creates the name inside an enumeration for distinguishing data
     88        types."""
     89        name = "%s_%s" % (self._name, entry.Name())
     90        return name.upper()
     91 
     92    @staticmethod
     93    def PrintIndented(filep, ident, code):
     94        """Takes an array, add indentation to each entry and prints it."""
     95        for entry in code:
     96            filep.write("%s%s\n" % (ident, entry))
     97 
     98 
     99 class StructCCode(Struct):
    100    """ Knows how to generate C code for a struct """
    101 
    102    def __init__(self, name):
    103        Struct.__init__(self, name)
    104 
    105    def PrintTags(self, filep):
    106        """Prints the tag definitions for a structure."""
    107        filep.write("/* Tag definition for %s */\n" % self._name)
    108        filep.write("enum %s_ {\n" % self._name.lower())
    109        for entry in self._entries:
    110            filep.write("  %s=%d,\n" % (self.EntryTagName(entry), entry.Tag()))
    111        filep.write("  %s_MAX_TAGS\n" % (self._name.upper()))
    112        filep.write("};\n\n")
    113 
    114    def PrintForwardDeclaration(self, filep):
    115        filep.write("struct %s;\n" % self._name)
    116 
    117    def PrintDeclaration(self, filep):
    118        filep.write("/* Structure declaration for %s */\n" % self._name)
    119        filep.write("struct %s_access_ {\n" % self._name)
    120        for entry in self._entries:
    121            dcl = entry.AssignDeclaration("(*%s_assign)" % entry.Name())
    122            dcl.extend(entry.GetDeclaration("(*%s_get)" % entry.Name()))
    123            if entry.Array():
    124                dcl.extend(entry.AddDeclaration("(*%s_add)" % entry.Name()))
    125            self.PrintIndented(filep, "  ", dcl)
    126        filep.write("};\n\n")
    127 
    128        filep.write("struct %s {\n" % self._name)
    129        filep.write("  struct %s_access_ *base;\n\n" % self._name)
    130        for entry in self._entries:
    131            dcl = entry.Declaration()
    132            self.PrintIndented(filep, "  ", dcl)
    133        filep.write("\n")
    134        for entry in self._entries:
    135            filep.write("  ev_uint8_t %s_set;\n" % entry.Name())
    136        filep.write("};\n\n")
    137 
    138        filep.write(
    139            """struct %(name)s *%(name)s_new(void);
    140 struct %(name)s *%(name)s_new_with_arg(void *);
    141 void %(name)s_free(struct %(name)s *);
    142 void %(name)s_clear(struct %(name)s *);
    143 void %(name)s_marshal(struct evbuffer *, const struct %(name)s *);
    144 int %(name)s_unmarshal(struct %(name)s *, struct evbuffer *);
    145 int %(name)s_complete(struct %(name)s *);
    146 void evtag_marshal_%(name)s(struct evbuffer *, ev_uint32_t,
    147    const struct %(name)s *);
    148 int evtag_unmarshal_%(name)s(struct evbuffer *, ev_uint32_t,
    149    struct %(name)s *);\n"""
    150            % {"name": self._name}
    151        )
    152 
    153        # Write a setting function of every variable
    154        for entry in self._entries:
    155            self.PrintIndented(
    156                filep, "", entry.AssignDeclaration(entry.AssignFuncName())
    157            )
    158            self.PrintIndented(filep, "", entry.GetDeclaration(entry.GetFuncName()))
    159            if entry.Array():
    160                self.PrintIndented(filep, "", entry.AddDeclaration(entry.AddFuncName()))
    161 
    162        filep.write("/* --- %s done --- */\n\n" % self._name)
    163 
    164    def PrintCode(self, filep):
    165        filep.write(
    166            """/*
    167 * Implementation of %s
    168 */
    169 """
    170            % (self._name)
    171        )
    172 
    173        filep.write(
    174            """
    175 static struct %(name)s_access_ %(name)s_base__ = {
    176 """
    177            % {"name": self._name}
    178        )
    179        for entry in self._entries:
    180            self.PrintIndented(filep, "  ", entry.CodeBase())
    181        filep.write("};\n\n")
    182 
    183        # Creation
    184        filep.write(
    185            """struct %(name)s *
    186 %(name)s_new(void)
    187 {
    188  return %(name)s_new_with_arg(NULL);
    189 }
    190 
    191 struct %(name)s *
    192 %(name)s_new_with_arg(void *unused)
    193 {
    194  struct %(name)s *tmp;
    195  if ((tmp = malloc(sizeof(struct %(name)s))) == NULL) {
    196    event_warn("%%s: malloc", __func__);
    197    return (NULL);
    198  }
    199  tmp->base = &%(name)s_base__;
    200 
    201 """
    202            % {"name": self._name}
    203        )
    204 
    205        for entry in self._entries:
    206            self.PrintIndented(filep, "  ", entry.CodeInitialize("tmp"))
    207            filep.write("  tmp->%s_set = 0;\n\n" % entry.Name())
    208 
    209        filep.write(
    210            """  return (tmp);
    211 }
    212 
    213 """
    214        )
    215 
    216        # Adding
    217        for entry in self._entries:
    218            if entry.Array():
    219                self.PrintIndented(filep, "", entry.CodeAdd())
    220            filep.write("\n")
    221 
    222        # Assigning
    223        for entry in self._entries:
    224            self.PrintIndented(filep, "", entry.CodeAssign())
    225            filep.write("\n")
    226 
    227        # Getting
    228        for entry in self._entries:
    229            self.PrintIndented(filep, "", entry.CodeGet())
    230            filep.write("\n")
    231 
    232        # Clearing
    233        filep.write(
    234            """void
    235 %(name)s_clear(struct %(name)s *tmp)
    236 {
    237 """
    238            % {"name": self._name}
    239        )
    240        for entry in self._entries:
    241            self.PrintIndented(filep, "  ", entry.CodeClear("tmp"))
    242 
    243        filep.write("}\n\n")
    244 
    245        # Freeing
    246        filep.write(
    247            """void
    248 %(name)s_free(struct %(name)s *tmp)
    249 {
    250 """
    251            % {"name": self._name}
    252        )
    253 
    254        for entry in self._entries:
    255            self.PrintIndented(filep, "  ", entry.CodeFree("tmp"))
    256 
    257        filep.write(
    258            """  free(tmp);
    259 }
    260 
    261 """
    262        )
    263 
    264        # Marshaling
    265        filep.write(
    266            """void
    267 %(name)s_marshal(struct evbuffer *evbuf, const struct %(name)s *tmp) {
    268 """
    269            % {"name": self._name}
    270        )
    271        for entry in self._entries:
    272            indent = "  "
    273            # Optional entries do not have to be set
    274            if entry.Optional():
    275                indent += "  "
    276                filep.write("  if (tmp->%s_set) {\n" % entry.Name())
    277            self.PrintIndented(
    278                filep,
    279                indent,
    280                entry.CodeMarshal(
    281                    "evbuf",
    282                    self.EntryTagName(entry),
    283                    entry.GetVarName("tmp"),
    284                    entry.GetVarLen("tmp"),
    285                ),
    286            )
    287            if entry.Optional():
    288                filep.write("  }\n")
    289 
    290        filep.write("}\n\n")
    291 
    292        # Unmarshaling
    293        filep.write(
    294            """int
    295 %(name)s_unmarshal(struct %(name)s *tmp, struct evbuffer *evbuf)
    296 {
    297  ev_uint32_t tag;
    298  while (evbuffer_get_length(evbuf) > 0) {
    299    if (evtag_peek(evbuf, &tag) == -1)
    300      return (-1);
    301    switch (tag) {
    302 
    303 """
    304            % {"name": self._name}
    305        )
    306        for entry in self._entries:
    307            filep.write("      case %s:\n" % (self.EntryTagName(entry)))
    308            if not entry.Array():
    309                filep.write(
    310                    """        if (tmp->%s_set)
    311          return (-1);
    312 """
    313                    % (entry.Name())
    314                )
    315 
    316            self.PrintIndented(
    317                filep,
    318                "        ",
    319                entry.CodeUnmarshal(
    320                    "evbuf",
    321                    self.EntryTagName(entry),
    322                    entry.GetVarName("tmp"),
    323                    entry.GetVarLen("tmp"),
    324                ),
    325            )
    326 
    327            filep.write(
    328                """        tmp->%s_set = 1;
    329        break;
    330 """
    331                % (entry.Name())
    332            )
    333        filep.write(
    334            """      default:
    335        return -1;
    336    }
    337  }
    338 
    339 """
    340        )
    341        # Check if it was decoded completely
    342        filep.write(
    343            """  if (%(name)s_complete(tmp) == -1)
    344    return (-1);
    345  return (0);
    346 }
    347 """
    348            % {"name": self._name}
    349        )
    350 
    351        # Checking if a structure has all the required data
    352        filep.write(
    353            """
    354 int
    355 %(name)s_complete(struct %(name)s *msg)
    356 {
    357 """
    358            % {"name": self._name}
    359        )
    360        for entry in self._entries:
    361            if not entry.Optional():
    362                code = [
    363                    """if (!msg->%(name)s_set)
    364    return (-1);"""
    365                ]
    366                code = TranslateList(code, entry.GetTranslation())
    367                self.PrintIndented(filep, "  ", code)
    368 
    369            self.PrintIndented(
    370                filep, "  ", entry.CodeComplete("msg", entry.GetVarName("msg"))
    371            )
    372        filep.write(
    373            """  return (0);
    374 }
    375 """
    376        )
    377 
    378        # Complete message unmarshaling
    379        filep.write(
    380            """
    381 int
    382 evtag_unmarshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t need_tag,
    383  struct %(name)s *msg)
    384 {
    385  ev_uint32_t tag;
    386  int res = -1;
    387 
    388  struct evbuffer *tmp = evbuffer_new();
    389 
    390  if (evtag_unmarshal(evbuf, &tag, tmp) == -1 || tag != need_tag)
    391    goto error;
    392 
    393  if (%(name)s_unmarshal(msg, tmp) == -1)
    394    goto error;
    395 
    396  res = 0;
    397 
    398 error:
    399  evbuffer_free(tmp);
    400  return (res);
    401 }
    402 """
    403            % {"name": self._name}
    404        )
    405 
    406        # Complete message marshaling
    407        filep.write(
    408            """
    409 void
    410 evtag_marshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t tag,
    411    const struct %(name)s *msg)
    412 {
    413  struct evbuffer *buf_ = evbuffer_new();
    414  assert(buf_ != NULL);
    415  %(name)s_marshal(buf_, msg);
    416  evtag_marshal_buffer(evbuf, tag, buf_);
    417  evbuffer_free(buf_);
    418 }
    419 
    420 """
    421            % {"name": self._name}
    422        )
    423 
    424 
    425 class Entry(object):
    426    def __init__(self, ent_type, name, tag):
    427        self._type = ent_type
    428        self._name = name
    429        self._tag = int(tag)
    430        self._ctype = ent_type
    431        self._optional = False
    432        self._can_be_array = False
    433        self._array = False
    434        self._line_count = -1
    435        self._struct = None
    436        self._refname = None
    437 
    438        self._optpointer = True
    439        self._optaddarg = True
    440 
    441    @staticmethod
    442    def GetInitializer():
    443        raise NotImplementedError("Entry does not provide an initializer")
    444 
    445    def SetStruct(self, struct):
    446        self._struct = struct
    447 
    448    def LineCount(self):
    449        assert self._line_count != -1
    450        return self._line_count
    451 
    452    def SetLineCount(self, number):
    453        self._line_count = number
    454 
    455    def Array(self):
    456        return self._array
    457 
    458    def Optional(self):
    459        return self._optional
    460 
    461    def Tag(self):
    462        return self._tag
    463 
    464    def Name(self):
    465        return self._name
    466 
    467    def Type(self):
    468        return self._type
    469 
    470    def MakeArray(self):
    471        self._array = True
    472 
    473    def MakeOptional(self):
    474        self._optional = True
    475 
    476    def Verify(self):
    477        if self.Array() and not self._can_be_array:
    478            raise RpcGenError(
    479                'Entry "%s" cannot be created as an array '
    480                "around line %d" % (self._name, self.LineCount())
    481            )
    482        if not self._struct:
    483            raise RpcGenError(
    484                'Entry "%s" does not know which struct it belongs to '
    485                "around line %d" % (self._name, self.LineCount())
    486            )
    487        if self._optional and self._array:
    488            raise RpcGenError(
    489                'Entry "%s" has illegal combination of optional and array '
    490                "around line %d" % (self._name, self.LineCount())
    491            )
    492 
    493    def GetTranslation(self, extradict=None):
    494        if extradict is None:
    495            extradict = {}
    496        mapping = {
    497            "parent_name": self._struct.Name(),
    498            "name": self._name,
    499            "ctype": self._ctype,
    500            "refname": self._refname,
    501            "optpointer": self._optpointer and "*" or "",
    502            "optreference": self._optpointer and "&" or "",
    503            "optaddarg": self._optaddarg and ", const %s value" % self._ctype or "",
    504        }
    505        for (k, v) in list(extradict.items()):
    506            mapping[k] = v
    507 
    508        return mapping
    509 
    510    def GetVarName(self, var):
    511        return "%(var)s->%(name)s_data" % self.GetTranslation({"var": var})
    512 
    513    def GetVarLen(self, _var):
    514        return "sizeof(%s)" % self._ctype
    515 
    516    def GetFuncName(self):
    517        return "%s_%s_get" % (self._struct.Name(), self._name)
    518 
    519    def GetDeclaration(self, funcname):
    520        code = [
    521            "int %s(struct %s *, %s *);" % (funcname, self._struct.Name(), self._ctype)
    522        ]
    523        return code
    524 
    525    def CodeGet(self):
    526        code = """int
    527 %(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, %(ctype)s *value)
    528 {
    529  if (msg->%(name)s_set != 1)
    530    return (-1);
    531  *value = msg->%(name)s_data;
    532  return (0);
    533 }"""
    534        code = code % self.GetTranslation()
    535        return code.split("\n")
    536 
    537    def AssignFuncName(self):
    538        return "%s_%s_assign" % (self._struct.Name(), self._name)
    539 
    540    def AddFuncName(self):
    541        return "%s_%s_add" % (self._struct.Name(), self._name)
    542 
    543    def AssignDeclaration(self, funcname):
    544        code = [
    545            "int %s(struct %s *, const %s);"
    546            % (funcname, self._struct.Name(), self._ctype)
    547        ]
    548        return code
    549 
    550    def CodeAssign(self):
    551        code = [
    552            "int",
    553            "%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,"
    554            " const %(ctype)s value)",
    555            "{",
    556            "  msg->%(name)s_set = 1;",
    557            "  msg->%(name)s_data = value;",
    558            "  return (0);",
    559            "}",
    560        ]
    561        code = "\n".join(code)
    562        code = code % self.GetTranslation()
    563        return code.split("\n")
    564 
    565    def CodeClear(self, structname):
    566        code = ["%s->%s_set = 0;" % (structname, self.Name())]
    567 
    568        return code
    569 
    570    @staticmethod
    571    def CodeComplete(_structname, _var_name):
    572        return []
    573 
    574    @staticmethod
    575    def CodeFree(_name):
    576        return []
    577 
    578    def CodeBase(self):
    579        code = ["%(parent_name)s_%(name)s_assign,", "%(parent_name)s_%(name)s_get,"]
    580        if self.Array():
    581            code.append("%(parent_name)s_%(name)s_add,")
    582 
    583        code = "\n".join(code)
    584        code = code % self.GetTranslation()
    585        return code.split("\n")
    586 
    587 
    588 class EntryBytes(Entry):
    589    def __init__(self, ent_type, name, tag, length):
    590        # Init base class
    591        super(EntryBytes, self).__init__(ent_type, name, tag)
    592 
    593        self._length = length
    594        self._ctype = "ev_uint8_t"
    595 
    596    @staticmethod
    597    def GetInitializer():
    598        return "NULL"
    599 
    600    def GetVarLen(self, _var):
    601        return "(%s)" % self._length
    602 
    603    @staticmethod
    604    def CodeArrayAdd(varname, _value):
    605        # XXX: copy here
    606        return ["%(varname)s = NULL;" % {"varname": varname}]
    607 
    608    def GetDeclaration(self, funcname):
    609        code = [
    610            "int %s(struct %s *, %s **);" % (funcname, self._struct.Name(), self._ctype)
    611        ]
    612        return code
    613 
    614    def AssignDeclaration(self, funcname):
    615        code = [
    616            "int %s(struct %s *, const %s *);"
    617            % (funcname, self._struct.Name(), self._ctype)
    618        ]
    619        return code
    620 
    621    def Declaration(self):
    622        dcl = ["ev_uint8_t %s_data[%s];" % (self._name, self._length)]
    623 
    624        return dcl
    625 
    626    def CodeGet(self):
    627        name = self._name
    628        code = [
    629            "int",
    630            "%s_%s_get(struct %s *msg, %s **value)"
    631            % (self._struct.Name(), name, self._struct.Name(), self._ctype),
    632            "{",
    633            "  if (msg->%s_set != 1)" % name,
    634            "    return (-1);",
    635            "  *value = msg->%s_data;" % name,
    636            "  return (0);",
    637            "}",
    638        ]
    639        return code
    640 
    641    def CodeAssign(self):
    642        name = self._name
    643        code = [
    644            "int",
    645            "%s_%s_assign(struct %s *msg, const %s *value)"
    646            % (self._struct.Name(), name, self._struct.Name(), self._ctype),
    647            "{",
    648            "  msg->%s_set = 1;" % name,
    649            "  memcpy(msg->%s_data, value, %s);" % (name, self._length),
    650            "  return (0);",
    651            "}",
    652        ]
    653        return code
    654 
    655    def CodeUnmarshal(self, buf, tag_name, var_name, var_len):
    656        code = [
    657            "if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, "
    658            "%(var)s, %(varlen)s) == -1) {",
    659            '  event_warnx("%%s: failed to unmarshal %(name)s", __func__);',
    660            "  return (-1);",
    661            "}",
    662        ]
    663        return TranslateList(
    664            code,
    665            self.GetTranslation(
    666                {"var": var_name, "varlen": var_len, "buf": buf, "tag": tag_name}
    667            ),
    668        )
    669 
    670    @staticmethod
    671    def CodeMarshal(buf, tag_name, var_name, var_len):
    672        code = ["evtag_marshal(%s, %s, %s, %s);" % (buf, tag_name, var_name, var_len)]
    673        return code
    674 
    675    def CodeClear(self, structname):
    676        code = [
    677            "%s->%s_set = 0;" % (structname, self.Name()),
    678            "memset(%s->%s_data, 0, sizeof(%s->%s_data));"
    679            % (structname, self._name, structname, self._name),
    680        ]
    681 
    682        return code
    683 
    684    def CodeInitialize(self, name):
    685        code = [
    686            "memset(%s->%s_data, 0, sizeof(%s->%s_data));"
    687            % (name, self._name, name, self._name)
    688        ]
    689        return code
    690 
    691    def Verify(self):
    692        if not self._length:
    693            raise RpcGenError(
    694                'Entry "%s" needs a length '
    695                "around line %d" % (self._name, self.LineCount())
    696            )
    697 
    698        super(EntryBytes, self).Verify()
    699 
    700 
    701 class EntryInt(Entry):
    702    def __init__(self, ent_type, name, tag, bits=32):
    703        # Init base class
    704        super(EntryInt, self).__init__(ent_type, name, tag)
    705 
    706        self._can_be_array = True
    707        if bits == 32:
    708            self._ctype = "ev_uint32_t"
    709            self._marshal_type = "int"
    710        if bits == 64:
    711            self._ctype = "ev_uint64_t"
    712            self._marshal_type = "int64"
    713 
    714    @staticmethod
    715    def GetInitializer():
    716        return "0"
    717 
    718    @staticmethod
    719    def CodeArrayFree(_var):
    720        return []
    721 
    722    @staticmethod
    723    def CodeArrayAssign(varname, srcvar):
    724        return ["%(varname)s = %(srcvar)s;" % {"varname": varname, "srcvar": srcvar}]
    725 
    726    @staticmethod
    727    def CodeArrayAdd(varname, value):
    728        """Returns a new entry of this type."""
    729        return ["%(varname)s = %(value)s;" % {"varname": varname, "value": value}]
    730 
    731    def CodeUnmarshal(self, buf, tag_name, var_name, _var_len):
    732        code = [
    733            "if (evtag_unmarshal_%(ma)s(%(buf)s, %(tag)s, &%(var)s) == -1) {",
    734            '  event_warnx("%%s: failed to unmarshal %(name)s", __func__);',
    735            "  return (-1);",
    736            "}",
    737        ]
    738        code = "\n".join(code) % self.GetTranslation(
    739            {"ma": self._marshal_type, "buf": buf, "tag": tag_name, "var": var_name}
    740        )
    741        return code.split("\n")
    742 
    743    def CodeMarshal(self, buf, tag_name, var_name, _var_len):
    744        code = [
    745            "evtag_marshal_%s(%s, %s, %s);"
    746            % (self._marshal_type, buf, tag_name, var_name)
    747        ]
    748        return code
    749 
    750    def Declaration(self):
    751        dcl = ["%s %s_data;" % (self._ctype, self._name)]
    752 
    753        return dcl
    754 
    755    def CodeInitialize(self, name):
    756        code = ["%s->%s_data = 0;" % (name, self._name)]
    757        return code
    758 
    759 
    760 class EntryString(Entry):
    761    def __init__(self, ent_type, name, tag):
    762        # Init base class
    763        super(EntryString, self).__init__(ent_type, name, tag)
    764 
    765        self._can_be_array = True
    766        self._ctype = "char *"
    767 
    768    @staticmethod
    769    def GetInitializer():
    770        return "NULL"
    771 
    772    @staticmethod
    773    def CodeArrayFree(varname):
    774        code = ["if (%(var)s != NULL) free(%(var)s);"]
    775 
    776        return TranslateList(code, {"var": varname})
    777 
    778    @staticmethod
    779    def CodeArrayAssign(varname, srcvar):
    780        code = [
    781            "if (%(var)s != NULL)",
    782            "  free(%(var)s);",
    783            "%(var)s = strdup(%(srcvar)s);",
    784            "if (%(var)s == NULL) {",
    785            '  event_warnx("%%s: strdup", __func__);',
    786            "  return (-1);",
    787            "}",
    788        ]
    789 
    790        return TranslateList(code, {"var": varname, "srcvar": srcvar})
    791 
    792    @staticmethod
    793    def CodeArrayAdd(varname, value):
    794        code = [
    795            "if (%(value)s != NULL) {",
    796            "  %(var)s = strdup(%(value)s);",
    797            "  if (%(var)s == NULL) {",
    798            "    goto error;",
    799            "  }",
    800            "} else {",
    801            "  %(var)s = NULL;",
    802            "}",
    803        ]
    804 
    805        return TranslateList(code, {"var": varname, "value": value})
    806 
    807    def GetVarLen(self, var):
    808        return "strlen(%s)" % self.GetVarName(var)
    809 
    810    @staticmethod
    811    def CodeMakeInitalize(varname):
    812        return "%(varname)s = NULL;" % {"varname": varname}
    813 
    814    def CodeAssign(self):
    815        code = """int
    816 %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,
    817    const %(ctype)s value)
    818 {
    819  if (msg->%(name)s_data != NULL)
    820    free(msg->%(name)s_data);
    821  if ((msg->%(name)s_data = strdup(value)) == NULL)
    822    return (-1);
    823  msg->%(name)s_set = 1;
    824  return (0);
    825 }""" % (
    826            self.GetTranslation()
    827        )
    828 
    829        return code.split("\n")
    830 
    831    def CodeUnmarshal(self, buf, tag_name, var_name, _var_len):
    832        code = [
    833            "if (evtag_unmarshal_string(%(buf)s, %(tag)s, &%(var)s) == -1) {",
    834            '  event_warnx("%%s: failed to unmarshal %(name)s", __func__);',
    835            "  return (-1);",
    836            "}",
    837        ]
    838        code = "\n".join(code) % self.GetTranslation(
    839            {"buf": buf, "tag": tag_name, "var": var_name}
    840        )
    841        return code.split("\n")
    842 
    843    @staticmethod
    844    def CodeMarshal(buf, tag_name, var_name, _var_len):
    845        code = ["evtag_marshal_string(%s, %s, %s);" % (buf, tag_name, var_name)]
    846        return code
    847 
    848    def CodeClear(self, structname):
    849        code = [
    850            "if (%s->%s_set == 1) {" % (structname, self.Name()),
    851            "  free(%s->%s_data);" % (structname, self.Name()),
    852            "  %s->%s_data = NULL;" % (structname, self.Name()),
    853            "  %s->%s_set = 0;" % (structname, self.Name()),
    854            "}",
    855        ]
    856 
    857        return code
    858 
    859    def CodeInitialize(self, name):
    860        code = ["%s->%s_data = NULL;" % (name, self._name)]
    861        return code
    862 
    863    def CodeFree(self, name):
    864        code = [
    865            "if (%s->%s_data != NULL)" % (name, self._name),
    866            "    free (%s->%s_data);" % (name, self._name),
    867        ]
    868 
    869        return code
    870 
    871    def Declaration(self):
    872        dcl = ["char *%s_data;" % self._name]
    873 
    874        return dcl
    875 
    876 
    877 class EntryStruct(Entry):
    878    def __init__(self, ent_type, name, tag, refname):
    879        # Init base class
    880        super(EntryStruct, self).__init__(ent_type, name, tag)
    881 
    882        self._optpointer = False
    883        self._can_be_array = True
    884        self._refname = refname
    885        self._ctype = "struct %s*" % refname
    886        self._optaddarg = False
    887 
    888    def GetInitializer(self):
    889        return "NULL"
    890 
    891    def GetVarLen(self, _var):
    892        return "-1"
    893 
    894    def CodeArrayAdd(self, varname, _value):
    895        code = [
    896            "%(varname)s = %(refname)s_new();",
    897            "if (%(varname)s == NULL)",
    898            "  goto error;",
    899        ]
    900 
    901        return TranslateList(code, self.GetTranslation({"varname": varname}))
    902 
    903    def CodeArrayFree(self, var):
    904        code = ["%(refname)s_free(%(var)s);" % self.GetTranslation({"var": var})]
    905        return code
    906 
    907    def CodeArrayAssign(self, var, srcvar):
    908        code = [
    909            "int had_error = 0;",
    910            "struct evbuffer *tmp = NULL;",
    911            "%(refname)s_clear(%(var)s);",
    912            "if ((tmp = evbuffer_new()) == NULL) {",
    913            '  event_warn("%%s: evbuffer_new()", __func__);',
    914            "  had_error = 1;",
    915            "  goto done;",
    916            "}",
    917            "%(refname)s_marshal(tmp, %(srcvar)s);",
    918            "if (%(refname)s_unmarshal(%(var)s, tmp) == -1) {",
    919            '  event_warnx("%%s: %(refname)s_unmarshal", __func__);',
    920            "  had_error = 1;",
    921            "  goto done;",
    922            "}",
    923            "done:",
    924            "if (tmp != NULL)",
    925            "  evbuffer_free(tmp);",
    926            "if (had_error) {",
    927            "  %(refname)s_clear(%(var)s);",
    928            "  return (-1);",
    929            "}",
    930        ]
    931 
    932        return TranslateList(code, self.GetTranslation({"var": var, "srcvar": srcvar}))
    933 
    934    def CodeGet(self):
    935        name = self._name
    936        code = [
    937            "int",
    938            "%s_%s_get(struct %s *msg, %s *value)"
    939            % (self._struct.Name(), name, self._struct.Name(), self._ctype),
    940            "{",
    941            "  if (msg->%s_set != 1) {" % name,
    942            "    msg->%s_data = %s_new();" % (name, self._refname),
    943            "    if (msg->%s_data == NULL)" % name,
    944            "      return (-1);",
    945            "    msg->%s_set = 1;" % name,
    946            "  }",
    947            "  *value = msg->%s_data;" % name,
    948            "  return (0);",
    949            "}",
    950        ]
    951        return code
    952 
    953    def CodeAssign(self):
    954        code = (
    955            """int
    956 %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,
    957    const %(ctype)s value)
    958 {
    959   struct evbuffer *tmp = NULL;
    960   if (msg->%(name)s_set) {
    961     %(refname)s_clear(msg->%(name)s_data);
    962     msg->%(name)s_set = 0;
    963   } else {
    964     msg->%(name)s_data = %(refname)s_new();
    965     if (msg->%(name)s_data == NULL) {
    966       event_warn("%%s: %(refname)s_new()", __func__);
    967       goto error;
    968     }
    969   }
    970   if ((tmp = evbuffer_new()) == NULL) {
    971     event_warn("%%s: evbuffer_new()", __func__);
    972     goto error;
    973   }
    974   %(refname)s_marshal(tmp, value);
    975   if (%(refname)s_unmarshal(msg->%(name)s_data, tmp) == -1) {
    976     event_warnx("%%s: %(refname)s_unmarshal", __func__);
    977     goto error;
    978   }
    979   msg->%(name)s_set = 1;
    980   evbuffer_free(tmp);
    981   return (0);
    982 error:
    983   if (tmp != NULL)
    984     evbuffer_free(tmp);
    985   if (msg->%(name)s_data != NULL) {
    986     %(refname)s_free(msg->%(name)s_data);
    987     msg->%(name)s_data = NULL;
    988   }
    989   return (-1);
    990 }"""
    991            % self.GetTranslation()
    992        )
    993        return code.split("\n")
    994 
    995    def CodeComplete(self, structname, var_name):
    996        code = [
    997            "if (%(structname)s->%(name)s_set && "
    998            "%(refname)s_complete(%(var)s) == -1)",
    999            "  return (-1);",
   1000        ]
   1001 
   1002        return TranslateList(
   1003            code, self.GetTranslation({"structname": structname, "var": var_name})
   1004        )
   1005 
   1006    def CodeUnmarshal(self, buf, tag_name, var_name, _var_len):
   1007        code = [
   1008            "%(var)s = %(refname)s_new();",
   1009            "if (%(var)s == NULL)",
   1010            "  return (-1);",
   1011            "if (evtag_unmarshal_%(refname)s(%(buf)s, %(tag)s, ",
   1012            "    %(var)s) == -1) {",
   1013            '  event_warnx("%%s: failed to unmarshal %(name)s", __func__);',
   1014            "  return (-1);",
   1015            "}",
   1016        ]
   1017        code = "\n".join(code) % self.GetTranslation(
   1018            {"buf": buf, "tag": tag_name, "var": var_name}
   1019        )
   1020        return code.split("\n")
   1021 
   1022    def CodeMarshal(self, buf, tag_name, var_name, _var_len):
   1023        code = [
   1024            "evtag_marshal_%s(%s, %s, %s);" % (self._refname, buf, tag_name, var_name)
   1025        ]
   1026        return code
   1027 
   1028    def CodeClear(self, structname):
   1029        code = [
   1030            "if (%s->%s_set == 1) {" % (structname, self.Name()),
   1031            "  %s_free(%s->%s_data);" % (self._refname, structname, self.Name()),
   1032            "  %s->%s_data = NULL;" % (structname, self.Name()),
   1033            "  %s->%s_set = 0;" % (structname, self.Name()),
   1034            "}",
   1035        ]
   1036 
   1037        return code
   1038 
   1039    def CodeInitialize(self, name):
   1040        code = ["%s->%s_data = NULL;" % (name, self._name)]
   1041        return code
   1042 
   1043    def CodeFree(self, name):
   1044        code = [
   1045            "if (%s->%s_data != NULL)" % (name, self._name),
   1046            "    %s_free(%s->%s_data);" % (self._refname, name, self._name),
   1047        ]
   1048 
   1049        return code
   1050 
   1051    def Declaration(self):
   1052        dcl = ["%s %s_data;" % (self._ctype, self._name)]
   1053 
   1054        return dcl
   1055 
   1056 
   1057 class EntryVarBytes(Entry):
   1058    def __init__(self, ent_type, name, tag):
   1059        # Init base class
   1060        super(EntryVarBytes, self).__init__(ent_type, name, tag)
   1061 
   1062        self._ctype = "ev_uint8_t *"
   1063 
   1064    @staticmethod
   1065    def GetInitializer():
   1066        return "NULL"
   1067 
   1068    def GetVarLen(self, var):
   1069        return "%(var)s->%(name)s_length" % self.GetTranslation({"var": var})
   1070 
   1071    @staticmethod
   1072    def CodeArrayAdd(varname, _value):
   1073        # xxx: copy
   1074        return ["%(varname)s = NULL;" % {"varname": varname}]
   1075 
   1076    def GetDeclaration(self, funcname):
   1077        code = [
   1078            "int %s(struct %s *, %s *, ev_uint32_t *);"
   1079            % (funcname, self._struct.Name(), self._ctype)
   1080        ]
   1081        return code
   1082 
   1083    def AssignDeclaration(self, funcname):
   1084        code = [
   1085            "int %s(struct %s *, const %s, ev_uint32_t);"
   1086            % (funcname, self._struct.Name(), self._ctype)
   1087        ]
   1088        return code
   1089 
   1090    def CodeAssign(self):
   1091        name = self._name
   1092        code = [
   1093            "int",
   1094            "%s_%s_assign(struct %s *msg, "
   1095            "const %s value, ev_uint32_t len)"
   1096            % (self._struct.Name(), name, self._struct.Name(), self._ctype),
   1097            "{",
   1098            "  if (msg->%s_data != NULL)" % name,
   1099            "    free (msg->%s_data);" % name,
   1100            "  msg->%s_data = malloc(len);" % name,
   1101            "  if (msg->%s_data == NULL)" % name,
   1102            "    return (-1);",
   1103            "  msg->%s_set = 1;" % name,
   1104            "  msg->%s_length = len;" % name,
   1105            "  memcpy(msg->%s_data, value, len);" % name,
   1106            "  return (0);",
   1107            "}",
   1108        ]
   1109        return code
   1110 
   1111    def CodeGet(self):
   1112        name = self._name
   1113        code = [
   1114            "int",
   1115            "%s_%s_get(struct %s *msg, %s *value, ev_uint32_t *plen)"
   1116            % (self._struct.Name(), name, self._struct.Name(), self._ctype),
   1117            "{",
   1118            "  if (msg->%s_set != 1)" % name,
   1119            "    return (-1);",
   1120            "  *value = msg->%s_data;" % name,
   1121            "  *plen = msg->%s_length;" % name,
   1122            "  return (0);",
   1123            "}",
   1124        ]
   1125        return code
   1126 
   1127    def CodeUnmarshal(self, buf, tag_name, var_name, var_len):
   1128        code = [
   1129            "if (evtag_payload_length(%(buf)s, &%(varlen)s) == -1)",
   1130            "  return (-1);",
   1131            # We do not want DoS opportunities
   1132            "if (%(varlen)s > evbuffer_get_length(%(buf)s))",
   1133            "  return (-1);",
   1134            "if ((%(var)s = malloc(%(varlen)s)) == NULL)",
   1135            "  return (-1);",
   1136            "if (evtag_unmarshal_fixed(%(buf)s, %(tag)s, %(var)s, "
   1137            "%(varlen)s) == -1) {",
   1138            '  event_warnx("%%s: failed to unmarshal %(name)s", __func__);',
   1139            "  return (-1);",
   1140            "}",
   1141        ]
   1142        code = "\n".join(code) % self.GetTranslation(
   1143            {"buf": buf, "tag": tag_name, "var": var_name, "varlen": var_len}
   1144        )
   1145        return code.split("\n")
   1146 
   1147    @staticmethod
   1148    def CodeMarshal(buf, tag_name, var_name, var_len):
   1149        code = ["evtag_marshal(%s, %s, %s, %s);" % (buf, tag_name, var_name, var_len)]
   1150        return code
   1151 
   1152    def CodeClear(self, structname):
   1153        code = [
   1154            "if (%s->%s_set == 1) {" % (structname, self.Name()),
   1155            "  free (%s->%s_data);" % (structname, self.Name()),
   1156            "  %s->%s_data = NULL;" % (structname, self.Name()),
   1157            "  %s->%s_length = 0;" % (structname, self.Name()),
   1158            "  %s->%s_set = 0;" % (structname, self.Name()),
   1159            "}",
   1160        ]
   1161 
   1162        return code
   1163 
   1164    def CodeInitialize(self, name):
   1165        code = [
   1166            "%s->%s_data = NULL;" % (name, self._name),
   1167            "%s->%s_length = 0;" % (name, self._name),
   1168        ]
   1169        return code
   1170 
   1171    def CodeFree(self, name):
   1172        code = [
   1173            "if (%s->%s_data != NULL)" % (name, self._name),
   1174            "    free(%s->%s_data);" % (name, self._name),
   1175        ]
   1176 
   1177        return code
   1178 
   1179    def Declaration(self):
   1180        dcl = [
   1181            "ev_uint8_t *%s_data;" % self._name,
   1182            "ev_uint32_t %s_length;" % self._name,
   1183        ]
   1184 
   1185        return dcl
   1186 
   1187 
   1188 class EntryArray(Entry):
   1189    _index = None
   1190 
   1191    def __init__(self, entry):
   1192        # Init base class
   1193        super(EntryArray, self).__init__(entry._type, entry._name, entry._tag)
   1194 
   1195        self._entry = entry
   1196        self._refname = entry._refname
   1197        self._ctype = self._entry._ctype
   1198        self._optional = True
   1199        self._optpointer = self._entry._optpointer
   1200        self._optaddarg = self._entry._optaddarg
   1201 
   1202        # provide a new function for accessing the variable name
   1203        def GetVarName(var_name):
   1204            return "%(var)s->%(name)s_data[%(index)s]" % self._entry.GetTranslation(
   1205                {"var": var_name, "index": self._index}
   1206            )
   1207 
   1208        self._entry.GetVarName = GetVarName
   1209 
   1210    def GetInitializer(self):
   1211        return "NULL"
   1212 
   1213    def GetVarName(self, var):
   1214        return var
   1215 
   1216    def GetVarLen(self, _var_name):
   1217        return "-1"
   1218 
   1219    def GetDeclaration(self, funcname):
   1220        """Allows direct access to elements of the array."""
   1221        code = [
   1222            "int %(funcname)s(struct %(parent_name)s *, int, %(ctype)s *);"
   1223            % self.GetTranslation({"funcname": funcname})
   1224        ]
   1225        return code
   1226 
   1227    def AssignDeclaration(self, funcname):
   1228        code = [
   1229            "int %s(struct %s *, int, const %s);"
   1230            % (funcname, self._struct.Name(), self._ctype)
   1231        ]
   1232        return code
   1233 
   1234    def AddDeclaration(self, funcname):
   1235        code = [
   1236            "%(ctype)s %(optpointer)s "
   1237            "%(funcname)s(struct %(parent_name)s *msg%(optaddarg)s);"
   1238            % self.GetTranslation({"funcname": funcname})
   1239        ]
   1240        return code
   1241 
   1242    def CodeGet(self):
   1243        code = """int
   1244 %(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, int offset,
   1245    %(ctype)s *value)
   1246 {
   1247  if (!msg->%(name)s_set || offset < 0 || offset >= msg->%(name)s_length)
   1248    return (-1);
   1249  *value = msg->%(name)s_data[offset];
   1250  return (0);
   1251 }
   1252 """ % (
   1253            self.GetTranslation()
   1254        )
   1255 
   1256        return code.splitlines()
   1257 
   1258    def CodeAssign(self):
   1259        code = [
   1260            "int",
   1261            "%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, int off,",
   1262            "  const %(ctype)s value)",
   1263            "{",
   1264            "  if (!msg->%(name)s_set || off < 0 || off >= msg->%(name)s_length)",
   1265            "    return (-1);",
   1266            "",
   1267            "  {",
   1268        ]
   1269        code = TranslateList(code, self.GetTranslation())
   1270 
   1271        codearrayassign = self._entry.CodeArrayAssign(
   1272            "msg->%(name)s_data[off]" % self.GetTranslation(), "value"
   1273        )
   1274        code += ["    " + x for x in codearrayassign]
   1275 
   1276        code += TranslateList(["  }", "  return (0);", "}"], self.GetTranslation())
   1277 
   1278        return code
   1279 
   1280    def CodeAdd(self):
   1281        codearrayadd = self._entry.CodeArrayAdd(
   1282            "msg->%(name)s_data[msg->%(name)s_length - 1]" % self.GetTranslation(),
   1283            "value",
   1284        )
   1285        code = [
   1286            "static int",
   1287            "%(parent_name)s_%(name)s_expand_to_hold_more("
   1288            "struct %(parent_name)s *msg)",
   1289            "{",
   1290            "  int tobe_allocated = msg->%(name)s_num_allocated;",
   1291            "  %(ctype)s* new_data = NULL;",
   1292            "  tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1;",
   1293            "  new_data = (%(ctype)s*) realloc(msg->%(name)s_data,",
   1294            "      tobe_allocated * sizeof(%(ctype)s));",
   1295            "  if (new_data == NULL)",
   1296            "    return -1;",
   1297            "  msg->%(name)s_data = new_data;",
   1298            "  msg->%(name)s_num_allocated = tobe_allocated;",
   1299            "  return 0;",
   1300            "}",
   1301            "",
   1302            "%(ctype)s %(optpointer)s",
   1303            "%(parent_name)s_%(name)s_add(struct %(parent_name)s *msg%(optaddarg)s)",
   1304            "{",
   1305            "  if (++msg->%(name)s_length >= msg->%(name)s_num_allocated) {",
   1306            "    if (%(parent_name)s_%(name)s_expand_to_hold_more(msg)<0)",
   1307            "      goto error;",
   1308            "  }",
   1309        ]
   1310 
   1311        code = TranslateList(code, self.GetTranslation())
   1312 
   1313        code += ["  " + x for x in codearrayadd]
   1314 
   1315        code += TranslateList(
   1316            [
   1317                "  msg->%(name)s_set = 1;",
   1318                "  return %(optreference)s(msg->%(name)s_data["
   1319                "msg->%(name)s_length - 1]);",
   1320                "error:",
   1321                "  --msg->%(name)s_length;",
   1322                "  return (NULL);",
   1323                "}",
   1324            ],
   1325            self.GetTranslation(),
   1326        )
   1327 
   1328        return code
   1329 
   1330    def CodeComplete(self, structname, var_name):
   1331        self._index = "i"
   1332        tmp = self._entry.CodeComplete(structname, self._entry.GetVarName(var_name))
   1333        # skip the whole loop if there is nothing to check
   1334        if not tmp:
   1335            return []
   1336 
   1337        translate = self.GetTranslation({"structname": structname})
   1338        code = [
   1339            "{",
   1340            "  int i;",
   1341            "  for (i = 0; i < %(structname)s->%(name)s_length; ++i) {",
   1342        ]
   1343 
   1344        code = TranslateList(code, translate)
   1345 
   1346        code += ["    " + x for x in tmp]
   1347 
   1348        code += ["  }", "}"]
   1349 
   1350        return code
   1351 
   1352    def CodeUnmarshal(self, buf, tag_name, var_name, _var_len):
   1353        translate = self.GetTranslation(
   1354            {
   1355                "var": var_name,
   1356                "buf": buf,
   1357                "tag": tag_name,
   1358                "init": self._entry.GetInitializer(),
   1359            }
   1360        )
   1361        code = [
   1362            "if (%(var)s->%(name)s_length >= %(var)s->%(name)s_num_allocated &&",
   1363            "    %(parent_name)s_%(name)s_expand_to_hold_more(%(var)s) < 0) {",
   1364            '  puts("HEY NOW");',
   1365            "  return (-1);",
   1366            "}",
   1367        ]
   1368 
   1369        # the unmarshal code directly returns
   1370        code = TranslateList(code, translate)
   1371 
   1372        self._index = "%(var)s->%(name)s_length" % translate
   1373        code += self._entry.CodeUnmarshal(
   1374            buf,
   1375            tag_name,
   1376            self._entry.GetVarName(var_name),
   1377            self._entry.GetVarLen(var_name),
   1378        )
   1379 
   1380        code += ["++%(var)s->%(name)s_length;" % translate]
   1381 
   1382        return code
   1383 
   1384    def CodeMarshal(self, buf, tag_name, var_name, _var_len):
   1385        code = ["{", "  int i;", "  for (i = 0; i < %(var)s->%(name)s_length; ++i) {"]
   1386 
   1387        self._index = "i"
   1388        code += self._entry.CodeMarshal(
   1389            buf,
   1390            tag_name,
   1391            self._entry.GetVarName(var_name),
   1392            self._entry.GetVarLen(var_name),
   1393        )
   1394        code += ["  }", "}"]
   1395 
   1396        code = "\n".join(code) % self.GetTranslation({"var": var_name})
   1397 
   1398        return code.split("\n")
   1399 
   1400    def CodeClear(self, structname):
   1401        translate = self.GetTranslation({"structname": structname})
   1402        codearrayfree = self._entry.CodeArrayFree(
   1403            "%(structname)s->%(name)s_data[i]"
   1404            % self.GetTranslation({"structname": structname})
   1405        )
   1406 
   1407        code = ["if (%(structname)s->%(name)s_set == 1) {"]
   1408 
   1409        if codearrayfree:
   1410            code += [
   1411                "  int i;",
   1412                "  for (i = 0; i < %(structname)s->%(name)s_length; ++i) {",
   1413            ]
   1414 
   1415        code = TranslateList(code, translate)
   1416 
   1417        if codearrayfree:
   1418            code += ["    " + x for x in codearrayfree]
   1419            code += ["  }"]
   1420 
   1421        code += TranslateList(
   1422            [
   1423                "  free(%(structname)s->%(name)s_data);",
   1424                "  %(structname)s->%(name)s_data = NULL;",
   1425                "  %(structname)s->%(name)s_set = 0;",
   1426                "  %(structname)s->%(name)s_length = 0;",
   1427                "  %(structname)s->%(name)s_num_allocated = 0;",
   1428                "}",
   1429            ],
   1430            translate,
   1431        )
   1432 
   1433        return code
   1434 
   1435    def CodeInitialize(self, name):
   1436        code = [
   1437            "%s->%s_data = NULL;" % (name, self._name),
   1438            "%s->%s_length = 0;" % (name, self._name),
   1439            "%s->%s_num_allocated = 0;" % (name, self._name),
   1440        ]
   1441        return code
   1442 
   1443    def CodeFree(self, structname):
   1444        code = self.CodeClear(structname)
   1445 
   1446        code += TranslateList(
   1447            ["free(%(structname)s->%(name)s_data);"],
   1448            self.GetTranslation({"structname": structname}),
   1449        )
   1450 
   1451        return code
   1452 
   1453    def Declaration(self):
   1454        dcl = [
   1455            "%s *%s_data;" % (self._ctype, self._name),
   1456            "int %s_length;" % self._name,
   1457            "int %s_num_allocated;" % self._name,
   1458        ]
   1459 
   1460        return dcl
   1461 
   1462 
   1463 def NormalizeLine(line):
   1464 
   1465    line = CPPCOMMENT_RE.sub("", line)
   1466    line = line.strip()
   1467    line = WHITESPACE_RE.sub(" ", line)
   1468 
   1469    return line
   1470 
   1471 
   1472 ENTRY_NAME_RE = re.compile(r"(?P<name>[^\[\]]+)(\[(?P<fixed_length>.*)\])?")
   1473 ENTRY_TAG_NUMBER_RE = re.compile(r"(0x)?\d+", re.I)
   1474 
   1475 
   1476 def ProcessOneEntry(factory, newstruct, entry):
   1477    optional = False
   1478    array = False
   1479    entry_type = ""
   1480    name = ""
   1481    tag = ""
   1482    tag_set = None
   1483    separator = ""
   1484    fixed_length = ""
   1485 
   1486    for token in entry.split(" "):
   1487        if not entry_type:
   1488            if not optional and token == "optional":
   1489                optional = True
   1490                continue
   1491 
   1492            if not array and token == "array":
   1493                array = True
   1494                continue
   1495 
   1496        if not entry_type:
   1497            entry_type = token
   1498            continue
   1499 
   1500        if not name:
   1501            res = ENTRY_NAME_RE.match(token)
   1502            if not res:
   1503                raise RpcGenError(
   1504                    r"""Cannot parse name: "%s" around line %d""" % (entry, LINE_COUNT)
   1505                )
   1506            name = res.group("name")
   1507            fixed_length = res.group("fixed_length")
   1508            continue
   1509 
   1510        if not separator:
   1511            separator = token
   1512            if separator != "=":
   1513                raise RpcGenError(
   1514                    r'''Expected "=" after name "%s" got "%s"''' % (name, token)
   1515                )
   1516            continue
   1517 
   1518        if not tag_set:
   1519            tag_set = 1
   1520            if not ENTRY_TAG_NUMBER_RE.match(token):
   1521                raise RpcGenError(r'''Expected tag number: "%s"''' % (entry))
   1522            tag = int(token, 0)
   1523            continue
   1524 
   1525        raise RpcGenError(r'''Cannot parse "%s"''' % (entry))
   1526 
   1527    if not tag_set:
   1528        raise RpcGenError(r'''Need tag number: "%s"''' % (entry))
   1529 
   1530    # Create the right entry
   1531    if entry_type == "bytes":
   1532        if fixed_length:
   1533            newentry = factory.EntryBytes(entry_type, name, tag, fixed_length)
   1534        else:
   1535            newentry = factory.EntryVarBytes(entry_type, name, tag)
   1536    elif entry_type == "int" and not fixed_length:
   1537        newentry = factory.EntryInt(entry_type, name, tag)
   1538    elif entry_type == "int64" and not fixed_length:
   1539        newentry = factory.EntryInt(entry_type, name, tag, bits=64)
   1540    elif entry_type == "string" and not fixed_length:
   1541        newentry = factory.EntryString(entry_type, name, tag)
   1542    else:
   1543        res = STRUCT_REF_RE.match(entry_type)
   1544        if res:
   1545            # References another struct defined in our file
   1546            newentry = factory.EntryStruct(entry_type, name, tag, res.group("name"))
   1547        else:
   1548            raise RpcGenError('Bad type: "%s" in "%s"' % (entry_type, entry))
   1549 
   1550    structs = []
   1551 
   1552    if optional:
   1553        newentry.MakeOptional()
   1554    if array:
   1555        newentry.MakeArray()
   1556 
   1557    newentry.SetStruct(newstruct)
   1558    newentry.SetLineCount(LINE_COUNT)
   1559    newentry.Verify()
   1560 
   1561    if array:
   1562        # We need to encapsulate this entry into a struct
   1563        newentry = factory.EntryArray(newentry)
   1564        newentry.SetStruct(newstruct)
   1565        newentry.SetLineCount(LINE_COUNT)
   1566        newentry.MakeArray()
   1567 
   1568    newstruct.AddEntry(newentry)
   1569 
   1570    return structs
   1571 
   1572 
   1573 def ProcessStruct(factory, data):
   1574    tokens = data.split(" ")
   1575 
   1576    # First three tokens are: 'struct' 'name' '{'
   1577    newstruct = factory.Struct(tokens[1])
   1578 
   1579    inside = " ".join(tokens[3:-1])
   1580 
   1581    tokens = inside.split(";")
   1582 
   1583    structs = []
   1584 
   1585    for entry in tokens:
   1586        entry = NormalizeLine(entry)
   1587        if not entry:
   1588            continue
   1589 
   1590        # It's possible that new structs get defined in here
   1591        structs.extend(ProcessOneEntry(factory, newstruct, entry))
   1592 
   1593    structs.append(newstruct)
   1594    return structs
   1595 
   1596 
   1597 C_COMMENT_START = "/*"
   1598 C_COMMENT_END = "*/"
   1599 
   1600 C_COMMENT_START_RE = re.compile(re.escape(C_COMMENT_START))
   1601 C_COMMENT_END_RE = re.compile(re.escape(C_COMMENT_END))
   1602 
   1603 C_COMMENT_START_SUB_RE = re.compile(r"%s.*$" % (re.escape(C_COMMENT_START)))
   1604 C_COMMENT_END_SUB_RE = re.compile(r"%s.*$" % (re.escape(C_COMMENT_END)))
   1605 
   1606 C_MULTILINE_COMMENT_SUB_RE = re.compile(
   1607    r"%s.*?%s" % (re.escape(C_COMMENT_START), re.escape(C_COMMENT_END))
   1608 )
   1609 CPP_CONDITIONAL_BLOCK_RE = re.compile(r"#(if( |def)|endif)")
   1610 INCLUDE_RE = re.compile(r'#include (".+"|<.+>)')
   1611 
   1612 
   1613 def GetNextStruct(filep):
   1614    global CPP_DIRECT
   1615    global LINE_COUNT
   1616 
   1617    got_struct = False
   1618    have_c_comment = False
   1619 
   1620    data = ""
   1621 
   1622    while True:
   1623        line = filep.readline()
   1624        if not line:
   1625            break
   1626 
   1627        LINE_COUNT += 1
   1628        line = line[:-1]
   1629 
   1630        if not have_c_comment and C_COMMENT_START_RE.search(line):
   1631            if C_MULTILINE_COMMENT_SUB_RE.search(line):
   1632                line = C_MULTILINE_COMMENT_SUB_RE.sub("", line)
   1633            else:
   1634                line = C_COMMENT_START_SUB_RE.sub("", line)
   1635                have_c_comment = True
   1636 
   1637        if have_c_comment:
   1638            if not C_COMMENT_END_RE.search(line):
   1639                continue
   1640            have_c_comment = False
   1641            line = C_COMMENT_END_SUB_RE.sub("", line)
   1642 
   1643        line = NormalizeLine(line)
   1644 
   1645        if not line:
   1646            continue
   1647 
   1648        if not got_struct:
   1649            if INCLUDE_RE.match(line):
   1650                CPP_DIRECT.append(line)
   1651            elif CPP_CONDITIONAL_BLOCK_RE.match(line):
   1652                CPP_DIRECT.append(line)
   1653            elif PREPROCESSOR_DEF_RE.match(line):
   1654                HEADER_DIRECT.append(line)
   1655            elif not STRUCT_DEF_RE.match(line):
   1656                raise RpcGenError("Missing struct on line %d: %s" % (LINE_COUNT, line))
   1657            else:
   1658                got_struct = True
   1659                data += line
   1660            continue
   1661 
   1662        # We are inside the struct
   1663        tokens = line.split("}")
   1664        if len(tokens) == 1:
   1665            data += " " + line
   1666            continue
   1667 
   1668        if tokens[1]:
   1669            raise RpcGenError("Trailing garbage after struct on line %d" % LINE_COUNT)
   1670 
   1671        # We found the end of the struct
   1672        data += " %s}" % tokens[0]
   1673        break
   1674 
   1675    # Remove any comments, that might be in there
   1676    data = re.sub(r"/\*.*\*/", "", data)
   1677 
   1678    return data
   1679 
   1680 
   1681 def Parse(factory, filep):
   1682    """
   1683    Parses the input file and returns C code and corresponding header file.
   1684    """
   1685 
   1686    entities = []
   1687 
   1688    while 1:
   1689        # Just gets the whole struct nicely formatted
   1690        data = GetNextStruct(filep)
   1691 
   1692        if not data:
   1693            break
   1694 
   1695        entities.extend(ProcessStruct(factory, data))
   1696 
   1697    return entities
   1698 
   1699 
   1700 class CCodeGenerator(object):
   1701    def __init__(self):
   1702        pass
   1703 
   1704    @staticmethod
   1705    def GuardName(name):
   1706        # Use the complete provided path to the input file, with all
   1707        # non-identifier characters replaced with underscores, to
   1708        # reduce the chance of a collision between guard macros.
   1709        return "EVENT_RPCOUT_%s_" % (NONIDENT_RE.sub("_", name).upper())
   1710 
   1711    def HeaderPreamble(self, name):
   1712        guard = self.GuardName(name)
   1713        pre = """
   1714 /*
   1715 * Automatically generated from %s
   1716 */
   1717 
   1718 #ifndef %s
   1719 #define %s
   1720 
   1721 """ % (
   1722            name,
   1723            guard,
   1724            guard,
   1725        )
   1726 
   1727        if HEADER_DIRECT:
   1728            for statement in HEADER_DIRECT:
   1729                pre += "%s\n" % statement
   1730            pre += "\n"
   1731 
   1732        pre += """
   1733 #include <event2/util.h> /* for ev_uint*_t */
   1734 #include <event2/rpc.h>
   1735 """
   1736 
   1737        return pre
   1738 
   1739    def HeaderPostamble(self, name):
   1740        guard = self.GuardName(name)
   1741        return "#endif  /* %s */" % (guard)
   1742 
   1743    @staticmethod
   1744    def BodyPreamble(name, header_file):
   1745        global _NAME
   1746        global _VERSION
   1747 
   1748        slash = header_file.rfind("/")
   1749        if slash != -1:
   1750            header_file = header_file[slash + 1 :]
   1751 
   1752        pre = """
   1753 /*
   1754 * Automatically generated from %(name)s
   1755 * by %(script_name)s/%(script_version)s.  DO NOT EDIT THIS FILE.
   1756 */
   1757 
   1758 #include <stdlib.h>
   1759 #include <string.h>
   1760 #include <assert.h>
   1761 #include <event2/event-config.h>
   1762 #include <event2/event.h>
   1763 #include <event2/buffer.h>
   1764 #include <event2/tag.h>
   1765 
   1766 #if defined(EVENT__HAVE___func__)
   1767 # ifndef __func__
   1768 #  define __func__ __func__
   1769 # endif
   1770 #elif defined(EVENT__HAVE___FUNCTION__)
   1771 # define __func__ __FUNCTION__
   1772 #else
   1773 # define __func__ __FILE__
   1774 #endif
   1775 
   1776 """ % {
   1777            "name": name,
   1778            "script_name": _NAME,
   1779            "script_version": _VERSION,
   1780        }
   1781 
   1782        for statement in CPP_DIRECT:
   1783            pre += "%s\n" % statement
   1784 
   1785        pre += '\n#include "%s"\n\n' % header_file
   1786 
   1787        pre += "void event_warn(const char *fmt, ...);\n"
   1788        pre += "void event_warnx(const char *fmt, ...);\n\n"
   1789 
   1790        return pre
   1791 
   1792    @staticmethod
   1793    def HeaderFilename(filename):
   1794        return ".".join(filename.split(".")[:-1]) + ".h"
   1795 
   1796    @staticmethod
   1797    def CodeFilename(filename):
   1798        return ".".join(filename.split(".")[:-1]) + ".gen.c"
   1799 
   1800    @staticmethod
   1801    def Struct(name):
   1802        return StructCCode(name)
   1803 
   1804    @staticmethod
   1805    def EntryBytes(entry_type, name, tag, fixed_length):
   1806        return EntryBytes(entry_type, name, tag, fixed_length)
   1807 
   1808    @staticmethod
   1809    def EntryVarBytes(entry_type, name, tag):
   1810        return EntryVarBytes(entry_type, name, tag)
   1811 
   1812    @staticmethod
   1813    def EntryInt(entry_type, name, tag, bits=32):
   1814        return EntryInt(entry_type, name, tag, bits)
   1815 
   1816    @staticmethod
   1817    def EntryString(entry_type, name, tag):
   1818        return EntryString(entry_type, name, tag)
   1819 
   1820    @staticmethod
   1821    def EntryStruct(entry_type, name, tag, struct_name):
   1822        return EntryStruct(entry_type, name, tag, struct_name)
   1823 
   1824    @staticmethod
   1825    def EntryArray(entry):
   1826        return EntryArray(entry)
   1827 
   1828 
   1829 class CommandLine(object):
   1830    def __init__(self, argv=None):
   1831        """Initialize a command-line to launch event_rpcgen, as if
   1832           from a command-line with CommandLine(sys.argv).  If you're
   1833           calling this directly, remember to provide a dummy value
   1834           for sys.argv[0]
   1835        """
   1836        global QUIETLY
   1837 
   1838        self.filename = None
   1839        self.header_file = None
   1840        self.impl_file = None
   1841        self.factory = CCodeGenerator()
   1842 
   1843        parser = argparse.ArgumentParser(
   1844            usage="%(prog)s [options] rpc-file [[h-file] c-file]"
   1845        )
   1846        parser.add_argument("--quiet", action="store_true", default=False)
   1847        parser.add_argument("rpc_file", type=argparse.FileType("r"))
   1848 
   1849        args, extra_args = parser.parse_known_args(args=argv)
   1850 
   1851        QUIETLY = args.quiet
   1852 
   1853        if extra_args:
   1854            if len(extra_args) == 1:
   1855                self.impl_file = extra_args[0].replace("\\", "/")
   1856            elif len(extra_args) == 2:
   1857                self.header_file = extra_args[0].replace("\\", "/")
   1858                self.impl_file = extra_args[1].replace("\\", "/")
   1859            else:
   1860                parser.error("Spurious arguments provided")
   1861 
   1862        self.rpc_file = args.rpc_file
   1863 
   1864        if not self.impl_file:
   1865            self.impl_file = self.factory.CodeFilename(self.rpc_file.name)
   1866 
   1867        if not self.header_file:
   1868            self.header_file = self.factory.HeaderFilename(self.impl_file)
   1869 
   1870        if not self.impl_file.endswith(".c"):
   1871            parser.error("can only generate C implementation files")
   1872        if not self.header_file.endswith(".h"):
   1873            parser.error("can only generate C header files")
   1874 
   1875    def run(self):
   1876        filename = self.rpc_file.name
   1877        header_file = self.header_file
   1878        impl_file = self.impl_file
   1879        factory = self.factory
   1880 
   1881        declare('Reading "%s"' % filename)
   1882 
   1883        with self.rpc_file:
   1884            entities = Parse(factory, self.rpc_file)
   1885 
   1886        declare('... creating "%s"' % header_file)
   1887        with open(header_file, "w") as header_fp:
   1888            header_fp.write(factory.HeaderPreamble(filename))
   1889 
   1890            # Create forward declarations: allows other structs to reference
   1891            # each other
   1892            for entry in entities:
   1893                entry.PrintForwardDeclaration(header_fp)
   1894            header_fp.write("\n")
   1895 
   1896            for entry in entities:
   1897                entry.PrintTags(header_fp)
   1898                entry.PrintDeclaration(header_fp)
   1899            header_fp.write(factory.HeaderPostamble(filename))
   1900 
   1901        declare('... creating "%s"' % impl_file)
   1902        with open(impl_file, "w") as impl_fp:
   1903            impl_fp.write(factory.BodyPreamble(filename, header_file))
   1904            for entry in entities:
   1905                entry.PrintCode(impl_fp)
   1906 
   1907 
   1908 def main(argv=None):
   1909    try:
   1910        CommandLine(argv=argv).run()
   1911        return 0
   1912    except RpcGenError as e:
   1913        sys.stderr.write(e)
   1914    except EnvironmentError as e:
   1915        if e.filename and e.strerror:
   1916            sys.stderr.write("%s: %s" % (e.filename, e.strerror))
   1917        elif e.strerror:
   1918            sys.stderr.write(e.strerror)
   1919        else:
   1920            raise
   1921    return 1
   1922 
   1923 
   1924 if __name__ == "__main__":
   1925    sys.exit(main(argv=sys.argv[1:]))