gensnmptree.c revision 150920
1/*
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 *	All rights reserved.
5 *
6 * Copyright (c) 2004
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: bsnmp/gensnmptree/gensnmptree.c,v 1.43 2005/10/04 11:21:29 brandt_h Exp $
34 *
35 * Generate OID table from table description.
36 *
37 * Syntax is:
38 * ---------
39 * file := tree | tree file
40 *
41 * tree := head elements ')'
42 *
43 * entry := head ':' index STRING elements ')'
44 *
45 * leaf := head TYPE STRING ACCESS ')'
46 *
47 * column := head TYPE ACCESS ')'
48 *
49 * head := '(' INT STRING
50 *
51 * elements := EMPTY | elements element
52 *
53 * element := tree | leaf | column
54 *
55 * index := TYPE | index TYPE
56 *
57 */
58#include <sys/types.h>
59#include <sys/param.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <stdarg.h>
63#include <unistd.h>
64#include <string.h>
65#include <ctype.h>
66#include <inttypes.h>
67#include <errno.h>
68#ifdef HAVE_ERR_H
69#include <err.h>
70#endif
71#include <sys/queue.h>
72#include "support.h"
73#include "asn1.h"
74#include "snmp.h"
75#include "snmpagent.h"
76
77/*
78 * Constant prefix for all OIDs
79 */
80static const asn_subid_t prefix[] = { 1, 3, 6 };
81#define	PREFIX_LEN	(sizeof(prefix) / sizeof(prefix[0]))
82
83u_int tree_size;
84static const char *file_prefix = "";
85static FILE *fp;
86
87/* if true generate local include paths */
88static int localincs = 0;
89
90static const char usgtxt[] = "\
91Generate SNMP tables. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
92Open Communication Systems (FhG Fokus). All rights reserved.\n\
93usage: gensnmptree [-hel] [-p prefix] [name]...\n\
94options:\n\
95  -h		print this info\n\
96  -e		extrace the named oids\n\
97  -l		generate local include directives\n\
98  -p prefix	prepend prefix to file and variable names\n\
99";
100
101/*
102 * A node in the OID tree
103 */
104enum ntype {
105	NODE_LEAF = 1,
106	NODE_TREE,
107	NODE_ENTRY,
108	NODE_COLUMN
109};
110
111enum {
112	FL_GET	= 0x01,
113	FL_SET	= 0x02,
114};
115
116struct node;
117TAILQ_HEAD(node_list, node);
118
119struct node {
120	enum ntype	type;
121	asn_subid_t	id;	/* last element of OID */
122	char		*name;	/* name of node */
123	TAILQ_ENTRY(node) link;
124	u_int		lno;	/* starting line number */
125	u_int		flags;	/* allowed operations */
126
127	union {
128	  struct tree {
129	    struct node_list subs;
130	  }		tree;
131
132	  struct entry {
133	    uint32_t	index;	/* index for table entry */
134	    char	*func;	/* function for tables */
135	    struct node_list subs;
136	  }		entry;
137
138	  struct leaf {
139	    enum snmp_syntax syntax;	/* syntax for this leaf */
140	    char	*func;		/* function name */
141	  }		leaf;
142
143	  struct column {
144	    enum snmp_syntax syntax;	/* syntax for this column */
145	  }		column;
146	}		u;
147};
148
149struct func {
150	const char	*name;
151	LIST_ENTRY(func) link;
152};
153
154static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
155
156/************************************************************
157 *
158 * Allocate memory and panic just in the case...
159 */
160static void *
161xalloc(size_t size)
162{
163	void *ptr;
164
165	if ((ptr = malloc(size)) == NULL)
166		err(1, "allocing %zu bytes", size);
167
168	return (ptr);
169}
170
171/************************************************************
172 *
173 * Parsing input
174 */
175enum tok {
176	TOK_EOF = 0200,	/* end-of-file seen */
177	TOK_NUM,	/* number */
178	TOK_STR,	/* string */
179	TOK_ACCESS,	/* access operator */
180	TOK_TYPE,	/* type operator */
181};
182
183static const struct {
184	const char *str;
185	enum tok tok;
186	u_int val;
187} keywords[] = {
188	{ "GET", TOK_ACCESS, FL_GET },
189	{ "SET", TOK_ACCESS, FL_SET },
190	{ "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
191	{ "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
192	{ "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
193	{ "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
194	{ "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
195	{ "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
196	{ "OID", TOK_TYPE, SNMP_SYNTAX_OID },
197	{ "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
198	{ "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
199	{ "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
200	{ "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
201	{ NULL, 0, 0 }
202};
203
204/* arbitrary upper limit on node names and function names */
205#define	MAXSTR	1000
206char	str[MAXSTR];
207u_long	val;		/* integer values */
208u_int 	lno = 1;	/* current line number */
209int	all_cond;	/* all conditions are true */
210
211static void report(const char *, ...) __dead2 __printflike(1, 2);
212static void report_node(const struct node *, const char *, ...)
213    __dead2 __printflike(2, 3);
214
215/*
216 * Report an error and exit.
217 */
218static void
219report(const char *fmt, ...)
220{
221	va_list ap;
222	int c;
223
224	va_start(ap, fmt);
225	fprintf(stderr, "line %u: ", lno);
226	vfprintf(stderr, fmt, ap);
227	fprintf(stderr, "\n");
228	fprintf(stderr, "context: \"");
229	while ((c = getchar()) != EOF && c != '\n')
230		fprintf(stderr, "%c", c);
231	fprintf(stderr, "\n");
232	va_end(ap);
233	exit(1);
234}
235static void
236report_node(const struct node *np, const char *fmt, ...)
237{
238	va_list ap;
239
240	va_start(ap, fmt);
241	fprintf(stderr, "line %u, node %s: ", np->lno, np->name);
242	vfprintf(stderr, fmt, ap);
243	fprintf(stderr, "\n");
244	va_end(ap);
245	exit(1);
246}
247
248/*
249 * Return a fresh copy of the string constituting the current token.
250 */
251static char *
252savetok(void)
253{
254	return (strcpy(xalloc(strlen(str)+1), str));
255}
256
257/*
258 * Get the next token from input.
259 */
260static int
261gettoken(void)
262{
263	int c;
264
265  again:
266	/*
267	 * Skip any whitespace before the next token
268	 */
269	while ((c = getchar()) != EOF) {
270		if (c == '\n')
271			lno++;
272		if (!isspace(c))
273			break;
274	}
275	if (c == EOF)
276		return (TOK_EOF);
277	if (!isascii(c))
278		report("unexpected character %#2x", (u_int)c);
279
280	/*
281	 * Skip comments
282	 */
283	if (c == '#') {
284		while ((c = getchar()) != EOF) {
285			if (c == '\n') {
286				lno++;
287				goto again;
288			}
289		}
290		report("unexpected EOF in comment");
291	}
292
293	/*
294	 * Single character tokens
295	 */
296	if (c == ')' || c == '(' || c == ':')
297		return (c);
298
299	/*
300	 * Sort out numbers
301	 */
302	if (isdigit(c)) {
303		ungetc(c, stdin);
304		scanf("%lu", &val);
305		return (TOK_NUM);
306	}
307
308	/*
309	 * So that has to be a string.
310	 */
311	if (isalpha(c) || c == '_') {
312		size_t n = 0;
313		str[n++] = c;
314		while ((c = getchar()) != EOF) {
315			if (!isalnum(c) && c != '_') {
316				ungetc(c, stdin);
317				break;
318			}
319			if (n == sizeof(str) - 1) {
320				str[n++] = '\0';
321				report("string too long '%s...'", str);
322			}
323			str[n++] = c;
324		}
325		str[n++] = '\0';
326
327		/*
328		 * Keywords
329		 */
330		for (c = 0; keywords[c].str != NULL; c++)
331			if (strcmp(keywords[c].str, str) == 0) {
332				val = keywords[c].val;
333				return (keywords[c].tok);
334			}
335
336		return (TOK_STR);
337	}
338	if (isprint(c))
339		errx(1, "%u: unexpected character '%c'", lno, c);
340	else
341		errx(1, "%u: unexpected character 0x%02x", lno, (u_int)c);
342}
343
344/*
345 * Parse the next node (complete with all subnodes)
346 */
347static struct node *
348parse(enum tok tok)
349{
350	struct node *node;
351	struct node *sub;
352	u_int index_count;
353
354	node = xalloc(sizeof(struct node));
355	node->lno = lno;
356
357	if (tok != '(')
358		report("'(' expected at begin of node");
359	if (gettoken() != TOK_NUM)
360		report("node id expected after opening '('");
361	if (val > ASN_MAXID)
362		report("subid too large '%lu'", val);
363	node->id = (asn_subid_t)val;
364	if (gettoken() != TOK_STR)
365		report("node name expected after '(' ID");
366	node->name = savetok();
367
368	if ((tok = gettoken()) == TOK_TYPE) {
369		/* LEAF or COLUM */
370		u_int syntax = val;
371
372		if ((tok = gettoken()) == TOK_STR) {
373			/* LEAF */
374			node->type = NODE_LEAF;
375			node->u.leaf.func = savetok();
376			node->u.leaf.syntax = syntax;
377			tok = gettoken();
378		} else {
379			/* COLUMN */
380			node->type = NODE_COLUMN;
381			node->u.column.syntax = syntax;
382		}
383
384		while (tok != ')') {
385			if (tok != TOK_ACCESS)
386				report("access keyword or ')' expected");
387			node->flags |= (u_int)val;
388			tok = gettoken();
389		}
390
391	} else if (tok == ':') {
392		/* ENTRY */
393		node->type = NODE_ENTRY;
394		TAILQ_INIT(&node->u.entry.subs);
395
396		index_count = 0;
397		node->u.entry.index = 0;
398		while ((tok = gettoken()) == TOK_TYPE) {
399			if (index_count++ == SNMP_INDEXES_MAX)
400				report("too many table indexes");
401			node->u.entry.index |=
402			    val << (SNMP_INDEX_SHIFT * index_count);
403		}
404		node->u.entry.index |= index_count;
405		if (index_count == 0)
406			report("need at least one index");
407
408		if (tok != TOK_STR)
409			report("function name expected");
410
411		node->u.entry.func = savetok();
412
413		tok = gettoken();
414
415		while (tok != ')') {
416			sub = parse(tok);
417			TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link);
418			tok = gettoken();
419		}
420
421	} else {
422		/* subtree */
423		node->type = NODE_TREE;
424		TAILQ_INIT(&node->u.tree.subs);
425
426		while (tok != ')') {
427			sub = parse(tok);
428			TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link);
429			tok = gettoken();
430		}
431	}
432	return (node);
433}
434
435/*
436 * Generate the C-code table part for one node.
437 */
438static void
439gen_node(struct node *np, struct asn_oid *oid, u_int idx, const char *func)
440{
441	u_int n;
442	struct node *sub;
443	u_int syntax;
444
445	if (oid->len == ASN_MAXOIDLEN)
446		report_node(np, "OID too long");
447	oid->subs[oid->len++] = np->id;
448
449	if (np->type == NODE_TREE) {
450		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
451			gen_node(sub, oid, 0, NULL);
452		oid->len--;
453		return;
454	}
455	if (np->type == NODE_ENTRY) {
456		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
457			gen_node(sub, oid, np->u.entry.index, np->u.entry.func);
458		oid->len--;
459		return;
460	}
461
462	/* leaf or column */
463	if ((np->flags & (FL_GET|FL_SET)) == 0) {
464		oid->len--;
465		return;
466	}
467
468	fprintf(fp, "    {{ %u, {", oid->len);
469	for (n = 0; n < oid->len; n++)
470		fprintf(fp, " %u,", oid->subs[n]);
471	fprintf(fp, " }}, \"%s\", ", np->name);
472
473	if (np->type == NODE_COLUMN) {
474		syntax = np->u.column.syntax;
475		fprintf(fp, "SNMP_NODE_COLUMN, ");
476	} else {
477		syntax = np->u.leaf.syntax;
478		fprintf(fp, "SNMP_NODE_LEAF, ");
479	}
480
481	switch (syntax) {
482
483	  case SNMP_SYNTAX_NULL:
484		fprintf(fp, "SNMP_SYNTAX_NULL, ");
485		break;
486
487	  case SNMP_SYNTAX_INTEGER:
488		fprintf(fp, "SNMP_SYNTAX_INTEGER, ");
489		break;
490
491	  case SNMP_SYNTAX_OCTETSTRING:
492		fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, ");
493		break;
494
495	  case SNMP_SYNTAX_IPADDRESS:
496		fprintf(fp, "SNMP_SYNTAX_IPADDRESS, ");
497		break;
498
499	  case SNMP_SYNTAX_OID:
500		fprintf(fp, "SNMP_SYNTAX_OID, ");
501		break;
502
503	  case SNMP_SYNTAX_TIMETICKS:
504		fprintf(fp, "SNMP_SYNTAX_TIMETICKS, ");
505		break;
506
507	  case SNMP_SYNTAX_COUNTER:
508		fprintf(fp, "SNMP_SYNTAX_COUNTER, ");
509		break;
510
511	  case SNMP_SYNTAX_GAUGE:
512		fprintf(fp, "SNMP_SYNTAX_GAUGE, ");
513		break;
514
515	  case SNMP_SYNTAX_COUNTER64:
516		fprintf(fp, "SNMP_SYNTAX_COUNTER64, ");
517		break;
518
519	  case SNMP_SYNTAX_NOSUCHOBJECT:
520	  case SNMP_SYNTAX_NOSUCHINSTANCE:
521	  case SNMP_SYNTAX_ENDOFMIBVIEW:
522		abort();
523	}
524
525	if (np->type == NODE_COLUMN)
526		fprintf(fp, "%s, ", func);
527	else
528		fprintf(fp, "%s, ", np->u.leaf.func);
529
530	fprintf(fp, "0");
531	if (np->flags & FL_SET)
532		fprintf(fp, "|SNMP_NODE_CANSET");
533	fprintf(fp, ", %#x, NULL, NULL },\n", idx);
534	oid->len--;
535	return;
536}
537
538/*
539 * Generate the header file with the function declarations.
540 */
541static void
542gen_header(struct node *np, u_int oidlen, const char *func)
543{
544	char f[MAXSTR + 4];
545	struct node *sub;
546	struct func *ptr;
547
548	oidlen++;
549	if (np->type == NODE_TREE) {
550		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
551			gen_header(sub, oidlen, NULL);
552		return;
553	}
554	if (np->type == NODE_ENTRY) {
555		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
556			gen_header(sub, oidlen, np->u.entry.func);
557		return;
558	}
559
560 	if((np->flags & (FL_GET|FL_SET)) == 0)
561		return;
562
563	if (np->type == NODE_COLUMN) {
564		if (func == NULL)
565			errx(1, "column without function (%s) - probably "
566			    "outside of a table", np->name);
567		sprintf(f, "%s", func);
568	} else
569		sprintf(f, "%s", np->u.leaf.func);
570
571	LIST_FOREACH(ptr, &funcs, link)
572		if (strcmp(ptr->name, f) == 0)
573			break;
574
575	if (ptr == NULL) {
576		ptr = xalloc(sizeof(*ptr));
577		ptr->name = strcpy(xalloc(strlen(f)+1), f);
578		LIST_INSERT_HEAD(&funcs, ptr, link);
579
580		fprintf(fp, "int	%s(struct snmp_context *, "
581		    "struct snmp_value *, u_int, u_int, "
582		    "enum snmp_op);\n", f);
583	}
584
585	fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id);
586}
587
588/*
589 * Generate the OID table.
590 */
591static void
592gen_table(struct node *node)
593{
594	struct asn_oid oid;
595
596	fprintf(fp, "#include <sys/types.h>\n");
597	fprintf(fp, "#include <stdio.h>\n");
598#ifdef HAVE_STDINT_H
599	fprintf(fp, "#include <stdint.h>\n");
600#endif
601	if (localincs) {
602		fprintf(fp, "#include \"asn1.h\"\n");
603		fprintf(fp, "#include \"snmp.h\"\n");
604		fprintf(fp, "#include \"snmpagent.h\"\n");
605	} else {
606		fprintf(fp, "#include <bsnmp/asn1.h>\n");
607		fprintf(fp, "#include <bsnmp/snmp.h>\n");
608		fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
609	}
610	fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
611	fprintf(fp, "\n");
612
613	fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
614
615	oid.len = PREFIX_LEN;
616	memcpy(oid.subs, prefix, sizeof(prefix));
617	gen_node(node, &oid, 0, NULL);
618
619	fprintf(fp, "};\n\n");
620}
621
622static void
623print_syntax(u_int syntax)
624{
625	u_int i;
626
627	for (i = 0; keywords[i].str != NULL; i++)
628		if (keywords[i].tok == TOK_TYPE &&
629		    keywords[i].val == syntax) {
630			printf(" %s", keywords[i].str);
631			return;
632	}
633	abort();
634}
635
636/*
637 * Generate a tree definition file
638 */
639static void
640gen_tree(const struct node *np, int level)
641{
642	const struct node *sp;
643	u_int i;
644
645	printf("%*s(%u %s", 2 * level, "", np->id, np->name);
646
647	switch (np->type) {
648
649	  case NODE_LEAF:
650		print_syntax(np->u.leaf.syntax);
651		printf(" %s%s%s)\n", np->u.leaf.func,
652		    (np->flags & FL_GET) ? " GET" : "",
653		    (np->flags & FL_SET) ? " SET" : "");
654		break;
655
656	  case NODE_TREE:
657		if (TAILQ_EMPTY(&np->u.tree.subs)) {
658			printf(")\n");
659		} else {
660			printf("\n");
661			TAILQ_FOREACH(sp, &np->u.tree.subs, link)
662				gen_tree(sp, level + 1);
663			printf("%*s)\n", 2 * level, "");
664		}
665		break;
666
667	  case NODE_ENTRY:
668		printf(" :");
669
670		for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++)
671			print_syntax(SNMP_INDEX(np->u.entry.index, i));
672		printf(" %s\n", np->u.entry.func);
673		TAILQ_FOREACH(sp, &np->u.entry.subs, link)
674			gen_tree(sp, level + 1);
675		printf("%*s)\n", 2 * level, "");
676		break;
677
678	  case NODE_COLUMN:
679		print_syntax(np->u.column.syntax);
680		printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
681		    (np->flags & FL_SET) ? " SET" : "");
682		break;
683
684	}
685}
686
687static int
688extract(const struct node *np, struct asn_oid *oid, const char *obj,
689    const struct asn_oid *idx, const char *iname)
690{
691	struct node *sub;
692	u_long n;
693
694	if (oid->len == ASN_MAXOIDLEN)
695		report_node(np, "OID too long");
696	oid->subs[oid->len++] = np->id;
697
698	if (strcmp(obj, np->name) == 0) {
699		if (oid->len + idx->len >= ASN_MAXOIDLEN)
700			report_node(np, "OID too long");
701		fprintf(fp, "#define OID_%s%s\t%u\n", np->name,
702		    iname ? iname : "", np->id);
703		fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,
704		    iname ? iname : "", oid->len + idx->len);
705		fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,
706		    iname ? iname : "", oid->len + idx->len);
707		for (n = 0; n < oid->len; n++)
708			fprintf(fp, " %u,", oid->subs[n]);
709		for (n = 0; n < idx->len; n++)
710			fprintf(fp, " %u,", idx->subs[n]);
711		fprintf(fp, " } }\n");
712		return (0);
713	}
714
715	if (np->type == NODE_TREE) {
716		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
717			if (!extract(sub, oid, obj, idx, iname))
718				return (0);
719	} else if (np->type == NODE_ENTRY) {
720		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
721			if (!extract(sub, oid, obj, idx, iname))
722				return (0);
723	}
724	oid->len--;
725	return (1);
726}
727
728static int
729gen_extract(const struct node *root, char *object)
730{
731	struct asn_oid oid;
732	struct asn_oid idx;
733	char *s, *e, *end, *iname;
734	u_long ul;
735	int ret;
736
737	/* look whether the object to extract has an index part */
738	idx.len = 0;
739	iname = NULL;
740	s = strchr(object, '.');
741	if (s != NULL) {
742		iname = malloc(strlen(s) + 1);
743		if (iname == NULL)
744			err(1, "cannot allocated index");
745
746		strcpy(iname, s);
747		for (e = iname; *e != '\0'; e++)
748			if (*e == '.')
749				*e = '_';
750
751		*s++ = '\0';
752		while (s != NULL) {
753			if (*s == '\0')
754				errx(1, "bad index syntax");
755			if ((e = strchr(s, '.')) != NULL)
756				*e++ = '\0';
757
758			errno = 0;
759			ul = strtoul(s, &end, 0);
760			if (*end != '\0')
761				errx(1, "bad index syntax '%s'", end);
762			if (errno != 0)
763				err(1, "bad index syntax");
764
765			if (idx.len == ASN_MAXOIDLEN)
766				errx(1, "index oid too large");
767			idx.subs[idx.len++] = ul;
768
769			s = e;
770		}
771	}
772
773	oid.len = PREFIX_LEN;
774	memcpy(oid.subs, prefix, sizeof(prefix));
775	ret = extract(root, &oid, object, &idx, iname);
776	if (iname != NULL)
777		free(iname);
778
779	return (ret);
780}
781
782
783static void
784check_sub_order(const struct node *np, const struct node_list *subs)
785{
786	int first;
787	const struct node *sub;
788	asn_subid_t maxid = 0;
789
790	/* ensure, that subids are ordered */
791	first = 1;
792	TAILQ_FOREACH(sub, subs, link) {
793		if (!first && sub->id <= maxid)
794			report_node(np, "subids not ordered at %s", sub->name);
795		maxid = sub->id;
796		first = 0;
797	}
798}
799
800/*
801 * Do some sanity checks on the tree definition and do some computations.
802 */
803static void
804check_tree(struct node *np)
805{
806	struct node *sub;
807
808	if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
809		if ((np->flags & (FL_GET|FL_SET)) != 0)
810			tree_size++;
811		return;
812	}
813
814	if (np->type == NODE_ENTRY) {
815		check_sub_order(np, &np->u.entry.subs);
816
817		/* ensure all subnodes are columns */
818		TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
819			if (sub->type != NODE_COLUMN)
820				report_node(np, "entry subnode '%s' is not "
821				    "a column", sub->name);
822			check_tree(sub);
823		}
824	} else {
825		check_sub_order(np, &np->u.tree.subs);
826
827		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
828			check_tree(sub);
829	}
830}
831
832static void
833merge_subs(struct node_list *s1, struct node_list *s2)
834{
835	struct node *n1, *n2;
836
837	while (!TAILQ_EMPTY(s2)) {
838		n2 = TAILQ_FIRST(s2);
839		TAILQ_REMOVE(s2, n2, link);
840
841		TAILQ_FOREACH(n1, s1, link)
842			if (n1->id >= n2->id)
843				break;
844		if (n1 == NULL)
845			TAILQ_INSERT_TAIL(s1, n2, link);
846		else if (n1->id > n2->id)
847			TAILQ_INSERT_BEFORE(n1, n2, link);
848		else {
849			if (n1->type == NODE_TREE && n2->type == NODE_TREE) {
850				if (strcmp(n1->name, n2->name) != 0)
851					errx(1, "trees to merge must have "
852					    "same name '%s' '%s'", n1->name,
853					    n2->name);
854				merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);
855				free(n2);
856			} else if (n1->type == NODE_ENTRY &&
857			    n2->type == NODE_ENTRY) {
858				if (strcmp(n1->name, n2->name) != 0)
859					errx(1, "entries to merge must have "
860					    "same name '%s' '%s'", n1->name,
861					    n2->name);
862				if (n1->u.entry.index != n2->u.entry.index)
863					errx(1, "entries to merge must have "
864					    "same index '%s'", n1->name);
865				if (strcmp(n1->u.entry.func,
866				    n2->u.entry.func) != 0)
867					errx(1, "entries to merge must have "
868					    "same op '%s'", n1->name);
869				merge_subs(&n1->u.entry.subs,
870				    &n2->u.entry.subs);
871				free(n2);
872			} else
873				errx(1, "entities to merge must be both "
874				    "trees or both entries: %s, %s",
875				    n1->name, n2->name);
876		}
877	}
878}
879
880static void
881merge(struct node *root, struct node *t)
882{
883
884	/* both must be trees */
885	if (root->type != NODE_TREE)
886		errx(1, "root is not a tree");
887	if (t->type != NODE_TREE)
888		errx(1, "can merge only with tree");
889	if (root->id != t->id)
890		errx(1, "trees to merge must have same id");
891
892	merge_subs(&root->u.tree.subs, &t->u.tree.subs);
893}
894
895int
896main(int argc, char *argv[])
897{
898	int do_extract = 0;
899	int do_tree = 0;
900	int opt;
901	struct node *root;
902	char fname[MAXPATHLEN + 1];
903	int tok;
904
905	while ((opt = getopt(argc, argv, "help:t")) != EOF)
906		switch (opt) {
907
908		  case 'h':
909			fprintf(stderr, "%s", usgtxt);
910			exit(0);
911
912		  case 'e':
913			do_extract = 1;
914			break;
915
916		  case 'l':
917			localincs = 1;
918			break;
919
920		  case 'p':
921			file_prefix = optarg;
922			if (strlen(file_prefix) + strlen("tree.c") >
923			    MAXPATHLEN)
924				errx(1, "prefix too long");
925			break;
926
927		  case 't':
928			do_tree = 1;
929			break;
930		}
931
932	if (do_extract && do_tree)
933		errx(1, "conflicting options -e and -t");
934	if (!do_extract && argc != optind)
935		errx(1, "no arguments allowed");
936	if (do_extract && argc == optind)
937		errx(1, "no objects specified");
938
939	root = parse(gettoken());
940	while ((tok = gettoken()) != TOK_EOF)
941		merge(root, parse(tok));
942
943	check_tree(root);
944
945	if (do_extract) {
946		fp = stdout;
947		while (optind < argc) {
948			if (gen_extract(root, argv[optind]))
949				errx(1, "object not found: %s", argv[optind]);
950			optind++;
951		}
952
953		return (0);
954	}
955	if (do_tree) {
956		gen_tree(root, 0);
957		return (0);
958	}
959	sprintf(fname, "%stree.h", file_prefix);
960	if ((fp = fopen(fname, "w")) == NULL)
961		err(1, "%s: ", fname);
962	gen_header(root, PREFIX_LEN, NULL);
963
964	fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
965	fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
966
967	fclose(fp);
968
969	sprintf(fname, "%stree.c", file_prefix);
970	if ((fp = fopen(fname, "w")) == NULL)
971		err(1, "%s: ", fname);
972	gen_table(root);
973	fclose(fp);
974
975	return (0);
976}
977