1 /*
2 * Copyright (c) 2001-2003
3 * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 * All rights reserved.
5 *
6 * Copyright (c) 2004-2006,2018
7 * Hartmut Brandt.
8 * All rights reserved.
9 *
10 * Author: Harti Brandt <harti@freebsd.org>
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * $Begemot: gensnmptree.c 383 2006-05-30 07:40:49Z brandt_h $
34 *
35 * Generate OID table from table description.
36 *
37 * Syntax is:
38 * ---------
39 * file := top | top file
40 *
41 * top := tree | typedef | include
42 *
43 * tree := head elements ')'
44 *
45 * entry := head ':' index STRING elements ')'
46 *
47 * leaf := head type STRING ACCESS ')'
48 *
49 * column := head type ACCESS ')'
50 *
51 * type := BASETYPE | BASETYPE '|' subtype | enum | bits
52 *
53 * subtype := STRING
54 *
55 * enum := ENUM '(' value ')'
56 *
57 * bits := BITS '(' value ')'
58 *
59 * value := optminus INT STRING | optminus INT STRING value
60 *
61 * optminus := '-' | EMPTY
62 *
63 * head := '(' INT STRING
64 *
65 * elements := EMPTY | elements element
66 *
67 * element := tree | leaf | column
68 *
69 * index := type | index type
70 *
71 * typedef := 'typedef' STRING type
72 *
73 * include := 'include' filespec
74 *
75 * filespec := '"' STRING '"' | '<' STRING '>'
76 */
77 #include <sys/types.h>
78 #include <sys/param.h>
79 #include <stdio.h>
80 #include <stdlib.h>
81 #include <stdarg.h>
82 #include <unistd.h>
83 #include <string.h>
84 #include <ctype.h>
85 #include <inttypes.h>
86 #include <errno.h>
87 #ifdef HAVE_ERR_H
88 #include <err.h>
89 #endif
90 #include <sys/queue.h>
91 #include "support.h"
92 #include "asn1.h"
93 #include "snmp.h"
94 #include "snmpagent.h"
95
96 /*
97 * Constant prefix for all OIDs
98 */
99 static const asn_subid_t prefix[] = { 1, 3, 6 };
100 #define PREFIX_LEN (sizeof(prefix) / sizeof(prefix[0]))
101
102 u_int tree_size;
103 static const char *file_prefix = "";
104
105 /* if true generate local include paths */
106 static int localincs = 0;
107
108 /* if true print tokens */
109 static int debug;
110
111 static const char usgtxt[] = "\
112 Generate SNMP tables.\n\
113 usage: gensnmptree [-dEeFfhlt] [-I directory] [-i infile] [-p prefix]\n\
114 [name]...\n\
115 options:\n\
116 -d debug mode\n\
117 -E extract the named or all enums and bits only\n\
118 -e extract the named oids or enums\n\
119 -F generate functions for -E into a .c file\n\
120 -f generate functions for -E into the header\n\
121 -h print this info\n\
122 -I directory add directory to include path\n\
123 -i ifile read from the named file instead of stdin\n\
124 -l generate local include directives\n\
125 -p prefix prepend prefix to file and variable names\n\
126 -t generate a .def file\n\
127 ";
128
129 /**
130 * Program operation.
131 */
132 enum op {
133 /** generate the tree */
134 OP_GEN,
135
136 /** extract OIDs */
137 OP_EXTRACT,
138
139 /** print the parsed tree */
140 OP_TREE,
141
142 /** extract enums */
143 OP_ENUMS,
144 };
145
146 /**
147 * Which functions to create.
148 */
149 enum gen_funcs {
150 /** none */
151 GEN_FUNCS_NONE,
152
153 /** functions for header files */
154 GEN_FUNCS_H,
155
156 /** functions for C files */
157 GEN_FUNCS_C,
158 };
159
160 /*
161 * A node in the OID tree
162 */
163 enum ntype {
164 NODE_LEAF = 1,
165 NODE_TREE,
166 NODE_ENTRY,
167 NODE_COLUMN
168 };
169
170 enum {
171 FL_GET = 0x01,
172 FL_SET = 0x02,
173 };
174
175 struct node;
176 TAILQ_HEAD(node_list, node);
177
178 struct node {
179 enum ntype type;
180 asn_subid_t id; /* last element of OID */
181 char *name; /* name of node */
182 TAILQ_ENTRY(node) link;
183 u_int lno; /* starting line number */
184 u_int flags; /* allowed operations */
185
186 union {
187 struct tree {
188 struct node_list subs;
189 } tree;
190
191 struct entry {
192 uint32_t index; /* index for table entry */
193 char *func; /* function for tables */
194 struct node_list subs;
195 char *subtypes[SNMP_INDEXES_MAX];
196 } entry;
197
198 struct leaf {
199 enum snmp_syntax syntax; /* syntax for this leaf */
200 char *func; /* function name */
201 char *subtype; /* subtype */
202 } leaf;
203
204 struct column {
205 enum snmp_syntax syntax; /* syntax for this column */
206 char *subtype; /* subtype */
207 } column;
208 } u;
209 };
210
211 struct func {
212 const char *name;
213 LIST_ENTRY(func) link;
214 };
215
216 static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
217
218 struct enums {
219 const char *name;
220 long value;
221 TAILQ_ENTRY(enums) link;
222 };
223
224 struct type {
225 const char *name;
226 const char *from_fname;
227 u_int from_lno;
228 u_int syntax;
229 int is_enum;
230 int is_bits;
231 TAILQ_HEAD(, enums) enums;
232 LIST_ENTRY(type) link;
233 };
234
235 static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types);
236
237 static void report(const char *, ...) __dead2 __printflike(1, 2);
238 static void report_node(const struct node *, const char *, ...)
239 __dead2 __printflike(2, 3);
240
241 /************************************************************
242 *
243 * Allocate memory and panic just in the case...
244 */
245 static void *
xalloc(size_t size)246 xalloc(size_t size)
247 {
248 void *ptr;
249
250 if ((ptr = calloc(1, size)) == NULL)
251 err(1, "allocing %zu bytes", size);
252
253 return (ptr);
254 }
255
256 static char *
savestr(const char * s)257 savestr(const char *s)
258 {
259
260 if (s == NULL)
261 return (NULL);
262 return (strcpy(xalloc(strlen(s) + 1), s));
263 }
264
265 /************************************************************
266 *
267 * Input stack
268 */
269 struct input {
270 FILE *fp;
271 u_int lno;
272 char *fname;
273 char *path;
274 LIST_ENTRY(input) link;
275 };
276 static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs);
277 static struct input *input = NULL;
278
279 #define MAX_PATHS 100
280 static u_int npaths = 2;
281 static u_int stdpaths = 2;
282 static const char *paths[MAX_PATHS + 1] = {
283 "/usr/share/snmp/defs",
284 "/usr/local/share/snmp/defs",
285 NULL
286 };
287
288 static int pbchar = -1;
289
290 static void
path_new(const char * path)291 path_new(const char *path)
292 {
293 if (npaths >= MAX_PATHS)
294 report("too many -I directives");
295 memmove(&paths[npaths - stdpaths + 1], &paths[npaths - stdpaths],
296 sizeof(path[0]) * stdpaths);
297 paths[npaths - stdpaths] = savestr(path);
298 npaths++;
299 }
300
301 static void
input_new(FILE * fp,const char * path,const char * fname)302 input_new(FILE *fp, const char *path, const char *fname)
303 {
304 struct input *ip;
305
306 ip = xalloc(sizeof(*ip));
307 ip->fp = fp;
308 ip->lno = 1;
309 ip->fname = savestr(fname);
310 ip->path = savestr(path);
311 LIST_INSERT_HEAD(&inputs, ip, link);
312
313 input = ip;
314 }
315
316 static void
input_close(void)317 input_close(void)
318 {
319
320 if (input == NULL)
321 return;
322 fclose(input->fp);
323 free(input->fname);
324 free(input->path);
325 LIST_REMOVE(input, link);
326 free(input);
327
328 input = LIST_FIRST(&inputs);
329 }
330
331 static FILE *
tryopen(const char * path,const char * fname)332 tryopen(const char *path, const char *fname)
333 {
334 char *fn;
335 FILE *fp;
336
337 if (path == NULL)
338 fn = savestr(fname);
339 else {
340 fn = xalloc(strlen(path) + strlen(fname) + 2);
341 sprintf(fn, "%s/%s", path, fname);
342 }
343 fp = fopen(fn, "r");
344 free(fn);
345 return (fp);
346 }
347
348 static void
input_fopen(const char * fname,int loc)349 input_fopen(const char *fname, int loc)
350 {
351 FILE *fp;
352 char *path;
353 u_int p;
354
355 if (fname[0] == '/') {
356 if ((fp = tryopen(NULL, fname)) != NULL) {
357 input_new(fp, NULL, fname);
358 return;
359 }
360
361 } else {
362 if (loc) {
363 if (input == NULL)
364 path = NULL;
365 else
366 path = input->path;
367
368 if ((fp = tryopen(path, fname)) != NULL) {
369 input_new(fp, NULL, fname);
370 return;
371 }
372 }
373
374 for (p = 0; paths[p] != NULL; p++)
375 if ((fp = tryopen(paths[p], fname)) != NULL) {
376 input_new(fp, paths[p], fname);
377 return;
378 }
379 }
380 report("cannot open '%s'", fname);
381 }
382
383 static int
tgetc(void)384 tgetc(void)
385 {
386 int c;
387
388 if (pbchar != -1) {
389 c = pbchar;
390 pbchar = -1;
391 return (c);
392 }
393
394 for (;;) {
395 if (input == NULL)
396 return (EOF);
397
398 if ((c = getc(input->fp)) != EOF)
399 return (c);
400
401 input_close();
402 }
403 }
404
405 static void
tungetc(int c)406 tungetc(int c)
407 {
408
409 if (pbchar != -1)
410 abort();
411 pbchar = c;
412 }
413
414 /************************************************************
415 *
416 * Parsing input
417 */
418 enum tok {
419 TOK_EOF = 0200, /* end-of-file seen */
420 TOK_NUM, /* number */
421 TOK_STR, /* string */
422 TOK_ACCESS, /* access operator */
423 TOK_TYPE, /* type operator */
424 TOK_ENUM, /* enum token (kind of a type) */
425 TOK_TYPEDEF, /* typedef directive */
426 TOK_DEFTYPE, /* defined type */
427 TOK_INCLUDE, /* include directive */
428 TOK_FILENAME, /* filename ("foo.bar" or <foo.bar>) */
429 TOK_BITS, /* bits token (kind of a type) */
430 };
431
432 static const struct {
433 const char *str;
434 enum tok tok;
435 u_int val;
436 } keywords[] = {
437 { "GET", TOK_ACCESS, FL_GET },
438 { "SET", TOK_ACCESS, FL_SET },
439 { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
440 { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
441 { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
442 { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
443 { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
444 { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
445 { "OID", TOK_TYPE, SNMP_SYNTAX_OID },
446 { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
447 { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
448 { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
449 { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
450 { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER },
451 { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING },
452 { "typedef", TOK_TYPEDEF, 0 },
453 { "include", TOK_INCLUDE, 0 },
454 { NULL, 0, 0 }
455 };
456
457 /* arbitrary upper limit on node names and function names */
458 #define MAXSTR 1000
459 static char str[MAXSTR];
460 static u_long val; /* integer values */
461 static int saved_token = -1;
462
463 /*
464 * Report an error and exit.
465 */
466 static void
report(const char * fmt,...)467 report(const char *fmt, ...)
468 {
469 va_list ap;
470 int c;
471
472 va_start(ap, fmt);
473 fprintf(stderr, "line %u: ", input->lno);
474 vfprintf(stderr, fmt, ap);
475 fprintf(stderr, "\n");
476 fprintf(stderr, "context: \"");
477 while ((c = tgetc()) != EOF && c != '\n')
478 fprintf(stderr, "%c", c);
479 fprintf(stderr, "\n");
480 va_end(ap);
481 exit(1);
482 }
483 static void
report_node(const struct node * np,const char * fmt,...)484 report_node(const struct node *np, const char *fmt, ...)
485 {
486 va_list ap;
487
488 va_start(ap, fmt);
489 fprintf(stderr, "line %u, node %s: ", np->lno, np->name);
490 vfprintf(stderr, fmt, ap);
491 fprintf(stderr, "\n");
492 va_end(ap);
493 exit(1);
494 }
495
496 /*
497 * Return a fresh copy of the string constituting the current token.
498 */
499 static char *
savetok(void)500 savetok(void)
501 {
502 return (savestr(str));
503 }
504
505 /*
506 * Get the next token from input.
507 */
508 static int
gettoken_internal(void)509 gettoken_internal(void)
510 {
511 int c;
512 struct type *t;
513
514 if (saved_token != -1) {
515 c = saved_token;
516 saved_token = -1;
517 return (c);
518 }
519
520 again:
521 /*
522 * Skip any whitespace before the next token
523 */
524 while ((c = tgetc()) != EOF) {
525 if (c == '\n')
526 input->lno++;
527 if (!isspace(c))
528 break;
529 }
530 if (c == EOF)
531 return (TOK_EOF);
532 if (!isascii(c))
533 report("unexpected character %#2x", (u_int)c);
534
535 /*
536 * Skip comments
537 */
538 if (c == '#') {
539 while ((c = tgetc()) != EOF) {
540 if (c == '\n') {
541 input->lno++;
542 goto again;
543 }
544 }
545 report("unexpected EOF in comment");
546 }
547
548 /*
549 * Single character tokens
550 */
551 if (strchr("():|-", c) != NULL)
552 return (c);
553
554 if (c == '"' || c == '<') {
555 int end = c;
556 size_t n = 0;
557
558 val = 1;
559 if (c == '<') {
560 val = 0;
561 end = '>';
562 }
563
564 while ((c = tgetc()) != EOF) {
565 if (c == end)
566 break;
567 if (n == sizeof(str) - 1) {
568 str[n++] = '\0';
569 report("filename too long '%s...'", str);
570 }
571 str[n++] = c;
572 }
573 str[n++] = '\0';
574 return (TOK_FILENAME);
575 }
576
577 /*
578 * Sort out numbers
579 */
580 if (isdigit(c)) {
581 size_t n = 0;
582 str[n++] = c;
583 while ((c = tgetc()) != EOF) {
584 if (!isdigit(c)) {
585 tungetc(c);
586 break;
587 }
588 if (n == sizeof(str) - 1) {
589 str[n++] = '\0';
590 report("number too long '%s...'", str);
591 }
592 str[n++] = c;
593 }
594 str[n++] = '\0';
595 sscanf(str, "%lu", &val);
596 return (TOK_NUM);
597 }
598
599 /*
600 * So that has to be a string.
601 */
602 if (isalpha(c) || c == '_') {
603 size_t n = 0;
604 str[n++] = c;
605 while ((c = tgetc()) != EOF) {
606 if (!isalnum(c) && c != '_' && c != '-') {
607 tungetc(c);
608 break;
609 }
610 if (n == sizeof(str) - 1) {
611 str[n++] = '\0';
612 report("string too long '%s...'", str);
613 }
614 str[n++] = c;
615 }
616 str[n++] = '\0';
617
618 /*
619 * Keywords
620 */
621 for (c = 0; keywords[c].str != NULL; c++)
622 if (strcmp(keywords[c].str, str) == 0) {
623 val = keywords[c].val;
624 return (keywords[c].tok);
625 }
626
627 LIST_FOREACH(t, &types, link) {
628 if (strcmp(t->name, str) == 0) {
629 val = t->syntax;
630 return (TOK_DEFTYPE);
631 }
632 }
633 return (TOK_STR);
634 }
635 if (isprint(c))
636 errx(1, "%u: unexpected character '%c'", input->lno, c);
637 else
638 errx(1, "%u: unexpected character 0x%02x", input->lno,
639 (u_int)c);
640 }
641 static int
gettoken(void)642 gettoken(void)
643 {
644 int tok = gettoken_internal();
645
646 if (debug) {
647 switch (tok) {
648
649 case TOK_EOF:
650 fprintf(stderr, "EOF ");
651 break;
652
653 case TOK_NUM:
654 fprintf(stderr, "NUM(%lu) ", val);
655 break;
656
657 case TOK_STR:
658 fprintf(stderr, "STR(%s) ", str);
659 break;
660
661 case TOK_ACCESS:
662 fprintf(stderr, "ACCESS(%lu) ", val);
663 break;
664
665 case TOK_TYPE:
666 fprintf(stderr, "TYPE(%lu) ", val);
667 break;
668
669 case TOK_ENUM:
670 fprintf(stderr, "ENUM ");
671 break;
672
673 case TOK_BITS:
674 fprintf(stderr, "BITS ");
675 break;
676
677 case TOK_TYPEDEF:
678 fprintf(stderr, "TYPEDEF ");
679 break;
680
681 case TOK_DEFTYPE:
682 fprintf(stderr, "DEFTYPE(%s,%lu) ", str, val);
683 break;
684
685 case TOK_INCLUDE:
686 fprintf(stderr, "INCLUDE ");
687 break;
688
689 case TOK_FILENAME:
690 fprintf(stderr, "FILENAME ");
691 break;
692
693 default:
694 if (tok < TOK_EOF) {
695 if (isprint(tok))
696 fprintf(stderr, "'%c' ", tok);
697 else if (tok == '\n')
698 fprintf(stderr, "\n");
699 else
700 fprintf(stderr, "%02x ", tok);
701 } else
702 abort();
703 break;
704 }
705 }
706 return (tok);
707 }
708
709 /**
710 * Pushback a token
711 */
712 static void
pushback(enum tok tok)713 pushback(enum tok tok)
714 {
715
716 if (saved_token != -1)
717 abort();
718 saved_token = tok;
719 }
720
721 /*
722 * Create a new type
723 */
724 static struct type *
make_type(const char * s)725 make_type(const char *s)
726 {
727 struct type *t;
728
729 t = xalloc(sizeof(*t));
730 t->name = savestr(s);
731 t->is_enum = 0;
732 t->syntax = SNMP_SYNTAX_NULL;
733 t->from_fname = savestr(input->fname);
734 t->from_lno = input->lno;
735 TAILQ_INIT(&t->enums);
736 LIST_INSERT_HEAD(&types, t, link);
737
738 return (t);
739 }
740
741 /*
742 * Parse a type. We've seen the ENUM or type keyword already. Leave next
743 * token.
744 */
745 static u_int
parse_type(enum tok * tok,struct type * t,const char * vname,char ** subtype)746 parse_type(enum tok *tok, struct type *t, const char *vname, char **subtype)
747 {
748 u_int syntax;
749 struct enums *e;
750
751 syntax = val;
752 if (subtype != NULL)
753 *subtype = NULL;
754
755 if (*tok == TOK_ENUM || *tok == TOK_BITS) {
756 if (t == NULL && vname != NULL) {
757 t = make_type(vname);
758 t->is_enum = (*tok == TOK_ENUM);
759 t->is_bits = (*tok == TOK_BITS);
760 t->syntax = syntax;
761 }
762 if (gettoken() != '(')
763 report("'(' expected after ENUM");
764
765 if ((*tok = gettoken()) == TOK_EOF)
766 report("unexpected EOF in ENUM");
767 do {
768 e = NULL;
769 if (t != NULL) {
770 e = xalloc(sizeof(*e));
771 }
772 if (*tok == '-') {
773 if ((*tok = gettoken()) == TOK_EOF)
774 report("unexpected EOF in ENUM");
775 e->value = -(long)val;
776 } else
777 e->value = val;
778
779 if (*tok != TOK_NUM)
780 report("need value for ENUM/BITS");
781 if (gettoken() != TOK_STR)
782 report("need string in ENUM/BITS");
783 e->name = savetok();
784 TAILQ_INSERT_TAIL(&t->enums, e, link);
785 if ((*tok = gettoken()) == TOK_EOF)
786 report("unexpected EOF in ENUM/BITS");
787 } while (*tok != ')');
788 *tok = gettoken();
789
790 } else if (*tok == TOK_DEFTYPE) {
791 *tok = gettoken();
792
793 } else {
794 if ((*tok = gettoken()) == '|') {
795 if (gettoken() != TOK_STR)
796 report("subtype expected after '|'");
797 if (subtype != NULL)
798 *subtype = savetok();
799 *tok = gettoken();
800 }
801 }
802
803 return (syntax);
804 }
805
806 /*
807 * Parse the next node (complete with all subnodes)
808 */
809 static struct node *
parse(enum tok tok)810 parse(enum tok tok)
811 {
812 struct node *node;
813 struct node *sub;
814 u_int index_count;
815
816 node = xalloc(sizeof(struct node));
817 node->lno = input->lno;
818 node->flags = 0;
819
820 if (tok != '(')
821 report("'(' expected at begin of node");
822 if (gettoken() != TOK_NUM)
823 report("node id expected after opening '('");
824 if (val > ASN_MAXID)
825 report("subid too large '%lu'", val);
826 node->id = (asn_subid_t)val;
827 if (gettoken() != TOK_STR)
828 report("node name expected after '(' ID");
829 node->name = savetok();
830
831 if ((tok = gettoken()) == TOK_TYPE || tok == TOK_DEFTYPE ||
832 tok == TOK_ENUM || tok == TOK_BITS) {
833 /* LEAF or COLUM */
834 char *subtype;
835 u_int syntax = parse_type(&tok, NULL, node->name, &subtype);
836
837 if (tok == TOK_STR) {
838 /* LEAF */
839 node->type = NODE_LEAF;
840 node->u.leaf.func = savetok();
841 node->u.leaf.syntax = syntax;
842 node->u.leaf.subtype = subtype;
843 tok = gettoken();
844 } else {
845 /* COLUMN */
846 node->type = NODE_COLUMN;
847 node->u.column.syntax = syntax;
848 node->u.column.subtype = subtype;
849 }
850
851 while (tok != ')') {
852 if (tok != TOK_ACCESS)
853 report("access keyword or ')' expected");
854 node->flags |= (u_int)val;
855 tok = gettoken();
856 }
857
858 } else if (tok == ':') {
859 /* ENTRY */
860 node->type = NODE_ENTRY;
861 TAILQ_INIT(&node->u.entry.subs);
862
863 index_count = 0;
864 node->u.entry.index = 0;
865 tok = gettoken();
866 while (tok == TOK_TYPE || tok == TOK_DEFTYPE ||
867 tok == TOK_ENUM || tok == TOK_BITS) {
868 char *subtype;
869 u_int syntax = parse_type(&tok, NULL, node->name,
870 &subtype);
871 if (index_count == SNMP_INDEXES_MAX)
872 report("too many table indexes");
873 node->u.entry.subtypes[index_count++] = subtype;
874 node->u.entry.index |=
875 syntax << (SNMP_INDEX_SHIFT * index_count);
876 }
877 node->u.entry.index |= index_count;
878 if (index_count == 0)
879 report("need at least one index");
880 if (tok != TOK_STR)
881 report("function name expected");
882
883 node->u.entry.func = savetok();
884
885 tok = gettoken();
886
887 while (tok != ')') {
888 sub = parse(tok);
889 TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link);
890 tok = gettoken();
891 }
892
893 } else {
894 /* subtree */
895 node->type = NODE_TREE;
896 TAILQ_INIT(&node->u.tree.subs);
897
898 while (tok != ')') {
899 sub = parse(tok);
900 TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link);
901 tok = gettoken();
902 }
903 }
904 return (node);
905 }
906
907 /*
908 * Parse a top level element. Return the tree if it was a tree, NULL
909 * otherwise.
910 */
911 static struct node *
parse_top(enum tok tok)912 parse_top(enum tok tok)
913 {
914 struct type *t;
915
916 if (tok == '(')
917 return (parse(tok));
918
919 if (tok == TOK_TYPEDEF) {
920 if (gettoken() != TOK_STR)
921 report("type name expected after typedef");
922
923 t = make_type(str);
924
925 tok = gettoken();
926 t->is_enum = (tok == TOK_ENUM);
927 t->is_bits = (tok == TOK_BITS);
928
929 t->syntax = parse_type(&tok, t, NULL, NULL);
930 pushback(tok);
931
932 return (NULL);
933 }
934
935 if (tok == TOK_INCLUDE) {
936 if (gettoken() != TOK_FILENAME)
937 report("filename expected in include directive");
938
939 input_fopen(str, val);
940 return (NULL);
941 }
942
943 report("'(' or 'typedef' expected");
944 }
945
946 /*
947 * Generate the C-code table part for one node.
948 */
949 static void
gen_node(FILE * fp,const struct node * np,struct asn_oid * oid,u_int idx,const char * func)950 gen_node(FILE *fp, const struct node *np, struct asn_oid *oid, u_int idx,
951 const char *func)
952 {
953 u_int n;
954 struct node *sub;
955 u_int syntax;
956
957 if (oid->len == ASN_MAXOIDLEN)
958 report_node(np, "OID too long");
959 oid->subs[oid->len++] = np->id;
960
961 if (np->type == NODE_TREE) {
962 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
963 gen_node(fp, sub, oid, 0, NULL);
964 oid->len--;
965 return;
966 }
967 if (np->type == NODE_ENTRY) {
968 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
969 gen_node(fp, sub, oid, np->u.entry.index,
970 np->u.entry.func);
971 oid->len--;
972 return;
973 }
974
975 /* leaf or column */
976 if ((np->flags & (FL_GET|FL_SET)) == 0) {
977 oid->len--;
978 return;
979 }
980
981 fprintf(fp, " {{ %u, {", oid->len);
982 for (n = 0; n < oid->len; n++)
983 fprintf(fp, " %u,", oid->subs[n]);
984 fprintf(fp, " }}, \"%s\", ", np->name);
985
986 if (np->type == NODE_COLUMN) {
987 syntax = np->u.column.syntax;
988 fprintf(fp, "SNMP_NODE_COLUMN, ");
989 } else {
990 syntax = np->u.leaf.syntax;
991 fprintf(fp, "SNMP_NODE_LEAF, ");
992 }
993
994 switch (syntax) {
995
996 case SNMP_SYNTAX_NULL:
997 fprintf(fp, "SNMP_SYNTAX_NULL, ");
998 break;
999
1000 case SNMP_SYNTAX_INTEGER:
1001 fprintf(fp, "SNMP_SYNTAX_INTEGER, ");
1002 break;
1003
1004 case SNMP_SYNTAX_OCTETSTRING:
1005 fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, ");
1006 break;
1007
1008 case SNMP_SYNTAX_IPADDRESS:
1009 fprintf(fp, "SNMP_SYNTAX_IPADDRESS, ");
1010 break;
1011
1012 case SNMP_SYNTAX_OID:
1013 fprintf(fp, "SNMP_SYNTAX_OID, ");
1014 break;
1015
1016 case SNMP_SYNTAX_TIMETICKS:
1017 fprintf(fp, "SNMP_SYNTAX_TIMETICKS, ");
1018 break;
1019
1020 case SNMP_SYNTAX_COUNTER:
1021 fprintf(fp, "SNMP_SYNTAX_COUNTER, ");
1022 break;
1023
1024 case SNMP_SYNTAX_GAUGE:
1025 fprintf(fp, "SNMP_SYNTAX_GAUGE, ");
1026 break;
1027
1028 case SNMP_SYNTAX_COUNTER64:
1029 fprintf(fp, "SNMP_SYNTAX_COUNTER64, ");
1030 break;
1031
1032 case SNMP_SYNTAX_NOSUCHOBJECT:
1033 case SNMP_SYNTAX_NOSUCHINSTANCE:
1034 case SNMP_SYNTAX_ENDOFMIBVIEW:
1035 abort();
1036 }
1037
1038 if (np->type == NODE_COLUMN)
1039 fprintf(fp, "%s, ", func);
1040 else
1041 fprintf(fp, "%s, ", np->u.leaf.func);
1042
1043 fprintf(fp, "0");
1044 if (np->flags & FL_SET)
1045 fprintf(fp, "|SNMP_NODE_CANSET");
1046 fprintf(fp, ", %#x, NULL, NULL },\n", idx);
1047 oid->len--;
1048 return;
1049 }
1050
1051 /*
1052 * Generate the header file with the function declarations.
1053 */
1054 static void
gen_header(FILE * fp,const struct node * np,u_int oidlen,const char * func)1055 gen_header(FILE *fp, const struct node *np, u_int oidlen, const char *func)
1056 {
1057 char f[MAXSTR + 4];
1058 struct node *sub;
1059 struct func *ptr;
1060
1061 oidlen++;
1062 if (np->type == NODE_TREE) {
1063 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
1064 gen_header(fp, sub, oidlen, NULL);
1065 return;
1066 }
1067 if (np->type == NODE_ENTRY) {
1068 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
1069 gen_header(fp, sub, oidlen, np->u.entry.func);
1070 return;
1071 }
1072
1073 if((np->flags & (FL_GET|FL_SET)) == 0)
1074 return;
1075
1076 if (np->type == NODE_COLUMN) {
1077 if (func == NULL)
1078 errx(1, "column without function (%s) - probably "
1079 "outside of a table", np->name);
1080 sprintf(f, "%s", func);
1081 } else
1082 sprintf(f, "%s", np->u.leaf.func);
1083
1084 LIST_FOREACH(ptr, &funcs, link)
1085 if (strcmp(ptr->name, f) == 0)
1086 break;
1087
1088 if (ptr == NULL) {
1089 ptr = xalloc(sizeof(*ptr));
1090 ptr->name = savestr(f);
1091 LIST_INSERT_HEAD(&funcs, ptr, link);
1092
1093 fprintf(fp, "int %s(struct snmp_context *, "
1094 "struct snmp_value *, u_int, u_int, "
1095 "enum snmp_op);\n", f);
1096 }
1097
1098 fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id);
1099 }
1100
1101 /*
1102 * Generate the OID table.
1103 */
1104 static void
gen_table(FILE * fp,const struct node * node)1105 gen_table(FILE *fp, const struct node *node)
1106 {
1107 struct asn_oid oid;
1108
1109 fprintf(fp, "#include <sys/types.h>\n");
1110 fprintf(fp, "#include <stdio.h>\n");
1111 #ifdef HAVE_STDINT_H
1112 fprintf(fp, "#include <stdint.h>\n");
1113 #endif
1114 if (localincs) {
1115 fprintf(fp, "#include \"asn1.h\"\n");
1116 fprintf(fp, "#include \"snmp.h\"\n");
1117 fprintf(fp, "#include \"snmpagent.h\"\n");
1118 } else {
1119 fprintf(fp, "#include <bsnmp/asn1.h>\n");
1120 fprintf(fp, "#include <bsnmp/snmp.h>\n");
1121 fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
1122 }
1123 fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
1124 fprintf(fp, "\n");
1125
1126 fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
1127
1128 oid.len = PREFIX_LEN;
1129 memcpy(oid.subs, prefix, sizeof(prefix));
1130 gen_node(fp, node, &oid, 0, NULL);
1131
1132 fprintf(fp, "};\n\n");
1133 }
1134
1135 static void
print_syntax(u_int syntax)1136 print_syntax(u_int syntax)
1137 {
1138 u_int i;
1139
1140 for (i = 0; keywords[i].str != NULL; i++)
1141 if (keywords[i].tok == TOK_TYPE &&
1142 keywords[i].val == syntax) {
1143 printf(" %s", keywords[i].str);
1144 return;
1145 }
1146 abort();
1147 }
1148
1149 /*
1150 * Generate a tree definition file
1151 */
1152 static void
gen_tree(const struct node * np,int level)1153 gen_tree(const struct node *np, int level)
1154 {
1155 const struct node *sp;
1156 u_int i;
1157
1158 printf("%*s(%u %s", 2 * level, "", np->id, np->name);
1159
1160 switch (np->type) {
1161
1162 case NODE_LEAF:
1163 print_syntax(np->u.leaf.syntax);
1164 if (np->u.leaf.subtype != NULL)
1165 printf(" | %s", np->u.leaf.subtype);
1166 printf(" %s%s%s)\n", np->u.leaf.func,
1167 (np->flags & FL_GET) ? " GET" : "",
1168 (np->flags & FL_SET) ? " SET" : "");
1169 break;
1170
1171 case NODE_TREE:
1172 if (TAILQ_EMPTY(&np->u.tree.subs)) {
1173 printf(")\n");
1174 } else {
1175 printf("\n");
1176 TAILQ_FOREACH(sp, &np->u.tree.subs, link)
1177 gen_tree(sp, level + 1);
1178 printf("%*s)\n", 2 * level, "");
1179 }
1180 break;
1181
1182 case NODE_ENTRY:
1183 printf(" :");
1184
1185 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++) {
1186 print_syntax(SNMP_INDEX(np->u.entry.index, i));
1187 if (np->u.entry.subtypes[i] != NULL)
1188 printf(" | %s", np->u.entry.subtypes[i]);
1189 }
1190 printf(" %s\n", np->u.entry.func);
1191 TAILQ_FOREACH(sp, &np->u.entry.subs, link)
1192 gen_tree(sp, level + 1);
1193 printf("%*s)\n", 2 * level, "");
1194 break;
1195
1196 case NODE_COLUMN:
1197 print_syntax(np->u.column.syntax);
1198 if (np->u.column.subtype != NULL)
1199 printf(" | %s", np->u.column.subtype);
1200 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
1201 (np->flags & FL_SET) ? " SET" : "");
1202 break;
1203 }
1204 }
1205
1206 static int
extract(FILE * fp,const struct node * np,struct asn_oid * oid,const char * obj,const struct asn_oid * idx,const char * iname)1207 extract(FILE *fp, const struct node *np, struct asn_oid *oid, const char *obj,
1208 const struct asn_oid *idx, const char *iname)
1209 {
1210 struct node *sub;
1211 u_long n;
1212
1213 if (oid->len == ASN_MAXOIDLEN)
1214 report_node(np, "OID too long");
1215 oid->subs[oid->len++] = np->id;
1216
1217 if (strcmp(obj, np->name) == 0) {
1218 if (oid->len + idx->len >= ASN_MAXOIDLEN)
1219 report_node(np, "OID too long");
1220 fprintf(fp, "#define OID_%s%s\t%u\n", np->name,
1221 iname ? iname : "", np->id);
1222 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,
1223 iname ? iname : "", oid->len + idx->len);
1224 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,
1225 iname ? iname : "", oid->len + idx->len);
1226 for (n = 0; n < oid->len; n++)
1227 fprintf(fp, " %u,", oid->subs[n]);
1228 for (n = 0; n < idx->len; n++)
1229 fprintf(fp, " %u,", idx->subs[n]);
1230 fprintf(fp, " } }\n");
1231 return (0);
1232 }
1233
1234 if (np->type == NODE_TREE) {
1235 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
1236 if (!extract(fp, sub, oid, obj, idx, iname))
1237 return (0);
1238 } else if (np->type == NODE_ENTRY) {
1239 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
1240 if (!extract(fp, sub, oid, obj, idx, iname))
1241 return (0);
1242 }
1243 oid->len--;
1244 return (1);
1245 }
1246
1247 static int
gen_extract(FILE * fp,const struct node * root,char * object)1248 gen_extract(FILE *fp, const struct node *root, char *object)
1249 {
1250 struct asn_oid oid;
1251 struct asn_oid idx;
1252 char *s, *e, *end, *iname;
1253 u_long ul;
1254 int ret;
1255
1256 /* look whether the object to extract has an index part */
1257 idx.len = 0;
1258 iname = NULL;
1259 s = strchr(object, '.');
1260 if (s != NULL) {
1261 iname = malloc(strlen(s) + 1);
1262 if (iname == NULL)
1263 err(1, "cannot allocated index");
1264
1265 strcpy(iname, s);
1266 for (e = iname; *e != '\0'; e++)
1267 if (*e == '.')
1268 *e = '_';
1269
1270 *s++ = '\0';
1271 while (s != NULL) {
1272 if (*s == '\0')
1273 errx(1, "bad index syntax");
1274 if ((e = strchr(s, '.')) != NULL)
1275 *e++ = '\0';
1276
1277 errno = 0;
1278 ul = strtoul(s, &end, 0);
1279 if (*end != '\0')
1280 errx(1, "bad index syntax '%s'", end);
1281 if (errno != 0)
1282 err(1, "bad index syntax");
1283
1284 if (idx.len == ASN_MAXOIDLEN)
1285 errx(1, "index oid too large");
1286 idx.subs[idx.len++] = ul;
1287
1288 s = e;
1289 }
1290 }
1291
1292 oid.len = PREFIX_LEN;
1293 memcpy(oid.subs, prefix, sizeof(prefix));
1294 ret = extract(fp, root, &oid, object, &idx, iname);
1295 if (iname != NULL)
1296 free(iname);
1297
1298 return (ret);
1299 }
1300
1301
1302 static void
check_sub_order(const struct node * np,const struct node_list * subs)1303 check_sub_order(const struct node *np, const struct node_list *subs)
1304 {
1305 int first;
1306 const struct node *sub;
1307 asn_subid_t maxid = 0;
1308
1309 /* ensure, that subids are ordered */
1310 first = 1;
1311 TAILQ_FOREACH(sub, subs, link) {
1312 if (!first && sub->id <= maxid)
1313 report_node(np, "subids not ordered at %s", sub->name);
1314 maxid = sub->id;
1315 first = 0;
1316 }
1317 }
1318
1319 /*
1320 * Do some sanity checks on the tree definition and do some computations.
1321 */
1322 static void
check_tree(struct node * np)1323 check_tree(struct node *np)
1324 {
1325 struct node *sub;
1326
1327 if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
1328 if ((np->flags & (FL_GET|FL_SET)) != 0)
1329 tree_size++;
1330 return;
1331 }
1332
1333 if (np->type == NODE_ENTRY) {
1334 check_sub_order(np, &np->u.entry.subs);
1335
1336 /* ensure all subnodes are columns */
1337 TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
1338 if (sub->type != NODE_COLUMN)
1339 report_node(np, "entry subnode '%s' is not "
1340 "a column", sub->name);
1341 check_tree(sub);
1342 }
1343 } else {
1344 check_sub_order(np, &np->u.tree.subs);
1345
1346 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
1347 check_tree(sub);
1348 }
1349 }
1350
1351 static void
merge_subs(struct node_list * s1,struct node_list * s2)1352 merge_subs(struct node_list *s1, struct node_list *s2)
1353 {
1354 struct node *n1, *n2;
1355
1356 while (!TAILQ_EMPTY(s2)) {
1357 n2 = TAILQ_FIRST(s2);
1358 TAILQ_REMOVE(s2, n2, link);
1359
1360 TAILQ_FOREACH(n1, s1, link)
1361 if (n1->id >= n2->id)
1362 break;
1363 if (n1 == NULL)
1364 TAILQ_INSERT_TAIL(s1, n2, link);
1365 else if (n1->id > n2->id)
1366 TAILQ_INSERT_BEFORE(n1, n2, link);
1367 else {
1368 if (n1->type == NODE_TREE && n2->type == NODE_TREE) {
1369 if (strcmp(n1->name, n2->name) != 0)
1370 errx(1, "trees to merge must have "
1371 "same name '%s' '%s'", n1->name,
1372 n2->name);
1373 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);
1374 free(n2);
1375 } else if (n1->type == NODE_ENTRY &&
1376 n2->type == NODE_ENTRY) {
1377 if (strcmp(n1->name, n2->name) != 0)
1378 errx(1, "entries to merge must have "
1379 "same name '%s' '%s'", n1->name,
1380 n2->name);
1381 if (n1->u.entry.index != n2->u.entry.index)
1382 errx(1, "entries to merge must have "
1383 "same index '%s'", n1->name);
1384 if (strcmp(n1->u.entry.func,
1385 n2->u.entry.func) != 0)
1386 errx(1, "entries to merge must have "
1387 "same op '%s'", n1->name);
1388 merge_subs(&n1->u.entry.subs,
1389 &n2->u.entry.subs);
1390 free(n2);
1391 } else
1392 errx(1, "entities to merge must be both "
1393 "trees or both entries: %s, %s",
1394 n1->name, n2->name);
1395 }
1396 }
1397 }
1398
1399 static void
merge(struct node ** root,struct node * t)1400 merge(struct node **root, struct node *t)
1401 {
1402
1403 if (*root == NULL) {
1404 *root = t;
1405 return;
1406 }
1407 if (t == NULL)
1408 return;
1409
1410 /* both must be trees */
1411 if ((*root)->type != NODE_TREE)
1412 errx(1, "root is not a tree");
1413 if (t->type != NODE_TREE)
1414 errx(1, "can merge only with tree");
1415 if ((*root)->id != t->id)
1416 errx(1, "trees to merge must have same id");
1417
1418 merge_subs(&(*root)->u.tree.subs, &t->u.tree.subs);
1419 }
1420
1421 static void
unminus(FILE * fp,const char * s)1422 unminus(FILE *fp, const char *s)
1423 {
1424
1425 while (*s != '\0') {
1426 if (*s == '-')
1427 fprintf(fp, "_");
1428 else
1429 fprintf(fp, "%c", *s);
1430 s++;
1431 }
1432 }
1433
1434 /**
1435 * Generate helper functions for an enum.
1436 *
1437 * We always generate a switch statement for the isok function. The compiler
1438 * optimizes this into range checks if possible.
1439 *
1440 * \param fp file to write to
1441 * \param t type
1442 * \param ccode generate externally visible non-inline functions
1443 */
1444 static void
gen_enum_funcs(FILE * fp,const struct type * t,int ccode)1445 gen_enum_funcs(FILE *fp, const struct type *t, int ccode)
1446 {
1447 fprintf(fp, "\n");
1448
1449 if (!ccode)
1450 fprintf(fp, "static inline ");
1451 fprintf(fp, "int\n");
1452 fprintf(fp, "isok_%s(enum %s s)\n", t->name, t->name);
1453 fprintf(fp, "{\n");
1454 fprintf(fp, " switch (s) {\n");
1455
1456 const struct enums *e;
1457 TAILQ_FOREACH(e, &t->enums, link) {
1458 fprintf(fp, "\t case %s_", t->name);
1459 unminus(fp, e->name);
1460 fprintf(fp, ":\n");
1461 }
1462
1463 fprintf(fp, " return (1);\n");
1464 fprintf(fp, " }\n");
1465 fprintf(fp, " return (0);\n");
1466 fprintf(fp, "}\n\n");
1467
1468 if (!ccode)
1469 fprintf(fp, "static inline ");
1470 fprintf(fp, "const char *\n");
1471 fprintf(fp, "tostr_%s(enum %s s)\n", t->name, t->name);
1472 fprintf(fp, "{\n");
1473 fprintf(fp, " static const char *vals[] = { STRING_%s };\n", t->name);
1474 fprintf(fp, "\n");
1475 fprintf(fp, " if (isok_%s(s))\n", t->name);
1476 fprintf(fp, " return (vals[(int)s - STROFF_%s]);\n", t->name);
1477 fprintf(fp, " return (\"%s???\");\n", t->name);
1478 fprintf(fp, "}\n\n");
1479
1480 if (!ccode)
1481 fprintf(fp, "static inline ");
1482 fprintf(fp, "int\n");
1483 fprintf(fp, "fromstr_%s(const char *str, enum %s *s)\n",
1484 t->name, t->name);
1485 fprintf(fp, "{\n");
1486 fprintf(fp, " static const char *vals[] = { STRING_%s };\n", t->name);
1487 fprintf(fp, "\n");
1488 fprintf(fp, " for (size_t i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) {\n");
1489 fprintf(fp, " if (vals[i] != NULL && strcmp(vals[i], str) == 0) {\n");
1490 fprintf(fp, " *s = i + STROFF_%s;\n", t->name);
1491 fprintf(fp, " return (1);\n");
1492 fprintf(fp, " }\n");
1493 fprintf(fp, " }\n");
1494 fprintf(fp, " return (0);\n");
1495 fprintf(fp, "}\n");
1496 }
1497
1498 /**
1499 * Generate a definition for the enum packed into a guard against multiple
1500 * definitions.
1501 *
1502 * \param fp file to write definition to
1503 * \param t type
1504 * \param dof generate functions too
1505 */
1506 static void
gen_enum(FILE * fp,const struct type * t,int dof)1507 gen_enum(FILE *fp, const struct type *t, int dof)
1508 {
1509 const struct enums *e;
1510 long min = LONG_MAX;
1511
1512 fprintf(fp, "\n");
1513 fprintf(fp, "#ifndef %s_defined__\n", t->name);
1514 fprintf(fp, "#define %s_defined__\n", t->name);
1515 fprintf(fp, "/*\n");
1516 fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno);
1517 fprintf(fp, " */\n");
1518 fprintf(fp, "enum %s {\n", t->name);
1519 TAILQ_FOREACH(e, &t->enums, link) {
1520 fprintf(fp, "\t%s_", t->name);
1521 unminus(fp, e->name);
1522 fprintf(fp, " = %ld,\n", e->value);
1523 if (e->value < min)
1524 min = e->value;
1525 }
1526 fprintf(fp, "};\n");
1527 fprintf(fp, "#define STROFF_%s %ld\n", t->name, min);
1528 fprintf(fp, "#define STRING_%s \\\n", t->name);
1529 TAILQ_FOREACH(e, &t->enums, link) {
1530 fprintf(fp, "\t[%ld] = \"%s_", e->value - min, t->name);
1531 unminus(fp, e->name);
1532 fprintf(fp, "\",\\\n");
1533 }
1534 fprintf(fp, "\n");
1535 if (dof) {
1536 fprintf(fp, "#ifdef SNMPENUM_FUNCS\n");
1537 fprintf(fp, "\n");
1538 gen_enum_funcs(fp, t, 0);
1539 fprintf(fp, "\n");
1540 fprintf(fp, "#endif\n");
1541 fprintf(fp, "\n");
1542 }
1543 fprintf(fp, "#endif /* %s_defined__ */\n", t->name);
1544 }
1545
1546 /**
1547 * Generate helper functions for an enum. This generates code for a c file.
1548 *
1549 * \param fp file to write to
1550 * \param name enum name
1551 */
1552 static int
gen_enum_funcs_str(FILE * fp,const char * name)1553 gen_enum_funcs_str(FILE *fp, const char *name)
1554 {
1555 const struct type *t;
1556
1557 LIST_FOREACH(t, &types, link)
1558 if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) {
1559 gen_enum_funcs(fp, t, 1);
1560 return (0);
1561 }
1562
1563 return (-1);
1564 }
1565
1566 /**
1567 * Generate helper functions for all enums.
1568 *
1569 * \param fp file to write to
1570 * \param ccode generate externally visible non-inline functions
1571 */
1572 static void
gen_all_enum_funcs(FILE * fp,int ccode)1573 gen_all_enum_funcs(FILE *fp, int ccode)
1574 {
1575 const struct type *t;
1576
1577 LIST_FOREACH(t, &types, link)
1578 if (t->is_enum || t->is_bits)
1579 gen_enum_funcs(fp, t, ccode);
1580 }
1581
1582 static void
gen_enums(FILE * fp,int dof)1583 gen_enums(FILE *fp, int dof)
1584 {
1585 const struct type *t;
1586
1587 LIST_FOREACH(t, &types, link)
1588 if (t->is_enum || t->is_bits)
1589 gen_enum(fp, t, dof);
1590 }
1591
1592 /**
1593 * Extract a given enum to the specified file and optionally generate static
1594 * inline helper functions for them.
1595 *
1596 * \param fp file to print on
1597 * \param name name of the enum
1598 * \param gen_funcs generate the functions too
1599 *
1600 * \return 0 if found, -1 otherwise
1601 */
1602 static int
extract_enum(FILE * fp,const char * name,int gen_funcs)1603 extract_enum(FILE *fp, const char *name, int gen_funcs)
1604 {
1605 const struct type *t;
1606
1607 LIST_FOREACH(t, &types, link)
1608 if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) {
1609 gen_enum(fp, t, gen_funcs);
1610 return (0);
1611 }
1612 return (-1);
1613 }
1614
1615 /**
1616 * Extract all enums to the given file and optionally generate static inline
1617 * helper functions for them.
1618 *
1619 * \param fp file to print on
1620 * \param gen_funcs generate the functions too
1621 */
1622 static void
extract_all_enums(FILE * fp,int gen_funcs)1623 extract_all_enums(FILE *fp, int gen_funcs)
1624 {
1625 const struct type *t;
1626
1627 LIST_FOREACH(t, &types, link)
1628 if (t->is_enum || t->is_bits)
1629 gen_enum(fp, t, gen_funcs);
1630 }
1631
1632 /**
1633 * Extract enums and optionally generate some helper functions for them.
1634 *
1635 * \param argc number of arguments
1636 * \param argv arguments (enum names)
1637 * \param gen_funcs which functions to generate
1638 */
1639 static void
make_enums(int argc,char * argv[],enum gen_funcs gen_funcs)1640 make_enums(int argc, char *argv[], enum gen_funcs gen_funcs)
1641 {
1642 if (gen_funcs == GEN_FUNCS_C) {
1643 if (argc == 0)
1644 gen_all_enum_funcs(stdout, 1);
1645 else {
1646 for (int i = 0; i < argc; i++)
1647 if (gen_enum_funcs_str(stdout, argv[i]))
1648 errx(1, "enum not found: %s", argv[i]);
1649 }
1650 } else {
1651 if (argc == 0)
1652 extract_all_enums(stdout, gen_funcs == GEN_FUNCS_H);
1653 else {
1654 for (int i = 0; i < argc; i++)
1655 if (extract_enum(stdout, argv[i],
1656 gen_funcs == GEN_FUNCS_H))
1657 errx(1, "enum not found: %s", argv[i]);
1658 }
1659 }
1660 }
1661
1662 /**
1663 * Produce the operation tables for the daemon or a module.
1664 *
1665 * \param root tree root
1666 * \param gen_funcs generate enum funcs
1667 */
1668 static void
make_table(const struct node * root,int gen_funcs)1669 make_table(const struct node *root, int gen_funcs)
1670 {
1671 FILE *fp;
1672
1673 char fname[MAXPATHLEN + 1];
1674 sprintf(fname, "%stree.h", file_prefix);
1675 if ((fp = fopen(fname, "w")) == NULL)
1676 err(1, "%s: ", fname);
1677 gen_header(fp, root, PREFIX_LEN, NULL);
1678
1679 fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n");
1680 gen_enums(fp, gen_funcs);
1681 fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n");
1682
1683 fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
1684 fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
1685
1686 fclose(fp);
1687
1688 sprintf(fname, "%stree.c", file_prefix);
1689 if ((fp = fopen(fname, "w")) == NULL)
1690 err(1, "%s: ", fname);
1691 gen_table(fp, root);
1692 fclose(fp);
1693 }
1694
1695 int
main(int argc,char * argv[])1696 main(int argc, char *argv[])
1697 {
1698 enum op op = OP_GEN;
1699 enum gen_funcs gen_funcs = GEN_FUNCS_NONE;
1700
1701 char *infile = NULL;
1702
1703 int opt;
1704 while ((opt = getopt(argc, argv, "dEeFfhI:i:lp:t")) != EOF)
1705 switch (opt) {
1706
1707 case 'd':
1708 debug = 1;
1709 break;
1710
1711 case 'E':
1712 if (op != OP_GEN && op != OP_ENUMS)
1713 errx(1, "-E conflicts with earlier options");
1714 op = OP_ENUMS;
1715 break;
1716
1717 case 'e':
1718 if (op != OP_GEN && op != OP_EXTRACT)
1719 errx(1, "-e conflicts with earlier options");
1720 op = OP_EXTRACT;
1721 break;
1722
1723 case 'F':
1724 if (gen_funcs != GEN_FUNCS_NONE &&
1725 gen_funcs != GEN_FUNCS_C)
1726 errx(1, "-F conflicts with -f");
1727 gen_funcs = GEN_FUNCS_C;
1728 break;
1729
1730 case 'f':
1731 if (gen_funcs != GEN_FUNCS_NONE &&
1732 gen_funcs != GEN_FUNCS_H)
1733 errx(1, "-f conflicts with -F");
1734 gen_funcs = GEN_FUNCS_H;
1735 break;
1736
1737 case 'h':
1738 fprintf(stderr, "%s", usgtxt);
1739 exit(0);
1740
1741 case 'I':
1742 path_new(optarg);
1743 break;
1744
1745 case 'i':
1746 infile = optarg;
1747 break;
1748
1749 case 'l':
1750 localincs = 1;
1751 break;
1752
1753 case 'p':
1754 file_prefix = optarg;
1755 if (strlen(file_prefix) + strlen("tree.c") >
1756 MAXPATHLEN)
1757 errx(1, "prefix too long");
1758 break;
1759
1760 case 't':
1761 if (op != OP_GEN && op != OP_TREE)
1762 errx(1, "-t conflicts with earlier options");
1763 op = OP_TREE;
1764 break;
1765 }
1766
1767 argc -= optind;
1768 argv += optind;
1769
1770 /* open input */
1771 if (infile == NULL) {
1772 input_new(stdin, NULL, "<stdin>");
1773 } else {
1774 FILE *fp;
1775 if ((fp = fopen(infile, "r")) == NULL)
1776 err(1, "%s", infile);
1777 input_new(fp, NULL, infile);
1778 }
1779
1780 /* parse and check input */
1781 struct node *root = parse_top(gettoken());
1782
1783 int tok;
1784 while ((tok = gettoken()) != TOK_EOF)
1785 merge(&root, parse_top(tok));
1786
1787 if (root)
1788 check_tree(root);
1789
1790 /* do what the user has requested */
1791 switch (op) {
1792
1793 case OP_EXTRACT:
1794 if (argc == 0)
1795 errx(1, "-e requires arguments");
1796
1797 for (int i = 0; i < argc; i++)
1798 if (gen_extract(stdout, root, argv[i]))
1799 errx(1, "object not found: %s", argv[i]);
1800 return (0);
1801
1802 case OP_ENUMS:
1803 make_enums(argc, argv, gen_funcs);
1804 return (0);
1805
1806 case OP_TREE:
1807 if (argc != 0)
1808 errx(1, "-t allows no arguments");
1809 gen_tree(root, 0);
1810 return (0);
1811
1812 case OP_GEN:
1813 if (argc != 0)
1814 errx(1, "tree generation allows no arguments");
1815 make_table(root, gen_funcs == GEN_FUNCS_H);
1816 return (0);
1817 }
1818 }
1819