1: #!/usr/bin/env python
2: #
3: # Copyright (c) 2005 Niels Provos <provos@citi.umich.edu>
4: # All rights reserved.
5: #
6: # Generates marshaling code based on libevent.
7:
8: import sys
9: import re
10:
11: #
12: _NAME = "event_rpcgen.py"
13: _VERSION = "0.1"
14: _STRUCT_RE = '[a-z][a-z_0-9]*'
15:
16: # Globals
17: line_count = 0
18:
19: white = re.compile(r'^\s+')
20: cppcomment = re.compile(r'\/\/.*$')
21: headerdirect = []
22: cppdirect = []
23:
24: # Holds everything that makes a struct
25: class Struct:
26: def __init__(self, name):
27: self._name = name
28: self._entries = []
29: self._tags = {}
30: print >>sys.stderr, ' Created struct: %s' % name
31:
32: def AddEntry(self, entry):
33: if self._tags.has_key(entry.Tag()):
34: print >>sys.stderr, ( 'Entry "%s" duplicates tag number '
35: '%d from "%s" around line %d' ) % (
36: entry.Name(), entry.Tag(),
37: self._tags[entry.Tag()], line_count)
38: sys.exit(1)
39: self._entries.append(entry)
40: self._tags[entry.Tag()] = entry.Name()
41: print >>sys.stderr, ' Added entry: %s' % entry.Name()
42:
43: def Name(self):
44: return self._name
45:
46: def EntryTagName(self, entry):
47: """Creates the name inside an enumeration for distinguishing data
48: types."""
49: name = "%s_%s" % (self._name, entry.Name())
50: return name.upper()
51:
52: def PrintIdented(self, file, ident, code):
53: """Takes an array, add indentation to each entry and prints it."""
54: for entry in code:
55: print >>file, '%s%s' % (ident, entry)
56:
57: def PrintTags(self, file):
58: """Prints the tag definitions for a structure."""
59: print >>file, '/* Tag definition for %s */' % self._name
60: print >>file, 'enum %s_ {' % self._name.lower()
61: for entry in self._entries:
62: print >>file, ' %s=%d,' % (self.EntryTagName(entry),
63: entry.Tag())
64: print >>file, ' %s_MAX_TAGS' % (self._name.upper())
65: print >>file, '};\n'
66:
67: def PrintForwardDeclaration(self, file):
68: print >>file, 'struct %s;' % self._name
69:
70: def PrintDeclaration(self, file):
71: print >>file, '/* Structure declaration for %s */' % self._name
72: print >>file, 'struct %s_access_ {' % self._name
73: for entry in self._entries:
74: dcl = entry.AssignDeclaration('(*%s_assign)' % entry.Name())
75: dcl.extend(
76: entry.GetDeclaration('(*%s_get)' % entry.Name()))
77: if entry.Array():
78: dcl.extend(
79: entry.AddDeclaration('(*%s_add)' % entry.Name()))
80: self.PrintIdented(file, ' ', dcl)
81: print >>file, '};\n'
82:
83: print >>file, 'struct %s {' % self._name
84: print >>file, ' struct %s_access_ *base;\n' % self._name
85: for entry in self._entries:
86: dcl = entry.Declaration()
87: self.PrintIdented(file, ' ', dcl)
88: print >>file, ''
89: for entry in self._entries:
90: print >>file, ' ev_uint8_t %s_set;' % entry.Name()
91: print >>file, '};\n'
92:
93: print >>file, \
94: """struct %(name)s *%(name)s_new(void);
95: void %(name)s_free(struct %(name)s *);
96: void %(name)s_clear(struct %(name)s *);
97: void %(name)s_marshal(struct evbuffer *, const struct %(name)s *);
98: int %(name)s_unmarshal(struct %(name)s *, struct evbuffer *);
99: int %(name)s_complete(struct %(name)s *);
100: void evtag_marshal_%(name)s(struct evbuffer *, ev_uint32_t,
101: const struct %(name)s *);
102: int evtag_unmarshal_%(name)s(struct evbuffer *, ev_uint32_t,
103: struct %(name)s *);""" % { 'name' : self._name }
104:
105:
106: # Write a setting function of every variable
107: for entry in self._entries:
108: self.PrintIdented(file, '', entry.AssignDeclaration(
109: entry.AssignFuncName()))
110: self.PrintIdented(file, '', entry.GetDeclaration(
111: entry.GetFuncName()))
112: if entry.Array():
113: self.PrintIdented(file, '', entry.AddDeclaration(
114: entry.AddFuncName()))
115:
116: print >>file, '/* --- %s done --- */\n' % self._name
117:
118: def PrintCode(self, file):
119: print >>file, ('/*\n'
120: ' * Implementation of %s\n'
121: ' */\n') % self._name
122:
123: print >>file, \
124: 'static struct %(name)s_access_ __%(name)s_base = {' % \
125: { 'name' : self._name }
126: for entry in self._entries:
127: self.PrintIdented(file, ' ', entry.CodeBase())
128: print >>file, '};\n'
129:
130: # Creation
131: print >>file, (
132: 'struct %(name)s *\n'
133: '%(name)s_new(void)\n'
134: '{\n'
135: ' struct %(name)s *tmp;\n'
136: ' if ((tmp = malloc(sizeof(struct %(name)s))) == NULL) {\n'
137: ' event_warn("%%s: malloc", __func__);\n'
138: ' return (NULL);\n'
139: ' }\n'
140: ' tmp->base = &__%(name)s_base;\n') % { 'name' : self._name }
141:
142: for entry in self._entries:
143: self.PrintIdented(file, ' ', entry.CodeNew('tmp'))
144: print >>file, ' tmp->%s_set = 0;\n' % entry.Name()
145:
146: print >>file, (
147: ' return (tmp);\n'
148: '}\n')
149:
150: # Adding
151: for entry in self._entries:
152: if entry.Array():
153: self.PrintIdented(file, '', entry.CodeAdd())
154: print >>file, ''
155:
156: # Assigning
157: for entry in self._entries:
158: self.PrintIdented(file, '', entry.CodeAssign())
159: print >>file, ''
160:
161: # Getting
162: for entry in self._entries:
163: self.PrintIdented(file, '', entry.CodeGet())
164: print >>file, ''
165:
166: # Clearing
167: print >>file, ( 'void\n'
168: '%(name)s_clear(struct %(name)s *tmp)\n'
169: '{'
170: ) % { 'name' : self._name }
171: for entry in self._entries:
172: self.PrintIdented(file, ' ', entry.CodeClear('tmp'))
173:
174: print >>file, '}\n'
175:
176: # Freeing
177: print >>file, ( 'void\n'
178: '%(name)s_free(struct %(name)s *tmp)\n'
179: '{'
180: ) % { 'name' : self._name }
181:
182: for entry in self._entries:
183: self.PrintIdented(file, ' ', entry.CodeFree('tmp'))
184:
185: print >>file, (' free(tmp);\n'
186: '}\n')
187:
188: # Marshaling
189: print >>file, ('void\n'
190: '%(name)s_marshal(struct evbuffer *evbuf, '
191: 'const struct %(name)s *tmp)'
192: '{') % { 'name' : self._name }
193: for entry in self._entries:
194: indent = ' '
195: # Optional entries do not have to be set
196: if entry.Optional():
197: indent += ' '
198: print >>file, ' if (tmp->%s_set) {' % entry.Name()
199: self.PrintIdented(
200: file, indent,
201: entry.CodeMarshal('evbuf', self.EntryTagName(entry), 'tmp'))
202: if entry.Optional():
203: print >>file, ' }'
204:
205: print >>file, '}\n'
206:
207: # Unmarshaling
208: print >>file, ('int\n'
209: '%(name)s_unmarshal(struct %(name)s *tmp, '
210: ' struct evbuffer *evbuf)\n'
211: '{\n'
212: ' ev_uint32_t tag;\n'
213: ' while (EVBUFFER_LENGTH(evbuf) > 0) {\n'
214: ' if (evtag_peek(evbuf, &tag) == -1)\n'
215: ' return (-1);\n'
216: ' switch (tag) {\n'
217: ) % { 'name' : self._name }
218: for entry in self._entries:
219: print >>file, ' case %s:\n' % self.EntryTagName(entry)
220: if not entry.Array():
221: print >>file, (
222: ' if (tmp->%s_set)\n'
223: ' return (-1);'
224: ) % (entry.Name())
225:
226: self.PrintIdented(
227: file, ' ',
228: entry.CodeUnmarshal('evbuf',
229: self.EntryTagName(entry), 'tmp'))
230:
231: print >>file, ( ' tmp->%s_set = 1;\n' % entry.Name() +
232: ' break;\n' )
233: print >>file, ( ' default:\n'
234: ' return -1;\n'
235: ' }\n'
236: ' }\n' )
237: # Check if it was decoded completely
238: print >>file, ( ' if (%(name)s_complete(tmp) == -1)\n'
239: ' return (-1);'
240: ) % { 'name' : self._name }
241:
242: # Successfully decoded
243: print >>file, ( ' return (0);\n'
244: '}\n')
245:
246: # Checking if a structure has all the required data
247: print >>file, (
248: 'int\n'
249: '%(name)s_complete(struct %(name)s *msg)\n'
250: '{' ) % { 'name' : self._name }
251: for entry in self._entries:
252: self.PrintIdented(
253: file, ' ',
254: entry.CodeComplete('msg'))
255: print >>file, (
256: ' return (0);\n'
257: '}\n' )
258:
259: # Complete message unmarshaling
260: print >>file, (
261: 'int\n'
262: 'evtag_unmarshal_%(name)s(struct evbuffer *evbuf, '
263: 'ev_uint32_t need_tag, struct %(name)s *msg)\n'
264: '{\n'
265: ' ev_uint32_t tag;\n'
266: ' int res = -1;\n'
267: '\n'
268: ' struct evbuffer *tmp = evbuffer_new();\n'
269: '\n'
270: ' if (evtag_unmarshal(evbuf, &tag, tmp) == -1'
271: ' || tag != need_tag)\n'
272: ' goto error;\n'
273: '\n'
274: ' if (%(name)s_unmarshal(msg, tmp) == -1)\n'
275: ' goto error;\n'
276: '\n'
277: ' res = 0;\n'
278: '\n'
279: ' error:\n'
280: ' evbuffer_free(tmp);\n'
281: ' return (res);\n'
282: '}\n' ) % { 'name' : self._name }
283:
284: # Complete message marshaling
285: print >>file, (
286: 'void\n'
287: 'evtag_marshal_%(name)s(struct evbuffer *evbuf, ev_uint32_t tag, '
288: 'const struct %(name)s *msg)\n'
289: '{\n'
290: ' struct evbuffer *_buf = evbuffer_new();\n'
291: ' assert(_buf != NULL);\n'
292: ' evbuffer_drain(_buf, -1);\n'
293: ' %(name)s_marshal(_buf, msg);\n'
294: ' evtag_marshal(evbuf, tag, EVBUFFER_DATA(_buf), '
295: 'EVBUFFER_LENGTH(_buf));\n'
296: ' evbuffer_free(_buf);\n'
297: '}\n' ) % { 'name' : self._name }
298:
299: class Entry:
300: def __init__(self, type, name, tag):
301: self._type = type
302: self._name = name
303: self._tag = int(tag)
304: self._ctype = type
305: self._optional = 0
306: self._can_be_array = 0
307: self._array = 0
308: self._line_count = -1
309: self._struct = None
310: self._refname = None
311:
312: def GetTranslation(self):
313: return { "parent_name" : self._struct.Name(),
314: "name" : self._name,
315: "ctype" : self._ctype,
316: "refname" : self._refname
317: }
318:
319: def SetStruct(self, struct):
320: self._struct = struct
321:
322: def LineCount(self):
323: assert self._line_count != -1
324: return self._line_count
325:
326: def SetLineCount(self, number):
327: self._line_count = number
328:
329: def Array(self):
330: return self._array
331:
332: def Optional(self):
333: return self._optional
334:
335: def Tag(self):
336: return self._tag
337:
338: def Name(self):
339: return self._name
340:
341: def Type(self):
342: return self._type
343:
344: def MakeArray(self, yes=1):
345: self._array = yes
346:
347: def MakeOptional(self):
348: self._optional = 1
349:
350: def GetFuncName(self):
351: return '%s_%s_get' % (self._struct.Name(), self._name)
352:
353: def GetDeclaration(self, funcname):
354: code = [ 'int %s(struct %s *, %s *);' % (
355: funcname, self._struct.Name(), self._ctype ) ]
356: return code
357:
358: def CodeGet(self):
359: code = (
360: 'int',
361: '%(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, '
362: '%(ctype)s *value)',
363: '{',
364: ' if (msg->%(name)s_set != 1)',
365: ' return (-1);',
366: ' *value = msg->%(name)s_data;',
367: ' return (0);',
368: '}' )
369: code = '\n'.join(code)
370: code = code % self.GetTranslation()
371: return code.split('\n')
372:
373: def AssignFuncName(self):
374: return '%s_%s_assign' % (self._struct.Name(), self._name)
375:
376: def AddFuncName(self):
377: return '%s_%s_add' % (self._struct.Name(), self._name)
378:
379: def AssignDeclaration(self, funcname):
380: code = [ 'int %s(struct %s *, const %s);' % (
381: funcname, self._struct.Name(), self._ctype ) ]
382: return code
383:
384: def CodeAssign(self):
385: code = [ 'int',
386: '%(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,'
387: ' const %(ctype)s value)',
388: '{',
389: ' msg->%(name)s_set = 1;',
390: ' msg->%(name)s_data = value;',
391: ' return (0);',
392: '}' ]
393: code = '\n'.join(code)
394: code = code % self.GetTranslation()
395: return code.split('\n')
396:
397: def CodeClear(self, structname):
398: code = [ '%s->%s_set = 0;' % (structname, self.Name()) ]
399:
400: return code
401:
402: def CodeComplete(self, structname):
403: if self.Optional():
404: return []
405:
406: code = [ 'if (!%s->%s_set)' % (structname, self.Name()),
407: ' return (-1);' ]
408:
409: return code
410:
411: def CodeFree(self, name):
412: return []
413:
414: def CodeBase(self):
415: code = [
416: '%(parent_name)s_%(name)s_assign,',
417: '%(parent_name)s_%(name)s_get,'
418: ]
419: if self.Array():
420: code.append('%(parent_name)s_%(name)s_add,')
421:
422: code = '\n'.join(code)
423: code = code % self.GetTranslation()
424: return code.split('\n')
425:
426: def Verify(self):
427: if self.Array() and not self._can_be_array:
428: print >>sys.stderr, (
429: 'Entry "%s" cannot be created as an array '
430: 'around line %d' ) % (self._name, self.LineCount())
431: sys.exit(1)
432: if not self._struct:
433: print >>sys.stderr, (
434: 'Entry "%s" does not know which struct it belongs to '
435: 'around line %d' ) % (self._name, self.LineCount())
436: sys.exit(1)
437: if self._optional and self._array:
438: print >>sys.stderr, ( 'Entry "%s" has illegal combination of '
439: 'optional and array around line %d' ) % (
440: self._name, self.LineCount() )
441: sys.exit(1)
442:
443: class EntryBytes(Entry):
444: def __init__(self, type, name, tag, length):
445: # Init base class
446: Entry.__init__(self, type, name, tag)
447:
448: self._length = length
449: self._ctype = 'ev_uint8_t'
450:
451: def GetDeclaration(self, funcname):
452: code = [ 'int %s(struct %s *, %s **);' % (
453: funcname, self._struct.Name(), self._ctype ) ]
454: return code
455:
456: def AssignDeclaration(self, funcname):
457: code = [ 'int %s(struct %s *, const %s *);' % (
458: funcname, self._struct.Name(), self._ctype ) ]
459: return code
460:
461: def Declaration(self):
462: dcl = ['ev_uint8_t %s_data[%s];' % (self._name, self._length)]
463:
464: return dcl
465:
466: def CodeGet(self):
467: name = self._name
468: code = [ 'int',
469: '%s_%s_get(struct %s *msg, %s **value)' % (
470: self._struct.Name(), name,
471: self._struct.Name(), self._ctype),
472: '{',
473: ' if (msg->%s_set != 1)' % name,
474: ' return (-1);',
475: ' *value = msg->%s_data;' % name,
476: ' return (0);',
477: '}' ]
478: return code
479:
480: def CodeAssign(self):
481: name = self._name
482: code = [ 'int',
483: '%s_%s_assign(struct %s *msg, const %s *value)' % (
484: self._struct.Name(), name,
485: self._struct.Name(), self._ctype),
486: '{',
487: ' msg->%s_set = 1;' % name,
488: ' memcpy(msg->%s_data, value, %s);' % (
489: name, self._length),
490: ' return (0);',
491: '}' ]
492: return code
493:
494: def CodeUnmarshal(self, buf, tag_name, var_name):
495: code = [ 'if (evtag_unmarshal_fixed(%s, %s, ' % (buf, tag_name) +
496: '%s->%s_data, ' % (var_name, self._name) +
497: 'sizeof(%s->%s_data)) == -1) {' % (
498: var_name, self._name),
499: ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
500: self._name ),
501: ' return (-1);',
502: '}'
503: ]
504: return code
505:
506: def CodeMarshal(self, buf, tag_name, var_name):
507: code = ['evtag_marshal(%s, %s, %s->%s_data, sizeof(%s->%s_data));' % (
508: buf, tag_name, var_name, self._name, var_name, self._name )]
509: return code
510:
511: def CodeClear(self, structname):
512: code = [ '%s->%s_set = 0;' % (structname, self.Name()),
513: 'memset(%s->%s_data, 0, sizeof(%s->%s_data));' % (
514: structname, self._name, structname, self._name)]
515:
516: return code
517:
518: def CodeNew(self, name):
519: code = ['memset(%s->%s_data, 0, sizeof(%s->%s_data));' % (
520: name, self._name, name, self._name)]
521: return code
522:
523: def Verify(self):
524: if not self._length:
525: print >>sys.stderr, 'Entry "%s" needs a length around line %d' % (
526: self._name, self.LineCount() )
527: sys.exit(1)
528:
529: Entry.Verify(self)
530:
531: class EntryInt(Entry):
532: def __init__(self, type, name, tag):
533: # Init base class
534: Entry.__init__(self, type, name, tag)
535:
536: self._ctype = 'ev_uint32_t'
537:
538: def CodeUnmarshal(self, buf, tag_name, var_name):
539: code = ['if (evtag_unmarshal_int(%s, %s, &%s->%s_data) == -1) {' % (
540: buf, tag_name, var_name, self._name),
541: ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
542: self._name ),
543: ' return (-1);',
544: '}' ]
545: return code
546:
547: def CodeMarshal(self, buf, tag_name, var_name):
548: code = ['evtag_marshal_int(%s, %s, %s->%s_data);' % (
549: buf, tag_name, var_name, self._name)]
550: return code
551:
552: def Declaration(self):
553: dcl = ['ev_uint32_t %s_data;' % self._name]
554:
555: return dcl
556:
557: def CodeNew(self, name):
558: code = ['%s->%s_data = 0;' % (name, self._name)]
559: return code
560:
561: class EntryString(Entry):
562: def __init__(self, type, name, tag):
563: # Init base class
564: Entry.__init__(self, type, name, tag)
565:
566: self._ctype = 'char *'
567:
568: def CodeAssign(self):
569: name = self._name
570: code = """int
571: %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,
572: const %(ctype)s value)
573: {
574: if (msg->%(name)s_data != NULL)
575: free(msg->%(name)s_data);
576: if ((msg->%(name)s_data = strdup(value)) == NULL)
577: return (-1);
578: msg->%(name)s_set = 1;
579: return (0);
580: }""" % self.GetTranslation()
581:
582: return code.split('\n')
583:
584: def CodeUnmarshal(self, buf, tag_name, var_name):
585: code = ['if (evtag_unmarshal_string(%s, %s, &%s->%s_data) == -1) {' % (
586: buf, tag_name, var_name, self._name),
587: ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
588: self._name ),
589: ' return (-1);',
590: '}'
591: ]
592: return code
593:
594: def CodeMarshal(self, buf, tag_name, var_name):
595: code = ['evtag_marshal_string(%s, %s, %s->%s_data);' % (
596: buf, tag_name, var_name, self._name)]
597: return code
598:
599: def CodeClear(self, structname):
600: code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()),
601: ' free (%s->%s_data);' % (structname, self.Name()),
602: ' %s->%s_data = NULL;' % (structname, self.Name()),
603: ' %s->%s_set = 0;' % (structname, self.Name()),
604: '}'
605: ]
606:
607: return code
608:
609: def CodeNew(self, name):
610: code = ['%s->%s_data = NULL;' % (name, self._name)]
611: return code
612:
613: def CodeFree(self, name):
614: code = ['if (%s->%s_data != NULL)' % (name, self._name),
615: ' free (%s->%s_data); ' % (name, self._name)]
616:
617: return code
618:
619: def Declaration(self):
620: dcl = ['char *%s_data;' % self._name]
621:
622: return dcl
623:
624: class EntryStruct(Entry):
625: def __init__(self, type, name, tag, refname):
626: # Init base class
627: Entry.__init__(self, type, name, tag)
628:
629: self._can_be_array = 1
630: self._refname = refname
631: self._ctype = 'struct %s*' % refname
632:
633: def CodeGet(self):
634: name = self._name
635: code = [ 'int',
636: '%s_%s_get(struct %s *msg, %s *value)' % (
637: self._struct.Name(), name,
638: self._struct.Name(), self._ctype),
639: '{',
640: ' if (msg->%s_set != 1) {' % name,
641: ' msg->%s_data = %s_new();' % (name, self._refname),
642: ' if (msg->%s_data == NULL)' % name,
643: ' return (-1);',
644: ' msg->%s_set = 1;' % name,
645: ' }',
646: ' *value = msg->%s_data;' % name,
647: ' return (0);',
648: '}' ]
649: return code
650:
651: def CodeAssign(self):
652: name = self._name
653: code = """int
654: %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg,
655: const %(ctype)s value)
656: {
657: struct evbuffer *tmp = NULL;
658: if (msg->%(name)s_set) {
659: %(refname)s_clear(msg->%(name)s_data);
660: msg->%(name)s_set = 0;
661: } else {
662: msg->%(name)s_data = %(refname)s_new();
663: if (msg->%(name)s_data == NULL) {
664: event_warn("%%s: %(refname)s_new()", __func__);
665: goto error;
666: }
667: }
668: if ((tmp = evbuffer_new()) == NULL) {
669: event_warn("%%s: evbuffer_new()", __func__);
670: goto error;
671: }
672: %(refname)s_marshal(tmp, value);
673: if (%(refname)s_unmarshal(msg->%(name)s_data, tmp) == -1) {
674: event_warnx("%%s: %(refname)s_unmarshal", __func__);
675: goto error;
676: }
677: msg->%(name)s_set = 1;
678: evbuffer_free(tmp);
679: return (0);
680: error:
681: if (tmp != NULL)
682: evbuffer_free(tmp);
683: if (msg->%(name)s_data != NULL) {
684: %(refname)s_free(msg->%(name)s_data);
685: msg->%(name)s_data = NULL;
686: }
687: return (-1);
688: }""" % self.GetTranslation()
689: return code.split('\n')
690:
691: def CodeComplete(self, structname):
692: if self.Optional():
693: code = [ 'if (%s->%s_set && %s_complete(%s->%s_data) == -1)' % (
694: structname, self.Name(),
695: self._refname, structname, self.Name()),
696: ' return (-1);' ]
697: else:
698: code = [ 'if (%s_complete(%s->%s_data) == -1)' % (
699: self._refname, structname, self.Name()),
700: ' return (-1);' ]
701:
702: return code
703:
704: def CodeUnmarshal(self, buf, tag_name, var_name):
705: code = ['%s->%s_data = %s_new();' % (
706: var_name, self._name, self._refname),
707: 'if (%s->%s_data == NULL)' % (var_name, self._name),
708: ' return (-1);',
709: 'if (evtag_unmarshal_%s(%s, %s, %s->%s_data) == -1) {' % (
710: self._refname, buf, tag_name, var_name, self._name),
711: ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
712: self._name ),
713: ' return (-1);',
714: '}'
715: ]
716: return code
717:
718: def CodeMarshal(self, buf, tag_name, var_name):
719: code = ['evtag_marshal_%s(%s, %s, %s->%s_data);' % (
720: self._refname, buf, tag_name, var_name, self._name)]
721: return code
722:
723: def CodeClear(self, structname):
724: code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()),
725: ' %s_free(%s->%s_data);' % (
726: self._refname, structname, self.Name()),
727: ' %s->%s_data = NULL;' % (structname, self.Name()),
728: ' %s->%s_set = 0;' % (structname, self.Name()),
729: '}'
730: ]
731:
732: return code
733:
734: def CodeNew(self, name):
735: code = ['%s->%s_data = NULL;' % (name, self._name)]
736: return code
737:
738: def CodeFree(self, name):
739: code = ['if (%s->%s_data != NULL)' % (name, self._name),
740: ' %s_free(%s->%s_data); ' % (
741: self._refname, name, self._name)]
742:
743: return code
744:
745: def Declaration(self):
746: dcl = ['%s %s_data;' % (self._ctype, self._name)]
747:
748: return dcl
749:
750: class EntryVarBytes(Entry):
751: def __init__(self, type, name, tag):
752: # Init base class
753: Entry.__init__(self, type, name, tag)
754:
755: self._ctype = 'ev_uint8_t *'
756:
757: def GetDeclaration(self, funcname):
758: code = [ 'int %s(struct %s *, %s *, ev_uint32_t *);' % (
759: funcname, self._struct.Name(), self._ctype ) ]
760: return code
761:
762: def AssignDeclaration(self, funcname):
763: code = [ 'int %s(struct %s *, const %s, ev_uint32_t);' % (
764: funcname, self._struct.Name(), self._ctype ) ]
765: return code
766:
767: def CodeAssign(self):
768: name = self._name
769: code = [ 'int',
770: '%s_%s_assign(struct %s *msg, '
771: 'const %s value, ev_uint32_t len)' % (
772: self._struct.Name(), name,
773: self._struct.Name(), self._ctype),
774: '{',
775: ' if (msg->%s_data != NULL)' % name,
776: ' free (msg->%s_data);' % name,
777: ' msg->%s_data = malloc(len);' % name,
778: ' if (msg->%s_data == NULL)' % name,
779: ' return (-1);',
780: ' msg->%s_set = 1;' % name,
781: ' msg->%s_length = len;' % name,
782: ' memcpy(msg->%s_data, value, len);' % name,
783: ' return (0);',
784: '}' ]
785: return code
786:
787: def CodeGet(self):
788: name = self._name
789: code = [ 'int',
790: '%s_%s_get(struct %s *msg, %s *value, ev_uint32_t *plen)' % (
791: self._struct.Name(), name,
792: self._struct.Name(), self._ctype),
793: '{',
794: ' if (msg->%s_set != 1)' % name,
795: ' return (-1);',
796: ' *value = msg->%s_data;' % name,
797: ' *plen = msg->%s_length;' % name,
798: ' return (0);',
799: '}' ]
800: return code
801:
802: def CodeUnmarshal(self, buf, tag_name, var_name):
803: code = ['if (evtag_payload_length(%s, &%s->%s_length) == -1)' % (
804: buf, var_name, self._name),
805: ' return (-1);',
806: # We do not want DoS opportunities
807: 'if (%s->%s_length > EVBUFFER_LENGTH(%s))' % (
808: var_name, self._name, buf),
809: ' return (-1);',
810: 'if ((%s->%s_data = malloc(%s->%s_length)) == NULL)' % (
811: var_name, self._name, var_name, self._name),
812: ' return (-1);',
813: 'if (evtag_unmarshal_fixed(%s, %s, %s->%s_data, '
814: '%s->%s_length) == -1) {' % (
815: buf, tag_name, var_name, self._name, var_name, self._name),
816: ' event_warnx("%%s: failed to unmarshal %s", __func__);' % (
817: self._name ),
818: ' return (-1);',
819: '}'
820: ]
821: return code
822:
823: def CodeMarshal(self, buf, tag_name, var_name):
824: code = ['evtag_marshal(%s, %s, %s->%s_data, %s->%s_length);' % (
825: buf, tag_name, var_name, self._name, var_name, self._name)]
826: return code
827:
828: def CodeClear(self, structname):
829: code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()),
830: ' free (%s->%s_data);' % (structname, self.Name()),
831: ' %s->%s_data = NULL;' % (structname, self.Name()),
832: ' %s->%s_length = 0;' % (structname, self.Name()),
833: ' %s->%s_set = 0;' % (structname, self.Name()),
834: '}'
835: ]
836:
837: return code
838:
839: def CodeNew(self, name):
840: code = ['%s->%s_data = NULL;' % (name, self._name),
841: '%s->%s_length = 0;' % (name, self._name) ]
842: return code
843:
844: def CodeFree(self, name):
845: code = ['if (%s->%s_data != NULL)' % (name, self._name),
846: ' free (%s->%s_data); ' % (name, self._name)]
847:
848: return code
849:
850: def Declaration(self):
851: dcl = ['ev_uint8_t *%s_data;' % self._name,
852: 'ev_uint32_t %s_length;' % self._name]
853:
854: return dcl
855:
856: class EntryArray(Entry):
857: def __init__(self, entry):
858: # Init base class
859: Entry.__init__(self, entry._type, entry._name, entry._tag)
860:
861: self._entry = entry
862: self._refname = entry._refname
863: self._ctype = 'struct %s *' % self._refname
864:
865: def GetDeclaration(self, funcname):
866: """Allows direct access to elements of the array."""
867: translate = self.GetTranslation()
868: translate["funcname"] = funcname
869: code = [
870: 'int %(funcname)s(struct %(parent_name)s *, int, %(ctype)s *);' %
871: translate ]
872: return code
873:
874: def AssignDeclaration(self, funcname):
875: code = [ 'int %s(struct %s *, int, const %s);' % (
876: funcname, self._struct.Name(), self._ctype ) ]
877: return code
878:
879: def AddDeclaration(self, funcname):
880: code = [ '%s %s(struct %s *);' % (
881: self._ctype, funcname, self._struct.Name() ) ]
882: return code
883:
884: def CodeGet(self):
885: code = """int
886: %(parent_name)s_%(name)s_get(struct %(parent_name)s *msg, int offset,
887: %(ctype)s *value)
888: {
889: if (!msg->%(name)s_set || offset < 0 || offset >= msg->%(name)s_length)
890: return (-1);
891: *value = msg->%(name)s_data[offset];
892: return (0);
893: }""" % self.GetTranslation()
894:
895: return code.split('\n')
896:
897: def CodeAssign(self):
898: code = """int
899: %(parent_name)s_%(name)s_assign(struct %(parent_name)s *msg, int off,
900: const %(ctype)s value)
901: {
902: struct evbuffer *tmp = NULL;
903: if (!msg->%(name)s_set || off < 0 || off >= msg->%(name)s_length)
904: return (-1);
905: %(refname)s_clear(msg->%(name)s_data[off]);
906: if ((tmp = evbuffer_new()) == NULL) {
907: event_warn("%%s: evbuffer_new()", __func__);
908: goto error;
909: }
910: %(refname)s_marshal(tmp, value);
911: if (%(refname)s_unmarshal(msg->%(name)s_data[off], tmp) == -1) {
912: event_warnx("%%s: %(refname)s_unmarshal", __func__);
913: goto error;
914: }
915: evbuffer_free(tmp);
916: return (0);
917: error:
918: if (tmp != NULL)
919: evbuffer_free(tmp);
920: %(refname)s_clear(msg->%(name)s_data[off]);
921: return (-1);
922: }""" % self.GetTranslation()
923:
924: return code.split('\n')
925:
926: def CodeAdd(self):
927: code = \
928: """%(ctype)s
929: %(parent_name)s_%(name)s_add(struct %(parent_name)s *msg)
930: {
931: if (++msg->%(name)s_length >= msg->%(name)s_num_allocated) {
932: int tobe_allocated = msg->%(name)s_num_allocated;
933: %(ctype)s* new_data = NULL;
934: tobe_allocated = !tobe_allocated ? 1 : tobe_allocated << 1;
935: new_data = (%(ctype)s*) realloc(msg->%(name)s_data,
936: tobe_allocated * sizeof(%(ctype)s));
937: if (new_data == NULL)
938: goto error;
939: msg->%(name)s_data = new_data;
940: msg->%(name)s_num_allocated = tobe_allocated;
941: }
942: msg->%(name)s_data[msg->%(name)s_length - 1] = %(refname)s_new();
943: if (msg->%(name)s_data[msg->%(name)s_length - 1] == NULL)
944: goto error;
945: msg->%(name)s_set = 1;
946: return (msg->%(name)s_data[msg->%(name)s_length - 1]);
947: error:
948: --msg->%(name)s_length;
949: return (NULL);
950: }
951: """ % self.GetTranslation()
952:
953: return code.split('\n')
954:
955: def CodeComplete(self, structname):
956: code = []
957: translate = self.GetTranslation()
958:
959: if self.Optional():
960: code.append( 'if (%(structname)s->%(name)s_set)' % translate)
961:
962: translate["structname"] = structname
963: tmp = """{
964: int i;
965: for (i = 0; i < %(structname)s->%(name)s_length; ++i) {
966: if (%(refname)s_complete(%(structname)s->%(name)s_data[i]) == -1)
967: return (-1);
968: }
969: }""" % translate
970: code.extend(tmp.split('\n'))
971:
972: return code
973:
974: def CodeUnmarshal(self, buf, tag_name, var_name):
975: translate = self.GetTranslation()
976: translate["var_name"] = var_name
977: translate["buf"] = buf
978: translate["tag_name"] = tag_name
979: code = """if (%(parent_name)s_%(name)s_add(%(var_name)s) == NULL)
980: return (-1);
981: if (evtag_unmarshal_%(refname)s(%(buf)s, %(tag_name)s,
982: %(var_name)s->%(name)s_data[%(var_name)s->%(name)s_length - 1]) == -1) {
983: --%(var_name)s->%(name)s_length;
984: event_warnx("%%s: failed to unmarshal %(name)s", __func__);
985: return (-1);
986: }""" % translate
987:
988: return code.split('\n')
989:
990: def CodeMarshal(self, buf, tag_name, var_name):
991: code = ['{',
992: ' int i;',
993: ' for (i = 0; i < %s->%s_length; ++i) {' % (
994: var_name, self._name),
995: ' evtag_marshal_%s(%s, %s, %s->%s_data[i]);' % (
996: self._refname, buf, tag_name, var_name, self._name),
997: ' }',
998: '}'
999: ]
1000: return code
1001:
1002: def CodeClear(self, structname):
1003: code = [ 'if (%s->%s_set == 1) {' % (structname, self.Name()),
1004: ' int i;',
1005: ' for (i = 0; i < %s->%s_length; ++i) {' % (
1006: structname, self.Name()),
1007: ' %s_free(%s->%s_data[i]);' % (
1008: self._refname, structname, self.Name()),
1009: ' }',
1010: ' free(%s->%s_data);' % (structname, self.Name()),
1011: ' %s->%s_data = NULL;' % (structname, self.Name()),
1012: ' %s->%s_set = 0;' % (structname, self.Name()),
1013: ' %s->%s_length = 0;' % (structname, self.Name()),
1014: ' %s->%s_num_allocated = 0;' % (structname, self.Name()),
1015: '}'
1016: ]
1017:
1018: return code
1019:
1020: def CodeNew(self, name):
1021: code = ['%s->%s_data = NULL;' % (name, self._name),
1022: '%s->%s_length = 0;' % (name, self._name),
1023: '%s->%s_num_allocated = 0;' % (name, self._name)]
1024: return code
1025:
1026: def CodeFree(self, name):
1027: code = ['if (%s->%s_data != NULL) {' % (name, self._name),
1028: ' int i;',
1029: ' for (i = 0; i < %s->%s_length; ++i) {' % (
1030: name, self._name),
1031: ' %s_free(%s->%s_data[i]); ' % (
1032: self._refname, name, self._name),
1033: ' %s->%s_data[i] = NULL;' % (name, self._name),
1034: ' }',
1035: ' free(%s->%s_data);' % (name, self._name),
1036: ' %s->%s_data = NULL;' % (name, self._name),
1037: ' %s->%s_length = 0;' % (name, self._name),
1038: ' %s->%s_num_allocated = 0;' % (name, self._name),
1039: '}'
1040: ]
1041:
1042: return code
1043:
1044: def Declaration(self):
1045: dcl = ['struct %s **%s_data;' % (self._refname, self._name),
1046: 'int %s_length;' % self._name,
1047: 'int %s_num_allocated;' % self._name ]
1048:
1049: return dcl
1050:
1051: def NormalizeLine(line):
1052: global white
1053: global cppcomment
1054:
1055: line = cppcomment.sub('', line)
1056: line = line.strip()
1057: line = white.sub(' ', line)
1058:
1059: return line
1060:
1061: def ProcessOneEntry(newstruct, entry):
1062: optional = 0
1063: array = 0
1064: entry_type = ''
1065: name = ''
1066: tag = ''
1067: tag_set = None
1068: separator = ''
1069: fixed_length = ''
1070:
1071: tokens = entry.split(' ')
1072: while tokens:
1073: token = tokens[0]
1074: tokens = tokens[1:]
1075:
1076: if not entry_type:
1077: if not optional and token == 'optional':
1078: optional = 1
1079: continue
1080:
1081: if not array and token == 'array':
1082: array = 1
1083: continue
1084:
1085: if not entry_type:
1086: entry_type = token
1087: continue
1088:
1089: if not name:
1090: res = re.match(r'^([^\[\]]+)(\[.*\])?$', token)
1091: if not res:
1092: print >>sys.stderr, 'Cannot parse name: \"%s\" around %d' % (
1093: entry, line_count)
1094: sys.exit(1)
1095: name = res.group(1)
1096: fixed_length = res.group(2)
1097: if fixed_length:
1098: fixed_length = fixed_length[1:-1]
1099: continue
1100:
1101: if not separator:
1102: separator = token
1103: if separator != '=':
1104: print >>sys.stderr, 'Expected "=" after name \"%s\" got %s' % (
1105: name, token)
1106: sys.exit(1)
1107: continue
1108:
1109: if not tag_set:
1110: tag_set = 1
1111: if not re.match(r'^(0x)?[0-9]+$', token):
1112: print >>sys.stderr, 'Expected tag number: \"%s\"' % entry
1113: sys.exit(1)
1114: tag = int(token, 0)
1115: continue
1116:
1117: print >>sys.stderr, 'Cannot parse \"%s\"' % entry
1118: sys.exit(1)
1119:
1120: if not tag_set:
1121: print >>sys.stderr, 'Need tag number: \"%s\"' % entry
1122: sys.exit(1)
1123:
1124: # Create the right entry
1125: if entry_type == 'bytes':
1126: if fixed_length:
1127: newentry = EntryBytes(entry_type, name, tag, fixed_length)
1128: else:
1129: newentry = EntryVarBytes(entry_type, name, tag)
1130: elif entry_type == 'int' and not fixed_length:
1131: newentry = EntryInt(entry_type, name, tag)
1132: elif entry_type == 'string' and not fixed_length:
1133: newentry = EntryString(entry_type, name, tag)
1134: else:
1135: res = re.match(r'^struct\[(%s)\]$' % _STRUCT_RE,
1136: entry_type, re.IGNORECASE)
1137: if res:
1138: # References another struct defined in our file
1139: newentry = EntryStruct(entry_type, name, tag, res.group(1))
1140: else:
1141: print >>sys.stderr, 'Bad type: "%s" in "%s"' % (entry_type, entry)
1142: sys.exit(1)
1143:
1144: structs = []
1145:
1146: if optional:
1147: newentry.MakeOptional()
1148: if array:
1149: newentry.MakeArray()
1150:
1151: newentry.SetStruct(newstruct)
1152: newentry.SetLineCount(line_count)
1153: newentry.Verify()
1154:
1155: if array:
1156: # We need to encapsulate this entry into a struct
1157: newname = newentry.Name()+ '_array'
1158:
1159: # Now borgify the new entry.
1160: newentry = EntryArray(newentry)
1161: newentry.SetStruct(newstruct)
1162: newentry.SetLineCount(line_count)
1163: newentry.MakeArray()
1164:
1165: newstruct.AddEntry(newentry)
1166:
1167: return structs
1168:
1169: def ProcessStruct(data):
1170: tokens = data.split(' ')
1171:
1172: # First three tokens are: 'struct' 'name' '{'
1173: newstruct = Struct(tokens[1])
1174:
1175: inside = ' '.join(tokens[3:-1])
1176:
1177: tokens = inside.split(';')
1178:
1179: structs = []
1180:
1181: for entry in tokens:
1182: entry = NormalizeLine(entry)
1183: if not entry:
1184: continue
1185:
1186: # It's possible that new structs get defined in here
1187: structs.extend(ProcessOneEntry(newstruct, entry))
1188:
1189: structs.append(newstruct)
1190: return structs
1191:
1192: def GetNextStruct(file):
1193: global line_count
1194: global cppdirect
1195:
1196: got_struct = 0
1197:
1198: processed_lines = []
1199:
1200: have_c_comment = 0
1201: data = ''
1202: while 1:
1203: line = file.readline()
1204: if not line:
1205: break
1206:
1207: line_count += 1
1208: line = line[:-1]
1209:
1210: if not have_c_comment and re.search(r'/\*', line):
1211: if re.search(r'/\*.*\*/', line):
1212: line = re.sub(r'/\*.*\*/', '', line)
1213: else:
1214: line = re.sub(r'/\*.*$', '', line)
1215: have_c_comment = 1
1216:
1217: if have_c_comment:
1218: if not re.search(r'\*/', line):
1219: continue
1220: have_c_comment = 0
1221: line = re.sub(r'^.*\*/', '', line)
1222:
1223: line = NormalizeLine(line)
1224:
1225: if not line:
1226: continue
1227:
1228: if not got_struct:
1229: if re.match(r'#include ["<].*[>"]', line):
1230: cppdirect.append(line)
1231: continue
1232:
1233: if re.match(r'^#(if( |def)|endif)', line):
1234: cppdirect.append(line)
1235: continue
1236:
1237: if re.match(r'^#define', line):
1238: headerdirect.append(line)
1239: continue
1240:
1241: if not re.match(r'^struct %s {$' % _STRUCT_RE,
1242: line, re.IGNORECASE):
1243: print >>sys.stderr, 'Missing struct on line %d: %s' % (
1244: line_count, line)
1245: sys.exit(1)
1246: else:
1247: got_struct = 1
1248: data += line
1249: continue
1250:
1251: # We are inside the struct
1252: tokens = line.split('}')
1253: if len(tokens) == 1:
1254: data += ' ' + line
1255: continue
1256:
1257: if len(tokens[1]):
1258: print >>sys.stderr, 'Trailing garbage after struct on line %d' % (
1259: line_count )
1260: sys.exit(1)
1261:
1262: # We found the end of the struct
1263: data += ' %s}' % tokens[0]
1264: break
1265:
1266: # Remove any comments, that might be in there
1267: data = re.sub(r'/\*.*\*/', '', data)
1268:
1269: return data
1270:
1271:
1272: def Parse(file):
1273: """
1274: Parses the input file and returns C code and corresponding header file.
1275: """
1276:
1277: entities = []
1278:
1279: while 1:
1280: # Just gets the whole struct nicely formatted
1281: data = GetNextStruct(file)
1282:
1283: if not data:
1284: break
1285:
1286: entities.extend(ProcessStruct(data))
1287:
1288: return entities
1289:
1290: def GuardName(name):
1291: name = '_'.join(name.split('.'))
1292: name = '_'.join(name.split('/'))
1293: guard = '_'+name.upper()+'_'
1294:
1295: return guard
1296:
1297: def HeaderPreamble(name):
1298: guard = GuardName(name)
1299: pre = (
1300: '/*\n'
1301: ' * Automatically generated from %s\n'
1302: ' */\n\n'
1303: '#ifndef %s\n'
1304: '#define %s\n\n' ) % (
1305: name, guard, guard)
1306:
1307: # insert stdint.h - let's hope everyone has it
1308: pre += (
1309: '#include <event-config.h>\n'
1310: '#ifdef _EVENT_HAVE_STDINT_H\n'
1311: '#include <stdint.h>\n'
1312: '#endif\n' )
1313:
1314: for statement in headerdirect:
1315: pre += '%s\n' % statement
1316: if headerdirect:
1317: pre += '\n'
1318:
1319: pre += (
1320: '#define EVTAG_HAS(msg, member) ((msg)->member##_set == 1)\n'
1321: '#ifdef __GNUC__\n'
1322: '#define EVTAG_ASSIGN(msg, member, args...) '
1323: '(*(msg)->base->member##_assign)(msg, ## args)\n'
1324: '#define EVTAG_GET(msg, member, args...) '
1325: '(*(msg)->base->member##_get)(msg, ## args)\n'
1326: '#else\n'
1327: '#define EVTAG_ASSIGN(msg, member, ...) '
1328: '(*(msg)->base->member##_assign)(msg, ## __VA_ARGS__)\n'
1329: '#define EVTAG_GET(msg, member, ...) '
1330: '(*(msg)->base->member##_get)(msg, ## __VA_ARGS__)\n'
1331: '#endif\n'
1332: '#define EVTAG_ADD(msg, member) (*(msg)->base->member##_add)(msg)\n'
1333: '#define EVTAG_LEN(msg, member) ((msg)->member##_length)\n'
1334: )
1335:
1336: return pre
1337:
1338:
1339: def HeaderPostamble(name):
1340: guard = GuardName(name)
1341: return '#endif /* %s */' % guard
1342:
1343: def BodyPreamble(name):
1344: global _NAME
1345: global _VERSION
1346:
1347: header_file = '.'.join(name.split('.')[:-1]) + '.gen.h'
1348:
1349: pre = ( '/*\n'
1350: ' * Automatically generated from %s\n'
1351: ' * by %s/%s. DO NOT EDIT THIS FILE.\n'
1352: ' */\n\n' ) % (name, _NAME, _VERSION)
1353: pre += ( '#include <sys/types.h>\n'
1354: '#ifdef _EVENT_HAVE_SYS_TIME_H\n'
1355: '#include <sys/time.h>\n'
1356: '#endif\n'
1357: '#include <stdlib.h>\n'
1358: '#include <string.h>\n'
1359: '#include <assert.h>\n'
1360: '#define EVENT_NO_STRUCT\n'
1361: '#include <event.h>\n\n'
1362: '#ifdef _EVENT___func__\n'
1363: '#define __func__ _EVENT___func__\n'
1364: '#endif\n' )
1365:
1366: for statement in cppdirect:
1367: pre += '%s\n' % statement
1368:
1369: pre += '\n#include "%s"\n\n' % header_file
1370:
1371: pre += 'void event_err(int eval, const char *fmt, ...);\n'
1372: pre += 'void event_warn(const char *fmt, ...);\n'
1373: pre += 'void event_errx(int eval, const char *fmt, ...);\n'
1374: pre += 'void event_warnx(const char *fmt, ...);\n\n'
1375:
1376: return pre
1377:
1378: def main(argv):
1379: if len(argv) < 2 or not argv[1]:
1380: print >>sys.stderr, 'Need RPC description file as first argument.'
1381: sys.exit(1)
1382:
1383: filename = argv[1]
1384:
1385: ext = filename.split('.')[-1]
1386: if ext != 'rpc':
1387: print >>sys.stderr, 'Unrecognized file extension: %s' % ext
1388: sys.exit(1)
1389:
1390: print >>sys.stderr, 'Reading \"%s\"' % filename
1391:
1392: fp = open(filename, 'r')
1393: entities = Parse(fp)
1394: fp.close()
1395:
1396: header_file = '.'.join(filename.split('.')[:-1]) + '.gen.h'
1397: impl_file = '.'.join(filename.split('.')[:-1]) + '.gen.c'
1398:
1399: print >>sys.stderr, '... creating "%s"' % header_file
1400: header_fp = open(header_file, 'w')
1401: print >>header_fp, HeaderPreamble(filename)
1402:
1403: # Create forward declarations: allows other structs to reference
1404: # each other
1405: for entry in entities:
1406: entry.PrintForwardDeclaration(header_fp)
1407: print >>header_fp, ''
1408:
1409: for entry in entities:
1410: entry.PrintTags(header_fp)
1411: entry.PrintDeclaration(header_fp)
1412: print >>header_fp, HeaderPostamble(filename)
1413: header_fp.close()
1414:
1415: print >>sys.stderr, '... creating "%s"' % impl_file
1416: impl_fp = open(impl_file, 'w')
1417: print >>impl_fp, BodyPreamble(filename)
1418: for entry in entities:
1419: entry.PrintCode(impl_fp)
1420: impl_fp.close()
1421:
1422: if __name__ == '__main__':
1423: main(sys.argv)
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>