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 */
99static const asn_subid_t prefix[] = { 1, 3, 6 };
100#define	PREFIX_LEN	(sizeof(prefix) / sizeof(prefix[0]))
101
102u_int tree_size;
103static const char *file_prefix = "";
104
105/* if true generate local include paths */
106static int localincs = 0;
107
108/* if true print tokens */
109static int debug;
110
111static const char usgtxt[] = "\
112Generate SNMP tables.\n\
113usage: gensnmptree [-dEeFfhlt] [-I directory] [-i infile] [-p prefix]\n\
114	    [name]...\n\
115options:\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 */
132enum 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 */
149enum 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 */
163enum ntype {
164	NODE_LEAF = 1,
165	NODE_TREE,
166	NODE_ENTRY,
167	NODE_COLUMN
168};
169
170enum {
171	FL_GET	= 0x01,
172	FL_SET	= 0x02,
173};
174
175struct node;
176TAILQ_HEAD(node_list, node);
177
178struct 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
211struct func {
212	const char	*name;
213	LIST_ENTRY(func) link;
214};
215
216static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
217
218struct enums {
219	const char	*name;
220	long		value;
221	TAILQ_ENTRY(enums) link;
222};
223
224struct 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
235static LIST_HEAD(, type) types = LIST_HEAD_INITIALIZER(types);
236
237static void report(const char *, ...) __dead2 __printflike(1, 2);
238static 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 */
245static void *
246xalloc(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
256static char *
257savestr(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 */
269struct input {
270	FILE		*fp;
271	u_int		lno;
272	char		*fname;
273	char		*path;
274	LIST_ENTRY(input) link;
275};
276static LIST_HEAD(, input) inputs = LIST_HEAD_INITIALIZER(inputs);
277static struct input *input = NULL;
278
279#define MAX_PATHS	100
280static u_int npaths = 2;
281static u_int stdpaths = 2;
282static const char *paths[MAX_PATHS + 1] = {
283	"/usr/share/snmp/defs",
284	"/usr/local/share/snmp/defs",
285	NULL
286};
287
288static int pbchar = -1;
289
290static void
291path_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
301static void
302input_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
316static void
317input_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
331static FILE *
332tryopen(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
348static void
349input_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
383static int
384tgetc(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
405static void
406tungetc(int c)
407{
408
409	if (pbchar != -1)
410		abort();
411	pbchar = c;
412}
413
414/************************************************************
415 *
416 * Parsing input
417 */
418enum 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
432static 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
459static char	str[MAXSTR];
460static u_long	val;		/* integer values */
461static int	saved_token = -1;
462
463/*
464 * Report an error and exit.
465 */
466static void
467report(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}
483static void
484report_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 */
499static char *
500savetok(void)
501{
502	return (savestr(str));
503}
504
505/*
506 * Get the next token from input.
507 */
508static int
509gettoken_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}
641static int
642gettoken(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 */
712static void
713pushback(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 */
724static struct type *
725make_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 */
745static u_int
746parse_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 */
809static struct node *
810parse(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 */
911static struct node *
912parse_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 */
949static void
950gen_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 */
1054static void
1055gen_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 */
1104static void
1105gen_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
1135static void
1136print_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 */
1152static void
1153gen_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
1206static int
1207extract(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
1247static int
1248gen_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
1302static void
1303check_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 */
1322static void
1323check_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
1351static void
1352merge_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
1399static void
1400merge(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
1421static void
1422unminus(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 */
1444static void
1445gen_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 */
1506static void
1507gen_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 */
1552static int
1553gen_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 */
1572static void
1573gen_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
1582static void
1583gen_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 */
1602static int
1603extract_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 */
1622static void
1623extract_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 */
1639static void
1640make_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 */
1668static void
1669make_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
1695int
1696main(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