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