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