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