]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/bsnmp/gensnmptree/gensnmptree.c
This commit was generated by cvs2svn to compensate for changes in r149511,
[FreeBSD/FreeBSD.git] / contrib / bsnmp / gensnmptree / gensnmptree.c
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.42 2005/04/26 16:26:19 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
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  */
438 static void
439 gen_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  */
541 static void
542 gen_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  */
591 static void
592 gen_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
620 static void
621 print_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  */
637 static void
638 gen_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
685 static int
686 extract(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
726 static int
727 gen_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
781 static void
782 check_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  */
801 static void
802 check_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
830 static void
831 merge_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
878 static void
879 merge(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
893 int
894 main(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 }