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