ovsdb-idl: Add "safe" iterator macro to generated code.
[sliver-openvswitch.git] / ovsdb / ovsdb-idlc.in
1 #! @PYTHON@
2
3 import getopt
4 import os
5 import re
6 import sys
7
8 sys.path.insert(0, "@abs_top_srcdir@/ovsdb")
9 import simplejson as json
10
11 from OVSDB import *
12
13 argv0 = sys.argv[0]
14
15 class Datum:
16     def __init__(self, type, values):
17         self.type = type
18         self.values = values
19
20     @staticmethod
21     def fromJson(type_, json):
22         if not type_.value:
23             if len(json) == 2 and json[0] == "set":
24                 values = []
25                 for atomJson in json[1]:
26                     values += [Atom.fromJson(type_.key, atomJson)]
27             else:
28                 values = [Atom.fromJson(type_.key, json)]
29         else:
30             if len(json) != 2 or json[0] != "map":
31                 raise Error("%s is not valid JSON for a map" % json)
32             values = []
33             for pairJson in json[1]:
34                 values += [(Atom.fromJson(type_.key, pairJson[0]),
35                             Atom.fromJson(type_.value, pairJson[1]))]
36         return Datum(type_, values)
37
38     def cInitDatum(self, var):
39         if len(self.values) == 0:
40             return ["ovsdb_datum_init_empty(%s);" % var]
41
42         s = ["%s->n = %d;" % (var, len(self.values))]
43         s += ["%s->keys = xmalloc(%d * sizeof *%s->keys);"
44               % (var, len(self.values), var)]
45
46         for i in range(len(self.values)):
47             key = self.values[i]
48             if self.type.value:
49                 key = key[0]
50             s += key.cInitAtom("%s->keys[%d]" % (var, i))
51         
52         if self.type.value:
53             s += ["%s->values = xmalloc(%d * sizeof *%s->values);"
54                   % (var, len(self.values), var)]
55             for i in range(len(self.values)):
56                 value = self.values[i][1]
57                 s += key.cInitAtom("%s->values[%d]" % (var, i))
58         else:
59             s += ["%s->values = NULL;" % var]
60
61         if len(self.values) > 1:
62             s += ["ovsdb_datum_sort_assert(%s, OVSDB_TYPE_%s);"
63                   % (var, self.type.key.upper())]
64
65         return s
66
67 def parseSchema(filename):
68     return IdlSchema.fromJson(json.load(open(filename, "r")))
69
70 def annotateSchema(schemaFile, annotationFile):
71     schemaJson = json.load(open(schemaFile, "r"))
72     execfile(annotationFile, globals(), {"s": schemaJson})
73     json.dump(schemaJson, sys.stdout)
74
75 def constify(cType, const):
76     if (const
77         and cType.endswith('*') and not cType.endswith('**')
78         and (cType.startswith('struct uuid') or cType.startswith('char'))):
79         return 'const %s' % cType
80     else:
81         return cType
82
83 def cMembers(prefix, columnName, column, const):
84     type = column.type
85     if type.min == 1 and type.max == 1:
86         singleton = True
87         pointer = ''
88     else:
89         singleton = False
90         if type.isOptionalPointer():
91             pointer = ''
92         else:
93             pointer = '*'
94
95     if type.value:
96         key = {'name': "key_%s" % columnName,
97                'type': constify(type.key.toCType(prefix) + pointer, const),
98                'comment': ''}
99         value = {'name': "value_%s" % columnName,
100                  'type': constify(type.value.toCType(prefix) + pointer, const),
101                  'comment': ''}
102         members = [key, value]
103     else:
104         m = {'name': columnName,
105              'type': constify(type.key.toCType(prefix) + pointer, const),
106              'comment': type.cDeclComment()}
107         members = [m]
108
109     if not singleton and not type.isOptionalPointer():
110         members.append({'name': 'n_%s' % columnName,
111                         'type': 'size_t ',
112                         'comment': ''})
113     return members
114
115 def printCIDLHeader(schemaFile):
116     schema = parseSchema(schemaFile)
117     prefix = schema.idlPrefix
118     print '''\
119 /* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
120
121 #ifndef %(prefix)sIDL_HEADER
122 #define %(prefix)sIDL_HEADER 1
123
124 #include <stdbool.h>
125 #include <stddef.h>
126 #include <stdint.h>
127 #include "ovsdb-idl-provider.h"
128 #include "uuid.h"''' % {'prefix': prefix.upper()}
129
130     for tableName, table in sorted(schema.tables.iteritems()):
131         structName = "%s%s" % (prefix, tableName.lower())
132
133         print "\f"
134         print "/* %s table. */" % tableName
135         print "struct %s {" % structName
136         print "\tstruct ovsdb_idl_row header_;"
137         for columnName, column in sorted(table.columns.iteritems()):
138             print "\n\t/* %s column. */" % columnName
139             for member in cMembers(prefix, columnName, column, False):
140                 print "\t%(type)s%(name)s;%(comment)s" % member
141         print "};"
142
143         # Column indexes.
144         printEnum(["%s_COL_%s" % (structName.upper(), columnName.upper())
145                    for columnName in sorted(table.columns)]
146                   + ["%s_N_COLUMNS" % structName.upper()])
147
148         print
149         for columnName in table.columns:
150             print "#define %(s)s_col_%(c)s (%(s)s_columns[%(S)s_COL_%(C)s])" % {
151                 's': structName,
152                 'S': structName.upper(),
153                 'c': columnName,
154                 'C': columnName.upper()}
155
156         print "\nextern struct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (structName, structName.upper())
157
158         print '''
159 const struct %(s)s *%(s)s_first(const struct ovsdb_idl *);
160 const struct %(s)s *%(s)s_next(const struct %(s)s *);
161 #define %(S)s_FOR_EACH(ROW, IDL) \\
162         for ((ROW) = %(s)s_first(IDL); \\
163              (ROW); \\
164              (ROW) = %(s)s_next(ROW))
165 #define %(S)s_FOR_EACH_SAFE(ROW, NEXT, IDL) \\
166         for ((ROW) = %(s)s_first(IDL); \\
167              (ROW) ? ((NEXT) = %(s)s_next(ROW), 1) : 0; \\
168              (ROW) = (NEXT))
169
170 void %(s)s_delete(const struct %(s)s *);
171 struct %(s)s *%(s)s_insert(struct ovsdb_idl_txn *);
172 ''' % {'s': structName, 'S': structName.upper()}
173
174         for columnName, column in sorted(table.columns.iteritems()):
175             print 'void %(s)s_verify_%(c)s(const struct %(s)s *);' % {'s': structName, 'c': columnName}
176
177         print
178         for columnName, column in sorted(table.columns.iteritems()):
179
180             print 'void %(s)s_set_%(c)s(const struct %(s)s *,' % {'s': structName, 'c': columnName},
181             args = ['%(type)s%(name)s' % member for member
182                     in cMembers(prefix, columnName, column, True)]
183             print '%s);' % ', '.join(args)
184
185     # Table indexes.
186     printEnum(["%sTABLE_%s" % (prefix.upper(), tableName.upper()) for tableName in sorted(schema.tables)] + ["%sN_TABLES" % prefix.upper()])
187     print
188     for tableName in schema.tables:
189         print "#define %(p)stable_%(t)s (%(p)stable_classes[%(P)sTABLE_%(T)s])" % {
190             'p': prefix,
191             'P': prefix.upper(),
192             't': tableName.lower(),
193             'T': tableName.upper()}
194     print "\nextern struct ovsdb_idl_table_class %stable_classes[%sN_TABLES];" % (prefix, prefix.upper())
195
196     print "\nextern struct ovsdb_idl_class %sidl_class;" % prefix
197     print "\nvoid %sinit(void);" % prefix
198     print "\n#endif /* %(prefix)sIDL_HEADER */" % {'prefix': prefix.upper()}
199
200 def printEnum(members):
201     if len(members) == 0:
202         return
203
204     print "\nenum {";
205     for member in members[:-1]:
206         print "    %s," % member
207     print "    %s" % members[-1]
208     print "};"
209
210 def printCIDLSource(schemaFile):
211     schema = parseSchema(schemaFile)
212     prefix = schema.idlPrefix
213     print '''\
214 /* Generated automatically -- do not modify!    -*- buffer-read-only: t -*- */
215
216 #include <config.h>
217 #include %s
218 #include <assert.h>
219 #include <limits.h>
220 #include "ovsdb-data.h"
221 #include "ovsdb-error.h"
222
223 static bool inited;
224 ''' % schema.idlHeader
225
226     # Cast functions.
227     for tableName, table in sorted(schema.tables.iteritems()):
228         structName = "%s%s" % (prefix, tableName.lower())
229         print '''
230 static struct %(s)s *
231 %(s)s_cast(const struct ovsdb_idl_row *row)
232 {
233     return row ? CONTAINER_OF(row, struct %(s)s, header_) : NULL;
234 }\
235 ''' % {'s': structName}
236
237
238     for tableName, table in sorted(schema.tables.iteritems()):
239         structName = "%s%s" % (prefix, tableName.lower())
240         print "\f"
241         print "/* %s table. */" % (tableName)
242
243         # Parse functions.
244         for columnName, column in sorted(table.columns.iteritems()):
245             print '''
246 static void
247 %(s)s_parse_%(c)s(struct ovsdb_idl_row *row_, const struct ovsdb_datum *datum)
248 {
249     struct %(s)s *row = %(s)s_cast(row_);''' % {'s': structName,
250                                                 'c': columnName}
251
252             type = column.type
253             if type.value:
254                 keyVar = "row->key_%s" % columnName
255                 valueVar = "row->value_%s" % columnName
256             else:
257                 keyVar = "row->%s" % columnName
258                 valueVar = None
259
260             if (type.min == 1 and type.max == 1) or type.isOptionalPointer():
261                 print
262                 print "    assert(inited);"
263                 print "    if (datum->n >= 1) {"
264                 if not type.key.refTable:
265                     print "        %s = datum->keys[0].%s;" % (keyVar, type.key.type)
266                 else:
267                     print "        %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[0].uuid));" % (keyVar, prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper())
268
269                 if valueVar:
270                     if type.value.refTable:
271                         print "        %s = datum->values[0].%s;" % (valueVar, type.value.type)
272                     else:
273                         print "        %s = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[0].uuid));" % (valueVar, prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper())
274                 print "    } else {"
275                 print "        %s" % type.key.initCDefault(keyVar, type.min == 0)
276                 if valueVar:
277                     print "        %s" % type.value.initCDefault(valueVar, type.min == 0)
278                 print "    }"
279             else:
280                 if type.max != 'unlimited':
281                     print "    size_t n = MIN(%d, datum->n);" % type.max
282                     nMax = "n"
283                 else:
284                     nMax = "datum->n"
285                 print "    size_t i;"
286                 print
287                 print "    assert(inited);"
288                 print "    %s = NULL;" % keyVar
289                 if valueVar:
290                     print "    %s = NULL;" % valueVar
291                 print "    row->n_%s = 0;" % columnName
292                 print "    for (i = 0; i < %s; i++) {" % nMax
293                 refs = []
294                 if type.key.refTable:
295                     print "        struct %s%s *keyRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->keys[i].uuid));" % (prefix, type.key.refTable.lower(), prefix, type.key.refTable.lower(), prefix, prefix.upper(), type.key.refTable.upper())
296                     keySrc = "keyRow"
297                     refs.append('keyRow')
298                 else:
299                     keySrc = "datum->keys[i].%s" % type.key.type
300                 if type.value and type.value.refTable:
301                     print "        struct %s%s *valueRow = %s%s_cast(ovsdb_idl_get_row_arc(row_, &%stable_classes[%sTABLE_%s], &datum->values[i].uuid));" % (prefix, type.value.refTable.lower(), prefix, type.value.refTable.lower(), prefix, prefix.upper(), type.value.refTable.upper())
302                     valueSrc = "valueRow"
303                     refs.append('valueRow')
304                 elif valueVar:
305                     valueSrc = "datum->values[i].%s" % type.value.type
306                 if refs:
307                     print "        if (%s) {" % ' && '.join(refs)
308                     indent = "            "
309                 else:
310                     indent = "        "
311                 print "%sif (!row->n_%s) {" % (indent, columnName)
312                 print "%s    %s = xmalloc(%s * sizeof *%s);" % (indent, keyVar, nMax, keyVar)
313                 if valueVar:
314                     print "%s    %s = xmalloc(%s * sizeof %s);" % (indent, valueVar, nMax, valueVar)
315                 print "%s}" % indent
316                 print "%s%s[row->n_%s] = %s;" % (indent, keyVar, columnName, keySrc)
317                 if valueVar:
318                     print "%s%s[row->n_%s] = %s;" % (indent, valueVar, columnName, valueSrc)
319                 print "%srow->n_%s++;" % (indent, columnName)
320                 if refs:
321                     print "        }"
322                 print "    }"
323             print "}"
324
325         # Unparse functions.
326         for columnName, column in sorted(table.columns.iteritems()):
327             type = column.type
328             if (type.min != 1 or type.max != 1) and not type.isOptionalPointer():
329                 print '''
330 static void
331 %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row_)
332 {
333     struct %(s)s *row = %(s)s_cast(row_);
334
335     assert(inited);''' % {'s': structName, 'c': columnName}
336                 if type.value:
337                     keyVar = "row->key_%s" % columnName
338                     valueVar = "row->value_%s" % columnName
339                 else:
340                     keyVar = "row->%s" % columnName
341                     valueVar = None
342                 print "    free(%s);" % keyVar
343                 if valueVar:
344                     print "    free(%s);" % valueVar
345                 print '}'
346             else:
347                 print '''
348 static void
349 %(s)s_unparse_%(c)s(struct ovsdb_idl_row *row OVS_UNUSED)
350 {
351     /* Nothing to do. */
352 }''' % {'s': structName, 'c': columnName}
353  
354         # First, next functions.
355         print '''
356 const struct %(s)s *
357 %(s)s_first(const struct ovsdb_idl *idl)
358 {
359     return %(s)s_cast(ovsdb_idl_first_row(idl, &%(p)stable_classes[%(P)sTABLE_%(T)s]));
360 }
361
362 const struct %(s)s *
363 %(s)s_next(const struct %(s)s *row)
364 {
365     return %(s)s_cast(ovsdb_idl_next_row(&row->header_));
366 }''' % {'s': structName,
367         'p': prefix,
368         'P': prefix.upper(),
369         'T': tableName.upper()}
370
371         print '''
372 void
373 %(s)s_delete(const struct %(s)s *row)
374 {
375     ovsdb_idl_txn_delete(&row->header_);
376 }
377
378 struct %(s)s *
379 %(s)s_insert(struct ovsdb_idl_txn *txn)
380 {
381     return %(s)s_cast(ovsdb_idl_txn_insert(txn, &%(p)stable_classes[%(P)sTABLE_%(T)s]));
382 }
383 ''' % {'s': structName,
384        'p': prefix,
385        'P': prefix.upper(),
386        'T': tableName.upper()}
387
388         # Verify functions.
389         for columnName, column in sorted(table.columns.iteritems()):
390             print '''
391 void
392 %(s)s_verify_%(c)s(const struct %(s)s *row)
393 {
394     assert(inited);
395     ovsdb_idl_txn_verify(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s]);
396 }''' % {'s': structName,
397         'S': structName.upper(),
398         'c': columnName,
399         'C': columnName.upper()}
400
401         # Set functions.
402         for columnName, column in sorted(table.columns.iteritems()):
403             type = column.type
404             print '\nvoid'
405             members = cMembers(prefix, columnName, column, True)
406             keyVar = members[0]['name']
407             nVar = None
408             valueVar = None
409             if type.value:
410                 valueVar = members[1]['name']
411                 if len(members) > 2:
412                     nVar = members[2]['name']
413             else:
414                 if len(members) > 1:
415                     nVar = members[1]['name']
416             print '%(s)s_set_%(c)s(const struct %(s)s *row, %(args)s)' % \
417                 {'s': structName, 'c': columnName,
418                  'args': ', '.join(['%(type)s%(name)s' % m for m in members])}
419             print "{"
420             print "    struct ovsdb_datum datum;"
421             if type.min == 1 and type.max == 1:
422                 print
423                 print "    assert(inited);"
424                 print "    datum.n = 1;"
425                 print "    datum.keys = xmalloc(sizeof *datum.keys);"
426                 print "    " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
427                 if type.value:
428                     print "    datum.values = xmalloc(sizeof *datum.values);"
429                     print "    "+ type.value.copyCValue("datum.values[0].%s" % type.value.type, valueVar)
430                 else:
431                     print "    datum.values = NULL;"
432             elif type.isOptionalPointer():
433                 print
434                 print "    assert(inited);"
435                 print "    if (%s) {" % keyVar
436                 print "        datum.n = 1;"
437                 print "        datum.keys = xmalloc(sizeof *datum.keys);"
438                 print "        " + type.key.copyCValue("datum.keys[0].%s" % type.key.type, keyVar)
439                 print "    } else {"
440                 print "        datum.n = 0;"
441                 print "        datum.keys = NULL;"
442                 print "    }"
443                 print "    datum.values = NULL;"
444             else:
445                 print "    size_t i;"
446                 print
447                 print "    assert(inited);"
448                 print "    datum.n = %s;" % nVar
449                 print "    datum.keys = xmalloc(%s * sizeof *datum.keys);" % nVar
450                 if type.value:
451                     print "    datum.values = xmalloc(%s * sizeof *datum.values);" % nVar
452                 else:
453                     print "    datum.values = NULL;"
454                 print "    for (i = 0; i < %s; i++) {" % nVar
455                 print "        " + type.key.copyCValue("datum.keys[i].%s" % type.key.type, "%s[i]" % keyVar)
456                 if type.value:
457                     print "        " + type.value.copyCValue("datum.values[i].%s" % type.value.type, "%s[i]" % valueVar)
458                 print "    }"
459             print "    ovsdb_idl_txn_write(&row->header_, &%(s)s_columns[%(S)s_COL_%(C)s], &datum);" \
460                 % {'s': structName,
461                    'S': structName.upper(),
462                    'C': columnName.upper()}
463             print "}"
464
465         # Table columns.
466         print "\nstruct ovsdb_idl_column %s_columns[%s_N_COLUMNS];" % (
467             structName, structName.upper())
468         print """
469 static void\n%s_columns_init(void)
470 {
471     struct ovsdb_idl_column *c;\
472 """ % structName
473         for columnName, column in sorted(table.columns.iteritems()):
474             cs = "%s_col_%s" % (structName, columnName)
475             d = {'cs': cs, 'c': columnName, 's': structName}
476             print
477             print "    /* Initialize %(cs)s. */" % d
478             print "    c = &%(cs)s;" % d
479             print "    c->name = \"%(c)s\";" % d
480             print column.type.cInitType("    ", "c->type")
481             print "    c->parse = %(s)s_parse_%(c)s;" % d
482             print "    c->unparse = %(s)s_unparse_%(c)s;" % d
483         print "}"
484
485     # Table classes.
486     print "\f"
487     print "struct ovsdb_idl_table_class %stable_classes[%sN_TABLES] = {" % (prefix, prefix.upper())
488     for tableName, table in sorted(schema.tables.iteritems()):
489         structName = "%s%s" % (prefix, tableName.lower())
490         print "    {\"%s\"," % tableName
491         print "     %s_columns, ARRAY_SIZE(%s_columns)," % (
492             structName, structName)
493         print "     sizeof(struct %s)}," % structName
494     print "};"
495
496     # IDL class.
497     print "\nstruct ovsdb_idl_class %sidl_class = {" % prefix
498     print "    \"%s\", %stable_classes, ARRAY_SIZE(%stable_classes)" % (
499         schema.name, prefix, prefix)
500     print "};"
501
502     # global init function
503     print """
504 void
505 %sinit(void)
506 {
507     if (inited) {
508         return;
509     }
510     inited = true;
511 """ % prefix
512     for tableName, table in sorted(schema.tables.iteritems()):
513         structName = "%s%s" % (prefix, tableName.lower())
514         print "    %s_columns_init();" % structName
515     print "}"
516
517 def ovsdb_escape(string):
518     def escape(match):
519         c = match.group(0)
520         if c == '\0':
521             raise Error("strings may not contain null bytes")
522         elif c == '\\':
523             return '\\\\'
524         elif c == '\n':
525             return '\\n'
526         elif c == '\r':
527             return '\\r'
528         elif c == '\t':
529             return '\\t'
530         elif c == '\b':
531             return '\\b'
532         elif c == '\a':
533             return '\\a'
534         else:
535             return '\\x%02x' % ord(c)
536     return re.sub(r'["\\\000-\037]', escape, string)
537
538
539
540 def usage():
541     print """\
542 %(argv0)s: ovsdb schema compiler
543 usage: %(argv0)s [OPTIONS] COMMAND ARG...
544
545 The following commands are supported:
546   annotate SCHEMA ANNOTATIONS print SCHEMA combined with ANNOTATIONS
547   c-idl-header IDL            print C header file for IDL
548   c-idl-source IDL            print C source file for IDL implementation
549   nroff IDL                   print schema documentation in nroff format
550
551 The following options are also available:
552   -h, --help                  display this help message
553   -V, --version               display version information\
554 """ % {'argv0': argv0}
555     sys.exit(0)
556
557 if __name__ == "__main__":
558     try:
559         try:
560             options, args = getopt.gnu_getopt(sys.argv[1:], 'C:hV',
561                                               ['directory',
562                                                'help',
563                                                'version'])
564         except getopt.GetoptError, geo:
565             sys.stderr.write("%s: %s\n" % (argv0, geo.msg))
566             sys.exit(1)
567             
568         for key, value in options:
569             if key in ['-h', '--help']:
570                 usage()
571             elif key in ['-V', '--version']:
572                 print "ovsdb-idlc (Open vSwitch) @VERSION@"
573             elif key in ['-C', '--directory']:
574                 os.chdir(value)
575             else:
576                 sys.exit(0)
577             
578         optKeys = [key for key, value in options]
579
580         if not args:
581             sys.stderr.write("%s: missing command argument "
582                              "(use --help for help)\n" % argv0)
583             sys.exit(1)
584
585         commands = {"annotate": (annotateSchema, 2),
586                     "c-idl-header": (printCIDLHeader, 1),
587                     "c-idl-source": (printCIDLSource, 1)}
588
589         if not args[0] in commands:
590             sys.stderr.write("%s: unknown command \"%s\" "
591                              "(use --help for help)\n" % (argv0, args[0]))
592             sys.exit(1)
593
594         func, n_args = commands[args[0]]
595         if len(args) - 1 != n_args:
596             sys.stderr.write("%s: \"%s\" requires %d arguments but %d "
597                              "provided\n"
598                              % (argv0, args[0], n_args, len(args) - 1))
599             sys.exit(1)
600
601         func(*args[1:])
602     except Error, e:
603         sys.stderr.write("%s: %s\n" % (argv0, e.msg))
604         sys.exit(1)
605
606 # Local variables:
607 # mode: python
608 # End: