xref: /freebsd/contrib/bsnmp/gensnmptree/gensnmptree.c (revision 7aa383846770374466b1dcb2cefd71bde9acf463)
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 }
1557