xref: /freebsd/contrib/bsnmp/gensnmptree/gensnmptree.c (revision 87569f75a91f298c52a71823c04d41cf53c88889)
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.44 2006/02/14 09:04:17 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  */
80 static const asn_subid_t prefix[] = { 1, 3, 6 };
81 #define	PREFIX_LEN	(sizeof(prefix) / sizeof(prefix[0]))
82 
83 u_int tree_size;
84 static const char *file_prefix = "";
85 static FILE *fp;
86 
87 /* if true generate local include paths */
88 static int localincs = 0;
89 
90 static const char usgtxt[] = "\
91 Generate SNMP tables. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
92 Open Communication Systems (FhG Fokus). All rights reserved.\n\
93 usage: gensnmptree [-hel] [-p prefix] [name]...\n\
94 options:\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  */
104 enum ntype {
105 	NODE_LEAF = 1,
106 	NODE_TREE,
107 	NODE_ENTRY,
108 	NODE_COLUMN
109 };
110 
111 enum {
112 	FL_GET	= 0x01,
113 	FL_SET	= 0x02,
114 };
115 
116 struct node;
117 TAILQ_HEAD(node_list, node);
118 
119 struct 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 
149 struct func {
150 	const char	*name;
151 	LIST_ENTRY(func) link;
152 };
153 
154 static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
155 
156 /************************************************************
157  *
158  * Allocate memory and panic just in the case...
159  */
160 static void *
161 xalloc(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  */
175 enum 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 
183 static 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
206 char	str[MAXSTR];
207 u_long	val;		/* integer values */
208 u_int 	lno = 1;	/* current line number */
209 int	all_cond;	/* all conditions are true */
210 
211 static void report(const char *, ...) __dead2 __printflike(1, 2);
212 static void report_node(const struct node *, const char *, ...)
213     __dead2 __printflike(2, 3);
214 
215 /*
216  * Report an error and exit.
217  */
218 static void
219 report(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 }
235 static void
236 report_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  */
251 static char *
252 savetok(void)
253 {
254 	return (strcpy(xalloc(strlen(str)+1), str));
255 }
256 
257 /*
258  * Get the next token from input.
259  */
260 static int
261 gettoken(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  */
347 static struct node *
348 parse(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 	node->flags = 0;
357 
358 	if (tok != '(')
359 		report("'(' expected at begin of node");
360 	if (gettoken() != TOK_NUM)
361 		report("node id expected after opening '('");
362 	if (val > ASN_MAXID)
363 		report("subid too large '%lu'", val);
364 	node->id = (asn_subid_t)val;
365 	if (gettoken() != TOK_STR)
366 		report("node name expected after '(' ID");
367 	node->name = savetok();
368 
369 	if ((tok = gettoken()) == TOK_TYPE) {
370 		/* LEAF or COLUM */
371 		u_int syntax = val;
372 
373 		if ((tok = gettoken()) == TOK_STR) {
374 			/* LEAF */
375 			node->type = NODE_LEAF;
376 			node->u.leaf.func = savetok();
377 			node->u.leaf.syntax = syntax;
378 			tok = gettoken();
379 		} else {
380 			/* COLUMN */
381 			node->type = NODE_COLUMN;
382 			node->u.column.syntax = syntax;
383 		}
384 
385 		while (tok != ')') {
386 			if (tok != TOK_ACCESS)
387 				report("access keyword or ')' expected");
388 			node->flags |= (u_int)val;
389 			tok = gettoken();
390 		}
391 
392 	} else if (tok == ':') {
393 		/* ENTRY */
394 		node->type = NODE_ENTRY;
395 		TAILQ_INIT(&node->u.entry.subs);
396 
397 		index_count = 0;
398 		node->u.entry.index = 0;
399 		while ((tok = gettoken()) == TOK_TYPE) {
400 			if (index_count++ == SNMP_INDEXES_MAX)
401 				report("too many table indexes");
402 			node->u.entry.index |=
403 			    val << (SNMP_INDEX_SHIFT * index_count);
404 		}
405 		node->u.entry.index |= index_count;
406 		if (index_count == 0)
407 			report("need at least one index");
408 
409 		if (tok != TOK_STR)
410 			report("function name expected");
411 
412 		node->u.entry.func = savetok();
413 
414 		tok = gettoken();
415 
416 		while (tok != ')') {
417 			sub = parse(tok);
418 			TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link);
419 			tok = gettoken();
420 		}
421 
422 	} else {
423 		/* subtree */
424 		node->type = NODE_TREE;
425 		TAILQ_INIT(&node->u.tree.subs);
426 
427 		while (tok != ')') {
428 			sub = parse(tok);
429 			TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link);
430 			tok = gettoken();
431 		}
432 	}
433 	return (node);
434 }
435 
436 /*
437  * Generate the C-code table part for one node.
438  */
439 static void
440 gen_node(struct node *np, struct asn_oid *oid, u_int idx, const char *func)
441 {
442 	u_int n;
443 	struct node *sub;
444 	u_int syntax;
445 
446 	if (oid->len == ASN_MAXOIDLEN)
447 		report_node(np, "OID too long");
448 	oid->subs[oid->len++] = np->id;
449 
450 	if (np->type == NODE_TREE) {
451 		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
452 			gen_node(sub, oid, 0, NULL);
453 		oid->len--;
454 		return;
455 	}
456 	if (np->type == NODE_ENTRY) {
457 		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
458 			gen_node(sub, oid, np->u.entry.index, np->u.entry.func);
459 		oid->len--;
460 		return;
461 	}
462 
463 	/* leaf or column */
464 	if ((np->flags & (FL_GET|FL_SET)) == 0) {
465 		oid->len--;
466 		return;
467 	}
468 
469 	fprintf(fp, "    {{ %u, {", oid->len);
470 	for (n = 0; n < oid->len; n++)
471 		fprintf(fp, " %u,", oid->subs[n]);
472 	fprintf(fp, " }}, \"%s\", ", np->name);
473 
474 	if (np->type == NODE_COLUMN) {
475 		syntax = np->u.column.syntax;
476 		fprintf(fp, "SNMP_NODE_COLUMN, ");
477 	} else {
478 		syntax = np->u.leaf.syntax;
479 		fprintf(fp, "SNMP_NODE_LEAF, ");
480 	}
481 
482 	switch (syntax) {
483 
484 	  case SNMP_SYNTAX_NULL:
485 		fprintf(fp, "SNMP_SYNTAX_NULL, ");
486 		break;
487 
488 	  case SNMP_SYNTAX_INTEGER:
489 		fprintf(fp, "SNMP_SYNTAX_INTEGER, ");
490 		break;
491 
492 	  case SNMP_SYNTAX_OCTETSTRING:
493 		fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, ");
494 		break;
495 
496 	  case SNMP_SYNTAX_IPADDRESS:
497 		fprintf(fp, "SNMP_SYNTAX_IPADDRESS, ");
498 		break;
499 
500 	  case SNMP_SYNTAX_OID:
501 		fprintf(fp, "SNMP_SYNTAX_OID, ");
502 		break;
503 
504 	  case SNMP_SYNTAX_TIMETICKS:
505 		fprintf(fp, "SNMP_SYNTAX_TIMETICKS, ");
506 		break;
507 
508 	  case SNMP_SYNTAX_COUNTER:
509 		fprintf(fp, "SNMP_SYNTAX_COUNTER, ");
510 		break;
511 
512 	  case SNMP_SYNTAX_GAUGE:
513 		fprintf(fp, "SNMP_SYNTAX_GAUGE, ");
514 		break;
515 
516 	  case SNMP_SYNTAX_COUNTER64:
517 		fprintf(fp, "SNMP_SYNTAX_COUNTER64, ");
518 		break;
519 
520 	  case SNMP_SYNTAX_NOSUCHOBJECT:
521 	  case SNMP_SYNTAX_NOSUCHINSTANCE:
522 	  case SNMP_SYNTAX_ENDOFMIBVIEW:
523 		abort();
524 	}
525 
526 	if (np->type == NODE_COLUMN)
527 		fprintf(fp, "%s, ", func);
528 	else
529 		fprintf(fp, "%s, ", np->u.leaf.func);
530 
531 	fprintf(fp, "0");
532 	if (np->flags & FL_SET)
533 		fprintf(fp, "|SNMP_NODE_CANSET");
534 	fprintf(fp, ", %#x, NULL, NULL },\n", idx);
535 	oid->len--;
536 	return;
537 }
538 
539 /*
540  * Generate the header file with the function declarations.
541  */
542 static void
543 gen_header(struct node *np, u_int oidlen, const char *func)
544 {
545 	char f[MAXSTR + 4];
546 	struct node *sub;
547 	struct func *ptr;
548 
549 	oidlen++;
550 	if (np->type == NODE_TREE) {
551 		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
552 			gen_header(sub, oidlen, NULL);
553 		return;
554 	}
555 	if (np->type == NODE_ENTRY) {
556 		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
557 			gen_header(sub, oidlen, np->u.entry.func);
558 		return;
559 	}
560 
561  	if((np->flags & (FL_GET|FL_SET)) == 0)
562 		return;
563 
564 	if (np->type == NODE_COLUMN) {
565 		if (func == NULL)
566 			errx(1, "column without function (%s) - probably "
567 			    "outside of a table", np->name);
568 		sprintf(f, "%s", func);
569 	} else
570 		sprintf(f, "%s", np->u.leaf.func);
571 
572 	LIST_FOREACH(ptr, &funcs, link)
573 		if (strcmp(ptr->name, f) == 0)
574 			break;
575 
576 	if (ptr == NULL) {
577 		ptr = xalloc(sizeof(*ptr));
578 		ptr->name = strcpy(xalloc(strlen(f)+1), f);
579 		LIST_INSERT_HEAD(&funcs, ptr, link);
580 
581 		fprintf(fp, "int	%s(struct snmp_context *, "
582 		    "struct snmp_value *, u_int, u_int, "
583 		    "enum snmp_op);\n", f);
584 	}
585 
586 	fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id);
587 }
588 
589 /*
590  * Generate the OID table.
591  */
592 static void
593 gen_table(struct node *node)
594 {
595 	struct asn_oid oid;
596 
597 	fprintf(fp, "#include <sys/types.h>\n");
598 	fprintf(fp, "#include <stdio.h>\n");
599 #ifdef HAVE_STDINT_H
600 	fprintf(fp, "#include <stdint.h>\n");
601 #endif
602 	if (localincs) {
603 		fprintf(fp, "#include \"asn1.h\"\n");
604 		fprintf(fp, "#include \"snmp.h\"\n");
605 		fprintf(fp, "#include \"snmpagent.h\"\n");
606 	} else {
607 		fprintf(fp, "#include <bsnmp/asn1.h>\n");
608 		fprintf(fp, "#include <bsnmp/snmp.h>\n");
609 		fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
610 	}
611 	fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
612 	fprintf(fp, "\n");
613 
614 	fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
615 
616 	oid.len = PREFIX_LEN;
617 	memcpy(oid.subs, prefix, sizeof(prefix));
618 	gen_node(node, &oid, 0, NULL);
619 
620 	fprintf(fp, "};\n\n");
621 }
622 
623 static void
624 print_syntax(u_int syntax)
625 {
626 	u_int i;
627 
628 	for (i = 0; keywords[i].str != NULL; i++)
629 		if (keywords[i].tok == TOK_TYPE &&
630 		    keywords[i].val == syntax) {
631 			printf(" %s", keywords[i].str);
632 			return;
633 	}
634 	abort();
635 }
636 
637 /*
638  * Generate a tree definition file
639  */
640 static void
641 gen_tree(const struct node *np, int level)
642 {
643 	const struct node *sp;
644 	u_int i;
645 
646 	printf("%*s(%u %s", 2 * level, "", np->id, np->name);
647 
648 	switch (np->type) {
649 
650 	  case NODE_LEAF:
651 		print_syntax(np->u.leaf.syntax);
652 		printf(" %s%s%s)\n", np->u.leaf.func,
653 		    (np->flags & FL_GET) ? " GET" : "",
654 		    (np->flags & FL_SET) ? " SET" : "");
655 		break;
656 
657 	  case NODE_TREE:
658 		if (TAILQ_EMPTY(&np->u.tree.subs)) {
659 			printf(")\n");
660 		} else {
661 			printf("\n");
662 			TAILQ_FOREACH(sp, &np->u.tree.subs, link)
663 				gen_tree(sp, level + 1);
664 			printf("%*s)\n", 2 * level, "");
665 		}
666 		break;
667 
668 	  case NODE_ENTRY:
669 		printf(" :");
670 
671 		for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++)
672 			print_syntax(SNMP_INDEX(np->u.entry.index, i));
673 		printf(" %s\n", np->u.entry.func);
674 		TAILQ_FOREACH(sp, &np->u.entry.subs, link)
675 			gen_tree(sp, level + 1);
676 		printf("%*s)\n", 2 * level, "");
677 		break;
678 
679 	  case NODE_COLUMN:
680 		print_syntax(np->u.column.syntax);
681 		printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
682 		    (np->flags & FL_SET) ? " SET" : "");
683 		break;
684 
685 	}
686 }
687 
688 static int
689 extract(const struct node *np, struct asn_oid *oid, const char *obj,
690     const struct asn_oid *idx, const char *iname)
691 {
692 	struct node *sub;
693 	u_long n;
694 
695 	if (oid->len == ASN_MAXOIDLEN)
696 		report_node(np, "OID too long");
697 	oid->subs[oid->len++] = np->id;
698 
699 	if (strcmp(obj, np->name) == 0) {
700 		if (oid->len + idx->len >= ASN_MAXOIDLEN)
701 			report_node(np, "OID too long");
702 		fprintf(fp, "#define OID_%s%s\t%u\n", np->name,
703 		    iname ? iname : "", np->id);
704 		fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,
705 		    iname ? iname : "", oid->len + idx->len);
706 		fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,
707 		    iname ? iname : "", oid->len + idx->len);
708 		for (n = 0; n < oid->len; n++)
709 			fprintf(fp, " %u,", oid->subs[n]);
710 		for (n = 0; n < idx->len; n++)
711 			fprintf(fp, " %u,", idx->subs[n]);
712 		fprintf(fp, " } }\n");
713 		return (0);
714 	}
715 
716 	if (np->type == NODE_TREE) {
717 		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
718 			if (!extract(sub, oid, obj, idx, iname))
719 				return (0);
720 	} else if (np->type == NODE_ENTRY) {
721 		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
722 			if (!extract(sub, oid, obj, idx, iname))
723 				return (0);
724 	}
725 	oid->len--;
726 	return (1);
727 }
728 
729 static int
730 gen_extract(const struct node *root, char *object)
731 {
732 	struct asn_oid oid;
733 	struct asn_oid idx;
734 	char *s, *e, *end, *iname;
735 	u_long ul;
736 	int ret;
737 
738 	/* look whether the object to extract has an index part */
739 	idx.len = 0;
740 	iname = NULL;
741 	s = strchr(object, '.');
742 	if (s != NULL) {
743 		iname = malloc(strlen(s) + 1);
744 		if (iname == NULL)
745 			err(1, "cannot allocated index");
746 
747 		strcpy(iname, s);
748 		for (e = iname; *e != '\0'; e++)
749 			if (*e == '.')
750 				*e = '_';
751 
752 		*s++ = '\0';
753 		while (s != NULL) {
754 			if (*s == '\0')
755 				errx(1, "bad index syntax");
756 			if ((e = strchr(s, '.')) != NULL)
757 				*e++ = '\0';
758 
759 			errno = 0;
760 			ul = strtoul(s, &end, 0);
761 			if (*end != '\0')
762 				errx(1, "bad index syntax '%s'", end);
763 			if (errno != 0)
764 				err(1, "bad index syntax");
765 
766 			if (idx.len == ASN_MAXOIDLEN)
767 				errx(1, "index oid too large");
768 			idx.subs[idx.len++] = ul;
769 
770 			s = e;
771 		}
772 	}
773 
774 	oid.len = PREFIX_LEN;
775 	memcpy(oid.subs, prefix, sizeof(prefix));
776 	ret = extract(root, &oid, object, &idx, iname);
777 	if (iname != NULL)
778 		free(iname);
779 
780 	return (ret);
781 }
782 
783 
784 static void
785 check_sub_order(const struct node *np, const struct node_list *subs)
786 {
787 	int first;
788 	const struct node *sub;
789 	asn_subid_t maxid = 0;
790 
791 	/* ensure, that subids are ordered */
792 	first = 1;
793 	TAILQ_FOREACH(sub, subs, link) {
794 		if (!first && sub->id <= maxid)
795 			report_node(np, "subids not ordered at %s", sub->name);
796 		maxid = sub->id;
797 		first = 0;
798 	}
799 }
800 
801 /*
802  * Do some sanity checks on the tree definition and do some computations.
803  */
804 static void
805 check_tree(struct node *np)
806 {
807 	struct node *sub;
808 
809 	if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
810 		if ((np->flags & (FL_GET|FL_SET)) != 0)
811 			tree_size++;
812 		return;
813 	}
814 
815 	if (np->type == NODE_ENTRY) {
816 		check_sub_order(np, &np->u.entry.subs);
817 
818 		/* ensure all subnodes are columns */
819 		TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
820 			if (sub->type != NODE_COLUMN)
821 				report_node(np, "entry subnode '%s' is not "
822 				    "a column", sub->name);
823 			check_tree(sub);
824 		}
825 	} else {
826 		check_sub_order(np, &np->u.tree.subs);
827 
828 		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
829 			check_tree(sub);
830 	}
831 }
832 
833 static void
834 merge_subs(struct node_list *s1, struct node_list *s2)
835 {
836 	struct node *n1, *n2;
837 
838 	while (!TAILQ_EMPTY(s2)) {
839 		n2 = TAILQ_FIRST(s2);
840 		TAILQ_REMOVE(s2, n2, link);
841 
842 		TAILQ_FOREACH(n1, s1, link)
843 			if (n1->id >= n2->id)
844 				break;
845 		if (n1 == NULL)
846 			TAILQ_INSERT_TAIL(s1, n2, link);
847 		else if (n1->id > n2->id)
848 			TAILQ_INSERT_BEFORE(n1, n2, link);
849 		else {
850 			if (n1->type == NODE_TREE && n2->type == NODE_TREE) {
851 				if (strcmp(n1->name, n2->name) != 0)
852 					errx(1, "trees to merge must have "
853 					    "same name '%s' '%s'", n1->name,
854 					    n2->name);
855 				merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);
856 				free(n2);
857 			} else if (n1->type == NODE_ENTRY &&
858 			    n2->type == NODE_ENTRY) {
859 				if (strcmp(n1->name, n2->name) != 0)
860 					errx(1, "entries to merge must have "
861 					    "same name '%s' '%s'", n1->name,
862 					    n2->name);
863 				if (n1->u.entry.index != n2->u.entry.index)
864 					errx(1, "entries to merge must have "
865 					    "same index '%s'", n1->name);
866 				if (strcmp(n1->u.entry.func,
867 				    n2->u.entry.func) != 0)
868 					errx(1, "entries to merge must have "
869 					    "same op '%s'", n1->name);
870 				merge_subs(&n1->u.entry.subs,
871 				    &n2->u.entry.subs);
872 				free(n2);
873 			} else
874 				errx(1, "entities to merge must be both "
875 				    "trees or both entries: %s, %s",
876 				    n1->name, n2->name);
877 		}
878 	}
879 }
880 
881 static void
882 merge(struct node *root, struct node *t)
883 {
884 
885 	/* both must be trees */
886 	if (root->type != NODE_TREE)
887 		errx(1, "root is not a tree");
888 	if (t->type != NODE_TREE)
889 		errx(1, "can merge only with tree");
890 	if (root->id != t->id)
891 		errx(1, "trees to merge must have same id");
892 
893 	merge_subs(&root->u.tree.subs, &t->u.tree.subs);
894 }
895 
896 int
897 main(int argc, char *argv[])
898 {
899 	int do_extract = 0;
900 	int do_tree = 0;
901 	int opt;
902 	struct node *root;
903 	char fname[MAXPATHLEN + 1];
904 	int tok;
905 
906 	while ((opt = getopt(argc, argv, "help:t")) != EOF)
907 		switch (opt) {
908 
909 		  case 'h':
910 			fprintf(stderr, "%s", usgtxt);
911 			exit(0);
912 
913 		  case 'e':
914 			do_extract = 1;
915 			break;
916 
917 		  case 'l':
918 			localincs = 1;
919 			break;
920 
921 		  case 'p':
922 			file_prefix = optarg;
923 			if (strlen(file_prefix) + strlen("tree.c") >
924 			    MAXPATHLEN)
925 				errx(1, "prefix too long");
926 			break;
927 
928 		  case 't':
929 			do_tree = 1;
930 			break;
931 		}
932 
933 	if (do_extract && do_tree)
934 		errx(1, "conflicting options -e and -t");
935 	if (!do_extract && argc != optind)
936 		errx(1, "no arguments allowed");
937 	if (do_extract && argc == optind)
938 		errx(1, "no objects specified");
939 
940 	root = parse(gettoken());
941 	while ((tok = gettoken()) != TOK_EOF)
942 		merge(root, parse(tok));
943 
944 	check_tree(root);
945 
946 	if (do_extract) {
947 		fp = stdout;
948 		while (optind < argc) {
949 			if (gen_extract(root, argv[optind]))
950 				errx(1, "object not found: %s", argv[optind]);
951 			optind++;
952 		}
953 
954 		return (0);
955 	}
956 	if (do_tree) {
957 		gen_tree(root, 0);
958 		return (0);
959 	}
960 	sprintf(fname, "%stree.h", file_prefix);
961 	if ((fp = fopen(fname, "w")) == NULL)
962 		err(1, "%s: ", fname);
963 	gen_header(root, PREFIX_LEN, NULL);
964 
965 	fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
966 	fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
967 
968 	fclose(fp);
969 
970 	sprintf(fname, "%stree.c", file_prefix);
971 	if ((fp = fopen(fname, "w")) == NULL)
972 		err(1, "%s: ", fname);
973 	gen_table(root);
974 	fclose(fp);
975 
976 	return (0);
977 }
978