]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/bsnmp/gensnmptree/gensnmptree.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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
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 [-dEehlt] [-I directory] [-i infile] [-p prefix]\n\
114             [name]...\n\
115 options:\n\
116   -d            debug mode\n\
117   -E            extract the named enums and bits only\n\
118   -e            extract the named oids or enums\n\
119   -h            print this info\n\
120   -I directory  add directory to include path\n\
121   -i ifile      read from the named file instead of stdin\n\
122   -l            generate local include directives\n\
123   -p prefix     prepend prefix to file and variable names\n\
124   -t            generated a .def file\n\
125 ";
126
127 /*
128  * A node in the OID tree
129  */
130 enum ntype {
131         NODE_LEAF = 1,
132         NODE_TREE,
133         NODE_ENTRY,
134         NODE_COLUMN
135 };
136
137 enum {
138         FL_GET  = 0x01,
139         FL_SET  = 0x02,
140 };
141
142 struct node;
143 TAILQ_HEAD(node_list, node);
144
145 struct node {
146         enum ntype      type;
147         asn_subid_t     id;     /* last element of OID */
148         char            *name;  /* name of node */
149         TAILQ_ENTRY(node) link;
150         u_int           lno;    /* starting line number */
151         u_int           flags;  /* allowed operations */
152
153         union {
154           struct tree {
155             struct node_list subs;
156           }             tree;
157
158           struct entry {
159             uint32_t    index;  /* index for table entry */
160             char        *func;  /* function for tables */
161             struct node_list subs;
162           }             entry;
163
164           struct leaf {
165             enum snmp_syntax syntax;    /* syntax for this leaf */
166             char        *func;          /* function name */
167           }             leaf;
168
169           struct column {
170             enum snmp_syntax syntax;    /* syntax for this column */
171           }             column;
172         }               u;
173 };
174
175 struct func {
176         const char      *name;
177         LIST_ENTRY(func) link;
178 };
179
180 static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
181
182 struct enums {
183         const char      *name;
184         long            value;
185         TAILQ_ENTRY(enums) link;
186 };
187
188 struct type {
189         const char      *name;
190         const char      *from_fname;
191         u_int           from_lno;
192         u_int           syntax;
193         int             is_enum;
194         int             is_bits;
195         TAILQ_HEAD(, enums) enums;
196         LIST_ENTRY(type) link;
197 };
198
199 static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types);
200
201 static void report(const char *, ...) __dead2 __printflike(1, 2);
202 static void report_node(const struct node *, const char *, ...)
203     __dead2 __printflike(2, 3);
204
205 /************************************************************
206  *
207  * Allocate memory and panic just in the case...
208  */
209 static void *
210 xalloc(size_t size)
211 {
212         void *ptr;
213
214         if ((ptr = malloc(size)) == NULL)
215                 err(1, "allocing %zu bytes", size);
216
217         return (ptr);
218 }
219
220 static char *
221 savestr(const char *s)
222 {
223
224         if (s == NULL)
225                 return (NULL);
226         return (strcpy(xalloc(strlen(s) + 1), s));
227 }
228
229 /************************************************************
230  *
231  * Input stack
232  */
233 struct input {
234         FILE            *fp;
235         u_int           lno;
236         char            *fname;
237         char            *path;
238         LIST_ENTRY(input) link;
239 };
240 static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs);
241 static struct input *input = NULL;
242
243 #define MAX_PATHS       100
244 static u_int npaths = 2;
245 static u_int stdpaths = 2;
246 static const char *paths[MAX_PATHS + 1] = {
247         "/usr/share/snmp/defs",
248         "/usr/local/share/snmp/defs",
249         NULL
250 };
251
252 static int pbchar = -1;
253
254 static void
255 path_new(const char *path)
256 {
257         if (npaths >= MAX_PATHS)
258                 report("too many -I directives");
259         memmove(&paths[npaths - stdpaths + 1], &paths[npaths - stdpaths],
260             sizeof(path[0]) * stdpaths);
261         paths[npaths - stdpaths] = savestr(path);
262         npaths++;
263 }
264
265 static void
266 input_new(FILE *fp, const char *path, const char *fname)
267 {
268         struct input *ip;
269
270         ip = xalloc(sizeof(*ip));
271         ip->fp = fp;
272         ip->lno = 1;
273         ip->fname = savestr(fname);
274         ip->path = savestr(path);
275         LIST_INSERT_HEAD(&inputs, ip, link);
276
277         input = ip;
278 }
279
280 static void
281 input_close(void)
282 {
283
284         if (input == NULL)
285                 return;
286         fclose(input->fp);
287         free(input->fname);
288         free(input->path);
289         LIST_REMOVE(input, link);
290         free(input);
291
292         input = LIST_FIRST(&inputs);
293 }
294
295 static FILE *
296 tryopen(const char *path, const char *fname)
297 {
298         char *fn;
299         FILE *fp;
300
301         if (path == NULL)
302                 fn = savestr(fname);
303         else {
304                 fn = xalloc(strlen(path) + strlen(fname) + 2);
305                 sprintf(fn, "%s/%s", path, fname);
306         }
307         fp = fopen(fn, "r");
308         free(fn);
309         return (fp);
310 }
311
312 static void
313 input_fopen(const char *fname, int loc)
314 {
315         FILE *fp;
316         char *path;
317         u_int p;
318
319         if (fname[0] == '/') {
320                 if ((fp = tryopen(NULL, fname)) != NULL) {
321                         input_new(fp, NULL, fname);
322                         return;
323                 }
324
325         } else {
326                 if (loc) {
327                         if (input == NULL)
328                                 path = NULL;
329                         else
330                                 path = input->path;
331
332                         if ((fp = tryopen(path, fname)) != NULL) {
333                                 input_new(fp, NULL, fname);
334                                 return;
335                         }
336                 }
337
338                 for (p = 0; paths[p] != NULL; p++)
339                         if ((fp = tryopen(paths[p], fname)) != NULL) {
340                                 input_new(fp, paths[p], fname);
341                                 return;
342                         }
343         }
344         report("cannot open '%s'", fname);
345 }
346
347 static int
348 tgetc(void)
349 {
350         int c;
351
352         if (pbchar != -1) {
353                 c = pbchar;
354                 pbchar = -1;
355                 return (c);
356         }
357
358         for (;;) {
359                 if (input == NULL)
360                         return (EOF);
361
362                 if ((c = getc(input->fp)) != EOF)
363                         return (c);
364
365                 input_close();
366         }
367 }
368
369 static void
370 tungetc(int c)
371 {
372
373         if (pbchar != -1)
374                 abort();
375         pbchar = c;
376 }
377
378 /************************************************************
379  *
380  * Parsing input
381  */
382 enum tok {
383         TOK_EOF = 0200, /* end-of-file seen */
384         TOK_NUM,        /* number */
385         TOK_STR,        /* string */
386         TOK_ACCESS,     /* access operator */
387         TOK_TYPE,       /* type operator */
388         TOK_ENUM,       /* enum token (kind of a type) */
389         TOK_TYPEDEF,    /* typedef directive */
390         TOK_DEFTYPE,    /* defined type */
391         TOK_INCLUDE,    /* include directive */
392         TOK_FILENAME,   /* filename ("foo.bar" or <foo.bar>) */
393         TOK_BITS,       /* bits token (kind of a type) */
394 };
395
396 static const struct {
397         const char *str;
398         enum tok tok;
399         u_int val;
400 } keywords[] = {
401         { "GET", TOK_ACCESS, FL_GET },
402         { "SET", TOK_ACCESS, FL_SET },
403         { "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
404         { "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
405         { "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
406         { "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
407         { "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
408         { "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
409         { "OID", TOK_TYPE, SNMP_SYNTAX_OID },
410         { "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
411         { "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
412         { "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
413         { "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
414         { "ENUM", TOK_ENUM, SNMP_SYNTAX_INTEGER },
415         { "BITS", TOK_BITS, SNMP_SYNTAX_OCTETSTRING },
416         { "typedef", TOK_TYPEDEF, 0 },
417         { "include", TOK_INCLUDE, 0 },
418         { NULL, 0, 0 }
419 };
420
421 /* arbitrary upper limit on node names and function names */
422 #define MAXSTR  1000
423 char    str[MAXSTR];
424 u_long  val;            /* integer values */
425 int     all_cond;       /* all conditions are true */
426 int     saved_token = -1;
427
428 /*
429  * Report an error and exit.
430  */
431 static void
432 report(const char *fmt, ...)
433 {
434         va_list ap;
435         int c;
436
437         va_start(ap, fmt);
438         fprintf(stderr, "line %u: ", input->lno);
439         vfprintf(stderr, fmt, ap);
440         fprintf(stderr, "\n");
441         fprintf(stderr, "context: \"");
442         while ((c = tgetc()) != EOF && c != '\n')
443                 fprintf(stderr, "%c", c);
444         fprintf(stderr, "\n");
445         va_end(ap);
446         exit(1);
447 }
448 static void
449 report_node(const struct node *np, const char *fmt, ...)
450 {
451         va_list ap;
452
453         va_start(ap, fmt);
454         fprintf(stderr, "line %u, node %s: ", np->lno, np->name);
455         vfprintf(stderr, fmt, ap);
456         fprintf(stderr, "\n");
457         va_end(ap);
458         exit(1);
459 }
460
461 /*
462  * Return a fresh copy of the string constituting the current token.
463  */
464 static char *
465 savetok(void)
466 {
467         return (savestr(str));
468 }
469
470 /*
471  * Get the next token from input.
472  */
473 static int
474 gettoken_internal(void)
475 {
476         int c;
477         struct type *t;
478
479         if (saved_token != -1) {
480                 c = saved_token;
481                 saved_token = -1;
482                 return (c);
483         }
484
485   again:
486         /*
487          * Skip any whitespace before the next token
488          */
489         while ((c = tgetc()) != EOF) {
490                 if (c == '\n')
491                         input->lno++;
492                 if (!isspace(c))
493                         break;
494         }
495         if (c == EOF)
496                 return (TOK_EOF);
497         if (!isascii(c))
498                 report("unexpected character %#2x", (u_int)c);
499
500         /*
501          * Skip comments
502          */
503         if (c == '#') {
504                 while ((c = tgetc()) != EOF) {
505                         if (c == '\n') {
506                                 input->lno++;
507                                 goto again;
508                         }
509                 }
510                 report("unexpected EOF in comment");
511         }
512
513         /*
514          * Single character tokens
515          */
516         if (strchr("():|-", c) != NULL)
517                 return (c);
518
519         if (c == '"' || c == '<') {
520                 int end = c;
521                 size_t n = 0;
522
523                 val = 1;
524                 if (c == '<') {
525                         val = 0;
526                         end = '>';
527                 }
528
529                 while ((c = tgetc()) != EOF) {
530                         if (c == end)
531                                 break;
532                         if (n == sizeof(str) - 1) {
533                                 str[n++] = '\0';
534                                 report("filename too long '%s...'", str);
535                         }
536                         str[n++] = c;
537                 }
538                 str[n++] = '\0';
539                 return (TOK_FILENAME);
540         }
541
542         /*
543          * Sort out numbers
544          */
545         if (isdigit(c)) {
546                 size_t n = 0;
547                 str[n++] = c;
548                 while ((c = tgetc()) != EOF) {
549                         if (!isdigit(c)) {
550                                 tungetc(c);
551                                 break;
552                         }
553                         if (n == sizeof(str) - 1) {
554                                 str[n++] = '\0';
555                                 report("number too long '%s...'", str);
556                         }
557                         str[n++] = c;
558                 }
559                 str[n++] = '\0';
560                 sscanf(str, "%lu", &val);
561                 return (TOK_NUM);
562         }
563
564         /*
565          * So that has to be a string.
566          */
567         if (isalpha(c) || c == '_') {
568                 size_t n = 0;
569                 str[n++] = c;
570                 while ((c = tgetc()) != EOF) {
571                         if (!isalnum(c) && c != '_' && c != '-') {
572                                 tungetc(c);
573                                 break;
574                         }
575                         if (n == sizeof(str) - 1) {
576                                 str[n++] = '\0';
577                                 report("string too long '%s...'", str);
578                         }
579                         str[n++] = c;
580                 }
581                 str[n++] = '\0';
582
583                 /*
584                  * Keywords
585                  */
586                 for (c = 0; keywords[c].str != NULL; c++)
587                         if (strcmp(keywords[c].str, str) == 0) {
588                                 val = keywords[c].val;
589                                 return (keywords[c].tok);
590                         }
591
592                 LIST_FOREACH(t, &types, link) {
593                         if (strcmp(t->name, str) == 0) {
594                                 val = t->syntax;
595                                 return (TOK_DEFTYPE);
596                         }
597                 }
598                 return (TOK_STR);
599         }
600         if (isprint(c))
601                 errx(1, "%u: unexpected character '%c'", input->lno, c);
602         else
603                 errx(1, "%u: unexpected character 0x%02x", input->lno,
604                     (u_int)c);
605 }
606 static int
607 gettoken(void)
608 {
609         int tok = gettoken_internal();
610
611         if (debug) {
612                 switch (tok) {
613
614                   case TOK_EOF:
615                         fprintf(stderr, "EOF ");
616                         break;
617
618                   case TOK_NUM:
619                         fprintf(stderr, "NUM(%lu) ", val);
620                         break;
621
622                   case TOK_STR:
623                         fprintf(stderr, "STR(%s) ", str);
624                         break;
625
626                   case TOK_ACCESS:
627                         fprintf(stderr, "ACCESS(%lu) ", val);
628                         break;
629
630                   case TOK_TYPE:
631                         fprintf(stderr, "TYPE(%lu) ", val);
632                         break;
633
634                   case TOK_ENUM:
635                         fprintf(stderr, "ENUM ");
636                         break;
637
638                   case TOK_BITS:
639                         fprintf(stderr, "BITS ");
640                         break;
641
642                   case TOK_TYPEDEF:
643                         fprintf(stderr, "TYPEDEF ");
644                         break;
645
646                   case TOK_DEFTYPE:
647                         fprintf(stderr, "DEFTYPE(%s,%lu) ", str, val);
648                         break;
649
650                   case TOK_INCLUDE:
651                         fprintf(stderr, "INCLUDE ");
652                         break;
653
654                   case TOK_FILENAME:
655                         fprintf(stderr, "FILENAME ");
656                         break;
657
658                   default:
659                         if (tok < TOK_EOF) {
660                                 if (isprint(tok))
661                                         fprintf(stderr, "'%c' ", tok);
662                                 else if (tok == '\n')
663                                         fprintf(stderr, "\n");
664                                 else
665                                         fprintf(stderr, "%02x ", tok);
666                         } else
667                                 abort();
668                         break;
669                 }
670         }
671         return (tok);
672 }
673
674 /**
675  * Pushback a token
676  */
677 static void
678 pushback(enum tok tok)
679 {
680
681         if (saved_token != -1)
682                 abort();
683         saved_token = tok;
684 }
685
686 /*
687  * Create a new type
688  */
689 static struct type *
690 make_type(const char *s)
691 {
692         struct type *t;
693
694         t = xalloc(sizeof(*t));
695         t->name = savestr(s);
696         t->is_enum = 0;
697         t->syntax = SNMP_SYNTAX_NULL;
698         t->from_fname = savestr(input->fname);
699         t->from_lno = input->lno;
700         TAILQ_INIT(&t->enums);
701         LIST_INSERT_HEAD(&types, t, link);
702
703         return (t);
704 }
705
706 /*
707  * Parse a type. We've seen the ENUM or type keyword already. Leave next
708  * token.
709  */
710 static u_int
711 parse_type(enum tok *tok, struct type *t, const char *vname)
712 {
713         u_int syntax;
714         struct enums *e;
715
716         syntax = val;
717
718         if (*tok == TOK_ENUM || *tok == TOK_BITS) {
719                 if (t == NULL && vname != NULL) {
720                         t = make_type(vname);
721                         t->is_enum = (*tok == TOK_ENUM);
722                         t->is_bits = (*tok == TOK_BITS);
723                         t->syntax = syntax;
724                 }
725                 if (gettoken() != '(')
726                         report("'(' expected after ENUM");
727
728                 if ((*tok = gettoken()) == TOK_EOF)
729                         report("unexpected EOF in ENUM");
730                 do {
731                         e = NULL;
732                         if (t != NULL) {
733                                 e = xalloc(sizeof(*e));
734                         }
735                         if (*tok == '-') {
736                                 if ((*tok = gettoken()) == TOK_EOF)
737                                         report("unexpected EOF in ENUM");
738                                 e->value = -(long)val;
739                         } else
740                                 e->value = val;
741                         
742                         if (*tok != TOK_NUM)
743                                 report("need value for ENUM/BITS");
744                         if (gettoken() != TOK_STR)
745                                 report("need string in ENUM/BITS");
746                         if (e != NULL) {
747                                 e->name = savetok();
748                                 TAILQ_INSERT_TAIL(&t->enums, e, link);
749                         }
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         if (localincs) {
1071                 fprintf(fp, "#include \"asn1.h\"\n");
1072                 fprintf(fp, "#include \"snmp.h\"\n");
1073                 fprintf(fp, "#include \"snmpagent.h\"\n");
1074         } else {
1075                 fprintf(fp, "#include <bsnmp/asn1.h>\n");
1076                 fprintf(fp, "#include <bsnmp/snmp.h>\n");
1077                 fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
1078         }
1079         fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
1080         fprintf(fp, "\n");
1081
1082         fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
1083
1084         oid.len = PREFIX_LEN;
1085         memcpy(oid.subs, prefix, sizeof(prefix));
1086         gen_node(fp, node, &oid, 0, NULL);
1087
1088         fprintf(fp, "};\n\n");
1089 }
1090
1091 static void
1092 print_syntax(u_int syntax)
1093 {
1094         u_int i;
1095
1096         for (i = 0; keywords[i].str != NULL; i++)
1097                 if (keywords[i].tok == TOK_TYPE &&
1098                     keywords[i].val == syntax) {
1099                         printf(" %s", keywords[i].str);
1100                         return;
1101         }
1102         abort();
1103 }
1104
1105 /*
1106  * Generate a tree definition file
1107  */
1108 static void
1109 gen_tree(const struct node *np, int level)
1110 {
1111         const struct node *sp;
1112         u_int i;
1113
1114         printf("%*s(%u %s", 2 * level, "", np->id, np->name);
1115
1116         switch (np->type) {
1117
1118           case NODE_LEAF:
1119                 print_syntax(np->u.leaf.syntax);
1120                 printf(" %s%s%s)\n", np->u.leaf.func,
1121                     (np->flags & FL_GET) ? " GET" : "",
1122                     (np->flags & FL_SET) ? " SET" : "");
1123                 break;
1124
1125           case NODE_TREE:
1126                 if (TAILQ_EMPTY(&np->u.tree.subs)) {
1127                         printf(")\n");
1128                 } else {
1129                         printf("\n");
1130                         TAILQ_FOREACH(sp, &np->u.tree.subs, link)
1131                                 gen_tree(sp, level + 1);
1132                         printf("%*s)\n", 2 * level, "");
1133                 }
1134                 break;
1135
1136           case NODE_ENTRY:
1137                 printf(" :");
1138
1139                 for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++)
1140                         print_syntax(SNMP_INDEX(np->u.entry.index, i));
1141                 printf(" %s\n", np->u.entry.func);
1142                 TAILQ_FOREACH(sp, &np->u.entry.subs, link)
1143                         gen_tree(sp, level + 1);
1144                 printf("%*s)\n", 2 * level, "");
1145                 break;
1146
1147           case NODE_COLUMN:
1148                 print_syntax(np->u.column.syntax);
1149                 printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
1150                     (np->flags & FL_SET) ? " SET" : "");
1151                 break;
1152         }
1153 }
1154
1155 static int
1156 extract(FILE *fp, const struct node *np, struct asn_oid *oid, const char *obj,
1157     const struct asn_oid *idx, const char *iname)
1158 {
1159         struct node *sub;
1160         u_long n;
1161
1162         if (oid->len == ASN_MAXOIDLEN)
1163                 report_node(np, "OID too long");
1164         oid->subs[oid->len++] = np->id;
1165
1166         if (strcmp(obj, np->name) == 0) {
1167                 if (oid->len + idx->len >= ASN_MAXOIDLEN)
1168                         report_node(np, "OID too long");
1169                 fprintf(fp, "#define OID_%s%s\t%u\n", np->name,
1170                     iname ? iname : "", np->id);
1171                 fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,
1172                     iname ? iname : "", oid->len + idx->len);
1173                 fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,
1174                     iname ? iname : "", oid->len + idx->len);
1175                 for (n = 0; n < oid->len; n++)
1176                         fprintf(fp, " %u,", oid->subs[n]);
1177                 for (n = 0; n < idx->len; n++)
1178                         fprintf(fp, " %u,", idx->subs[n]);
1179                 fprintf(fp, " } }\n");
1180                 return (0);
1181         }
1182
1183         if (np->type == NODE_TREE) {
1184                 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
1185                         if (!extract(fp, sub, oid, obj, idx, iname))
1186                                 return (0);
1187         } else if (np->type == NODE_ENTRY) {
1188                 TAILQ_FOREACH(sub, &np->u.entry.subs, link)
1189                         if (!extract(fp, sub, oid, obj, idx, iname))
1190                                 return (0);
1191         }
1192         oid->len--;
1193         return (1);
1194 }
1195
1196 static int
1197 gen_extract(FILE *fp, const struct node *root, char *object)
1198 {
1199         struct asn_oid oid;
1200         struct asn_oid idx;
1201         char *s, *e, *end, *iname;
1202         u_long ul;
1203         int ret;
1204
1205         /* look whether the object to extract has an index part */
1206         idx.len = 0;
1207         iname = NULL;
1208         s = strchr(object, '.');
1209         if (s != NULL) {
1210                 iname = malloc(strlen(s) + 1);
1211                 if (iname == NULL)
1212                         err(1, "cannot allocated index");
1213
1214                 strcpy(iname, s);
1215                 for (e = iname; *e != '\0'; e++)
1216                         if (*e == '.')
1217                                 *e = '_';
1218
1219                 *s++ = '\0';
1220                 while (s != NULL) {
1221                         if (*s == '\0')
1222                                 errx(1, "bad index syntax");
1223                         if ((e = strchr(s, '.')) != NULL)
1224                                 *e++ = '\0';
1225
1226                         errno = 0;
1227                         ul = strtoul(s, &end, 0);
1228                         if (*end != '\0')
1229                                 errx(1, "bad index syntax '%s'", end);
1230                         if (errno != 0)
1231                                 err(1, "bad index syntax");
1232
1233                         if (idx.len == ASN_MAXOIDLEN)
1234                                 errx(1, "index oid too large");
1235                         idx.subs[idx.len++] = ul;
1236
1237                         s = e;
1238                 }
1239         }
1240
1241         oid.len = PREFIX_LEN;
1242         memcpy(oid.subs, prefix, sizeof(prefix));
1243         ret = extract(fp, root, &oid, object, &idx, iname);
1244         if (iname != NULL)
1245                 free(iname);
1246
1247         return (ret);
1248 }
1249
1250
1251 static void
1252 check_sub_order(const struct node *np, const struct node_list *subs)
1253 {
1254         int first;
1255         const struct node *sub;
1256         asn_subid_t maxid = 0;
1257
1258         /* ensure, that subids are ordered */
1259         first = 1;
1260         TAILQ_FOREACH(sub, subs, link) {
1261                 if (!first && sub->id <= maxid)
1262                         report_node(np, "subids not ordered at %s", sub->name);
1263                 maxid = sub->id;
1264                 first = 0;
1265         }
1266 }
1267
1268 /*
1269  * Do some sanity checks on the tree definition and do some computations.
1270  */
1271 static void
1272 check_tree(struct node *np)
1273 {
1274         struct node *sub;
1275
1276         if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
1277                 if ((np->flags & (FL_GET|FL_SET)) != 0)
1278                         tree_size++;
1279                 return;
1280         }
1281
1282         if (np->type == NODE_ENTRY) {
1283                 check_sub_order(np, &np->u.entry.subs);
1284
1285                 /* ensure all subnodes are columns */
1286                 TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
1287                         if (sub->type != NODE_COLUMN)
1288                                 report_node(np, "entry subnode '%s' is not "
1289                                     "a column", sub->name);
1290                         check_tree(sub);
1291                 }
1292         } else {
1293                 check_sub_order(np, &np->u.tree.subs);
1294
1295                 TAILQ_FOREACH(sub, &np->u.tree.subs, link)
1296                         check_tree(sub);
1297         }
1298 }
1299
1300 static void
1301 merge_subs(struct node_list *s1, struct node_list *s2)
1302 {
1303         struct node *n1, *n2;
1304
1305         while (!TAILQ_EMPTY(s2)) {
1306                 n2 = TAILQ_FIRST(s2);
1307                 TAILQ_REMOVE(s2, n2, link);
1308
1309                 TAILQ_FOREACH(n1, s1, link)
1310                         if (n1->id >= n2->id)
1311                                 break;
1312                 if (n1 == NULL)
1313                         TAILQ_INSERT_TAIL(s1, n2, link);
1314                 else if (n1->id > n2->id)
1315                         TAILQ_INSERT_BEFORE(n1, n2, link);
1316                 else {
1317                         if (n1->type == NODE_TREE && n2->type == NODE_TREE) {
1318                                 if (strcmp(n1->name, n2->name) != 0)
1319                                         errx(1, "trees to merge must have "
1320                                             "same name '%s' '%s'", n1->name,
1321                                             n2->name);
1322                                 merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);
1323                                 free(n2);
1324                         } else if (n1->type == NODE_ENTRY &&
1325                             n2->type == NODE_ENTRY) {
1326                                 if (strcmp(n1->name, n2->name) != 0)
1327                                         errx(1, "entries to merge must have "
1328                                             "same name '%s' '%s'", n1->name,
1329                                             n2->name);
1330                                 if (n1->u.entry.index != n2->u.entry.index)
1331                                         errx(1, "entries to merge must have "
1332                                             "same index '%s'", n1->name);
1333                                 if (strcmp(n1->u.entry.func,
1334                                     n2->u.entry.func) != 0)
1335                                         errx(1, "entries to merge must have "
1336                                             "same op '%s'", n1->name);
1337                                 merge_subs(&n1->u.entry.subs,
1338                                     &n2->u.entry.subs);
1339                                 free(n2);
1340                         } else
1341                                 errx(1, "entities to merge must be both "
1342                                     "trees or both entries: %s, %s",
1343                                     n1->name, n2->name);
1344                 }
1345         }
1346 }
1347
1348 static void
1349 merge(struct node **root, struct node *t)
1350 {
1351
1352         if (*root == NULL) {
1353                 *root = t;
1354                 return;
1355         }
1356         if (t == NULL)
1357                 return;
1358
1359         /* both must be trees */
1360         if ((*root)->type != NODE_TREE)
1361                 errx(1, "root is not a tree");
1362         if (t->type != NODE_TREE)
1363                 errx(1, "can merge only with tree");
1364         if ((*root)->id != t->id)
1365                 errx(1, "trees to merge must have same id");
1366
1367         merge_subs(&(*root)->u.tree.subs, &t->u.tree.subs);
1368 }
1369
1370 static void
1371 unminus(FILE *fp, const char *s)
1372 {
1373
1374         while (*s != '\0') {
1375                 if (*s == '-')
1376                         fprintf(fp, "_");
1377                 else
1378                         fprintf(fp, "%c", *s);
1379                 s++;
1380         }
1381 }
1382
1383 static void
1384 gen_enum(FILE *fp, const struct type *t)
1385 {
1386         const struct enums *e;
1387         long min = LONG_MAX;
1388
1389         fprintf(fp, "\n");
1390         fprintf(fp, "#ifndef %s_defined__\n", t->name);
1391         fprintf(fp, "#define %s_defined__\n", t->name);
1392         fprintf(fp, "/*\n");
1393         fprintf(fp, " * From %s:%u\n", t->from_fname, t->from_lno);
1394         fprintf(fp, " */\n");
1395         fprintf(fp, "enum %s {\n", t->name);
1396         TAILQ_FOREACH(e, &t->enums, link) {
1397                 fprintf(fp, "\t%s_", t->name);
1398                 unminus(fp, e->name);
1399                 fprintf(fp, " = %ld,\n", e->value);
1400                 if (e->value < min)
1401                         min = e->value;
1402         }
1403         fprintf(fp, "};\n");
1404         fprintf(fp, "#define    STROFF_%s %ld\n", t->name, min);
1405         fprintf(fp, "#define    STRING_%s \\\n", t->name);
1406         TAILQ_FOREACH(e, &t->enums, link) {
1407                 fprintf(fp, "\t[%ld] \"%s_", e->value - min, t->name);
1408                 unminus(fp, e->name);
1409                 fprintf(fp, "\",\\\n");
1410         }
1411         fprintf(fp, "\n");
1412         fprintf(fp, "#endif /* %s_defined__ */\n", t->name);
1413 }
1414
1415 static void
1416 gen_enums(FILE *fp)
1417 {
1418         const struct type *t;
1419
1420         LIST_FOREACH(t, &types, link)
1421                 if (t->is_enum || t->is_bits)
1422                         gen_enum(fp, t);
1423 }
1424
1425 static int
1426 extract_enum(FILE *fp, const char *name)
1427 {
1428         const struct type *t;
1429
1430         LIST_FOREACH(t, &types, link)
1431                 if ((t->is_enum || t->is_bits) && strcmp(t->name, name) == 0) {
1432                         gen_enum(fp, t);
1433                         return (0);
1434                 }
1435         return (-1);
1436 }
1437
1438 int
1439 main(int argc, char *argv[])
1440 {
1441         int do_extract = 0;
1442         int do_tree = 0;
1443         int do_enums = 0;
1444         int opt;
1445         struct node *root;
1446         char fname[MAXPATHLEN + 1];
1447         int tok;
1448         FILE *fp;
1449         char *infile = NULL;
1450
1451         while ((opt = getopt(argc, argv, "dEehI:i:lp:t")) != EOF)
1452                 switch (opt) {
1453
1454                   case 'd':
1455                         debug = 1;
1456                         break;
1457
1458                   case 'h':
1459                         fprintf(stderr, "%s", usgtxt);
1460                         exit(0);
1461
1462                   case 'E':
1463                         do_enums = 1;
1464                         break;
1465
1466                   case 'e':
1467                         do_extract = 1;
1468                         break;
1469
1470                   case 'I':
1471                         path_new(optarg);
1472                         break;
1473
1474                   case 'i':
1475                         infile = optarg;
1476                         break;
1477
1478                   case 'l':
1479                         localincs = 1;
1480                         break;
1481
1482                   case 'p':
1483                         file_prefix = optarg;
1484                         if (strlen(file_prefix) + strlen("tree.c") >
1485                             MAXPATHLEN)
1486                                 errx(1, "prefix too long");
1487                         break;
1488
1489                   case 't':
1490                         do_tree = 1;
1491                         break;
1492                 }
1493
1494         if (do_extract + do_tree + do_enums > 1)
1495                 errx(1, "conflicting options -e/-t/-E");
1496         if (!do_extract && !do_enums && argc != optind)
1497                 errx(1, "no arguments allowed");
1498         if ((do_extract || do_enums) && argc == optind)
1499                 errx(1, "no objects specified");
1500
1501         if (infile == NULL) {
1502                 input_new(stdin, NULL, "<stdin>");
1503         } else {
1504                 if ((fp = fopen(infile, "r")) == NULL)
1505                         err(1, "%s", infile);
1506                 input_new(fp, NULL, infile);
1507         }
1508
1509         root = parse_top(gettoken());
1510         while ((tok = gettoken()) != TOK_EOF)
1511                 merge(&root, parse_top(tok));
1512
1513         check_tree(root);
1514
1515         if (do_extract) {
1516                 while (optind < argc) {
1517                         if (gen_extract(stdout, root, argv[optind]))
1518                                 errx(1, "object not found: %s", argv[optind]);
1519                         optind++;
1520                 }
1521                 return (0);
1522         }
1523         if (do_enums) {
1524                 while (optind < argc) {
1525                         if (extract_enum(stdout, argv[optind]))
1526                                 errx(1, "enum not found: %s", argv[optind]);
1527                         optind++;
1528                 }
1529                 return (0);
1530         }
1531         if (do_tree) {
1532                 gen_tree(root, 0);
1533                 return (0);
1534         }
1535         sprintf(fname, "%stree.h", file_prefix);
1536         if ((fp = fopen(fname, "w")) == NULL)
1537                 err(1, "%s: ", fname);
1538         gen_header(fp, root, PREFIX_LEN, NULL);
1539
1540         fprintf(fp, "\n#ifdef SNMPTREE_TYPES\n");
1541         gen_enums(fp);
1542         fprintf(fp, "\n#endif /* SNMPTREE_TYPES */\n\n");
1543
1544         fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
1545         fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
1546
1547         fclose(fp);
1548
1549         sprintf(fname, "%stree.c", file_prefix);
1550         if ((fp = fopen(fname, "w")) == NULL)
1551                 err(1, "%s: ", fname);
1552         gen_table(fp, root);
1553         fclose(fp);
1554
1555         return (0);
1556 }