]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bsnmp/gensnmptree/gensnmptree.c
sysctl(9): Fix a few mandoc related issues
[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 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 *
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 *
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
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
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
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 *
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
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
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
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
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
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 *
500 savetok(void)
501 {
502         return (savestr(str));
503 }
504
505 /*
506  * Get the next token from input.
507  */
508 static int
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
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
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 *
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
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }