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
22import argparse
23import re
24import sys
25
26_NAME = "event_rpcgen.py"
27_VERSION = "0.1"
28
29# Globals
30LINE_COUNT = 0
31
32CPPCOMMENT_RE = re.compile(r"\/\/.*$")
33NONIDENT_RE = re.compile(r"\W")
34PREPROCESSOR_DEF_RE = re.compile(r"^#define")
35STRUCT_REF_RE = re.compile(r"^struct\[(?P<name>[a-zA-Z_][a-zA-Z0-9_]*)\]$")
36STRUCT_DEF_RE = re.compile(r"^struct +[a-zA-Z_][a-zA-Z0-9_]* *{$")
37WHITESPACE_RE = re.compile(r"\s+")
38
39HEADER_DIRECT = []
40CPP_DIRECT = []
41
42QUIETLY = False
43
44
45def declare(s):
46    if not QUIETLY:
47        print(s)
48
49
50def TranslateList(mylist, mydict):
51    return [x % mydict for x in mylist]
52
53
54class 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
65class 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
99class 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);
140struct %(name)s *%(name)s_new_with_arg(void *);
141void %(name)s_free(struct %(name)s *);
142void %(name)s_clear(struct %(name)s *);
143void %(name)s_marshal(struct evbuffer *, const struct %(name)s *);
144int %(name)s_unmarshal(struct %(name)s *, struct evbuffer *);
145int %(name)s_complete(struct %(name)s *);
146void evtag_marshal_%(name)s(struct evbuffer *, ev_uint32_t,
147    const struct %(name)s *);
148int 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            """
175static 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
191struct %(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            """
354int
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            """
381int
382evtag_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            """
409void
410evtag_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
425class 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
588class 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
701class 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
760class 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
877class 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
1057class 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
1188class 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
1463def 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
1472ENTRY_NAME_RE = re.compile(r"(?P<name>[^\[\]]+)(\[(?P<fixed_length>.*)\])?")
1473ENTRY_TAG_NUMBER_RE = re.compile(r"(0x)?\d+", re.I)
1474
1475
1476def 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
1573def 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
1597C_COMMENT_START = "/*"
1598C_COMMENT_END = "*/"
1599
1600C_COMMENT_START_RE = re.compile(re.escape(C_COMMENT_START))
1601C_COMMENT_END_RE = re.compile(re.escape(C_COMMENT_END))
1602
1603C_COMMENT_START_SUB_RE = re.compile(r"%s.*$" % (re.escape(C_COMMENT_START)))
1604C_COMMENT_END_SUB_RE = re.compile(r"%s.*$" % (re.escape(C_COMMENT_END)))
1605
1606C_MULTILINE_COMMENT_SUB_RE = re.compile(
1607    r"%s.*?%s" % (re.escape(C_COMMENT_START), re.escape(C_COMMENT_END))
1608)
1609CPP_CONDITIONAL_BLOCK_RE = re.compile(r"#(if( |def)|endif)")
1610INCLUDE_RE = re.compile(r'#include (".+"|<.+>)')
1611
1612
1613def 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
1681def 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
1700class 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
1829class 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
1908def 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
1924if __name__ == "__main__":
1925    sys.exit(main(argv=sys.argv[1:]))
1926