gensnmptree.c revision 145557
1129468Sru/*
2129468Sru * Copyright (c) 2001-2003
3147647Shmp *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4246751Sglebius *	All rights reserved.
5137872Srwatson *
6129468Sru * Copyright (c) 2004
7137872Srwatson *	Hartmut Brandt.
8129468Sru *	All rights reserved.
9129468Sru *
10129468Sru * Author: Harti Brandt <harti@freebsd.org>
11129468Sru *
12129468Sru * Redistribution and use in source and binary forms, with or without
13129468Sru * modification, are permitted provided that the following conditions
14129468Sru * are met:
15129468Sru * 1. Redistributions of source code must retain the above copyright
16129468Sru *    notice, this list of conditions and the following disclaimer.
17129468Sru * 2. Redistributions in binary form must reproduce the above copyright
18129468Sru *    notice, this list of conditions and the following disclaimer in the
19129468Sru *    documentation and/or other materials provided with the distribution.
20129468Sru *
21129468Sru * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
22129468Sru * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23175253Smaxim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24129468Sru * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
25129468Sru * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26129468Sru * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27129468Sru * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28129468Sru * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29129468Sru * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30129468Sru * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31129468Sru * SUCH DAMAGE.
32231564Sed *
33129468Sru * $Begemot: bsnmp/gensnmptree/gensnmptree.c,v 1.42 2005/04/26 16:26:19 brandt_h Exp $
34129468Sru *
35129468Sru * Generate OID table from table description.
36129468Sru *
37129468Sru * Syntax is:
38129468Sru * ---------
39129468Sru * file := tree | tree file
40129468Sru *
41129468Sru * tree := head elements ')'
42129468Sru *
43129468Sru * entry := head ':' index STRING elements ')'
44129468Sru *
45129468Sru * leaf := head TYPE STRING ACCESS ')'
46129468Sru *
47129468Sru * column := head TYPE ACCESS ')'
48129468Sru *
49129468Sru * head := '(' INT STRING
50129468Sru *
51129468Sru * elements := EMPTY | elements element
52129468Sru *
53129468Sru * element := tree | leaf | column
54231564Sed *
55129468Sru * index := TYPE | index TYPE
56129468Sru *
57129468Sru */
58129468Sru#include <sys/types.h>
59129468Sru#include <sys/param.h>
60129468Sru#include <stdio.h>
61129468Sru#include <stdlib.h>
62137872Srwatson#include <stdarg.h>
63137872Srwatson#include <unistd.h>
64137872Srwatson#include <string.h>
65137872Srwatson#include <ctype.h>
66137872Srwatson#include <inttypes.h>
67137872Srwatson#include <errno.h>
68137872Srwatson#ifdef HAVE_ERR_H
69137953Srwatson#include <err.h>
70137872Srwatson#endif
71137872Srwatson#include <sys/queue.h>
72175247Smaxim#include "support.h"
73175247Smaxim#include "asn1.h"
74137872Srwatson#include "snmp.h"
75137872Srwatson#include "snmpagent.h"
76137872Srwatson
77137872Srwatson/*
78137872Srwatson * Constant prefix for all OIDs
79137872Srwatson */
80137872Srwatsonstatic const asn_subid_t prefix[] = { 1, 3, 6 };
81137953Srwatson#define	PREFIX_LEN	(sizeof(prefix) / sizeof(prefix[0]))
82137953Srwatson
83137953Srwatsonu_int tree_size;
84137953Srwatsonstatic const char *file_prefix = "";
85137872Srwatsonstatic FILE *fp;
86137872Srwatson
87137872Srwatson/* if true generate local include paths */
88137872Srwatsonstatic int localincs = 0;
89129468Sru
90129468Srustatic const char usgtxt[] = "\
91129468SruGenerate SNMP tables. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
92129468SruOpen Communication Systems (FhG Fokus). All rights reserved.\n\
93129468Sruusage: gensnmptree [-hel] [-p prefix] [name]...\n\
94129468Sruoptions:\n\
95129468Sru  -h		print this info\n\
96129468Sru  -e		extrace the named oids\n\
97129468Sru  -l		generate local include directives\n\
98129468Sru  -p prefix	prepend prefix to file and variable names\n\
99129468Sru";
100129468Sru
101129468Sru/*
102129468Sru * A node in the OID tree
103231564Sed */
104231564Sedenum ntype {
105231564Sed	NODE_LEAF = 1,
106129468Sru	NODE_TREE,
107129468Sru	NODE_ENTRY,
108129468Sru	NODE_COLUMN
109129468Sru};
110129468Sru
111129468Sruenum {
112129468Sru	FL_GET	= 0x01,
113129468Sru	FL_SET	= 0x02,
114129468Sru};
115129468Sru
116129468Srustruct node;
117129468SruTAILQ_HEAD(node_list, node);
118129468Sru
119129468Srustruct node {
120129468Sru	enum ntype	type;
121129468Sru	asn_subid_t	id;	/* last element of OID */
122129468Sru	char		*name;	/* name of node */
123136347Sglebius	TAILQ_ENTRY(node) link;
124140140Sru	u_int		lno;	/* starting line number */
125129468Sru	u_int		flags;	/* allowed operations */
126129468Sru
127129468Sru	union {
128129468Sru	  struct tree {
129129468Sru	    struct node_list subs;
130129468Sru	  }		tree;
131129468Sru
132129468Sru	  struct entry {
133129468Sru	    uint32_t	index;	/* index for table entry */
134129468Sru	    char	*func;	/* function for tables */
135129468Sru	    struct node_list subs;
136129468Sru	  }		entry;
137129468Sru
138129468Sru	  struct leaf {
139129468Sru	    enum snmp_syntax syntax;	/* syntax for this leaf */
140129468Sru	    char	*func;		/* function name */
141129468Sru	  }		leaf;
142129468Sru
143129468Sru	  struct column {
144129468Sru	    enum snmp_syntax syntax;	/* syntax for this column */
145129468Sru	  }		column;
146129468Sru	}		u;
147129468Sru};
148129468Sru
149129468Srustruct func {
150129468Sru	const char	*name;
151129468Sru	LIST_ENTRY(func) link;
152129468Sru};
153129468Sru
154129468Srustatic LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
155129468Sru
156129468Sru/************************************************************
157129468Sru *
158129468Sru * Allocate memory and panic just in the case...
159129468Sru */
160129468Srustatic void *
161129468Sruxalloc(size_t size)
162129468Sru{
163129468Sru	void *ptr;
164129468Sru
165129468Sru	if ((ptr = malloc(size)) == NULL)
166129468Sru		err(1, "allocing %zu bytes", size);
167129468Sru
168129468Sru	return (ptr);
169129468Sru}
170129468Sru
171129468Sru/************************************************************
172129468Sru *
173129468Sru * Parsing input
174129468Sru */
175129468Sruenum tok {
176129468Sru	TOK_EOF = 0200,	/* end-of-file seen */
177129468Sru	TOK_NUM,	/* number */
178129468Sru	TOK_STR,	/* string */
179129468Sru	TOK_ACCESS,	/* access operator */
180129468Sru	TOK_TYPE,	/* type operator */
181129468Sru};
182129468Sru
183129468Srustatic const struct {
184129468Sru	const char *str;
185129468Sru	enum tok tok;
186129468Sru	u_int val;
187129468Sru} keywords[] = {
188129468Sru	{ "GET", TOK_ACCESS, FL_GET },
189129468Sru	{ "SET", TOK_ACCESS, FL_SET },
190129468Sru	{ "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
191129468Sru	{ "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
192129468Sru	{ "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
193129468Sru	{ "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
194129468Sru	{ "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
195129468Sru	{ "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
196129468Sru	{ "OID", TOK_TYPE, SNMP_SYNTAX_OID },
197129468Sru	{ "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
198129468Sru	{ "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
199129468Sru	{ "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
200129468Sru	{ "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
201129468Sru	{ NULL, 0, 0 }
202129468Sru};
203129468Sru
204129468Sru/* arbitrary upper limit on node names and function names */
205129468Sru#define	MAXSTR	1000
206129468Sruchar	str[MAXSTR];
207129468Sruu_long	val;		/* integer values */
208129468Sruu_int 	lno = 1;	/* current line number */
209129468Sruint	all_cond;	/* all conditions are true */
210129468Sru
211129468Srustatic void report(const char *, ...) __dead2 __printflike(1, 2);
212129468Srustatic void report_node(const struct node *, const char *, ...)
213129468Sru    __dead2 __printflike(2, 3);
214129468Sru
215129468Sru/*
216129468Sru * Report an error and exit.
217129468Sru */
218129468Srustatic void
219129468Srureport(const char *fmt, ...)
220129468Sru{
221129468Sru	va_list ap;
222129468Sru	int c;
223129468Sru
224129468Sru	va_start(ap, fmt);
225129468Sru	fprintf(stderr, "line %u: ", lno);
226136347Sglebius	vfprintf(stderr, fmt, ap);
227136347Sglebius	fprintf(stderr, "\n");
228136347Sglebius	fprintf(stderr, "context: \"");
229140140Sru	while ((c = getchar()) != EOF && c != '\n')
230140140Sru		fprintf(stderr, "%c", c);
231140140Sru	fprintf(stderr, "\n");
232136347Sglebius	va_end(ap);
233129468Sru	exit(1);
234129468Sru}
235129468Srustatic void
236129468Srureport_node(const struct node *np, const char *fmt, ...)
237129468Sru{
238129468Sru	va_list ap;
239129468Sru
240129468Sru	va_start(ap, fmt);
241129468Sru	fprintf(stderr, "line %u, node %s: ", np->lno, np->name);
242129468Sru	vfprintf(stderr, fmt, ap);
243129468Sru	fprintf(stderr, "\n");
244129468Sru	va_end(ap);
245129468Sru	exit(1);
246129468Sru}
247129468Sru
248129468Sru/*
249129468Sru * Return a fresh copy of the string constituting the current token.
250129468Sru */
251129468Srustatic char *
252129468Srusavetok(void)
253129468Sru{
254129468Sru	return (strcpy(xalloc(strlen(str)+1), str));
255129468Sru}
256129468Sru
257129468Sru/*
258129468Sru * Get the next token from input.
259129468Sru */
260129468Srustatic int
261129468Srugettoken(void)
262129468Sru{
263129468Sru	int c;
264129468Sru
265129468Sru  again:
266129468Sru	/*
267129468Sru	 * Skip any whitespace before the next token
268129468Sru	 */
269129468Sru	while ((c = getchar()) != EOF) {
270129468Sru		if (c == '\n')
271129468Sru			lno++;
272129468Sru		if (!isspace(c))
273129468Sru			break;
274129468Sru	}
275129468Sru	if (c == EOF)
276129468Sru		return (TOK_EOF);
277129468Sru	if (!isascii(c))
278129468Sru		report("unexpected character %#2x", (u_int)c);
279129468Sru
280129468Sru	/*
281129468Sru	 * Skip comments
282129468Sru	 */
283129468Sru	if (c == '#') {
284129468Sru		while ((c = getchar()) != EOF) {
285129468Sru			if (c == '\n') {
286129468Sru				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	fprintf(fp, "#include <stdint.h>\n");
599	if (localincs) {
600		fprintf(fp, "#include \"asn1.h\"\n");
601		fprintf(fp, "#include \"snmp.h\"\n");
602		fprintf(fp, "#include \"snmpagent.h\"\n");
603	} else {
604		fprintf(fp, "#include <bsnmp/asn1.h>\n");
605		fprintf(fp, "#include <bsnmp/snmp.h>\n");
606		fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
607	}
608	fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
609	fprintf(fp, "\n");
610
611	fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
612
613	oid.len = PREFIX_LEN;
614	memcpy(oid.subs, prefix, sizeof(prefix));
615	gen_node(node, &oid, 0, NULL);
616
617	fprintf(fp, "};\n\n");
618}
619
620static void
621print_syntax(u_int syntax)
622{
623	u_int i;
624
625	for (i = 0; keywords[i].str != NULL; i++)
626		if (keywords[i].tok == TOK_TYPE &&
627		    keywords[i].val == syntax) {
628			printf(" %s", keywords[i].str);
629			return;
630	}
631	abort();
632}
633
634/*
635 * Generate a tree definition file
636 */
637static void
638gen_tree(const struct node *np, int level)
639{
640	const struct node *sp;
641	u_int i;
642
643	printf("%*s(%u %s", 2 * level, "", np->id, np->name);
644
645	switch (np->type) {
646
647	  case NODE_LEAF:
648		print_syntax(np->u.leaf.syntax);
649		printf(" %s%s%s)\n", np->u.leaf.func,
650		    (np->flags & FL_GET) ? " GET" : "",
651		    (np->flags & FL_SET) ? " SET" : "");
652		break;
653
654	  case NODE_TREE:
655		if (TAILQ_EMPTY(&np->u.tree.subs)) {
656			printf(")\n");
657		} else {
658			printf("\n");
659			TAILQ_FOREACH(sp, &np->u.tree.subs, link)
660				gen_tree(sp, level + 1);
661			printf("%*s)\n", 2 * level, "");
662		}
663		break;
664
665	  case NODE_ENTRY:
666		printf(" :");
667
668		for (i = 0; i < SNMP_INDEX_COUNT(np->u.entry.index); i++)
669			print_syntax(SNMP_INDEX(np->u.entry.index, i));
670		printf(" %s\n", np->u.entry.func);
671		TAILQ_FOREACH(sp, &np->u.entry.subs, link)
672			gen_tree(sp, level + 1);
673		printf("%*s)\n", 2 * level, "");
674		break;
675
676	  case NODE_COLUMN:
677		print_syntax(np->u.column.syntax);
678		printf("%s%s)\n", (np->flags & FL_GET) ? " GET" : "",
679		    (np->flags & FL_SET) ? " SET" : "");
680		break;
681
682	}
683}
684
685static int
686extract(const struct node *np, struct asn_oid *oid, const char *obj,
687    const struct asn_oid *idx, const char *iname)
688{
689	struct node *sub;
690	u_long n;
691
692	if (oid->len == ASN_MAXOIDLEN)
693		report_node(np, "OID too long");
694	oid->subs[oid->len++] = np->id;
695
696	if (strcmp(obj, np->name) == 0) {
697		if (oid->len + idx->len >= ASN_MAXOIDLEN)
698			report_node(np, "OID too long");
699		fprintf(fp, "#define OID_%s%s\t%u\n", np->name,
700		    iname ? iname : "", np->id);
701		fprintf(fp, "#define OIDLEN_%s%s\t%u\n", np->name,
702		    iname ? iname : "", oid->len + idx->len);
703		fprintf(fp, "#define OIDX_%s%s\t{ %u, {", np->name,
704		    iname ? iname : "", oid->len + idx->len);
705		for (n = 0; n < oid->len; n++)
706			fprintf(fp, " %u,", oid->subs[n]);
707		for (n = 0; n < idx->len; n++)
708			fprintf(fp, " %u,", idx->subs[n]);
709		fprintf(fp, " } }\n");
710		return (0);
711	}
712
713	if (np->type == NODE_TREE) {
714		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
715			if (!extract(sub, oid, obj, idx, iname))
716				return (0);
717	} else if (np->type == NODE_ENTRY) {
718		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
719			if (!extract(sub, oid, obj, idx, iname))
720				return (0);
721	}
722	oid->len--;
723	return (1);
724}
725
726static int
727gen_extract(const struct node *root, char *object)
728{
729	struct asn_oid oid;
730	struct asn_oid idx;
731	char *s, *e, *end, *iname;
732	u_long ul;
733	int ret;
734
735	/* look whether the object to extract has an index part */
736	idx.len = 0;
737	iname = NULL;
738	s = strchr(object, '.');
739	if (s != NULL) {
740		iname = malloc(strlen(s) + 1);
741		if (iname == NULL)
742			err(1, "cannot allocated index");
743
744		strcpy(iname, s);
745		for (e = iname; *e != '\0'; e++)
746			if (*e == '.')
747				*e = '_';
748
749		*s++ = '\0';
750		while (s != NULL) {
751			if (*s == '\0')
752				errx(1, "bad index syntax");
753			if ((e = strchr(s, '.')) != NULL)
754				*e++ = '\0';
755
756			errno = 0;
757			ul = strtoul(s, &end, 0);
758			if (*end != '\0')
759				errx(1, "bad index syntax '%s'", end);
760			if (errno != 0)
761				err(1, "bad index syntax");
762
763			if (idx.len == ASN_MAXOIDLEN)
764				errx(1, "index oid too large");
765			idx.subs[idx.len++] = ul;
766
767			s = e;
768		}
769	}
770
771	oid.len = PREFIX_LEN;
772	memcpy(oid.subs, prefix, sizeof(prefix));
773	ret = extract(root, &oid, object, &idx, iname);
774	if (iname != NULL)
775		free(iname);
776
777	return (ret);
778}
779
780
781static void
782check_sub_order(const struct node *np, const struct node_list *subs)
783{
784	int first;
785	const struct node *sub;
786	asn_subid_t maxid = 0;
787
788	/* ensure, that subids are ordered */
789	first = 1;
790	TAILQ_FOREACH(sub, subs, link) {
791		if (!first && sub->id <= maxid)
792			report_node(np, "subids not ordered at %s", sub->name);
793		maxid = sub->id;
794		first = 0;
795	}
796}
797
798/*
799 * Do some sanity checks on the tree definition and do some computations.
800 */
801static void
802check_tree(struct node *np)
803{
804	struct node *sub;
805
806	if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
807		if ((np->flags & (FL_GET|FL_SET)) != 0)
808			tree_size++;
809		return;
810	}
811
812	if (np->type == NODE_ENTRY) {
813		check_sub_order(np, &np->u.entry.subs);
814
815		/* ensure all subnodes are columns */
816		TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
817			if (sub->type != NODE_COLUMN)
818				report_node(np, "entry subnode '%s' is not "
819				    "a column", sub->name);
820			check_tree(sub);
821		}
822	} else {
823		check_sub_order(np, &np->u.tree.subs);
824
825		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
826			check_tree(sub);
827	}
828}
829
830static void
831merge_subs(struct node_list *s1, struct node_list *s2)
832{
833	struct node *n1, *n2;
834
835	while (!TAILQ_EMPTY(s2)) {
836		n2 = TAILQ_FIRST(s2);
837		TAILQ_REMOVE(s2, n2, link);
838
839		TAILQ_FOREACH(n1, s1, link)
840			if (n1->id >= n2->id)
841				break;
842		if (n1 == NULL)
843			TAILQ_INSERT_TAIL(s1, n2, link);
844		else if (n1->id > n2->id)
845			TAILQ_INSERT_BEFORE(n1, n2, link);
846		else {
847			if (n1->type == NODE_TREE && n2->type == NODE_TREE) {
848				if (strcmp(n1->name, n2->name) != 0)
849					errx(1, "trees to merge must have "
850					    "same name '%s' '%s'", n1->name,
851					    n2->name);
852				merge_subs(&n1->u.tree.subs, &n2->u.tree.subs);
853				free(n2);
854			} else if (n1->type == NODE_ENTRY &&
855			    n2->type == NODE_ENTRY) {
856				if (strcmp(n1->name, n2->name) != 0)
857					errx(1, "entries to merge must have "
858					    "same name '%s' '%s'", n1->name,
859					    n2->name);
860				if (n1->u.entry.index != n2->u.entry.index)
861					errx(1, "entries to merge must have "
862					    "same index '%s'", n1->name);
863				if (strcmp(n1->u.entry.func,
864				    n2->u.entry.func) != 0)
865					errx(1, "entries to merge must have "
866					    "same op '%s'", n1->name);
867				merge_subs(&n1->u.entry.subs,
868				    &n2->u.entry.subs);
869				free(n2);
870			} else
871				errx(1, "entities to merge must be both "
872				    "trees or both entries: %s, %s",
873				    n1->name, n2->name);
874		}
875	}
876}
877
878static void
879merge(struct node *root, struct node *t)
880{
881
882	/* both must be trees */
883	if (root->type != NODE_TREE)
884		errx(1, "root is not a tree");
885	if (t->type != NODE_TREE)
886		errx(1, "can merge only with tree");
887	if (root->id != t->id)
888		errx(1, "trees to merge must have same id");
889
890	merge_subs(&root->u.tree.subs, &t->u.tree.subs);
891}
892
893int
894main(int argc, char *argv[])
895{
896	int do_extract = 0;
897	int do_tree = 0;
898	int opt;
899	struct node *root;
900	char fname[MAXPATHLEN + 1];
901	int tok;
902
903	while ((opt = getopt(argc, argv, "help:t")) != EOF)
904		switch (opt) {
905
906		  case 'h':
907			fprintf(stderr, "%s", usgtxt);
908			exit(0);
909
910		  case 'e':
911			do_extract = 1;
912			break;
913
914		  case 'l':
915			localincs = 1;
916			break;
917
918		  case 'p':
919			file_prefix = optarg;
920			if (strlen(file_prefix) + strlen("tree.c") >
921			    MAXPATHLEN)
922				errx(1, "prefix too long");
923			break;
924
925		  case 't':
926			do_tree = 1;
927			break;
928		}
929
930	if (do_extract && do_tree)
931		errx(1, "conflicting options -e and -t");
932	if (!do_extract && argc != optind)
933		errx(1, "no arguments allowed");
934	if (do_extract && argc == optind)
935		errx(1, "no objects specified");
936
937	root = parse(gettoken());
938	while ((tok = gettoken()) != TOK_EOF)
939		merge(root, parse(tok));
940
941	check_tree(root);
942
943	if (do_extract) {
944		fp = stdout;
945		while (optind < argc) {
946			if (gen_extract(root, argv[optind]))
947				errx(1, "object not found: %s", argv[optind]);
948			optind++;
949		}
950
951		return (0);
952	}
953	if (do_tree) {
954		gen_tree(root, 0);
955		return (0);
956	}
957	sprintf(fname, "%stree.h", file_prefix);
958	if ((fp = fopen(fname, "w")) == NULL)
959		err(1, "%s: ", fname);
960	gen_header(root, PREFIX_LEN, NULL);
961
962	fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
963	fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
964
965	fclose(fp);
966
967	sprintf(fname, "%stree.c", file_prefix);
968	if ((fp = fopen(fname, "w")) == NULL)
969		err(1, "%s: ", fname);
970	gen_table(root);
971	fclose(fp);
972
973	return (0);
974}
975