#!/usr/bin/env python # # Copyright (c) 2005 Niels Provos <provos@citi.umich.edu> # All rights reserved. # # Generates marshaling code based on libevent. import sys import re # _NAME = "event_rpcgen.py" _VERSION = "0.1" _STRUCT_RE = '[a-z][a-z_0-9]*' # Globals line_count = 0 white = re.compile(r'^\s+') cppcomment = re.compile(r'\/\/.*$') headerdirect = [] cppdirect = [] # Holds everything that makes a struct class Struct: def __init__(self, name): self._name = name self._entries = [] self._tags = {} print >>sys.stderr, ' Created struct: %s' % name def AddEntry(self, entry): if self._tags.has_key(entry.Tag()): print >>sys.stderr, ( 'Entry "%s" duplicates tag number ' '%d from "%s" around line %d' ) % ( entry.Name(), entry.Tag(), self._tags[entry.Tag()], line_count) sys.exit(1) self._entries.append(entry) self._tags[entry.Tag()] = entry.Name() print >>sys.stderr, ' Added entry: %s' % entry.Name() def Name(self): return self._name def EntryTagName(self, entry): """Creates the name inside an enumeration for distinguishing data types.""" name = "%s_%s" % (self._name, entry.Name()) return name.upper() def PrintIdented(self, file, ident, code): """Takes an array, add indentation to each entry and prints it.""" for entry in code: print >>file, '%s%s' % (ident, entry) def PrintTags(self, file): """Prints the tag definitions for a structure.""" print >>file, '/* Tag definition for %s */' % self._name print >>file, 'enum %s_ {' % self._name.lower() for entry in self._entries: print >>file, ' %s=%d,' % (self.EntryTagName(entry), entry.Tag()) print >>file, ' %s_MAX_TAGS' % (self._name.upper()) print >>file, '};\n' def PrintForwardDeclaration(self, file): print >>file, 'struct %s;' % self._name def PrintDeclaration(self, file): print >>file, '/* Structure declaration for %s */' % self._name print >>file, 'struct %s_access_ {' % self._name for entry in self._entries: dcl = entry.AssignDeclaration('(*%s_assign)' % entry.Name()) dcl.extend( entry.GetDeclaration('(*%s_get)' % entry.Name())) if entry.Array(): dcl.extend( entry.AddDeclaration('(*%s_add)' % entry.Name())) self.PrintIdented(file, ' ', dcl) print >>file, '};\n' print >>file, 'struct %s {' % self._name print >>file, ' struct %s_access_ *base;\n' % self._name for entry in self._entries: dcl = entry.Declaration() self.PrintIdented(file, ' ', dcl) print >>file, '' for entry in self._entries: print >>file, ' ev_uint8_t %s_set;' % entry.Name() print >>file, '};\n' print >>file, \ """struct %(name)s *%(name)s_new(void); void %(name)s_free(struct %(name)s *); void %(name)s_clear(struct %(name)s *); void %(name)s_marshal(struct evbuffer *, const struct %(name)s *); int %(name)s_unmarshal(struct %(name)s *, struct evbuffer *); int %(name)s_complete(struct %(name)s *); void evtag_marshal_%(name)s(struct evbuffer *, ev_uint32_t, const struct %(name)s *); int evtag_unmarshal_%(name)s(struct evbuffer *, ev_uint32_t, struct %(name)s *);""" % { 'name' : self._name } # Write a setting function of every variable for entry in self._entries: self.PrintIdented(file, '', entry.AssignDeclaration( entry.AssignFuncName())) self.PrintIdented(file, '', entry.GetDeclaration( entry.GetFuncName())) if entry.Array(): self.PrintIdented(file, '', entry.AddDeclaration( entry.AddFuncName())) print >>file, '/* --- %s done --- */\n' % self._name def PrintCode(self, file): print >>file, ('/*\n' ' * Implementation of %s\n' ' */\n') % self._name print >>file, \ 'static struct %(name)s_access_ __%(name)s_base = {' % \ { 'name' : self._name } for entry in self._entries: self.PrintIdented(file, ' ', entry.CodeBase()) print >>file, '};\n' # Creation print >>file, ( 'struct %(name)s *\n' '%(name)s_new(void)\n' '{\n' ' struct %(name)s *tmp;\n' ' if ((tmp = malloc(sizeof(struct %(name)s))) == NULL) {\n' ' event_warn("%%s: malloc", __func__);\n' ' return (NULL);\n' ' }\n' ' tmp->base = &__%(name)s_base;\n') % { 'name' : self._name } for entry in self._entries: self.PrintIdented(file, ' ', entry.CodeNew('tmp')) print >>file, ' tmp->%s_set = 0;\n' % entry.Name() print >>file, ( ' return (tmp);\n' '}\n') # Adding for entry in self._entries: if entry.Array(): self.PrintIdented(file, '', entry.CodeAdd()) print >>file, '' # Assigning for entry in self._entries: self.PrintIdented(file, '', entry.CodeAssign()) print >>file, '' # Getting for entry in self._entries: self.PrintIdented(file, '', entry.CodeGet()) print >>file, '' # Clearing print >>file, ( 'void\n' '%(name)s_clear(struct %(name)s *tmp)\n' '{' ) % { 'name' : self._name } for entry in self._entries: self.PrintIdented(file, ' ', entry.CodeClear('tmp')) print >>file, '}\n' # Freeing print >>file, ( 'void\n' '%(name)s_free(struct %(name)s *tmp)\n' '{' ) % { 'name' : self._name } for entry in self._entries: self.PrintIdented(file, ' ', entry.CodeFree('tmp')) print >>file, (' free(tmp);\n' '}\n') # Marshaling print >>file, ('void\n' '%(name)s_marshal(struct evbuffer *evbuf, ' 'const struct %(name)s *tmp)' '{') % { 'name' : self._name } for entry in self._entries: indent = ' ' # Optional entries do not have to be set if entry.Optional(): indent += ' ' print >>file, ' if (tmp->%s_set) {' % entry.Name() self.PrintIdented( file, indent, entry.CodeMarshal('evbuf', self.EntryTagName(entry), 'tmp')) if entry.Optional(): print >>file, ' }' print >>file, '}\n' # Unmarshaling print >>file, ('int\n' '%(name)s_unmarshal(struct %(name)s *tmp, ' ' struct evbuffer *evbuf)\n' '{\n' ' ev_uint32_t tag;\n' ' while (EVBUFFER_LENGTH(evbuf) > 0) {\n' ' if (evtag_peek(evbuf, &tag) == -1)\n' ' return (-1);\n' ' switch (tag) {\n' ) % { 'name' : self._name } for entry in self._entries: print >>file, ' case %s:\n' % self.EntryTagName(entry) if not entry.Array(): print >>file, ( ' if (tmp->%s_set)\n' ' return (-1);' ) % (entry.Name()) self.PrintIdented( file, ' ', entry.CodeUnmarshal('evbuf', self.EntryTagName(entry), 'tmp')) print >>file, ( ' tmp->%s_set = 1;\n' % entry.Name() + ' break;\n' ) print >>file, ( ' default:\n' ' return -1;\n' ' }\n' ' }\n' ) # Check if it was decoded completely print >>file, ( ' if (%(name)s_complete(tmp) == -1)\n' ' return (-1);' ) % { 'name' : self._name } # Successfully decoded print >>file, ( ' return (0);\n' '}\n') # Checking if a structure has all the required data print >>file, ( 'int\n' '%(name)s_complete(struct %(name)s *msg)\n' '{' ) % { 'name' : self._name } for entry in self._entries: self.PrintIdented( file, ' ', entry.CodeComplete('msg')) print >>file, ( ' return (0);\n' '}\n' ) # Complete message unmarshaling print >>file, ( 'int\n' 'evtag_unmarshal_%(name)s(struct evbuffer *evbuf, ' 'ev_uint32_t need_tag, struct %(name)s *msg)\n' '{\n' ' ev_uint32_t tag;\n' ' int res = -1;\n' '\n' ' struct evbuffer *tmp = evbuffer_new();\n' '\n' ' if (evtag_unmarshal(evbuf, &tag, tmp) == -1' ' || tag != need_tag)\n' ' goto error;\n' '\n' ' if (%(name)s_unmarshal(msg, tmp) == -1)\n' ' goto error;\n' '\n' ' res = 0;\n' '\n' ' error:\n' ' evbuffer_free(tmp);\n' ' return (res);\n' '}\n' ) % { 'name' : self._name } # Complete message marshaling print >>file, ( 'void\n' 'evtag_marshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t tag, ' 'const struct %(name)s *msg)\n' '{\n' ' struct evbuffer *_buf = evbuffer_new();\n' ' assert(_buf != NULL);\n' ' evbuffer_drain(_buf, -1);\n' ' %(name)s_marshal(_buf, msg);\n' ' evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), ' 'EVBUFFER_LENGTH(_buf));\n' ' evbuffer_free(_buf);\n' '}\n' ) % { 'name' : self._name } class Entry: def __init__(self, type, name, tag): self._type = type self._name = name self._tag = int(tag) self._ctype = type self._optional = 0 self._can_be_array = 0 self._array = 0 self._line_count = -1 self._struct = None self._refname = None def GetTranslation(self): return { "parent_name" : self._struct.Name(), "name" : self._name, "ctype" : self._ctype, "refname" : self._refname } def SetStruct(self, struct): self._struct = struct def LineCount(self): assert self._line_count != -1 return self._line_count def SetLineCount(self, number): self._line_count = number def Array(self): return self._array def Optional(self): return self._optional def Tag(self): return self._tag def Name(self): return self._name def Type(self): return self._type def MakeArray(self, yes=1): self._array = yes def MakeOptional(self): self._optional = 1 def GetFuncName(self): return '%s_%s_get' % (self._struct.Name(), self._name) def GetDeclaration(self, funcname): code = [ 'int %s(struct %s *, %s *);' % ( funcname, self._struct.Name(), self._ctype ) ] return code def CodeGet(self): code = ( 'int', '%(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, ' '%(ctype)s *value)', '{', ' if (msg->%(name)s_set != 1)', ' return (-1);', ' *value = msg->%(name)s_data;', ' return (0);', '}' ) code = '\n'.join(code) code = code % self.GetTranslation() return code.split('\n') def AssignFuncName(self): return '%s_%s_assign' % (self._struct.Name(), self._name) def AddFuncName(self): return '%s_%s_add' % (self._struct.Name(), self._name) def AssignDeclaration(self, funcname): code = [ 'int %s(struct %s *, const %s);' % ( funcname, self._struct.Name(), self._ctype ) ] return code def CodeAssign(self): code = [ 'int', '%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,' ' const %(ctype)s value)', '{', ' msg->%(name)s_set = 1;', ' msg->%(name)s_data = value;', ' return (0);', '}' ] code = '\n'.join(code) code = code % self.GetTranslation() return code.split('\n') def CodeClear(self, structname): code = [ '%s->%s_set = 0;' % (structname, self.Name()) ] return code def CodeComplete(self, structname): if self.Optional(): return [] code = [ 'if (!%s->%s_set)' % (structname, self.Name()), ' return (-1);' ] return code def CodeFree(self, name): return [] def CodeBase(self): code = [ '%(parent_name)s_%(name)s_assign,', '%(parent_name)s_%(name)s_get,' ] if self.Array(): code.append('%(parent_name)s_%(name)s_add,') code = '\n'.join(code) code = code % self.GetTranslation() return code.split('\n') def Verify(self): if self.Array() and not self._can_be_array: print >>sys.stderr, ( 'Entry "%s" cannot be created as an array ' 'around line %d' ) % (self._name, self.LineCount()) sys.exit(1) if not self._struct: print >>sys.stderr, ( 'Entry "%s" does not know which struct it belongs to ' 'around line %d' ) % (self._name, self.LineCount()) sys.exit(1) if self._optional and self._array: print >>sys.stderr, ( 'Entry "%s" has illegal combination of ' 'optional and array around line %d' ) % ( self._name, self.LineCount() ) sys.exit(1) class EntryBytes(Entry): def __init__(self, type, name, tag, length): # Init base class Entry.__init__(self, type, name, tag) self._length = length self._ctype = 'ev_uint8_t' def GetDeclaration(self, funcname): code = [ 'int %s(struct %s *, %s **);' % ( funcname, self._struct.Name(), self._ctype ) ] return code def AssignDeclaration(self, funcname): code = [ 'int %s(struct %s *, const %s *);' % ( funcname, self._struct.Name(), self._ctype ) ] return code def Declaration(self): dcl = ['ev_uint8_t %s_data[%s];' % (self._name, self._length)] return dcl def CodeGet(self): name = self._name code = [ 'int', '%s_%s_get(struct %s *msg, %s **value)' % ( self._struct.Name(), name, self._struct.Name(), self._ctype), '{', ' if (msg->%s_set != 1)' % name, ' return (-1);', ' *value = msg->%s_data;' % name, ' return (0);', '}' ] return code def CodeAssign(self): name = self._name code = [ 'int', '%s_%s_assign(struct %s *msg, const %s *value)' % ( self._struct.Name(), name, self._struct.Name(), self._ctype), '{', ' msg->%s_set = 1;' % name, ' memcpy(msg->%s_data, value, %s);' % ( name, self._length), ' return (0);', '}' ] return code def CodeUnmarshal(self, buf, tag_name, var_name): code = [ 'if (evtag_unmarshal_fixed(%s, %s, ' % (buf, tag_name) + '%s->%s_data, ' % (var_name, self._name) + 'sizeof(%s->%s_data)) == -1) {' % ( var_name, self._name), ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( self._name ), ' return (-1);', '}' ] return code def CodeMarshal(self, buf, tag_name, var_name): code = ['evtag_marshal(%s, %s, %s->%s_data, sizeof(%s->%s_data));' % ( buf, tag_name, var_name, self._name, var_name, self._name )] return code def CodeClear(self, structname): code = [ '%s->%s_set = 0;' % (structname, self.Name()), 'memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( structname, self._name, structname, self._name)] return code def CodeNew(self, name): code = ['memset(%s->%s_data, 0, sizeof(%s->%s_data));' % ( name, self._name, name, self._name)] return code def Verify(self): if not self._length: print >>sys.stderr, 'Entry "%s" needs a length around line %d' % ( self._name, self.LineCount() ) sys.exit(1) Entry.Verify(self) class EntryInt(Entry): def __init__(self, type, name, tag): # Init base class Entry.__init__(self, type, name, tag) self._ctype = 'ev_uint32_t' def CodeUnmarshal(self, buf, tag_name, var_name): code = ['if (evtag_unmarshal_int(%s, %s, &%s->%s_data) == -1) {' % ( buf, tag_name, var_name, self._name), ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( self._name ), ' return (-1);', '}' ] return code def CodeMarshal(self, buf, tag_name, var_name): code = ['evtag_marshal_int(%s, %s, %s->%s_data);' % ( buf, tag_name, var_name, self._name)] return code def Declaration(self): dcl = ['ev_uint32_t %s_data;' % self._name] return dcl def CodeNew(self, name): code = ['%s->%s_data = 0;' % (name, self._name)] return code class EntryString(Entry): def __init__(self, type, name, tag): # Init base class Entry.__init__(self, type, name, tag) self._ctype = 'char *' def CodeAssign(self): name = self._name code = """int %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, const %(ctype)s value) { if (msg->%(name)s_data != NULL) free(msg->%(name)s_data); if ((msg->%(name)s_data = strdup(value)) == NULL) return (-1); msg->%(name)s_set = 1; return (0); }""" % self.GetTranslation() return code.split('\n') def CodeUnmarshal(self, buf, tag_name, var_name): code = ['if (evtag_unmarshal_string(%s, %s, &%s->%s_data) == -1) {' % ( buf, tag_name, var_name, self._name), ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( self._name ), ' return (-1);', '}' ] return code def CodeMarshal(self, buf, tag_name, var_name): code = ['evtag_marshal_string(%s, %s, %s->%s_data);' % ( buf, tag_name, var_name, self._name)] return code def CodeClear(self, structname): code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), ' free (%s->%s_data);' % (structname, self.Name()), ' %s->%s_data = NULL;' % (structname, self.Name()), ' %s->%s_set = 0;' % (structname, self.Name()), '}' ] return code def CodeNew(self, name): code = ['%s->%s_data = NULL;' % (name, self._name)] return code def CodeFree(self, name): code = ['if (%s->%s_data != NULL)' % (name, self._name), ' free (%s->%s_data); ' % (name, self._name)] return code def Declaration(self): dcl = ['char *%s_data;' % self._name] return dcl class EntryStruct(Entry): def __init__(self, type, name, tag, refname): # Init base class Entry.__init__(self, type, name, tag) self._can_be_array = 1 self._refname = refname self._ctype = 'struct %s*' % refname def CodeGet(self): name = self._name code = [ 'int', '%s_%s_get(struct %s *msg, %s *value)' % ( self._struct.Name(), name, self._struct.Name(), self._ctype), '{', ' if (msg->%s_set != 1) {' % name, ' msg->%s_data = %s_new();' % (name, self._refname), ' if (msg->%s_data == NULL)' % name, ' return (-1);', ' msg->%s_set = 1;' % name, ' }', ' *value = msg->%s_data;' % name, ' return (0);', '}' ] return code def CodeAssign(self): name = self._name code = """int %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, const %(ctype)s value) { struct evbuffer *tmp = NULL; if (msg->%(name)s_set) { %(refname)s_clear(msg->%(name)s_data); msg->%(name)s_set = 0; } else { msg->%(name)s_data = %(refname)s_new(); if (msg->%(name)s_data == NULL) { event_warn("%%s: %(refname)s_new()", __func__); goto error; } } if ((tmp = evbuffer_new()) == NULL) { event_warn("%%s: evbuffer_new()", __func__); goto error; } %(refname)s_marshal(tmp, value); if (%(refname)s_unmarshal(msg->%(name)s_data, tmp) == -1) { event_warnx("%%s: %(refname)s_unmarshal", __func__); goto error; } msg->%(name)s_set = 1; evbuffer_free(tmp); return (0); error: if (tmp != NULL) evbuffer_free(tmp); if (msg->%(name)s_data != NULL) { %(refname)s_free(msg->%(name)s_data); msg->%(name)s_data = NULL; } return (-1); }""" % self.GetTranslation() return code.split('\n') def CodeComplete(self, structname): if self.Optional(): code = [ 'if (%s->%s_set && %s_complete(%s->%s_data) == -1)' % ( structname, self.Name(), self._refname, structname, self.Name()), ' return (-1);' ] else: code = [ 'if (%s_complete(%s->%s_data) == -1)' % ( self._refname, structname, self.Name()), ' return (-1);' ] return code def CodeUnmarshal(self, buf, tag_name, var_name): code = ['%s->%s_data = %s_new();' % ( var_name, self._name, self._refname), 'if (%s->%s_data == NULL)' % (var_name, self._name), ' return (-1);', 'if (evtag_unmarshal_%s(%s, %s, %s->%s_data) == -1) {' % ( self._refname, buf, tag_name, var_name, self._name), ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( self._name ), ' return (-1);', '}' ] return code def CodeMarshal(self, buf, tag_name, var_name): code = ['evtag_marshal_%s(%s, %s, %s->%s_data);' % ( self._refname, buf, tag_name, var_name, self._name)] return code def CodeClear(self, structname): code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), ' %s_free(%s->%s_data);' % ( self._refname, structname, self.Name()), ' %s->%s_data = NULL;' % (structname, self.Name()), ' %s->%s_set = 0;' % (structname, self.Name()), '}' ] return code def CodeNew(self, name): code = ['%s->%s_data = NULL;' % (name, self._name)] return code def CodeFree(self, name): code = ['if (%s->%s_data != NULL)' % (name, self._name), ' %s_free(%s->%s_data); ' % ( self._refname, name, self._name)] return code def Declaration(self): dcl = ['%s %s_data;' % (self._ctype, self._name)] return dcl class EntryVarBytes(Entry): def __init__(self, type, name, tag): # Init base class Entry.__init__(self, type, name, tag) self._ctype = 'ev_uint8_t *' def GetDeclaration(self, funcname): code = [ 'int %s(struct %s *, %s *, ev_uint32_t *);' % ( funcname, self._struct.Name(), self._ctype ) ] return code def AssignDeclaration(self, funcname): code = [ 'int %s(struct %s *, const %s, ev_uint32_t);' % ( funcname, self._struct.Name(), self._ctype ) ] return code def CodeAssign(self): name = self._name code = [ 'int', '%s_%s_assign(struct %s *msg, ' 'const %s value, ev_uint32_t len)' % ( self._struct.Name(), name, self._struct.Name(), self._ctype), '{', ' if (msg->%s_data != NULL)' % name, ' free (msg->%s_data);' % name, ' msg->%s_data = malloc(len);' % name, ' if (msg->%s_data == NULL)' % name, ' return (-1);', ' msg->%s_set = 1;' % name, ' msg->%s_length = len;' % name, ' memcpy(msg->%s_data, value, len);' % name, ' return (0);', '}' ] return code def CodeGet(self): name = self._name code = [ 'int', '%s_%s_get(struct %s *msg, %s *value, ev_uint32_t *plen)' % ( self._struct.Name(), name, self._struct.Name(), self._ctype), '{', ' if (msg->%s_set != 1)' % name, ' return (-1);', ' *value = msg->%s_data;' % name, ' *plen = msg->%s_length;' % name, ' return (0);', '}' ] return code def CodeUnmarshal(self, buf, tag_name, var_name): code = ['if (evtag_payload_length(%s, &%s->%s_length) == -1)' % ( buf, var_name, self._name), ' return (-1);', # We do not want DoS opportunities 'if (%s->%s_length > EVBUFFER_LENGTH(%s))' % ( var_name, self._name, buf), ' return (-1);', 'if ((%s->%s_data = malloc(%s->%s_length)) == NULL)' % ( var_name, self._name, var_name, self._name), ' return (-1);', 'if (evtag_unmarshal_fixed(%s, %s, %s->%s_data, ' '%s->%s_length) == -1) {' % ( buf, tag_name, var_name, self._name, var_name, self._name), ' event_warnx("%%s: failed to unmarshal %s", __func__);' % ( self._name ), ' return (-1);', '}' ] return code def CodeMarshal(self, buf, tag_name, var_name): code = ['evtag_marshal(%s, %s, %s->%s_data, %s->%s_length);' % ( buf, tag_name, var_name, self._name, var_name, self._name)] return code def CodeClear(self, structname): code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), ' free (%s->%s_data);' % (structname, self.Name()), ' %s->%s_data = NULL;' % (structname, self.Name()), ' %s->%s_length = 0;' % (structname, self.Name()), ' %s->%s_set = 0;' % (structname, self.Name()), '}' ] return code def CodeNew(self, name): code = ['%s->%s_data = NULL;' % (name, self._name), '%s->%s_length = 0;' % (name, self._name) ] return code def CodeFree(self, name): code = ['if (%s->%s_data != NULL)' % (name, self._name), ' free (%s->%s_data); ' % (name, self._name)] return code def Declaration(self): dcl = ['ev_uint8_t *%s_data;' % self._name, 'ev_uint32_t %s_length;' % self._name] return dcl class EntryArray(Entry): def __init__(self, entry): # Init base class Entry.__init__(self, entry._type, entry._name, entry._tag) self._entry = entry self._refname = entry._refname self._ctype = 'struct %s *' % self._refname def GetDeclaration(self, funcname): """Allows direct access to elements of the array.""" translate = self.GetTranslation() translate["funcname"] = funcname code = [ 'int %(funcname)s(struct %(parent_name)s *, int, %(ctype)s *);' % translate ] return code def AssignDeclaration(self, funcname): code = [ 'int %s(struct %s *, int, const %s);' % ( funcname, self._struct.Name(), self._ctype ) ] return code def AddDeclaration(self, funcname): code = [ '%s %s(struct %s *);' % ( self._ctype, funcname, self._struct.Name() ) ] return code def CodeGet(self): code = """int %(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, int offset, %(ctype)s *value) { if (!msg->%(name)s_set || offset < 0 || offset >= msg->%(name)s_length) return (-1); *value = msg->%(name)s_data[offset]; return (0); }""" % self.GetTranslation() return code.split('\n') def CodeAssign(self): code = """int %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, int off, const %(ctype)s value) { struct evbuffer *tmp = NULL; if (!msg->%(name)s_set || off < 0 || off >= msg->%(name)s_length) return (-1); %(refname)s_clear(msg->%(name)s_data[off]); if ((tmp = evbuffer_new()) == NULL) { event_warn("%%s: evbuffer_new()", __func__); goto error; } %(refname)s_marshal(tmp, value); if (%(refname)s_unmarshal(msg->%(name)s_data[off], tmp) == -1) { event_warnx("%%s: %(refname)s_unmarshal", __func__); goto error; } evbuffer_free(tmp); return (0); error: if (tmp != NULL) evbuffer_free(tmp); %(refname)s_clear(msg->%(name)s_data[off]); return (-1); }""" % self.GetTranslation() return code.split('\n') def CodeAdd(self): code = \ """%(ctype)s %(parent_name)s_%(name)s_add(struct %(parent_name)s *msg) { if (++msg->%(name)s_length >= msg->%(name)s_num_allocated) { int tobe_allocated = msg->%(name)s_num_allocated; %(ctype)s* new_data = NULL; tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1; new_data = (%(ctype)s*) realloc(msg->%(name)s_data, tobe_allocated * sizeof(%(ctype)s)); if (new_data == NULL) goto error; msg->%(name)s_data = new_data; msg->%(name)s_num_allocated = tobe_allocated; } msg->%(name)s_data[msg->%(name)s_length - 1] = %(refname)s_new(); if (msg->%(name)s_data[msg->%(name)s_length - 1] == NULL) goto error; msg->%(name)s_set = 1; return (msg->%(name)s_data[msg->%(name)s_length - 1]); error: --msg->%(name)s_length; return (NULL); } """ % self.GetTranslation() return code.split('\n') def CodeComplete(self, structname): code = [] translate = self.GetTranslation() if self.Optional(): code.append( 'if (%(structname)s->%(name)s_set)' % translate) translate["structname"] = structname tmp = """{ int i; for (i = 0; i < %(structname)s->%(name)s_length; ++i) { if (%(refname)s_complete(%(structname)s->%(name)s_data[i]) == -1) return (-1); } }""" % translate code.extend(tmp.split('\n')) return code def CodeUnmarshal(self, buf, tag_name, var_name): translate = self.GetTranslation() translate["var_name"] = var_name translate["buf"] = buf translate["tag_name"] = tag_name code = """if (%(parent_name)s_%(name)s_add(%(var_name)s) == NULL) return (-1); if (evtag_unmarshal_%(refname)s(%(buf)s, %(tag_name)s, %(var_name)s->%(name)s_data[%(var_name)s->%(name)s_length - 1]) == -1) { --%(var_name)s->%(name)s_length; event_warnx("%%s: failed to unmarshal %(name)s", __func__); return (-1); }""" % translate return code.split('\n') def CodeMarshal(self, buf, tag_name, var_name): code = ['{', ' int i;', ' for (i = 0; i < %s->%s_length; ++i) {' % ( var_name, self._name), ' evtag_marshal_%s(%s, %s, %s->%s_data[i]);' % ( self._refname, buf, tag_name, var_name, self._name), ' }', '}' ] return code def CodeClear(self, structname): code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()), ' int i;', ' for (i = 0; i < %s->%s_length; ++i) {' % ( structname, self.Name()), ' %s_free(%s->%s_data[i]);' % ( self._refname, structname, self.Name()), ' }', ' free(%s->%s_data);' % (structname, self.Name()), ' %s->%s_data = NULL;' % (structname, self.Name()), ' %s->%s_set = 0;' % (structname, self.Name()), ' %s->%s_length = 0;' % (structname, self.Name()), ' %s->%s_num_allocated = 0;' % (structname, self.Name()), '}' ] return code def CodeNew(self, name): code = ['%s->%s_data = NULL;' % (name, self._name), '%s->%s_length = 0;' % (name, self._name), '%s->%s_num_allocated = 0;' % (name, self._name)] return code def CodeFree(self, name): code = ['if (%s->%s_data != NULL) {' % (name, self._name), ' int i;', ' for (i = 0; i < %s->%s_length; ++i) {' % ( name, self._name), ' %s_free(%s->%s_data[i]); ' % ( self._refname, name, self._name), ' %s->%s_data[i] = NULL;' % (name, self._name), ' }', ' free(%s->%s_data);' % (name, self._name), ' %s->%s_data = NULL;' % (name, self._name), ' %s->%s_length = 0;' % (name, self._name), ' %s->%s_num_allocated = 0;' % (name, self._name), '}' ] return code def Declaration(self): dcl = ['struct %s **%s_data;' % (self._refname, self._name), 'int %s_length;' % self._name, 'int %s_num_allocated;' % self._name ] return dcl def NormalizeLine(line): global white global cppcomment line = cppcomment.sub('', line) line = line.strip() line = white.sub(' ', line) return line def ProcessOneEntry(newstruct, entry): optional = 0 array = 0 entry_type = '' name = '' tag = '' tag_set = None separator = '' fixed_length = '' tokens = entry.split(' ') while tokens: token = tokens[0] tokens = tokens[1:] if not entry_type: if not optional and token == 'optional': optional = 1 continue if not array and token == 'array': array = 1 continue if not entry_type: entry_type = token continue if not name: res = re.match(r'^([^\[\]]+)(\[.*\])?$', token) if not res: print >>sys.stderr, 'Cannot parse name: \"%s\" around %d' % ( entry, line_count) sys.exit(1) name = res.group(1) fixed_length = res.group(2) if fixed_length: fixed_length = fixed_length[1:-1] continue if not separator: separator = token if separator != '=': print >>sys.stderr, 'Expected "=" after name \"%s\" got %s' % ( name, token) sys.exit(1) continue if not tag_set: tag_set = 1 if not re.match(r'^(0x)?[0-9]+$', token): print >>sys.stderr, 'Expected tag number: \"%s\"' % entry sys.exit(1) tag = int(token, 0) continue print >>sys.stderr, 'Cannot parse \"%s\"' % entry sys.exit(1) if not tag_set: print >>sys.stderr, 'Need tag number: \"%s\"' % entry sys.exit(1) # Create the right entry if entry_type == 'bytes': if fixed_length: newentry = EntryBytes(entry_type, name, tag, fixed_length) else: newentry = EntryVarBytes(entry_type, name, tag) elif entry_type == 'int' and not fixed_length: newentry = EntryInt(entry_type, name, tag) elif entry_type == 'string' and not fixed_length: newentry = EntryString(entry_type, name, tag) else: res = re.match(r'^struct\[(%s)\]$' % _STRUCT_RE, entry_type, re.IGNORECASE) if res: # References another struct defined in our file newentry = EntryStruct(entry_type, name, tag, res.group(1)) else: print >>sys.stderr, 'Bad type: "%s" in "%s"' % (entry_type, entry) sys.exit(1) structs = [] if optional: newentry.MakeOptional() if array: newentry.MakeArray() newentry.SetStruct(newstruct) newentry.SetLineCount(line_count) newentry.Verify() if array: # We need to encapsulate this entry into a struct newname = newentry.Name()+ '_array' # Now borgify the new entry. newentry = EntryArray(newentry) newentry.SetStruct(newstruct) newentry.SetLineCount(line_count) newentry.MakeArray() newstruct.AddEntry(newentry) return structs def ProcessStruct(data): tokens = data.split(' ') # First three tokens are: 'struct' 'name' '{' newstruct = Struct(tokens[1]) inside = ' '.join(tokens[3:-1]) tokens = inside.split(';') structs = [] for entry in tokens: entry = NormalizeLine(entry) if not entry: continue # It's possible that new structs get defined in here structs.extend(ProcessOneEntry(newstruct, entry)) structs.append(newstruct) return structs def GetNextStruct(file): global line_count global cppdirect got_struct = 0 processed_lines = [] have_c_comment = 0 data = '' while 1: line = file.readline() if not line: break line_count += 1 line = line[:-1] if not have_c_comment and re.search(r'/\*', line): if re.search(r'/\*.*\*/', line): line = re.sub(r'/\*.*\*/', '', line) else: line = re.sub(r'/\*.*$', '', line) have_c_comment = 1 if have_c_comment: if not re.search(r'\*/', line): continue have_c_comment = 0 line = re.sub(r'^.*\*/', '', line) line = NormalizeLine(line) if not line: continue if not got_struct: if re.match(r'#include ["<].*[>"]', line): cppdirect.append(line) continue if re.match(r'^#(if( |def)|endif)', line): cppdirect.append(line) continue if re.match(r'^#define', line): headerdirect.append(line) continue if not re.match(r'^struct %s {$' % _STRUCT_RE, line, re.IGNORECASE): print >>sys.stderr, 'Missing struct on line %d: %s' % ( line_count, line) sys.exit(1) else: got_struct = 1 data += line continue # We are inside the struct tokens = line.split('}') if len(tokens) == 1: data += ' ' + line continue if len(tokens[1]): print >>sys.stderr, 'Trailing garbage after struct on line %d' % ( line_count ) sys.exit(1) # We found the end of the struct data += ' %s}' % tokens[0] break # Remove any comments, that might be in there data = re.sub(r'/\*.*\*/', '', data) return data def Parse(file): """ Parses the input file and returns C code and corresponding header file. """ entities = [] while 1: # Just gets the whole struct nicely formatted data = GetNextStruct(file) if not data: break entities.extend(ProcessStruct(data)) return entities def GuardName(name): name = '_'.join(name.split('.')) name = '_'.join(name.split('/')) guard = '_'+name.upper()+'_' return guard def HeaderPreamble(name): guard = GuardName(name) pre = ( '/*\n' ' * Automatically generated from %s\n' ' */\n\n' '#ifndef %s\n' '#define %s\n\n' ) % ( name, guard, guard) # insert stdint.h - let's hope everyone has it pre += ( '#include <event-config.h>\n' '#ifdef _EVENT_HAVE_STDINT_H\n' '#include <stdint.h>\n' '#endif\n' ) for statement in headerdirect: pre += '%s\n' % statement if headerdirect: pre += '\n' pre += ( '#define EVTAG_HAS(msg, member) ((msg)->member##_set == 1)\n' '#ifdef __GNUC__\n' '#define EVTAG_ASSIGN(msg, member, args...) ' '(*(msg)->base->member##_assign)(msg, ## args)\n' '#define EVTAG_GET(msg, member, args...) ' '(*(msg)->base->member##_get)(msg, ## args)\n' '#else\n' '#define EVTAG_ASSIGN(msg, member, ...) ' '(*(msg)->base->member##_assign)(msg, ## __VA_ARGS__)\n' '#define EVTAG_GET(msg, member, ...) ' '(*(msg)->base->member##_get)(msg, ## __VA_ARGS__)\n' '#endif\n' '#define EVTAG_ADD(msg, member) (*(msg)->base->member##_add)(msg)\n' '#define EVTAG_LEN(msg, member) ((msg)->member##_length)\n' ) return pre def HeaderPostamble(name): guard = GuardName(name) return '#endif /* %s */' % guard def BodyPreamble(name): global _NAME global _VERSION header_file = '.'.join(name.split('.')[:-1]) + '.gen.h' pre = ( '/*\n' ' * Automatically generated from %s\n' ' * by %s/%s. DO NOT EDIT THIS FILE.\n' ' */\n\n' ) % (name, _NAME, _VERSION) pre += ( '#include <sys/types.h>\n' '#ifdef _EVENT_HAVE_SYS_TIME_H\n' '#include <sys/time.h>\n' '#endif\n' '#include <stdlib.h>\n' '#include <string.h>\n' '#include <assert.h>\n' '#define EVENT_NO_STRUCT\n' '#include <event.h>\n\n' '#ifdef _EVENT___func__\n' '#define __func__ _EVENT___func__\n' '#endif\n' ) for statement in cppdirect: pre += '%s\n' % statement pre += '\n#include "%s"\n\n' % header_file pre += 'void event_err(int eval, const char *fmt, ...);\n' pre += 'void event_warn(const char *fmt, ...);\n' pre += 'void event_errx(int eval, const char *fmt, ...);\n' pre += 'void event_warnx(const char *fmt, ...);\n\n' return pre def main(argv): if len(argv) < 2 or not argv[1]: print >>sys.stderr, 'Need RPC description file as first argument.' sys.exit(1) filename = argv[1] ext = filename.split('.')[-1] if ext != 'rpc': print >>sys.stderr, 'Unrecognized file extension: %s' % ext sys.exit(1) print >>sys.stderr, 'Reading \"%s\"' % filename fp = open(filename, 'r') entities = Parse(fp) fp.close() header_file = '.'.join(filename.split('.')[:-1]) + '.gen.h' impl_file = '.'.join(filename.split('.')[:-1]) + '.gen.c' print >>sys.stderr, '... creating "%s"' % header_file header_fp = open(header_file, 'w') print >>header_fp, HeaderPreamble(filename) # Create forward declarations: allows other structs to reference # each other for entry in entities: entry.PrintForwardDeclaration(header_fp) print >>header_fp, '' for entry in entities: entry.PrintTags(header_fp) entry.PrintDeclaration(header_fp) print >>header_fp, HeaderPostamble(filename) header_fp.close() print >>sys.stderr, '... creating "%s"' % impl_file impl_fp = open(impl_file, 'w') print >>impl_fp, BodyPreamble(filename) for entry in entities: entry.PrintCode(impl_fp) impl_fp.close() if __name__ == '__main__': main(sys.argv)