Home | History | Annotate | Line # | Download | only in libevent
event_rpcgen.py revision 1.1.1.2
      1 #!/usr/bin/env python
      2 #
      3 # Copyright (c) 2005-2007 Niels Provos <provos (at] 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:]))
   1926