xref: /freebsd/contrib/bsnmp/gensnmptree/gensnmptree.c (revision d37ea99837e6ad50837fd9fe1771ddf1c3ba6002)
1 /*
2  * Copyright (c) 2001-2003
3  *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4  *	All rights reserved.
5  *
6  * Author: Harti Brandt <harti@freebsd.org>
7  *
8  * Redistribution of this software and documentation and use in source and
9  * binary forms, with or without modification, are permitted provided that
10  * the following conditions are met:
11  *
12  * 1. Redistributions of source code or documentation must retain the above
13  *    copyright notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
22  * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
25  * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS  BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
28  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
31  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  *
33  * $Begemot: bsnmp/gensnmptree/gensnmptree.c,v 1.37 2004/04/13 15:18:15 novo Exp $
34  *
35  * Generate OID table from table description.
36  *
37  * Syntax is:
38  * ---------
39  * tree := head elements ')'
40  *
41  * entry := head ':' index STRING elements ')'
42  *
43  * leaf := head TYPE STRING ACCESS ')'
44  *
45  * column := head TYPE ACCESS ')'
46  *
47  * head := '(' INT STRING
48  *
49  * elements := EMPTY | elements element
50  *
51  * element := tree | leaf
52  *
53  * index := TYPE | index TYPE
54  *
55  */
56 #include <sys/types.h>
57 #include <sys/param.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <stdarg.h>
61 #include <unistd.h>
62 #include <string.h>
63 #include <ctype.h>
64 #include <err.h>
65 #include <sys/queue.h>
66 #include "asn1.h"
67 #include "snmp.h"
68 #include "snmpagent.h"
69 
70 /*
71  * Constant prefix for all OIDs
72  */
73 static const asn_subid_t prefix[] = { 1, 3, 6 };
74 #define	PREFIX_LEN	(sizeof(prefix) / sizeof(prefix[0]))
75 
76 u_int tree_size;
77 static const char *file_prefix = "";
78 static FILE *fp;
79 
80 /* if true generate local include paths */
81 static int localincs = 0;
82 
83 static const char usgtxt[] = "\
84 Generate SNMP tables. Copyright (c) 2001-2002 Fraunhofer Institute for\n\
85 Open Communication Systems (FhG Fokus). All rights reserved.\n\
86 usage: gensnmptree [-hel] [-p prefix] [name]...\n\
87 options:\n\
88   -h		print this info\n\
89   -e		extrace the named oids\n\
90   -l		generate local include directives\n\
91   -p prefix	prepend prefix to file and variable names\n\
92 ";
93 
94 /*
95  * A node in the OID tree
96  */
97 enum ntype {
98 	NODE_LEAF = 1,
99 	NODE_TREE,
100 	NODE_ENTRY,
101 	NODE_COLUMN
102 };
103 
104 enum {
105 	FL_GET	= 0x01,
106 	FL_SET	= 0x02,
107 };
108 
109 struct node;
110 TAILQ_HEAD(node_list, node);
111 
112 struct node {
113 	enum ntype	type;
114 	asn_subid_t	id;	/* last element of OID */
115 	char		*name;	/* name of node */
116 	TAILQ_ENTRY(node) link;
117 	u_int		lno;	/* starting line number */
118 	u_int		flags;	/* allowed operations */
119 
120 	union {
121 	  struct tree {
122 	    struct node_list subs;
123 	  }		tree;
124 
125 	  struct entry {
126 	    u_int32_t	index;	/* index for table entry */
127 	    char	*func;	/* function for tables */
128 	    struct node_list subs;
129 	  }		entry;
130 
131 	  struct leaf {
132 	    enum snmp_syntax syntax;	/* syntax for this leaf */
133 	    char	*func;		/* function name */
134 	  }		leaf;
135 
136 	  struct column {
137 	    enum snmp_syntax syntax;	/* syntax for this column */
138 	  }		column;
139 	}		u;
140 };
141 
142 struct func {
143 	const char	*name;
144 	LIST_ENTRY(func) link;
145 };
146 
147 static LIST_HEAD(, func) funcs = LIST_HEAD_INITIALIZER(funcs);
148 
149 /************************************************************
150  *
151  * Allocate memory and panic just in the case...
152  */
153 static void *
154 xalloc(size_t size)
155 {
156 	void *ptr;
157 
158 	if ((ptr = malloc(size)) == NULL)
159 		err(1, "allocing %u bytes", size);
160 
161 	return (ptr);
162 }
163 
164 /************************************************************
165  *
166  * Parsing input
167  */
168 enum tok {
169 	TOK_EOF = 0200,	/* end-of-file seen */
170 	TOK_NUM,	/* number */
171 	TOK_STR,	/* string */
172 	TOK_ACCESS,	/* access operator */
173 	TOK_TYPE,	/* type operator */
174 };
175 
176 static const struct {
177 	const char *str;
178 	enum tok tok;
179 	u_int val;
180 } keywords[] = {
181 	{ "GET", TOK_ACCESS, FL_GET },
182 	{ "SET", TOK_ACCESS, FL_SET },
183 	{ "NULL", TOK_TYPE, SNMP_SYNTAX_NULL },
184 	{ "INTEGER", TOK_TYPE, SNMP_SYNTAX_INTEGER },
185 	{ "INTEGER32", TOK_TYPE, SNMP_SYNTAX_INTEGER },
186 	{ "UNSIGNED32", TOK_TYPE, SNMP_SYNTAX_GAUGE },
187 	{ "OCTETSTRING", TOK_TYPE, SNMP_SYNTAX_OCTETSTRING },
188 	{ "IPADDRESS", TOK_TYPE, SNMP_SYNTAX_IPADDRESS },
189 	{ "OID", TOK_TYPE, SNMP_SYNTAX_OID },
190 	{ "TIMETICKS", TOK_TYPE, SNMP_SYNTAX_TIMETICKS },
191 	{ "COUNTER", TOK_TYPE, SNMP_SYNTAX_COUNTER },
192 	{ "GAUGE", TOK_TYPE, SNMP_SYNTAX_GAUGE },
193 	{ "COUNTER64", TOK_TYPE, SNMP_SYNTAX_COUNTER64 },
194 	{ NULL, 0, 0 }
195 };
196 
197 /* arbitrary upper limit on node names and function names */
198 #define	MAXSTR	1000
199 char	str[MAXSTR];
200 u_long	val;		/* integer values */
201 u_int 	lno = 1;	/* current line number */
202 
203 static void report(const char *, ...) __dead2 __printflike(1, 2);
204 static void report_node(const struct node *, const char *, ...)
205     __dead2 __printflike(2, 3);
206 
207 /*
208  * Report an error and exit.
209  */
210 static void
211 report(const char *fmt, ...)
212 {
213 	va_list ap;
214 	int c;
215 
216 	va_start(ap, fmt);
217 	fprintf(stderr, "line %u: ", lno);
218 	vfprintf(stderr, fmt, ap);
219 	fprintf(stderr, "\n");
220 	fprintf(stderr, "context: \"");
221 	while ((c = getchar()) != EOF && c != '\n')
222 		fprintf(stderr, "%c", c);
223 	fprintf(stderr, "\n");
224 	va_end(ap);
225 	exit(1);
226 }
227 static void
228 report_node(const struct node *np, const char *fmt, ...)
229 {
230 	va_list ap;
231 
232 	va_start(ap, fmt);
233 	fprintf(stderr, "line %u, node %s: ", np->lno, np->name);
234 	vfprintf(stderr, fmt, ap);
235 	fprintf(stderr, "\n");
236 	va_end(ap);
237 	exit(1);
238 }
239 
240 /*
241  * Return a fresh copy of the string constituting the current token.
242  */
243 static char *
244 savetok(void)
245 {
246 	return (strcpy(xalloc(strlen(str)+1), str));
247 }
248 
249 /*
250  * Get the next token from input.
251  */
252 static int
253 gettoken(void)
254 {
255 	int c;
256 
257   again:
258 	/*
259 	 * Skip any whitespace before the next token
260 	 */
261 	while ((c = getchar()) != EOF) {
262 		if (c == '\n')
263 			lno++;
264 		if (!isspace(c))
265 			break;
266 	}
267 	if (c == EOF)
268 		return (TOK_EOF);
269 	if (!isascii(c))
270 		report("unexpected character %#2x", (u_int)c);
271 
272 	/*
273 	 * Skip comments
274 	 */
275 	if (c == '#') {
276 		while ((c = getchar()) != EOF) {
277 			if (c == '\n') {
278 				lno++;
279 				goto again;
280 			}
281 		}
282 		report("unexpected EOF in comment");
283 	}
284 
285 	/*
286 	 * Single character tokens
287 	 */
288 	if (c == ')' || c == '(' || c == ':')
289 		return (c);
290 
291 	/*
292 	 * Sort out numbers
293 	 */
294 	if (isdigit(c)) {
295 		ungetc(c, stdin);
296 		scanf("%lu", &val);
297 		return (TOK_NUM);
298 	}
299 
300 	/*
301 	 * So that has to be a string.
302 	 */
303 	if (isalpha(c) || c == '_') {
304 		size_t n = 0;
305 		str[n++] = c;
306 		while ((c = getchar()) != EOF) {
307 			if (!isalnum(c) && c != '_') {
308 				ungetc(c, stdin);
309 				break;
310 			}
311 			if (n == sizeof(str) - 1) {
312 				str[n++] = '\0';
313 				report("string too long '%s...'", str);
314 			}
315 			str[n++] = c;
316 		}
317 		str[n++] = '\0';
318 
319 		/*
320 		 * Keywords
321 		 */
322 		for (c = 0; keywords[c].str != NULL; c++)
323 			if (strcmp(keywords[c].str, str) == 0) {
324 				val = keywords[c].val;
325 				return (keywords[c].tok);
326 			}
327 
328 		return (TOK_STR);
329 	}
330 	if (isprint(c))
331 		errx(1, "%u: unexpected character '%c'", lno, c);
332 	else
333 		errx(1, "%u: unexpected character 0x%02x", lno, (u_int)c);
334 }
335 
336 /*
337  * Parse the next node (complete with all subnodes)
338  */
339 static struct node *
340 parse(enum tok tok)
341 {
342 	struct node *node;
343 	struct node *sub;
344 	u_int index_count;
345 
346 	node = xalloc(sizeof(struct node));
347 	node->lno = lno;
348 
349 	if (tok != '(')
350 		report("'(' expected at begin of node");
351 	if (gettoken() != TOK_NUM)
352 		report("node id expected after opening '('");
353 	if (val > ASN_MAXID)
354 		report("subid too large '%lu'", val);
355 	node->id = (asn_subid_t)val;
356 	if (gettoken() != TOK_STR)
357 		report("node name expected after '(' ID");
358 	node->name = savetok();
359 
360 	if ((tok = gettoken()) == TOK_TYPE) {
361 		/* LEAF or COLUM */
362 		u_int syntax = val;
363 
364 		if ((tok = gettoken()) == TOK_STR) {
365 			/* LEAF */
366 			node->type = NODE_LEAF;
367 			node->u.leaf.func = savetok();
368 			node->u.leaf.syntax = syntax;
369 			tok = gettoken();
370 		} else {
371 			/* COLUMN */
372 			node->type = NODE_COLUMN;
373 			node->u.column.syntax = syntax;
374 		}
375 
376 		while (tok != ')') {
377 			if (tok != TOK_ACCESS)
378 				report("access keyword or ')' expected");
379 			node->flags |= (u_int)val;
380 			tok = gettoken();
381 		}
382 
383 	} else if (tok == ':') {
384 		/* ENTRY */
385 		node->type = NODE_ENTRY;
386 		TAILQ_INIT(&node->u.entry.subs);
387 
388 		index_count = 0;
389 		node->u.entry.index = 0;
390 		while ((tok = gettoken()) == TOK_TYPE) {
391 			if (index_count++ == SNMP_INDEXES_MAX)
392 				report("too many table indexes");
393 			node->u.entry.index |=
394 			    val << (SNMP_INDEX_SHIFT * index_count);
395 		}
396 		node->u.entry.index |= index_count;
397 		if (index_count == 0)
398 			report("need at least one index");
399 
400 		if (tok != TOK_STR)
401 			report("function name expected");
402 
403 		node->u.entry.func = savetok();
404 
405 		tok = gettoken();
406 
407 		while (tok != ')') {
408 			sub = parse(tok);
409 			TAILQ_INSERT_TAIL(&node->u.entry.subs, sub, link);
410 			tok = gettoken();
411 		}
412 
413 	} else {
414 		/* subtree */
415 		node->type = NODE_TREE;
416 		TAILQ_INIT(&node->u.tree.subs);
417 
418 		while (tok != ')') {
419 			sub = parse(tok);
420 			TAILQ_INSERT_TAIL(&node->u.tree.subs, sub, link);
421 			tok = gettoken();
422 		}
423 	}
424 	return (node);
425 }
426 
427 /*
428  * Generate the C-code table part for one node.
429  */
430 static void
431 gen_node(struct node *np, struct asn_oid *oid, u_int idx, const char *func)
432 {
433 	u_int n;
434 	struct node *sub;
435 	u_int syntax;
436 
437 	if (oid->len == ASN_MAXOIDLEN)
438 		report_node(np, "OID too long");
439 	oid->subs[oid->len++] = np->id;
440 
441 	if (np->type == NODE_TREE) {
442 		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
443 			gen_node(sub, oid, 0, NULL);
444 		oid->len--;
445 		return;
446 	}
447 	if (np->type == NODE_ENTRY) {
448 		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
449 			gen_node(sub, oid, np->u.entry.index, np->u.entry.func);
450 		oid->len--;
451 		return;
452 	}
453 
454 	/* leaf or column */
455 	if ((np->flags & (FL_GET|FL_SET)) == 0) {
456 		oid->len--;
457 		return;
458 	}
459 
460 	fprintf(fp, "    {{ %u, {", oid->len);
461 	for (n = 0; n < oid->len; n++)
462 		fprintf(fp, " %u,", oid->subs[n]);
463 	fprintf(fp, " }}, \"%s\", ", np->name);
464 
465 	if (np->type == NODE_COLUMN) {
466 		syntax = np->u.column.syntax;
467 		fprintf(fp, "SNMP_NODE_COLUMN, ");
468 	} else {
469 		syntax = np->u.leaf.syntax;
470 		fprintf(fp, "SNMP_NODE_LEAF, ");
471 	}
472 
473 	switch (syntax) {
474 
475 	  case SNMP_SYNTAX_NULL:
476 		fprintf(fp, "SNMP_SYNTAX_NULL, ");
477 		break;
478 
479 	  case SNMP_SYNTAX_INTEGER:
480 		fprintf(fp, "SNMP_SYNTAX_INTEGER, ");
481 		break;
482 
483 	  case SNMP_SYNTAX_OCTETSTRING:
484 		fprintf(fp, "SNMP_SYNTAX_OCTETSTRING, ");
485 		break;
486 
487 	  case SNMP_SYNTAX_IPADDRESS:
488 		fprintf(fp, "SNMP_SYNTAX_IPADDRESS, ");
489 		break;
490 
491 	  case SNMP_SYNTAX_OID:
492 		fprintf(fp, "SNMP_SYNTAX_OID, ");
493 		break;
494 
495 	  case SNMP_SYNTAX_TIMETICKS:
496 		fprintf(fp, "SNMP_SYNTAX_TIMETICKS, ");
497 		break;
498 
499 	  case SNMP_SYNTAX_COUNTER:
500 		fprintf(fp, "SNMP_SYNTAX_COUNTER, ");
501 		break;
502 
503 	  case SNMP_SYNTAX_GAUGE:
504 		fprintf(fp, "SNMP_SYNTAX_GAUGE, ");
505 		break;
506 
507 	  case SNMP_SYNTAX_COUNTER64:
508 		fprintf(fp, "SNMP_SYNTAX_COUNTER64, ");
509 		break;
510 
511 	  case SNMP_SYNTAX_NOSUCHOBJECT:
512 	  case SNMP_SYNTAX_NOSUCHINSTANCE:
513 	  case SNMP_SYNTAX_ENDOFMIBVIEW:
514 		abort();
515 	}
516 
517 	if (np->type == NODE_COLUMN)
518 		fprintf(fp, "%s, ", func);
519 	else
520 		fprintf(fp, "%s, ", np->u.leaf.func);
521 
522 	fprintf(fp, "0");
523 	if (np->flags & FL_SET)
524 		fprintf(fp, "|SNMP_NODE_CANSET");
525 	fprintf(fp, ", %#x, NULL, NULL },\n", idx);
526 	oid->len--;
527 	return;
528 }
529 
530 /*
531  * Generate the header file with the function declarations.
532  */
533 static void
534 gen_header(struct node *np, u_int oidlen, const char *func)
535 {
536 	char f[MAXSTR + 4];
537 	struct node *sub;
538 	struct func *ptr;
539 
540 	oidlen++;
541 	if (np->type == NODE_TREE) {
542 		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
543 			gen_header(sub, oidlen, NULL);
544 		return;
545 	}
546 	if (np->type == NODE_ENTRY) {
547 		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
548 			gen_header(sub, oidlen, np->u.entry.func);
549 		return;
550 	}
551 
552  	if((np->flags & (FL_GET|FL_SET)) == 0)
553 		return;
554 
555 	if (np->type == NODE_COLUMN)
556 		sprintf(f, "%s", func);
557 	else
558 		sprintf(f, "%s", np->u.leaf.func);
559 
560 	LIST_FOREACH(ptr, &funcs, link)
561 		if (strcmp(ptr->name, f) == 0)
562 			break;
563 
564 	if (ptr == NULL) {
565 		ptr = xalloc(sizeof(*ptr));
566 		ptr->name = strcpy(xalloc(strlen(f)+1), f);
567 		LIST_INSERT_HEAD(&funcs, ptr, link);
568 
569 		fprintf(fp, "int	%s(struct snmp_context *, "
570 		    "struct snmp_value *, u_int, u_int, "
571 		    "enum snmp_op);\n", f);
572 	}
573 
574 	fprintf(fp, "# define LEAF_%s %u\n", np->name, np->id);
575 }
576 
577 /*
578  * Generate the OID table.
579  */
580 static void
581 gen_table(struct node *node)
582 {
583 	struct asn_oid oid;
584 
585 	fprintf(fp, "#include <sys/types.h>\n");
586 	fprintf(fp, "#include <stdio.h>\n");
587 	if (localincs) {
588 		fprintf(fp, "#include \"asn1.h\"\n");
589 		fprintf(fp, "#include \"snmp.h\"\n");
590 		fprintf(fp, "#include \"snmpagent.h\"\n");
591 	} else {
592 		fprintf(fp, "#include <bsnmp/asn1.h>\n");
593 		fprintf(fp, "#include <bsnmp/snmp.h>\n");
594 		fprintf(fp, "#include <bsnmp/snmpagent.h>\n");
595 	}
596 	fprintf(fp, "#include \"%stree.h\"\n", file_prefix);
597 	fprintf(fp, "\n");
598 
599 	fprintf(fp, "const struct snmp_node %sctree[] = {\n", file_prefix);
600 
601 	oid.len = PREFIX_LEN;
602 	memcpy(oid.subs, prefix, sizeof(prefix));
603 	gen_node(node, &oid, 0, NULL);
604 
605 	fprintf(fp, "};\n\n");
606 }
607 
608 static int
609 extract(const struct node *np, struct asn_oid *oid, const char *obj)
610 {
611 	struct node *sub;
612 	u_long n;
613 
614 	if (oid->len == ASN_MAXOIDLEN)
615 		report_node(np, "OID too long");
616 	oid->subs[oid->len++] = np->id;
617 
618 	if (strcmp(obj, np->name) == 0) {
619 		fprintf(fp, "#define OID_%s\t%u\n", np->name, np->id);
620 		fprintf(fp, "#define OIDLEN_%s\t%u\n", np->name, oid->len);
621 		fprintf(fp, "#define OIDX_%s\t{ %u, {", np->name, oid->len);
622 		for (n = 0; n < oid->len; n++)
623 			fprintf(fp, " %u,", oid->subs[n]);
624 		fprintf(fp, " } }\n");
625 		return (0);
626 	}
627 
628 	if (np->type == NODE_TREE) {
629 		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
630 			if (!extract(sub, oid, obj))
631 				return (0);
632 	} else if (np->type == NODE_ENTRY) {
633 		TAILQ_FOREACH(sub, &np->u.entry.subs, link)
634 			if (!extract(sub, oid, obj))
635 				return (0);
636 	}
637 	oid->len--;
638 	return (1);
639 }
640 
641 static int
642 gen_extract(const struct node *root, const char *object)
643 {
644 	struct asn_oid oid;
645 
646 	oid.len = PREFIX_LEN;
647 	memcpy(oid.subs, prefix, sizeof(prefix));
648 	return (extract(root, &oid, object));
649 }
650 
651 
652 static void
653 check_sub_order(const struct node *np, const struct node_list *subs)
654 {
655 	int first;
656 	const struct node *sub;
657 	asn_subid_t maxid = 0;
658 
659 	/* ensure, that subids are ordered */
660 	first = 1;
661 	TAILQ_FOREACH(sub, subs, link) {
662 		if (!first && sub->id <= maxid)
663 			report_node(np, "subids not ordered at %s", sub->name);
664 		maxid = sub->id;
665 		first = 0;
666 	}
667 }
668 
669 /*
670  * Do some sanity checks on the tree definition and do some computations.
671  */
672 static void
673 check_tree(struct node *np)
674 {
675 	struct node *sub;
676 
677 	if (np->type == NODE_LEAF || np->type == NODE_COLUMN) {
678 		if ((np->flags & (FL_GET|FL_SET)) != 0)
679 			tree_size++;
680 		return;
681 	}
682 
683 	if (np->type == NODE_ENTRY) {
684 		check_sub_order(np, &np->u.entry.subs);
685 
686 		/* ensure all subnodes are columns */
687 		TAILQ_FOREACH(sub, &np->u.entry.subs, link) {
688 			if (sub->type != NODE_COLUMN)
689 				report_node(np, "entry subnode '%s' is not "
690 				    "a column", sub->name);
691 			check_tree(sub);
692 		}
693 	} else {
694 		check_sub_order(np, &np->u.tree.subs);
695 
696 		TAILQ_FOREACH(sub, &np->u.tree.subs, link)
697 			check_tree(sub);
698 	}
699 }
700 
701 int
702 main(int argc, char *argv[])
703 {
704 	int do_extract = 0;
705 	int opt;
706 	struct node *root;
707 	char fname[MAXPATHLEN + 1];
708 
709 	while ((opt = getopt(argc, argv, "help:")) != EOF)
710 		switch (opt) {
711 
712 		  case 'h':
713 			fprintf(stderr, "%s", usgtxt);
714 			exit(0);
715 
716 		  case 'e':
717 			do_extract = 1;
718 			break;
719 
720 		  case 'l':
721 			localincs = 1;
722 			break;
723 
724 		  case 'p':
725 			file_prefix = optarg;
726 			if (strlen(file_prefix) + strlen("tree.c") >
727 			    MAXPATHLEN)
728 				errx(1, "prefix too long");
729 			break;
730 		}
731 
732 	if (!do_extract && argc != optind)
733 		errx(1, "no arguments allowed");
734 	if (do_extract && argc == optind)
735 		errx(1, "no objects specified");
736 
737 	root = parse(gettoken());
738 	if (gettoken() != TOK_EOF)
739 		report("junk after closing ')'");
740 
741 	check_tree(root);
742 
743 	if (do_extract) {
744 		fp = stdout;
745 		while (optind < argc) {
746 			if (gen_extract(root, argv[optind]))
747 				errx(1, "object not found: %s", argv[optind]);
748 			optind++;
749 		}
750 
751 		return (0);
752 	}
753 	sprintf(fname, "%stree.h", file_prefix);
754 	if ((fp = fopen(fname, "w")) == NULL)
755 		err(1, "%s: ", fname);
756 	gen_header(root, PREFIX_LEN, NULL);
757 
758 	fprintf(fp, "#define %sCTREE_SIZE %u\n", file_prefix, tree_size);
759 	fprintf(fp, "extern const struct snmp_node %sctree[];\n", file_prefix);
760 
761 	fclose(fp);
762 
763 	sprintf(fname, "%stree.c", file_prefix);
764 	if ((fp = fopen(fname, "w")) == NULL)
765 		err(1, "%s: ", fname);
766 	gen_table(root);
767 	fclose(fp);
768 
769 	return (0);
770 }
771