summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2006-01-27 21:36:35 +0000
committerStef Walter <stef@memberwebs.com>2006-01-27 21:36:35 +0000
commitcadd830e5aca1f208541ea6d38da5b4a863db5cc (patch)
tree597b1bd012ddd2fb1950b4bdc84c17b69ed37e2c
parent36f86d822d09ec0d91839ee68178d8602e1970e2 (diff)
Added textual MIB parsing support. See #45
-rw-r--r--daemon/Makefile.am3
-rw-r--r--daemon/config.c10
-rw-r--r--daemon/rrdbotd.c67
-rw-r--r--daemon/rrdbotd.h19
-rw-r--r--daemon/snmp-help.c99
-rw-r--r--mib/parse-compat.inc.c241
-rw-r--r--mib/parse-net-snmp.patch184
-rw-r--r--mib/parse.c5244
-rw-r--r--mib/parse.c.orig5326
-rw-r--r--mib/parse.h241
-rw-r--r--mib/parse.h.orig246
11 files changed, 11642 insertions, 38 deletions
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index c83aabe..969bafb 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -6,6 +6,7 @@ rrdbotd_SOURCES = rrdbotd.c rrdbotd.h config.c usuals.h \
../common/server-mainloop.c ../common/server-mainloop.h \
../common/sock-any.h ../common/sock-any.c \
../common/stringx.h ../common/stringx.c \
- ../common/hash.h ../common/hash.c
+ ../common/hash.h ../common/hash.c \
+ ../mib/parse.c
rrdbotd_CFLAGS = -I${top_srcdir}/common/ -I${top_srcdir}/bsnmp/ -I${top_srcdir}
rrdbotd_LDADD = $(top_builddir)/bsnmp/libbsnmp-custom.a
diff --git a/daemon/config.c b/daemon/config.c
index 78ad04b..d33ab69 100644
--- a/daemon/config.c
+++ b/daemon/config.c
@@ -233,6 +233,9 @@ parse_uri(char *uri, char** scheme, char** host,
*path = uri;
+ while((*path)[0] == '/')
+ (*path)++;
+
/* This copy only for error messages */
free(copy);
}
@@ -303,8 +306,11 @@ parse_item(const char* field, char* uri, config_ctx *ctx)
ritem->value = RB_UNKNOWN;
/* And parse the OID */
- if(rb_parse_mib(path, &(ritem->snmpfield)) == -1)
- errx(2, "%s: invalid OID: %s", ctx->confname, path + 1);
+ if(rb_snmp_parse_mib(path, &(ritem->snmpfield)) == -1)
+ errx(2, "%s: invalid MIB: %s", ctx->confname, path);
+
+ rb_messagex(LOG_DEBUG, "parsed MIB into oid: %s -> %s", path,
+ asn_oid2str(&(ritem->snmpfield.var)));
/* And add it to the list */
ritem->next = ctx->items;
diff --git a/daemon/rrdbotd.c b/daemon/rrdbotd.c
index 39094cd..8434e35 100644
--- a/daemon/rrdbotd.c
+++ b/daemon/rrdbotd.c
@@ -62,10 +62,41 @@
/* The one main state object */
rb_state g_state;
+/* Whether we print warnings when loading MIBs or not */
+int g_mib_warnings = 0;
+
/* Some logging flags */
static int daemonized = 0;
static int debug_level = LOG_ERR;
+#include "mib/parse.h"
+
+static void
+test(int argc, char* argv[])
+{
+ struct snmp_value val;
+ mib_node n, n2;
+
+ debug_level = 4;
+
+ if(argc < 2)
+ errx(2, "specify arguments");
+
+ while(argc > 1)
+ {
+ if(rb_snmp_parse_mib(argv[1], &val) == -1)
+ warnx("couldn't parse mib value: %s", argv[1]);
+ else
+ fprintf(stderr, "the oid is: %s\n", asn_oid2str(&(val.var)));
+
+ argc--;
+ argv++;
+ }
+
+ rb_mib_uninit();
+ exit(1);
+}
+
/* -----------------------------------------------------------------------------
* CLEANUP
*/
@@ -120,8 +151,8 @@ rb_atexit(voidfunc func, void* data)
* LOGGING
*/
-static void
-vmessage (int level, int err, const char* msg, va_list ap)
+void
+rb_vmessage(int level, int err, const char* msg, va_list ap)
{
#define MAX_MSGLEN 1024
char buf[MAX_MSGLEN];
@@ -156,7 +187,7 @@ rb_messagex (int level, const char* msg, ...)
{
va_list ap;
va_start(ap, msg);
- vmessage(level, 0, msg, ap);
+ rb_vmessage(level, 0, msg, ap);
va_end(ap);
}
@@ -165,7 +196,7 @@ rb_message (int level, const char* msg, ...)
{
va_list ap;
va_start(ap, msg);
- vmessage(level, 1, msg, ap);
+ rb_vmessage(level, 1, msg, ap);
va_end(ap);
}
@@ -176,7 +207,7 @@ rb_message (int level, const char* msg, ...)
static void
usage()
{
- fprintf(stderr, "usage: rrdbotd [-c confdir] [-w workdir] [-d level] [-p pidfile] [-r retries] [-t timeout]\n");
+ fprintf(stderr, "usage: rrdbotd [-M] [-c confdir] [-w workdir] [-d level] [-p pidfile] [-r retries] [-t timeout]\n");
fprintf(stderr, " rrdbotd -v\n");
exit(2);
}
@@ -212,6 +243,8 @@ main(int argc, char* argv[])
char ch;
char* t;
+ /* test(argc, argv); */
+
/* Initialize the state stuff */
memset(&g_state, 0, sizeof(g_state));
@@ -221,7 +254,7 @@ main(int argc, char* argv[])
g_state.timeout = DEFAULT_TIMEOUT;
/* Parse the arguments nicely */
- while((ch = getopt(argc, argv, "c:d:p:r:t:w:v")) != -1)
+ while((ch = getopt(argc, argv, "c:d:Mp:r:t:w:v")) != -1)
{
switch(ch)
{
@@ -240,6 +273,11 @@ main(int argc, char* argv[])
debug_level += LOG_ERR;
break;
+ /* MIB load warnings */
+ case 'M':
+ g_mib_warnings = 1;
+ break;
+
/* Write out a pid file */
case 'p':
pidfile = optarg;
@@ -289,6 +327,11 @@ main(int argc, char* argv[])
/* Parse config and setup SNMP system */
rb_config_parse();
+
+ /* As an optimization we unload the MIB processing data here */
+ rb_mib_uninit();
+
+ /* Rev up the main engine */
rb_snmp_engine_init();
if(daemonize)
@@ -302,12 +345,12 @@ main(int argc, char* argv[])
}
/* Handle signals */
- signal(SIGPIPE, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
- signal(SIGINT, on_quit);
- signal(SIGTERM, on_quit);
- siginterrupt(SIGINT, 1);
- siginterrupt(SIGTERM, 1);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, on_quit);
+ signal(SIGTERM, on_quit);
+ siginterrupt(SIGINT, 1);
+ siginterrupt(SIGTERM, 1);
/* Open the system log */
openlog("rrdbotd", 0, LOG_DAEMON);
diff --git a/daemon/rrdbotd.h b/daemon/rrdbotd.h
index 74f8fd5..ce16ebd 100644
--- a/daemon/rrdbotd.h
+++ b/daemon/rrdbotd.h
@@ -41,6 +41,7 @@
#include <values.h>
#include <stdint.h>
+#include <stdarg.h>
#include "asn1.h"
#include "snmp.h"
@@ -147,8 +148,9 @@ extern rb_state g_state;
* UTILITIES (rrdbotd.c)
*/
-void rb_messagex (int level, const char* msg, ...);
-void rb_message (int level, const char* msg, ...);
+void rb_messagex(int level, const char* msg, ...);
+void rb_message(int level, const char* msg, ...);
+void rb_vmessage(int level, int err, const char* msg, va_list ap);
typedef void (*voidfunc)(void*);
void rb_atexit (voidfunc func, void* data);
@@ -179,4 +181,17 @@ void rb_snmp_engine_uninit();
void rb_rrd_update(rb_poller *poll);
+/* -----------------------------------------------------------------------------
+ * MIB PARSING
+ */
+
+typedef void* mib_node;
+
+void rb_mib_init(int warnings);
+mib_node rb_mib_lookup(const char* match);
+int rb_mib_subid(mib_node n, const char* name);
+void rb_mib_oid(mib_node n, struct asn_oid* oid);
+mib_node rb_mib_node(struct asn_oid* oid);
+void rb_mib_uninit();
+
#endif /* __RRDBOTD_H__ */
diff --git a/daemon/snmp-help.c b/daemon/snmp-help.c
index cf13d40..8a54bb1 100644
--- a/daemon/snmp-help.c
+++ b/daemon/snmp-help.c
@@ -46,9 +46,13 @@
#include "stringx.h"
#include "rrdbotd.h"
+/* Whether we print warnings when loading MIBs or not */
+extern int g_mib_warnings;
+
static int
-parse_numeric_mib(const char* mib, struct asn_oid* oid)
+parse_mixed_mib(const char* mib, struct asn_oid* oid)
{
+ mib_node n;
int ret = 0;
unsigned int sub;
char* next;
@@ -58,37 +62,67 @@ parse_numeric_mib(const char* mib, struct asn_oid* oid)
memset(oid, 0, sizeof(*oid));
- copy = src = strdup(mib);
- if(!src)
+ copy = strdup(mib);
+ if(!copy)
+ {
+ errno = ENOMEM;
return -1;
+ }
- while(src && *src)
+ for(src = copy; src && *src; src = next)
{
next = strchr(src, '.');
if(next)
+ {
*next = 0;
+ next++;
+ }
sub = strtoul(src, &t, 10);
- /* Too many parts */
- if(oid->len > ASN_MAXOIDLEN)
- ret = -1;
+ /* An invalid number, try getting a named MIB */
+ if(*t || sub < 0)
+ {
+ /* Only initializes first time around */
+ rb_mib_init(g_mib_warnings);
+
+ /*
+ * If we haven't parsed anything yet, try a symbolic
+ * search for root
+ */
+
+ if(oid->len == 0)
+ {
+ n = rb_mib_lookup(src);
+ if(n)
+ {
+ /* That took care of it */
+ rb_mib_oid(n, oid);
+ continue;
+ }
+ }
+
+ /* Try a by name search for sub item */
+ n = rb_mib_node(oid);
+ if(n == NULL)
+ sub = -1;
+ else
+ sub = rb_mib_subid(n, src);
+ }
- /* An invalid number */
- if(*t)
+ /* Make sure this is a valid part */
+ if(sub < 0 || (oid->len == 0 && sub < 1) || sub >= ASN_MAXID)
ret = -1;
- /* Make sure this is a valid part */
- if((oid->len == 0 && sub < 1) || sub < 0 || sub >= ASN_MAXID)
- ret -1;
+ /* Too many parts */
+ if(oid->len > ASN_MAXOIDLEN)
+ ret = -1;
if(ret < 0)
break;
oid->subs[oid->len] = sub;
oid->len++;
-
- src = next ? next + 1 : NULL;
}
free(copy);
@@ -96,16 +130,39 @@ parse_numeric_mib(const char* mib, struct asn_oid* oid)
}
int
-rb_parse_mib(const char* mib, struct snmp_value* value)
+rb_snmp_parse_mib(const char* mib, struct snmp_value* value)
{
- /*
- * TODO: Eventually we'll have code here to parse symbolic
- * names, and initialize snmp_value to the right type and
- * all that jazz. For now we just have numeric OID support.
- */
+ int ret;
+ mib_node n;
value->syntax = SNMP_SYNTAX_NULL;
memset(&(value->v), 0, sizeof(value->v));
- return parse_numeric_mib(mib, &(value->var));
+ /* An initial dot */
+ if(*mib == '.')
+ mib++;
+
+ /*
+ * First try parsing a numeric OID. This will fall
+ * back to mixed mode MIB's if necassary. Allows us
+ * to avoid loading all the MIB files when not
+ * necessary
+ */
+
+ ret = parse_mixed_mib(mib, &(value->var));
+
+ /* Next try a symolic search */
+ if(ret == -1)
+ {
+ rb_mib_init(g_mib_warnings);
+
+ n = rb_mib_lookup(mib);
+ if(n == NULL)
+ return -1;
+
+ rb_mib_oid(n, &(value->var));
+ return 0;
+ }
+
+ return ret;
}
diff --git a/mib/parse-compat.inc.c b/mib/parse-compat.inc.c
new file mode 100644
index 0000000..c520c7a
--- /dev/null
+++ b/mib/parse-compat.inc.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2005, Nate Nielsen
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ *
+ * CONTRIBUTORS
+ * Nate Nielsen <nielsen@memberwebs.com>
+ *
+ */
+
+/*
+ * This file is not compiled on it's own. It's included into parse.c
+ * and provides compatibility definitions for making it work without
+ * the rest of net-snmp
+ */
+
+#include "usuals.h"
+#include "rrdbotd.h"
+
+static int with_warnings = 0;
+static int initialized = 0;
+
+/* -----------------------------------------------------------------------------
+ * DEFINITIONS
+ */
+
+#define FALSE 0
+#define TRUE 1
+
+/* No need to implement these */
+#define DEBUGMSGTL
+#define set_function(tp)
+
+/* Just return the tree head */
+#define get_tree_head() \
+ (tree_head)
+
+#define snmp_get_do_debugging() (0)
+
+typedef u_long oid;
+
+#define NETSNMP_DS_LIBRARY_ID 0
+#define NETSNMP_DS_LIB_MIB_WARNINGS 1
+#define NETSNMP_DS_LIB_MIB_REPLACE 2
+#define NETSNMP_DS_LIB_SAVE_MIB_DESCRS 3
+#define NETSNMP_DS_LIB_MIB_ERRORS 4
+#define NETSNMP_DS_LIB_MIB_PARSE_LABEL 5
+#define NETSNMP_DS_LIB_MIB_COMMENT_TERM 6
+
+#define netsnmp_ds_get_boolean(d, v) \
+ netsnmp_ds_get_int(d, v)
+
+static int
+netsnmp_ds_get_int(int dummy, int var)
+{
+ switch(var)
+ {
+ case NETSNMP_DS_LIB_MIB_WARNINGS:
+ return with_warnings;
+ case NETSNMP_DS_LIB_MIB_REPLACE:
+ return 0;
+ case NETSNMP_DS_LIB_SAVE_MIB_DESCRS:
+ return 0;
+ case NETSNMP_DS_LIB_MIB_PARSE_LABEL:
+ return 1;
+ case NETSNMP_DS_LIB_MIB_COMMENT_TERM:
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+#define netsnmp_ds_set_int(a, b, c)
+#define netsnmp_ds_set_boolean(a, b, c)
+#define netsnmp_ds_toggle_boolean(a, b)
+
+static int
+snmp_log(int level, const char* msg, ...)
+{
+ va_list ap;
+
+ if(level >= LOG_WARNING && !with_warnings)
+ return;
+
+ va_start(ap, msg);
+ rb_vmessage(level, 0, msg, ap);
+ va_end(ap);
+}
+
+/* Only used to open files */
+static int
+snmp_log_perror(const char* file)
+{
+ rb_message(LOG_ERR, "couldn't open file: %s", file);
+}
+
+#define SNMP_FREE(s) do { if (s) { free((void *)s); s=NULL; } } while(0)
+
+/* -----------------------------------------------------------------------------
+ * RRDBOT GLUE CODE
+ */
+
+static void
+clear_tree_flags(struct tree *tp)
+{
+ for( ; tp; tp = tp->next_peer)
+ {
+ tp->reported = 0;
+ if(tp->child_list)
+ clear_tree_flags(tp->child_list);
+ }
+}
+
+void
+rb_mib_init(int warnings)
+{
+ if(initialized)
+ return;
+
+ with_warnings = warnings;
+
+ init_mib_internals();
+ add_mibdir("/usr/share/snmp/mibs");
+ read_all_mibs();
+
+ rb_messagex(LOG_DEBUG, "loaded all MIB files");
+ initialized = 1;
+}
+
+mib_node
+rb_mib_lookup(const char* match)
+{
+ extern struct tree *tree_head;
+ struct tree* mib;
+
+ ASSERT(initialized);
+
+ clear_tree_flags(tree_head);
+ mib = find_best_tree_node(match, NULL, NULL);
+ return (mib_node)mib;
+}
+
+int
+rb_mib_subid(mib_node n, const char* name)
+{
+ struct tree *parent = (struct tree*)n;
+ struct tree *tp = NULL;
+
+ ASSERT(initialized);
+
+ for(tp = parent->child_list; tp; tp = tp->next_peer)
+ {
+ if(strcasecmp(name, tp->label) == 0)
+ return tp->subid;
+ }
+
+ return -1;
+}
+
+void
+rb_mib_oid(mib_node n, struct asn_oid* oid)
+{
+ struct tree* mib = (struct tree*)n;
+ struct tree *tp = NULL;
+ int len;
+
+ ASSERT(mib);
+
+ /* Figure out where to start */
+ len = 0;
+ for(tp = mib; tp; tp = tp->parent)
+ len++;
+
+ oid->len = len;
+ for(tp = mib; tp; tp = tp->parent)
+ oid->subs[--len] = tp->subid;
+}
+
+mib_node
+rb_mib_node(struct asn_oid* oid)
+{
+ extern struct tree *tree_head;
+ struct tree *tp = NULL;
+ asn_subid_t subid;
+ int i;
+
+ ASSERT(initialized);
+
+ for(i = 0, tp = tree_head; tp && i < oid->len;
+ i++, tp = tp ? tp->child_list : NULL)
+ {
+ subid = oid->subs[i];
+
+ while(tp && tp->subid != subid)
+ tp = tp->next_peer;
+
+ /* Did we find a match? */
+ if(tp && i == oid->len - 1)
+ break;
+ }
+
+ return tp;
+}
+
+void
+rb_mib_uninit()
+{
+ if(initialized) {
+ unload_all_mibs();
+ rb_messagex(LOG_DEBUG, "unloaded all MIB files");
+ }
+ initialized = 0;
+}
diff --git a/mib/parse-net-snmp.patch b/mib/parse-net-snmp.patch
new file mode 100644
index 0000000..31b0e81
--- /dev/null
+++ b/mib/parse-net-snmp.patch
@@ -0,0 +1,184 @@
+--- parse.c.orig 2006-01-27 14:11:27.000000000
++++ parse.c 2006-01-27 15:20:14.000000000
+@@ -1,4 +1,11 @@
+ /*
++ * Nate Nielsen
++ *
++ * Borrowed from net-snmp. Cleaned up a bit (see parse-net-snmp.patch)
++ * and readied for inclusion in rrdbot. Most of the additional code
++ * is at the top.
++ */
++/*
+ * parse.c
+ *
+ * Update: 1998-09-22 <mslifcak@iss.net>
+@@ -47,75 +54,29 @@
+ * Use is subject to license terms specified in the COPYING file
+ * distributed with the Net-SNMP package.
+ */
+-#include <net-snmp/net-snmp-config.h>
+
+-#include <stdio.h>
+-#if HAVE_STDLIB_H
++/* -----------------------------------------------------------------------------
++ * ADDITIONAL RRDBOT COMPATIBILITY CODE
++ */
++
++#include <sys/types.h>
+ #include <stdlib.h>
+-#endif
+-#if HAVE_STRING_H
++#include <stdio.h>
+ #include <string.h>
+-#else
+-#include <strings.h>
+-#endif
+-#include <ctype.h>
+-#include <sys/types.h>
+-#include <sys/stat.h>
++#include <dirent.h>
++#include <syslog.h>
+
+-/*
+- * Wow. This is ugly. -- Wes
++#include "parse.h"
++
++/*
++ * A file with compatibility definitions for making the following
++ * code work without the net-snmp library and all that.
+ */
+-#if HAVE_DIRENT_H
+-# include <dirent.h>
+-# define NAMLEN(dirent) strlen((dirent)->d_name)
+-#else
+-# define dirent direct
+-# define NAMLEN(dirent) (dirent)->d_namlen
+-# if HAVE_SYS_NDIR_H
+-# include <sys/ndir.h>
+-# endif
+-# if HAVE_SYS_DIR_H
+-# include <sys/dir.h>
+-# endif
+-# if HAVE_NDIR_H
+-# include <ndir.h>
+-# endif
+-#endif
+-#if TIME_WITH_SYS_TIME
+-# ifdef WIN32
+-# include <sys/timeb.h>
+-# else
+-# include <sys/time.h>
+-# endif
+-# include <time.h>
+-#else
+-# if HAVE_SYS_TIME_H
+-# include <sys/time.h>
+-# else
+-# include <time.h>
+-# endif
+-#endif
+-#if HAVE_WINSOCK_H
+-#include <winsock.h>
+-#endif
+-#if HAVE_NETINET_IN_H
+-#include <netinet/in.h>
+-#endif
+-#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
+-#include <regex.h>
+-#endif
+-#if HAVE_DMALLOC_H
+-#include <dmalloc.h>
+-#endif
++#include "parse-compat.inc.c"
++
++/* -------------------------------------------------------------------------- */
++
+
+-#include <net-snmp/types.h>
+-#include <net-snmp/output_api.h>
+-#include <net-snmp/config_api.h>
+-#include <net-snmp/utilities.h>
+-
+-#include <net-snmp/library/parse.h>
+-#include <net-snmp/library/mib.h>
+-#include <net-snmp/library/snmp_api.h>
+
+ /*
+ * This is one element of an object identifier with either an integer
+@@ -138,8 +99,8 @@
+ struct range_list *ranges;
+ } tclist[MAXTC];
+
+-int mibLine = 0;
+-const char *File = "(none)";
++static int mibLine = 0;
++static const char *File = "(none)";
+ static int anonymous = 0;
+
+ struct objgroup {
+@@ -4569,51 +4530,8 @@
+ char token[MAXTOKEN], token2[MAXTOKEN];
+ char tmpstr[300];
+ int count = 0;
+-#if !(defined(WIN32) || defined(cygwin))
+- char space;
+- char newline;
+- struct stat dir_stat, idx_stat;
+- char tmpstr1[300];
+-#endif
+
+ DEBUGMSGTL(("parse-mibs", "Scanning directory %s\n", dirname));
+-#if !(defined(WIN32) || defined(cygwin))
+- snprintf(token, sizeof(token), "%s/%s", dirname, ".index");
+- token[ sizeof(token)-1 ] = 0;
+- if (stat(token, &idx_stat) == 0 && stat(dirname, &dir_stat) == 0) {
+- if (dir_stat.st_mtime < idx_stat.st_mtime) {
+- DEBUGMSGTL(("parse-mibs", "The index is good\n"));
+- if ((ip = fopen(token, "r")) != NULL) {
+- while (fscanf(ip, "%127s%c%299s%c", token, &space, tmpstr,
+- &newline) == 4) {
+-
+- /*
+- * If an overflow of the token or tmpstr buffers has been
+- * found log a message and break out of the while loop,
+- * thus the rest of the file tokens will be ignored.
+- */
+- if (space != ' ' || newline != '\n') {
+- snmp_log(LOG_ERR,
+- "add_mibdir: strings scanned in from %s/%s " \
+- "are too large. count = %d\n ", dirname,
+- ".index", count);
+- break;
+- }
+-
+- snprintf(tmpstr1, sizeof(tmpstr1), "%s/%s", dirname, tmpstr);
+- tmpstr1[ sizeof(tmpstr1)-1 ] = 0;
+- new_module(token, tmpstr1);
+- count++;
+- }
+- fclose(ip);
+- return count;
+- } else
+- DEBUGMSGTL(("parse-mibs", "Can't read index\n"));
+- } else
+- DEBUGMSGTL(("parse-mibs", "Index outdated\n"));
+- } else
+- DEBUGMSGTL(("parse-mibs", "No index\n"));
+-#endif
+
+ if ((dir = opendir(dirname))) {
+ snprintf(tmpstr, sizeof(tmpstr), "%s/.index", dirname);
+--- parse.h.orig 2003-05-08 11:32:04.000000000
++++ parse.h 2006-01-27 12:56:14.000000000
+@@ -119,11 +119,6 @@
+ struct varbind_list *varbinds;
+ char *hint;
+ char *units;
+- int (*printomat) (u_char **, size_t *, size_t *, int,
+- const netsnmp_variable_list *,
+- const struct enum_list *, const char *,
+- const char *);
+- void (*printer) (char *, const netsnmp_variable_list *, const struct enum_list *, const char *, const char *); /* Value printing function */
+ char *description; /* description (a quoted string) */
+ int reported; /* 1=report started in print_subtree... */
+ char *defaultValue;
diff --git a/mib/parse.c b/mib/parse.c
new file mode 100644
index 0000000..d4987d3
--- /dev/null
+++ b/mib/parse.c
@@ -0,0 +1,5244 @@
+/*
+ * Nate Nielsen
+ *
+ * Borrowed from net-snmp. Cleaned up a bit (see parse-net-snmp.patch)
+ * and readied for inclusion in rrdbot. Most of the additional code
+ * is at the top.
+ */
+/*
+ * parse.c
+ *
+ * Update: 1998-09-22 <mslifcak@iss.net>
+ * Clear nbuckets in init_node_hash.
+ * New method xcalloc returns zeroed data structures.
+ * New method alloc_node encapsulates common node creation.
+ * New method to configure terminate comment at end of line.
+ * New method to configure accept underscore in labels.
+ *
+ * Update: 1998-10-10 <daves@csc.liv.ac.uk>
+ * fully qualified OID parsing patch
+ *
+ * Update: 1998-10-20 <daves@csc.liv.ac.uk>
+ * merge_anon_children patch
+ *
+ * Update: 1998-10-21 <mslifcak@iss.net>
+ * Merge_parse_objectid associates information with last node in chain.
+ */
+/* Portions of this file are subject to the following copyrights. See
+ * the Net-SNMP's COPYING file for more details and other copyrights
+ * that may apply:
+ */
+/******************************************************************
+ Copyright 1989, 1991, 1992 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of CMU not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+******************************************************************/
+/*
+ * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms specified in the COPYING file
+ * distributed with the Net-SNMP package.
+ */
+
+/* -----------------------------------------------------------------------------
+ * ADDITIONAL RRDBOT COMPATIBILITY CODE
+ */
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <syslog.h>
+
+#include "parse.h"
+
+/*
+ * A file with compatibility definitions for making the following
+ * code work without the net-snmp library and all that.
+ */
+#include "parse-compat.inc.c"
+
+/* -------------------------------------------------------------------------- */
+
+
+
+/*
+ * This is one element of an object identifier with either an integer
+ * subidentifier, or a textual string label, or both.
+ * The subid is -1 if not present, and label is NULL if not present.
+ */
+struct subid_s {
+ int subid;
+ int modid;
+ char *label;
+};
+
+#define MAXTC 4096
+struct tc { /* textual conventions */
+ int type;
+ int modid;
+ char *descriptor;
+ char *hint;
+ struct enum_list *enums;
+ struct range_list *ranges;
+} tclist[MAXTC];
+
+static int mibLine = 0;
+static const char *File = "(none)";
+static int anonymous = 0;
+
+struct objgroup {
+ char *name;
+ int line;
+ struct objgroup *next;
+} *objgroups = NULL, *objects = NULL, *notifs = NULL;
+
+#define SYNTAX_MASK 0x80
+/*
+ * types of tokens
+ * Tokens wiht the SYNTAX_MASK bit set are syntax tokens
+ */
+#define CONTINUE -1
+#define ENDOFFILE 0
+#define LABEL 1
+#define SUBTREE 2
+#define SYNTAX 3
+#define OBJID (4 | SYNTAX_MASK)
+#define OCTETSTR (5 | SYNTAX_MASK)
+#define INTEGER (6 | SYNTAX_MASK)
+#define NETADDR (7 | SYNTAX_MASK)
+#define IPADDR (8 | SYNTAX_MASK)
+#define COUNTER (9 | SYNTAX_MASK)
+#define GAUGE (10 | SYNTAX_MASK)
+#define TIMETICKS (11 | SYNTAX_MASK)
+#define KW_OPAQUE (12 | SYNTAX_MASK)
+#define NUL (13 | SYNTAX_MASK)
+#define SEQUENCE 14
+#define OF 15 /* SEQUENCE OF */
+#define OBJTYPE 16
+#define ACCESS 17
+#define READONLY 18
+#define READWRITE 19
+#define WRITEONLY 20
+#ifdef NOACCESS
+#undef NOACCESS /* agent 'NOACCESS' token */
+#endif
+#define NOACCESS 21
+#define STATUS 22
+#define MANDATORY 23
+#define KW_OPTIONAL 24
+#define OBSOLETE 25
+/*
+ * #define RECOMMENDED 26
+ */
+#define PUNCT 27
+#define EQUALS 28
+#define NUMBER 29
+#define LEFTBRACKET 30
+#define RIGHTBRACKET 31
+#define LEFTPAREN 32
+#define RIGHTPAREN 33
+#define COMMA 34
+#define DESCRIPTION 35
+#define QUOTESTRING 36
+#define INDEX 37
+#define DEFVAL 38
+#define DEPRECATED 39
+#define SIZE 40
+#define BITSTRING (41 | SYNTAX_MASK)
+#define NSAPADDRESS (42 | SYNTAX_MASK)
+#define COUNTER64 (43 | SYNTAX_MASK)
+#define OBJGROUP 44
+#define NOTIFTYPE 45
+#define AUGMENTS 46
+#define COMPLIANCE 47
+#define READCREATE 48
+#define UNITS 49
+#define REFERENCE 50
+#define NUM_ENTRIES 51
+#define MODULEIDENTITY 52
+#define LASTUPDATED 53
+#define ORGANIZATION 54
+#define CONTACTINFO 55
+#define UINTEGER32 (56 | SYNTAX_MASK)
+#define CURRENT 57
+#define DEFINITIONS 58
+#define END 59
+#define SEMI 60
+#define TRAPTYPE 61
+#define ENTERPRISE 62
+/*
+ * #define DISPLAYSTR (63 | SYNTAX_MASK)
+ */
+#define BEGIN 64
+#define IMPORTS 65
+#define EXPORTS 66
+#define ACCNOTIFY 67
+#define BAR 68
+#define RANGE 69
+#define CONVENTION 70
+#define DISPLAYHINT 71
+#define FROM 72
+#define AGENTCAP 73
+#define MACRO 74
+#define IMPLIED 75
+#define SUPPORTS 76
+#define INCLUDES 77
+#define VARIATION 78
+#define REVISION 79
+#define NOTIMPL 80
+#define OBJECTS 81
+#define NOTIFICATIONS 82
+#define MODULE 83
+#define MINACCESS 84
+#define PRODREL 85
+#define WRSYNTAX 86
+#define CREATEREQ 87
+#define NOTIFGROUP 88
+#define MANDATORYGROUPS 89
+#define GROUP 90
+#define OBJECT 91
+#define IDENTIFIER 92
+#define CHOICE 93
+#define LEFTSQBRACK 95
+#define RIGHTSQBRACK 96
+#define IMPLICIT 97
+#define APPSYNTAX (98 | SYNTAX_MASK)
+#define OBJSYNTAX (99 | SYNTAX_MASK)
+#define SIMPLESYNTAX (100 | SYNTAX_MASK)
+#define OBJNAME (101 | SYNTAX_MASK)
+#define NOTIFNAME (102 | SYNTAX_MASK)
+#define VARIABLES 103
+#define UNSIGNED32 (104 | SYNTAX_MASK)
+#define INTEGER32 (105 | SYNTAX_MASK)
+/*
+ * Beware of reaching SYNTAX_MASK (0x80)
+ */
+
+struct tok {
+ const char *name; /* token name */
+ int len; /* length not counting nul */
+ int token; /* value */
+ int hash; /* hash of name */
+ struct tok *next; /* pointer to next in hash table */
+};
+
+
+static struct tok tokens[] = {
+ {"obsolete", sizeof("obsolete") - 1, OBSOLETE}
+ ,
+ {"Opaque", sizeof("Opaque") - 1, KW_OPAQUE}
+ ,
+ {"optional", sizeof("optional") - 1, KW_OPTIONAL}
+ ,
+ {"LAST-UPDATED", sizeof("LAST-UPDATED") - 1, LASTUPDATED}
+ ,
+ {"ORGANIZATION", sizeof("ORGANIZATION") - 1, ORGANIZATION}
+ ,
+ {"CONTACT-INFO", sizeof("CONTACT-INFO") - 1, CONTACTINFO}
+ ,
+ {"MODULE-IDENTITY", sizeof("MODULE-IDENTITY") - 1, MODULEIDENTITY}
+ ,
+ {"MODULE-COMPLIANCE", sizeof("MODULE-COMPLIANCE") - 1, COMPLIANCE}
+ ,
+ {"DEFINITIONS", sizeof("DEFINITIONS") - 1, DEFINITIONS}
+ ,
+ {"END", sizeof("END") - 1, END}
+ ,
+ {"AUGMENTS", sizeof("AUGMENTS") - 1, AUGMENTS}
+ ,
+ {"not-accessible", sizeof("not-accessible") - 1, NOACCESS}
+ ,
+ {"write-only", sizeof("write-only") - 1, WRITEONLY}
+ ,
+ {"NsapAddress", sizeof("NsapAddress") - 1, NSAPADDRESS}
+ ,
+ {"UNITS", sizeof("Units") - 1, UNITS}
+ ,
+ {"REFERENCE", sizeof("REFERENCE") - 1, REFERENCE}
+ ,
+ {"NUM-ENTRIES", sizeof("NUM-ENTRIES") - 1, NUM_ENTRIES}
+ ,
+ {"BITSTRING", sizeof("BITSTRING") - 1, BITSTRING}
+ ,
+ {"BIT", sizeof("BIT") - 1, CONTINUE}
+ ,
+ {"BITS", sizeof("BITS") - 1, BITSTRING}
+ ,
+ {"Counter64", sizeof("Counter64") - 1, COUNTER64}
+ ,
+ {"TimeTicks", sizeof("TimeTicks") - 1, TIMETICKS}
+ ,
+ {"NOTIFICATION-TYPE", sizeof("NOTIFICATION-TYPE") - 1, NOTIFTYPE}
+ ,
+ {"OBJECT-GROUP", sizeof("OBJECT-GROUP") - 1, OBJGROUP}
+ ,
+ {"OBJECT-IDENTITY", sizeof("OBJECT-IDENTITY") - 1, OBJGROUP}
+ ,
+ {"IDENTIFIER", sizeof("IDENTIFIER") - 1, IDENTIFIER}
+ ,
+ {"OBJECT", sizeof("OBJECT") - 1, OBJECT}
+ ,
+ {"NetworkAddress", sizeof("NetworkAddress") - 1, NETADDR}
+ ,
+ {"Gauge", sizeof("Gauge") - 1, GAUGE}
+ ,
+ {"Gauge32", sizeof("Gauge32") - 1, GAUGE}
+ ,
+ {"Unsigned32", sizeof("Unsigned32") - 1, UNSIGNED32}
+ ,
+ {"read-write", sizeof("read-write") - 1, READWRITE}
+ ,
+ {"read-create", sizeof("read-create") - 1, READCREATE}
+ ,
+ {"OCTETSTRING", sizeof("OCTETSTRING") - 1, OCTETSTR}
+ ,
+ {"OCTET", sizeof("OCTET") - 1, CONTINUE}
+ ,
+ {"OF", sizeof("OF") - 1, OF}
+ ,
+ {"SEQUENCE", sizeof("SEQUENCE") - 1, SEQUENCE}
+ ,
+ {"NULL", sizeof("NULL") - 1, NUL}
+ ,
+ {"IpAddress", sizeof("IpAddress") - 1, IPADDR}
+ ,
+ {"UInteger32", sizeof("UInteger32") - 1, UINTEGER32}
+ ,
+ {"INTEGER", sizeof("INTEGER") - 1, INTEGER}
+ ,
+ {"Integer32", sizeof("Integer32") - 1, INTEGER32}
+ ,
+ {"Counter", sizeof("Counter") - 1, COUNTER}
+ ,
+ {"Counter32", sizeof("Counter32") - 1, COUNTER}
+ ,
+ {"read-only", sizeof("read-only") - 1, READONLY}
+ ,
+ {"DESCRIPTION", sizeof("DESCRIPTION") - 1, DESCRIPTION}
+ ,
+ {"INDEX", sizeof("INDEX") - 1, INDEX}
+ ,
+ {"DEFVAL", sizeof("DEFVAL") - 1, DEFVAL}
+ ,
+ {"deprecated", sizeof("deprecated") - 1, DEPRECATED}
+ ,
+ {"SIZE", sizeof("SIZE") - 1, SIZE}
+ ,
+ {"MAX-ACCESS", sizeof("MAX-ACCESS") - 1, ACCESS}
+ ,
+ {"ACCESS", sizeof("ACCESS") - 1, ACCESS}
+ ,
+ {"mandatory", sizeof("mandatory") - 1, MANDATORY}
+ ,
+ {"current", sizeof("current") - 1, CURRENT}
+ ,
+ {"STATUS", sizeof("STATUS") - 1, STATUS}
+ ,
+ {"SYNTAX", sizeof("SYNTAX") - 1, SYNTAX}
+ ,
+ {"OBJECT-TYPE", sizeof("OBJECT-TYPE") - 1, OBJTYPE}
+ ,
+ {"TRAP-TYPE", sizeof("TRAP-TYPE") - 1, TRAPTYPE}
+ ,
+ {"ENTERPRISE", sizeof("ENTERPRISE") - 1, ENTERPRISE}
+ ,
+ {"BEGIN", sizeof("BEGIN") - 1, BEGIN}
+ ,
+ {"IMPORTS", sizeof("IMPORTS") - 1, IMPORTS}
+ ,
+ {"EXPORTS", sizeof("EXPORTS") - 1, EXPORTS}
+ ,
+ {"accessible-for-notify", sizeof("accessible-for-notify") - 1,
+ ACCNOTIFY}
+ ,
+ {"TEXTUAL-CONVENTION", sizeof("TEXTUAL-CONVENTION") - 1, CONVENTION}
+ ,
+ {"NOTIFICATION-GROUP", sizeof("NOTIFICATION-GROUP") - 1, NOTIFGROUP}
+ ,
+ {"DISPLAY-HINT", sizeof("DISPLAY-HINT") - 1, DISPLAYHINT}
+ ,
+ {"FROM", sizeof("FROM") - 1, FROM}
+ ,
+ {"AGENT-CAPABILITIES", sizeof("AGENT-CAPABILITIES") - 1, AGENTCAP}
+ ,
+ {"MACRO", sizeof("MACRO") - 1, MACRO}
+ ,
+ {"IMPLIED", sizeof("IMPLIED") - 1, IMPLIED}
+ ,
+ {"SUPPORTS", sizeof("SUPPORTS") - 1, SUPPORTS}
+ ,
+ {"INCLUDES", sizeof("INCLUDES") - 1, INCLUDES}
+ ,
+ {"VARIATION", sizeof("VARIATION") - 1, VARIATION}
+ ,
+ {"REVISION", sizeof("REVISION") - 1, REVISION}
+ ,
+ {"not-implemented", sizeof("not-implemented") - 1, NOTIMPL}
+ ,
+ {"OBJECTS", sizeof("OBJECTS") - 1, OBJECTS}
+ ,
+ {"NOTIFICATIONS", sizeof("NOTIFICATIONS") - 1, NOTIFICATIONS}
+ ,
+ {"MODULE", sizeof("MODULE") - 1, MODULE}
+ ,
+ {"MIN-ACCESS", sizeof("MIN-ACCESS") - 1, MINACCESS}
+ ,
+ {"PRODUCT-RELEASE", sizeof("PRODUCT-RELEASE") - 1, PRODREL}
+ ,
+ {"WRITE-SYNTAX", sizeof("WRITE-SYNTAX") - 1, WRSYNTAX}
+ ,
+ {"CREATION-REQUIRES", sizeof("CREATION-REQUIRES") - 1, CREATEREQ}
+ ,
+ {"MANDATORY-GROUPS", sizeof("MANDATORY-GROUPS") - 1, MANDATORYGROUPS}
+ ,
+ {"GROUP", sizeof("GROUP") - 1, GROUP}
+ ,
+ {"CHOICE", sizeof("CHOICE") - 1, CHOICE}
+ ,
+ {"IMPLICIT", sizeof("IMPLICIT") - 1, IMPLICIT}
+ ,
+ {"ObjectSyntax", sizeof("ObjectSyntax") - 1, OBJSYNTAX}
+ ,
+ {"SimpleSyntax", sizeof("SimpleSyntax") - 1, SIMPLESYNTAX}
+ ,
+ {"ApplicationSyntax", sizeof("ApplicationSyntax") - 1, APPSYNTAX}
+ ,
+ {"ObjectName", sizeof("ObjectName") - 1, OBJNAME}
+ ,
+ {"NotificationName", sizeof("NotificationName") - 1, NOTIFNAME}
+ ,
+ {"VARIABLES", sizeof("VARIABLES") - 1, VARIABLES}
+ ,
+ {NULL}
+};
+
+static struct module_compatability *module_map_head;
+static struct module_compatability module_map[] = {
+ {"RFC1065-SMI", "RFC1155-SMI", NULL, 0},
+ {"RFC1066-MIB", "RFC1156-MIB", NULL, 0},
+ /*
+ * 'mib' -> 'mib-2'
+ */
+ {"RFC1156-MIB", "RFC1158-MIB", NULL, 0},
+ /*
+ * 'snmpEnableAuthTraps' -> 'snmpEnableAuthenTraps'
+ */
+ {"RFC1158-MIB", "RFC1213-MIB", NULL, 0},
+ /*
+ * 'nullOID' -> 'zeroDotZero'
+ */
+ {"RFC1155-SMI", "SNMPv2-SMI", NULL, 0},
+ {"RFC1213-MIB", "SNMPv2-SMI", "mib-2", 0},
+ {"RFC1213-MIB", "SNMPv2-MIB", "sys", 3},
+ {"RFC1213-MIB", "IF-MIB", "if", 2},
+ {"RFC1213-MIB", "IP-MIB", "ip", 2},
+ {"RFC1213-MIB", "IP-MIB", "icmp", 4},
+ {"RFC1213-MIB", "TCP-MIB", "tcp", 3},
+ {"RFC1213-MIB", "UDP-MIB", "udp", 3},
+ {"RFC1213-MIB", "SNMPv2-SMI", "transmission", 0},
+ {"RFC1213-MIB", "SNMPv2-MIB", "snmp", 4},
+ {"RFC1231-MIB", "TOKENRING-MIB", NULL, 0},
+ {"RFC1271-MIB", "RMON-MIB", NULL, 0},
+ {"RFC1286-MIB", "SOURCE-ROUTING-MIB", "dot1dSr", 7},
+ {"RFC1286-MIB", "BRIDGE-MIB", NULL, 0},
+ {"RFC1315-MIB", "FRAME-RELAY-DTE-MIB", NULL, 0},
+ {"RFC1316-MIB", "CHARACTER-MIB", NULL, 0},
+ {"RFC1406-MIB", "DS1-MIB", NULL, 0},
+ {"RFC-1213", "RFC1213-MIB", NULL, 0},
+};
+
+#define MODULE_NOT_FOUND 0
+#define MODULE_LOADED_OK 1
+#define MODULE_ALREADY_LOADED 2
+/*
+ * #define MODULE_LOAD_FAILED 3
+ */
+#define MODULE_LOAD_FAILED MODULE_NOT_FOUND
+
+
+#define HASHSIZE 32
+#define BUCKET(x) (x & (HASHSIZE-1))
+
+#define NHASHSIZE 128
+#define NBUCKET(x) (x & (NHASHSIZE-1))
+
+static struct tok *buckets[HASHSIZE];
+
+static struct node *nbuckets[NHASHSIZE];
+static struct tree *tbuckets[NHASHSIZE];
+static struct module *module_head = NULL;
+
+struct node *orphan_nodes = NULL;
+struct tree *tree_head = NULL;
+
+#define NUMBER_OF_ROOT_NODES 3
+static struct module_import root_imports[NUMBER_OF_ROOT_NODES];
+
+static int current_module = 0;
+static int max_module = 0;
+static char *last_err_module = 0; /* no repeats on "Cannot find module..." */
+
+static void tree_from_node(struct tree *tp, struct node *np);
+static void do_subtree(struct tree *, struct node **);
+static void do_linkup(struct module *, struct node *);
+static void dump_module_list(void);
+static int get_token(FILE *, char *, int);
+static int parseQuoteString(FILE *, char *, int);
+static int tossObjectIdentifier(FILE *);
+static int name_hash(const char *);
+static void init_node_hash(struct node *);
+static void print_error(const char *, const char *, int);
+static void free_tree(struct tree *);
+static void free_partial_tree(struct tree *, int);
+static void free_node(struct node *);
+static void build_translation_table(void);
+static void init_tree_roots(void);
+static void merge_anon_children(struct tree *, struct tree *);
+static void unlink_tbucket(struct tree *);
+static void unlink_tree(struct tree *);
+static int getoid(FILE *, struct subid_s *, int);
+static struct node *parse_objectid(FILE *, char *);
+static int get_tc(const char *, int, int *, struct enum_list **,
+ struct range_list **, char **);
+static int get_tc_index(const char *, int);
+static struct enum_list *parse_enumlist(FILE *, struct enum_list **);
+static struct range_list *parse_ranges(FILE * fp, struct range_list **);
+static struct node *parse_asntype(FILE *, char *, int *, char *);
+static struct node *parse_objecttype(FILE *, char *);
+static struct node *parse_objectgroup(FILE *, char *, int,
+ struct objgroup **);
+static struct node *parse_notificationDefinition(FILE *, char *);
+static struct node *parse_trapDefinition(FILE *, char *);
+static struct node *parse_compliance(FILE *, char *);
+static struct node *parse_capabilities(FILE *, char *);
+static struct node *parse_moduleIdentity(FILE *, char *);
+static struct node *parse_macro(FILE *, char *);
+static void parse_imports(FILE *);
+static struct node *parse(FILE *, struct node *);
+
+static int read_module_internal(const char *);
+static void read_module_replacements(const char *);
+static void read_import_replacements(const char *,
+ struct module_import *);
+
+static void new_module(const char *, const char *);
+
+static struct node *merge_parse_objectid(struct node *, FILE *, char *);
+static struct index_list *getIndexes(FILE * fp, struct index_list **);
+static struct varbind_list *getVarbinds(FILE * fp, struct varbind_list **);
+static void free_indexes(struct index_list **);
+static void free_varbinds(struct varbind_list **);
+static void free_ranges(struct range_list **);
+static void free_enums(struct enum_list **);
+static struct range_list *copy_ranges(struct range_list *);
+static struct enum_list *copy_enums(struct enum_list *);
+
+static u_int compute_match(const char *search_base, const char *key);
+
+void
+snmp_mib_toggle_options_usage(const char *lead, FILE * outf)
+{
+ fprintf(outf, "%su: %sallow the use of underlines in MIB symbols\n",
+ lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_PARSE_LABEL)) ?
+ "dis" : ""));
+ fprintf(outf, "%sc: %sallow the use of \"--\" to terminate comments\n",
+ lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_COMMENT_TERM)) ?
+ "" : "dis"));
+
+ fprintf(outf, "%sd: %ssave the DESCRIPTIONs of the MIB objects\n",
+ lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) ?
+ "do not " : ""));
+
+ fprintf(outf, "%se: disable errors when MIB symbols conflict\n", lead);
+
+ fprintf(outf, "%sw: enable warnings when MIB symbols conflict\n", lead);
+
+ fprintf(outf, "%sW: enable detailed warnings when MIB symbols conflict\n",
+ lead);
+
+ fprintf(outf, "%sR: replace MIB symbols from latest module\n", lead);
+}
+
+char *
+snmp_mib_toggle_options(char *options)
+{
+ if (options) {
+ while (*options) {
+ switch (*options) {
+ case 'u':
+ netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIB_PARSE_LABEL,
+ !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_PARSE_LABEL));
+ break;
+
+ case 'c':
+ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_COMMENT_TERM);
+ break;
+
+ case 'e':
+ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_ERRORS);
+ break;
+
+ case 'w':
+ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS, 1);
+ break;
+
+ case 'W':
+ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS, 2);
+ break;
+
+ case 'd':
+ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS);
+ break;
+
+ case 'R':
+ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_REPLACE);
+ break;
+
+ default:
+ /*
+ * return at the unknown option
+ */
+ return options;
+ }
+ options++;
+ }
+ }
+ return NULL;
+}
+
+static int
+name_hash(const char *name)
+{
+ int hash = 0;
+ const char *cp;
+
+ if (!name)
+ return 0;
+ for (cp = name; *cp; cp++)
+ hash += tolower(*cp);
+ return (hash);
+}
+
+void
+init_mib_internals(void)
+{
+ register struct tok *tp;
+ register int b, i;
+ int max_modc;
+
+ if (tree_head)
+ return;
+
+ /*
+ * Set up hash list of pre-defined tokens
+ */
+ memset(buckets, 0, sizeof(buckets));
+ for (tp = tokens; tp->name; tp++) {
+ tp->hash = name_hash(tp->name);
+ b = BUCKET(tp->hash);
+ if (buckets[b])
+ tp->next = buckets[b]; /* BUG ??? */
+ buckets[b] = tp;
+ }
+
+ /*
+ * Initialise other internal structures
+ */
+
+ max_modc = sizeof(module_map) / sizeof(module_map[0]) - 1;
+ for (i = 0; i < max_modc; ++i)
+ module_map[i].next = &(module_map[i + 1]);
+ module_map[max_modc].next = NULL;
+ module_map_head = module_map;
+
+ memset(nbuckets, 0, sizeof(nbuckets));
+ memset(tbuckets, 0, sizeof(tbuckets));
+ memset(tclist, 0, MAXTC * sizeof(struct tc));
+ build_translation_table();
+ init_tree_roots(); /* Set up initial roots */
+ /*
+ * Relies on 'add_mibdir' having set up the modules
+ */
+}
+
+static void
+init_node_hash(struct node *nodes)
+{
+ struct node *np, *nextp;
+ int hash;
+
+ memset(nbuckets, 0, sizeof(nbuckets));
+ for (np = nodes; np;) {
+ nextp = np->next;
+ hash = NBUCKET(name_hash(np->parent));
+ np->next = nbuckets[hash];
+ nbuckets[hash] = np;
+ np = nextp;
+ }
+}
+
+static int erroneousMibs = 0;
+
+int
+get_mib_parse_error_count(void)
+{
+ return erroneousMibs;
+}
+
+
+static void
+print_error(const char *string, const char *token, int type)
+{
+ erroneousMibs++;
+ DEBUGMSGTL(("parse-mibs", "\n"));
+ if (type == ENDOFFILE)
+ snmp_log(LOG_ERR, "%s (EOF): At line %d in %s\n", string, mibLine,
+ File);
+ else if (token && *token)
+ snmp_log(LOG_ERR, "%s (%s): At line %d in %s\n", string, token,
+ mibLine, File);
+ else
+ snmp_log(LOG_ERR, "%s: At line %d in %s\n", string, mibLine, File);
+}
+
+static void
+print_module_not_found(const char *cp)
+{
+ if (!last_err_module || strcmp(cp, last_err_module))
+ print_error("Cannot find module", cp, CONTINUE);
+ if (last_err_module)
+ free(last_err_module);
+ last_err_module = strdup(cp);
+}
+
+static struct node *
+alloc_node(int modid)
+{
+ struct node *np;
+ np = (struct node *) calloc(1, sizeof(struct node));
+ if (np) {
+ np->tc_index = -1;
+ np->modid = modid;
+ np->filename = strdup(File);
+ np->lineno = mibLine;
+ }
+ return np;
+}
+
+static void
+unlink_tbucket(struct tree *tp)
+{
+ int hash = NBUCKET(name_hash(tp->label));
+ struct tree *otp = NULL, *ntp = tbuckets[hash];
+
+ while (ntp && ntp != tp) {
+ otp = ntp;
+ ntp = ntp->next;
+ }
+ if (!ntp)
+ snmp_log(LOG_EMERG, "Can't find %s in tbuckets\n", tp->label);
+ else if (otp)
+ otp->next = ntp->next;
+ else
+ tbuckets[hash] = tp->next;
+}
+
+static void
+unlink_tree(struct tree *tp)
+{
+ struct tree *otp = NULL, *ntp = tp->parent;
+
+ if (!ntp) { /* this tree has no parent */
+ DEBUGMSGTL(("unlink_tree", "Tree node %s has no parent\n",
+ tp->label));
+ } else {
+ ntp = ntp->child_list;
+
+ while (ntp && ntp != tp) {
+ otp = ntp;
+ ntp = ntp->next_peer;
+ }
+ if (!ntp)
+ snmp_log(LOG_EMERG, "Can't find %s in %s's children\n",
+ tp->label, tp->parent->label);
+ else if (otp)
+ otp->next_peer = ntp->next_peer;
+ else
+ tp->parent->child_list = tp->next_peer;
+ }
+
+ if (tree_head == tp)
+ tree_head = tp->next_peer;
+}
+
+static void
+free_partial_tree(struct tree *tp, int keep_label)
+{
+ if (!tp)
+ return;
+
+ /*
+ * remove the data from this tree node
+ */
+ free_enums(&tp->enums);
+ free_ranges(&tp->ranges);
+ free_indexes(&tp->indexes);
+ free_varbinds(&tp->varbinds);
+ if (!keep_label)
+ SNMP_FREE(tp->label);
+ SNMP_FREE(tp->hint);
+ SNMP_FREE(tp->units);
+ SNMP_FREE(tp->description);
+ SNMP_FREE(tp->augments);
+ SNMP_FREE(tp->defaultValue);
+}
+
+/*
+ * free a tree node. Note: the node must already have been unlinked
+ * from the tree when calling this routine
+ */
+static void
+free_tree(struct tree *Tree)
+{
+ if (!Tree)
+ return;
+
+ unlink_tbucket(Tree);
+ free_partial_tree(Tree, FALSE);
+ if (Tree->number_modules > 1)
+ free((char *) Tree->module_list);
+ free((char *) Tree);
+}
+
+static void
+free_node(struct node *np)
+{
+ if (!np)
+ return;
+
+ free_enums(&np->enums);
+ free_ranges(&np->ranges);
+ free_indexes(&np->indexes);
+ free_varbinds(&np->varbinds);
+ if (np->label)
+ free(np->label);
+ if (np->hint)
+ free(np->hint);
+ if (np->units)
+ free(np->units);
+ if (np->description)
+ free(np->description);
+ if (np->defaultValue)
+ free(np->defaultValue);
+ if (np->parent)
+ free(np->parent);
+ if (np->augments)
+ free(np->augments);
+ if (np->filename)
+ free(np->filename);
+ free((char *) np);
+}
+
+#ifdef TEST
+static void
+print_nodes(FILE * fp, struct node *root)
+{
+ extern void xmalloc_stats(FILE *);
+ struct enum_list *ep;
+ struct index_list *ip;
+ struct range_list *rp;
+ struct varbind_list *vp;
+ struct node *np;
+
+ for (np = root; np; np = np->next) {
+ fprintf(fp, "%s ::= { %s %ld } (%d)\n", np->label, np->parent,
+ np->subid, np->type);
+ if (np->tc_index >= 0)
+ fprintf(fp, " TC = %s\n", tclist[np->tc_index].descriptor);
+ if (np->enums) {
+ fprintf(fp, " Enums: \n");
+ for (ep = np->enums; ep; ep = ep->next) {
+ fprintf(fp, " %s(%d)\n", ep->label, ep->value);
+ }
+ }
+ if (np->ranges) {
+ fprintf(fp, " Ranges: \n");
+ for (rp = np->ranges; rp; rp = rp->next) {
+ fprintf(fp, " %d..%d\n", rp->low, rp->high);
+ }
+ }
+ if (np->indexes) {
+ fprintf(fp, " Indexes: \n");
+ for (ip = np->indexes; ip; ip = ip->next) {
+ fprintf(fp, " %s\n", ip->ilabel);
+ }
+ }
+ if (np->augments)
+ fprintf(fp, " Augments: %s\n", np->augments);
+ if (np->varbinds) {
+ fprintf(fp, " Varbinds: \n");
+ for (vp = np->varbinds; vp; vp = vp->next) {
+ fprintf(fp, " %s\n", vp->vblabel);
+ }
+ }
+ if (np->hint)
+ fprintf(fp, " Hint: %s\n", np->hint);
+ if (np->units)
+ fprintf(fp, " Units: %s\n", np->units);
+ if (np->defaultValue)
+ fprintf(fp, " DefaultValue: %s\n", np->defaultValue);
+ }
+}
+#endif
+
+void
+print_subtree(FILE * f, struct tree *tree, int count)
+{
+ struct tree *tp;
+ int i;
+ char modbuf[256];
+
+ for (i = 0; i < count; i++)
+ fprintf(f, " ");
+ fprintf(f, "Children of %s(%ld):\n", tree->label, tree->subid);
+ count++;
+ for (tp = tree->child_list; tp; tp = tp->next_peer) {
+ for (i = 0; i < count; i++)
+ fprintf(f, " ");
+ fprintf(f, "%s:%s(%ld) type=%d",
+ module_name(tp->module_list[0], modbuf),
+ tp->label, tp->subid, tp->type);
+ if (tp->tc_index != -1)
+ fprintf(f, " tc=%d", tp->tc_index);
+ if (tp->hint)
+ fprintf(f, " hint=%s", tp->hint);
+ if (tp->units)
+ fprintf(f, " units=%s", tp->units);
+ if (tp->number_modules > 1) {
+ fprintf(f, " modules:");
+ for (i = 1; i < tp->number_modules; i++)
+ fprintf(f, " %s", module_name(tp->module_list[i], modbuf));
+ }
+ fprintf(f, "\n");
+ }
+ for (tp = tree->child_list; tp; tp = tp->next_peer) {
+ if (tp->child_list)
+ print_subtree(f, tp, count);
+ }
+}
+
+void
+print_ascii_dump_tree(FILE * f, struct tree *tree, int count)
+{
+ struct tree *tp;
+
+ count++;
+ for (tp = tree->child_list; tp; tp = tp->next_peer) {
+ fprintf(f, "%s OBJECT IDENTIFIER ::= { %s %ld }\n", tp->label,
+ tree->label, tp->subid);
+ }
+ for (tp = tree->child_list; tp; tp = tp->next_peer) {
+ if (tp->child_list)
+ print_ascii_dump_tree(f, tp, count);
+ }
+}
+
+static int translation_table[256];
+
+static void
+build_translation_table()
+{
+ int count;
+
+ for (count = 0; count < 256; count++) {
+ switch (count) {
+ case OBJID:
+ translation_table[count] = TYPE_OBJID;
+ break;
+ case OCTETSTR:
+ translation_table[count] = TYPE_OCTETSTR;
+ break;
+ case INTEGER:
+ translation_table[count] = TYPE_INTEGER;
+ break;
+ case NETADDR:
+ translation_table[count] = TYPE_NETADDR;
+ break;
+ case IPADDR:
+ translation_table[count] = TYPE_IPADDR;
+ break;
+ case COUNTER:
+ translation_table[count] = TYPE_COUNTER;
+ break;
+ case GAUGE:
+ translation_table[count] = TYPE_GAUGE;
+ break;
+ case TIMETICKS:
+ translation_table[count] = TYPE_TIMETICKS;
+ break;
+ case KW_OPAQUE:
+ translation_table[count] = TYPE_OPAQUE;
+ break;
+ case NUL:
+ translation_table[count] = TYPE_NULL;
+ break;
+ case COUNTER64:
+ translation_table[count] = TYPE_COUNTER64;
+ break;
+ case BITSTRING:
+ translation_table[count] = TYPE_BITSTRING;
+ break;
+ case NSAPADDRESS:
+ translation_table[count] = TYPE_NSAPADDRESS;
+ break;
+ case INTEGER32:
+ translation_table[count] = TYPE_INTEGER32;
+ break;
+ case UINTEGER32:
+ translation_table[count] = TYPE_UINTEGER;
+ break;
+ case UNSIGNED32:
+ translation_table[count] = TYPE_UNSIGNED32;
+ break;
+ case TRAPTYPE:
+ translation_table[count] = TYPE_TRAPTYPE;
+ break;
+ case NOTIFTYPE:
+ translation_table[count] = TYPE_NOTIFTYPE;
+ break;
+ case OBJGROUP:
+ translation_table[count] = TYPE_OBJGROUP;
+ break;
+ case MODULEIDENTITY:
+ translation_table[count] = TYPE_MODID;
+ break;
+ case AGENTCAP:
+ translation_table[count] = TYPE_AGENTCAP;
+ break;
+ case COMPLIANCE:
+ translation_table[count] = TYPE_MODCOMP;
+ break;
+ default:
+ translation_table[count] = TYPE_OTHER;
+ break;
+ }
+ }
+}
+
+static void
+init_tree_roots()
+{
+ struct tree *tp, *lasttp;
+ int base_modid;
+ int hash;
+
+ base_modid = which_module("SNMPv2-SMI");
+ if (base_modid == -1)
+ base_modid = which_module("RFC1155-SMI");
+ if (base_modid == -1)
+ base_modid = which_module("RFC1213-MIB");
+
+ /*
+ * build root node
+ */
+ tp = (struct tree *) calloc(1, sizeof(struct tree));
+ if (tp == NULL)
+ return;
+ tp->label = strdup("joint-iso-ccitt");
+ tp->modid = base_modid;
+ tp->number_modules = 1;
+ tp->module_list = &(tp->modid);
+ tp->subid = 2;
+ tp->tc_index = -1;
+ set_function(tp); /* from mib.c */
+ hash = NBUCKET(name_hash(tp->label));
+ tp->next = tbuckets[hash];
+ tbuckets[hash] = tp;
+ lasttp = tp;
+ root_imports[0].label = strdup(tp->label);
+ root_imports[0].modid = base_modid;
+
+ /*
+ * build root node
+ */
+ tp = (struct tree *) calloc(1, sizeof(struct tree));
+ if (tp == NULL)
+ return;
+ tp->next_peer = lasttp;
+ tp->label = strdup("ccitt");
+ tp->modid = base_modid;
+ tp->number_modules = 1;
+ tp->module_list = &(tp->modid);
+ tp->subid = 0;
+ tp->tc_index = -1;
+ set_function(tp); /* from mib.c */
+ hash = NBUCKET(name_hash(tp->label));
+ tp->next = tbuckets[hash];
+ tbuckets[hash] = tp;
+ lasttp = tp;
+ root_imports[1].label = strdup(tp->label);
+ root_imports[1].modid = base_modid;
+
+ /*
+ * build root node
+ */
+ tp = (struct tree *) calloc(1, sizeof(struct tree));
+ if (tp == NULL)
+ return;
+ tp->next_peer = lasttp;
+ tp->label = strdup("iso");
+ tp->modid = base_modid;
+ tp->number_modules = 1;
+ tp->module_list = &(tp->modid);
+ tp->subid = 1;
+ tp->tc_index = -1;
+ set_function(tp); /* from mib.c */
+ hash = NBUCKET(name_hash(tp->label));
+ tp->next = tbuckets[hash];
+ tbuckets[hash] = tp;
+ lasttp = tp;
+ root_imports[2].label = strdup(tp->label);
+ root_imports[2].modid = base_modid;
+
+ tree_head = tp;
+}
+
+#ifdef STRICT_MIB_PARSEING
+#define label_compare strcasecmp
+#else
+#define label_compare strcmp
+#endif
+
+
+struct tree *
+find_tree_node(const char *name, int modid)
+{
+ struct tree *tp, *headtp;
+ int count, *int_p;
+
+ if (!name || !*name)
+ return (NULL);
+
+ headtp = tbuckets[NBUCKET(name_hash(name))];
+ for (tp = headtp; tp; tp = tp->next) {
+ if (tp->label && !label_compare(tp->label, name)) {
+
+ if (modid == -1) /* Any module */
+ return (tp);
+
+ for (int_p = tp->module_list, count = 0;
+ count < tp->number_modules; ++count, ++int_p)
+ if (*int_p == modid)
+ return (tp);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * computes a value which represents how close name1 is to name2.
+ * * high scores mean a worse match.
+ * * (yes, the algorithm sucks!)
+ */
+#define MAX_BAD 0xffffff
+
+static u_int
+compute_match(const char *search_base, const char *key)
+{
+#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
+ int rc;
+ regex_t parsetree;
+ regmatch_t pmatch;
+
+ rc = regcomp(&parsetree, key, REG_ICASE | REG_EXTENDED);
+ if (rc == 0)
+ rc = regexec(&parsetree, search_base, 1, &pmatch, 0);
+ regfree(&parsetree);
+ if (rc == 0) {
+ /*
+ * found
+ */
+ return pmatch.rm_so;
+ }
+#else /* use our own wildcard matcher */
+ /*
+ * first find the longest matching substring (ick)
+ */
+ char *first = NULL, *result = NULL, *entry;
+ const char *position;
+ char *newkey = strdup(key);
+ char *st;
+
+
+ entry = strtok_r(newkey, "*", &st);
+ position = search_base;
+ while (entry) {
+ result = strcasestr(position, entry);
+
+ if (result == NULL) {
+ free(newkey);
+ return MAX_BAD;
+ }
+
+ if (first == NULL)
+ first = result;
+
+ position = result + strlen(entry);
+ entry = strtok_r(NULL, "*", &st);
+ }
+ free(newkey);
+ if (result)
+ return (first - search_base);
+#endif
+
+ /*
+ * not found
+ */
+ return MAX_BAD;
+}
+
+/*
+ * Find the tree node that best matches the pattern string.
+ * Use the "reported" flag such that only one match
+ * is attempted for every node.
+ *
+ * Warning! This function may recurse.
+ *
+ * Caller _must_ invoke clear_tree_flags before first call
+ * to this function. This function may be called multiple times
+ * to ensure that the entire tree is traversed.
+ */
+
+struct tree *
+find_best_tree_node(const char *pattrn, struct tree *tree_top,
+ u_int * match)
+{
+ struct tree *tp, *best_so_far = NULL, *retptr;
+ u_int old_match = MAX_BAD, new_match = MAX_BAD;
+
+ if (!pattrn || !*pattrn)
+ return (NULL);
+
+ if (!tree_top)
+ tree_top = get_tree_head();
+
+ for (tp = tree_top; tp; tp = tp->next_peer) {
+ if (!tp->reported && tp->label)
+ new_match = compute_match(tp->label, pattrn);
+ tp->reported = 1;
+
+ if (new_match < old_match) {
+ best_so_far = tp;
+ old_match = new_match;
+ }
+ if (new_match == 0)
+ break; /* this is the best result we can get */
+ if (tp->child_list) {
+ retptr =
+ find_best_tree_node(pattrn, tp->child_list, &new_match);
+ if (new_match < old_match) {
+ best_so_far = retptr;
+ old_match = new_match;
+ }
+ if (new_match == 0)
+ break; /* this is the best result we can get */
+ }
+ }
+
+ if (match)
+ *match = old_match;
+ return (best_so_far);
+}
+
+
+static void
+merge_anon_children(struct tree *tp1, struct tree *tp2)
+ /*
+ * NB: tp1 is the 'anonymous' node
+ */
+{
+ struct tree *child1, *child2, *previous;
+
+ for (child1 = tp1->child_list; child1;) {
+
+ for (child2 = tp2->child_list, previous = NULL;
+ child2; previous = child2, child2 = child2->next_peer) {
+
+ if (child1->subid == child2->subid) {
+ /*
+ * Found 'matching' children,
+ * so merge them
+ */
+ if (!strncmp(child1->label, ANON, ANON_LEN)) {
+ merge_anon_children(child1, child2);
+
+ child1->child_list = NULL;
+ previous = child1; /* Finished with 'child1' */
+ child1 = child1->next_peer;
+ free_tree(previous);
+ goto next;
+ }
+
+ else if (!strncmp(child2->label, ANON, ANON_LEN)) {
+ merge_anon_children(child2, child1);
+
+ if (previous)
+ previous->next_peer = child2->next_peer;
+ else
+ tp2->child_list = child2->next_peer;
+ free_tree(child2);
+
+ previous = child1; /* Move 'child1' to 'tp2' */
+ child1 = child1->next_peer;
+ previous->next_peer = tp2->child_list;
+ tp2->child_list = previous;
+ for (previous = tp2->child_list;
+ previous; previous = previous->next_peer)
+ previous->parent = tp2;
+ goto next;
+ } else if (!label_compare(child1->label, child2->label)) {
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Warning: %s.%ld is both %s and %s (%s)\n",
+ tp2->label, child1->subid, child1->label,
+ child2->label, File);
+ }
+ continue;
+ } else {
+ /*
+ * Two copies of the same node.
+ * 'child2' adopts the children of 'child1'
+ */
+
+ if (child2->child_list) {
+ for (previous = child2->child_list; previous->next_peer; previous = previous->next_peer); /* Find the end of the list */
+ previous->next_peer = child1->child_list;
+ } else
+ child2->child_list = child1->child_list;
+ for (previous = child1->child_list;
+ previous; previous = previous->next_peer)
+ previous->parent = child2;
+ child1->child_list = NULL;
+
+ previous = child1; /* Finished with 'child1' */
+ child1 = child1->next_peer;
+ free_tree(previous);
+ goto next;
+ }
+ }
+ }
+ /*
+ * If no match, move 'child1' to 'tp2' child_list
+ */
+ if (child1) {
+ previous = child1;
+ child1 = child1->next_peer;
+ previous->parent = tp2;
+ previous->next_peer = tp2->child_list;
+ tp2->child_list = previous;
+ }
+ next:;
+ }
+}
+
+
+/*
+ * Find all the children of root in the list of nodes. Link them into the
+ * tree and out of the nodes list.
+ */
+static void
+do_subtree(struct tree *root, struct node **nodes)
+{
+ struct tree *tp, *anon_tp = NULL;
+ struct tree *xroot = root;
+ struct node *np, **headp;
+ struct node *oldnp = NULL, *child_list = NULL, *childp = NULL;
+ int hash;
+ int *int_p;
+
+ while (xroot->next_peer && xroot->next_peer->subid == root->subid) {
+#if 0
+ printf("xroot: %s.%s => %s\n", xroot->parent->label, xroot->label,
+ xroot->next_peer->label);
+#endif
+ xroot = xroot->next_peer;
+ }
+
+ tp = root;
+ headp = &nbuckets[NBUCKET(name_hash(tp->label))];
+ /*
+ * Search each of the nodes for one whose parent is root, and
+ * move each into a separate list.
+ */
+ for (np = *headp; np; np = np->next) {
+ if (!label_compare(tp->label, np->parent)) {
+ /*
+ * take this node out of the node list
+ */
+ if (oldnp == NULL) {
+ *headp = np->next; /* fix root of node list */
+ } else {
+ oldnp->next = np->next; /* link around this node */
+ }
+ if (child_list)
+ childp->next = np;
+ else
+ child_list = np;
+ childp = np;
+ } else {
+ oldnp = np;
+ }
+
+ }
+ if (childp)
+ childp->next = NULL;
+ /*
+ * Take each element in the child list and place it into the tree.
+ */
+ for (np = child_list; np; np = np->next) {
+ struct tree *otp = NULL;
+ struct tree *xxroot = xroot;
+ anon_tp = NULL;
+ tp = xroot->child_list;
+
+ if (np->subid == -1) {
+ /*
+ * name ::= { parent }
+ */
+ np->subid = xroot->subid;
+ tp = xroot;
+ xxroot = xroot->parent;
+ }
+
+ while (tp) {
+ if (tp->subid == np->subid)
+ break;
+ else {
+ otp = tp;
+ tp = tp->next_peer;
+ }
+ }
+ if (tp) {
+ if (!label_compare(tp->label, np->label)) {
+ /*
+ * Update list of modules
+ */
+ int_p =
+ (int *) malloc((tp->number_modules + 1) * sizeof(int));
+ if (int_p == NULL)
+ return;
+ memcpy(int_p, tp->module_list,
+ tp->number_modules * sizeof(int));
+ int_p[tp->number_modules] = np->modid;
+ if (tp->number_modules > 1)
+ free((char *) tp->module_list);
+ ++tp->number_modules;
+ tp->module_list = int_p;
+
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_REPLACE)) {
+ /*
+ * Replace from node
+ */
+ tree_from_node(tp, np);
+ }
+ /*
+ * Handle children
+ */
+ do_subtree(tp, nodes);
+ continue;
+ }
+ if (!strncmp(np->label, ANON, ANON_LEN) ||
+ !strncmp(tp->label, ANON, ANON_LEN)) {
+ anon_tp = tp; /* Need to merge these two trees later */
+ } else if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Warning: %s.%ld is both %s and %s (%s)\n",
+ root->label, np->subid, tp->label, np->label,
+ File);
+ }
+ }
+
+ tp = (struct tree *) calloc(1, sizeof(struct tree));
+ if (tp == NULL)
+ return;
+ tp->parent = xxroot;
+ tp->modid = np->modid;
+ tp->number_modules = 1;
+ tp->module_list = &(tp->modid);
+ tree_from_node(tp, np);
+ tp->next_peer = otp ? otp->next_peer : xxroot->child_list;
+ if (otp)
+ otp->next_peer = tp;
+ else
+ xxroot->child_list = tp;
+ hash = NBUCKET(name_hash(tp->label));
+ tp->next = tbuckets[hash];
+ tbuckets[hash] = tp;
+ do_subtree(tp, nodes);
+
+ if (anon_tp) {
+ if (!strncmp(tp->label, ANON, ANON_LEN)) {
+ /*
+ * The new node is anonymous,
+ * so merge it with the existing one.
+ */
+ merge_anon_children(tp, anon_tp);
+
+ /*
+ * unlink and destroy tp
+ */
+ unlink_tree(tp);
+ free_tree(tp);
+ } else if (!strncmp(anon_tp->label, ANON, ANON_LEN)) {
+ struct tree *ntp;
+ /*
+ * The old node was anonymous,
+ * so merge it with the existing one,
+ * and fill in the full information.
+ */
+ merge_anon_children(anon_tp, tp);
+
+ /*
+ * unlink anon_tp from the hash
+ */
+ unlink_tbucket(anon_tp);
+
+ /*
+ * get rid of old contents of anon_tp
+ */
+ free_partial_tree(anon_tp, FALSE);
+
+ /*
+ * put in the current information
+ */
+ anon_tp->label = tp->label;
+ anon_tp->child_list = tp->child_list;
+ anon_tp->modid = tp->modid;
+ anon_tp->tc_index = tp->tc_index;
+ anon_tp->type = tp->type;
+ anon_tp->enums = tp->enums;
+ anon_tp->indexes = tp->indexes;
+ anon_tp->augments = tp->augments;
+ anon_tp->varbinds = tp->varbinds;
+ anon_tp->ranges = tp->ranges;
+ anon_tp->hint = tp->hint;
+ anon_tp->units = tp->units;
+ anon_tp->description = tp->description;
+ anon_tp->defaultValue = tp->defaultValue;
+ anon_tp->parent = tp->parent;
+
+ set_function(anon_tp);
+
+ /*
+ * update parent pointer in moved children
+ */
+ ntp = anon_tp->child_list;
+ while (ntp) {
+ ntp->parent = anon_tp;
+ ntp = ntp->next_peer;
+ }
+
+ /*
+ * hash in anon_tp in its new place
+ */
+ hash = NBUCKET(name_hash(anon_tp->label));
+ anon_tp->next = tbuckets[hash];
+ tbuckets[hash] = anon_tp;
+
+ /*
+ * unlink and destroy tp
+ */
+ unlink_tbucket(tp);
+ unlink_tree(tp);
+ free(tp);
+ } else {
+ /*
+ * Uh? One of these two should have been anonymous!
+ */
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Warning: expected anonymous node (either %s or %s) in %s\n",
+ tp->label, anon_tp->label, File);
+ }
+ }
+ anon_tp = NULL;
+ }
+ }
+ /*
+ * free all nodes that were copied into tree
+ */
+ oldnp = NULL;
+ for (np = child_list; np; np = np->next) {
+ if (oldnp)
+ free_node(oldnp);
+ oldnp = np;
+ }
+ if (oldnp)
+ free_node(oldnp);
+}
+
+static void
+do_linkup(struct module *mp, struct node *np)
+{
+ struct module_import *mip;
+ struct node *onp, *oldp, *newp;
+ struct tree *tp;
+ int i, more;
+ /*
+ * All modules implicitly import
+ * the roots of the tree
+ */
+ if (snmp_get_do_debugging() > 1)
+ dump_module_list();
+ DEBUGMSGTL(("parse-mibs", "Processing IMPORTS for module %d %s\n",
+ mp->modid, mp->name));
+ if (mp->no_imports == 0) {
+ mp->no_imports = NUMBER_OF_ROOT_NODES;
+ mp->imports = root_imports;
+ }
+
+ /*
+ * Build the tree
+ */
+ init_node_hash(np);
+ for (i = 0, mip = mp->imports; i < mp->no_imports; ++i, ++mip) {
+ char modbuf[256];
+ DEBUGMSGTL(("parse-mibs", " Processing import: %s\n",
+ mip->label));
+ if (get_tc_index(mip->label, mip->modid) != -1)
+ continue;
+ tp = find_tree_node(mip->label, mip->modid);
+ if (!tp) {
+ if (mip->modid != -1)
+ snmp_log(LOG_WARNING,
+ "Did not find '%s' in module %s (%s)\n",
+ mip->label, module_name(mip->modid, modbuf),
+ File);
+ continue;
+ }
+ do_subtree(tp, &np);
+ }
+
+ /*
+ * If any nodes left over,
+ * check that they're not the result of a "fully qualified"
+ * name, and then add them to the list of orphans
+ */
+
+ if (!np)
+ return;
+ for (tp = tree_head; tp; tp = tp->next_peer)
+ do_subtree(tp, &np);
+ if (!np)
+ return;
+
+ /*
+ * quietly move all internal references to the orphan list
+ */
+ oldp = orphan_nodes;
+ do {
+ for (i = 0; i < NHASHSIZE; i++)
+ for (onp = nbuckets[i]; onp; onp = onp->next) {
+ struct node *op = NULL;
+ int hash = NBUCKET(name_hash(onp->label));
+ np = nbuckets[hash];
+ while (np) {
+ if (label_compare(onp->label, np->parent)) {
+ op = np;
+ np = np->next;
+ } else {
+ if (op)
+ op->next = np->next;
+ else
+ nbuckets[hash] = np->next;
+ np->next = orphan_nodes;
+ orphan_nodes = np;
+ op = NULL;
+ np = nbuckets[hash];
+ }
+ }
+ }
+ newp = orphan_nodes;
+ more = 0;
+ for (onp = orphan_nodes; onp != oldp; onp = onp->next) {
+ struct node *op = NULL;
+ int hash = NBUCKET(name_hash(onp->label));
+ np = nbuckets[hash];
+ while (np) {
+ if (label_compare(onp->label, np->parent)) {
+ op = np;
+ np = np->next;
+ } else {
+ if (op)
+ op->next = np->next;
+ else
+ nbuckets[hash] = np->next;
+ np->next = orphan_nodes;
+ orphan_nodes = np;
+ op = NULL;
+ np = nbuckets[hash];
+ more = 1;
+ }
+ }
+ }
+ oldp = newp;
+ } while (more);
+
+ /*
+ * complain about left over nodes
+ */
+ for (np = orphan_nodes; np && np->next; np = np->next); /* find the end of the orphan list */
+ for (i = 0; i < NHASHSIZE; i++)
+ if (nbuckets[i]) {
+ if (orphan_nodes)
+ onp = np->next = nbuckets[i];
+ else
+ onp = orphan_nodes = nbuckets[i];
+ nbuckets[i] = NULL;
+ while (onp) {
+ snmp_log(LOG_WARNING,
+ "Unlinked OID in %s: %s ::= { %s %ld }\n",
+ (mp->name ? mp->name : "<no module>"),
+ (onp->label ? onp->label : "<no label>"),
+ (onp->parent ? onp->parent : "<no parent>"),
+ onp->subid);
+ snmp_log(LOG_WARNING,
+ "Undefined identifier: %s near line %d of %s\n",
+ (onp->parent ? onp->parent : "<no parent>"),
+ onp->lineno, onp->filename);
+ np = onp;
+ onp = onp->next;
+ }
+ }
+ return;
+}
+
+
+/*
+ * Takes a list of the form:
+ * { iso org(3) dod(6) 1 }
+ * and creates several nodes, one for each parent-child pair.
+ * Returns 0 on error.
+ */
+static int
+getoid(FILE * fp, struct subid_s *id, /* an array of subids */
+ int length)
+{ /* the length of the array */
+ register int count;
+ int type;
+ char token[MAXTOKEN];
+
+ if ((type = get_token(fp, token, MAXTOKEN)) != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ return 0;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ for (count = 0; count < length; count++, id++) {
+ id->label = NULL;
+ id->modid = current_module;
+ id->subid = -1;
+ if (type == RIGHTBRACKET)
+ return count;
+ if (type == LABEL) {
+ /*
+ * this entry has a label
+ */
+ id->label = strdup(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LEFTPAREN) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == NUMBER) {
+ id->subid = strtoul(token, NULL, 10);
+ if ((type =
+ get_token(fp, token, MAXTOKEN)) != RIGHTPAREN) {
+ print_error("Expected a closing parenthesis",
+ token, type);
+ return 0;
+ }
+ } else {
+ print_error("Expected a number", token, type);
+ return 0;
+ }
+ } else {
+ continue;
+ }
+ } else if (type == NUMBER) {
+ /*
+ * this entry has just an integer sub-identifier
+ */
+ id->subid = strtoul(token, NULL, 10);
+ } else {
+ print_error("Expected label or number", token, type);
+ return 0;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ print_error("Too long OID", token, type);
+ return 0;
+}
+
+/*
+ * Parse a sequence of object subidentifiers for the given name.
+ * The "label OBJECT IDENTIFIER ::=" portion has already been parsed.
+ *
+ * The majority of cases take this form :
+ * label OBJECT IDENTIFIER ::= { parent 2 }
+ * where a parent label and a child subidentifier number are specified.
+ *
+ * Variations on the theme include cases where a number appears with
+ * the parent, or intermediate subidentifiers are specified by label,
+ * by number, or both.
+ *
+ * Here are some representative samples :
+ * internet OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 }
+ * mgmt OBJECT IDENTIFIER ::= { internet 2 }
+ * rptrInfoHealth OBJECT IDENTIFIER ::= { snmpDot3RptrMgt 0 4 }
+ *
+ * Here is a very rare form :
+ * iso OBJECT IDENTIFIER ::= { 1 }
+ *
+ * Returns NULL on error. When this happens, memory may be leaked.
+ */
+static struct node *
+parse_objectid(FILE * fp, char *name)
+{
+ register int count;
+ register struct subid_s *op, *nop;
+ int length;
+ struct subid_s loid[32];
+ struct node *np, *root = NULL, *oldnp = NULL;
+ struct tree *tp;
+
+ if ((length = getoid(fp, loid, 32)) == 0) {
+ print_error("Bad object identifier", NULL, CONTINUE);
+ return NULL;
+ }
+
+ /*
+ * Handle numeric-only object identifiers,
+ * by labelling the first sub-identifier
+ */
+ op = loid;
+ if (!op->label)
+ for (tp = tree_head; tp; tp = tp->next_peer)
+ if ((int) tp->subid == op->subid) {
+ op->label = strdup(tp->label);
+ break;
+ }
+
+ /*
+ * Handle "label OBJECT-IDENTIFIER ::= { subid }"
+ */
+ if (length == 1) {
+ op = loid;
+ np = alloc_node(op->modid);
+ if (np == NULL)
+ return (NULL);
+ np->subid = op->subid;
+ np->label = strdup(name);
+ np->parent = op->label;
+ return np;
+ }
+
+ /*
+ * For each parent-child subid pair in the subid array,
+ * create a node and link it into the node list.
+ */
+ for (count = 0, op = loid, nop = loid + 1; count < (length - 1);
+ count++, op++, nop++) {
+ /*
+ * every node must have parent's name and child's name or number
+ */
+ /*
+ * XX the next statement is always true -- does it matter ??
+ */
+ if (op->label && (nop->label || (nop->subid != -1))) {
+ np = alloc_node(nop->modid);
+ if (np == NULL)
+ return (NULL);
+ if (root == NULL)
+ root = np;
+
+ np->parent = strdup(op->label);
+ if (count == (length - 2)) {
+ /*
+ * The name for this node is the label for this entry
+ */
+ np->label = strdup(name);
+ } else {
+ if (!nop->label) {
+ nop->label = (char *) malloc(20 + ANON_LEN);
+ if (nop->label == NULL)
+ return (NULL);
+ sprintf(nop->label, "%s%d", ANON, anonymous++);
+ }
+ np->label = strdup(nop->label);
+ }
+ if (nop->subid != -1)
+ np->subid = nop->subid;
+ else
+ print_error("Warning: This entry is pretty silly",
+ np->label, CONTINUE);
+
+ /*
+ * set up next entry
+ */
+ if (oldnp)
+ oldnp->next = np;
+ oldnp = np;
+ } /* end if(op->label... */
+ }
+
+ /*
+ * free the loid array
+ */
+ for (count = 0, op = loid; count < length; count++, op++) {
+ if (op->label)
+ free(op->label);
+ }
+
+ return root;
+}
+
+static int
+get_tc(const char *descriptor,
+ int modid,
+ int *tc_index,
+ struct enum_list **ep, struct range_list **rp, char **hint)
+{
+ int i;
+ struct tc *tcp;
+
+ i = get_tc_index(descriptor, modid);
+ if (tc_index)
+ *tc_index = i;
+ if (i != -1) {
+ tcp = &tclist[i];
+ if (ep) {
+ free_enums(ep);
+ *ep = copy_enums(tcp->enums);
+ }
+ if (rp) {
+ free_ranges(rp);
+ *rp = copy_ranges(tcp->ranges);
+ }
+ if (hint) {
+ if (*hint)
+ free(*hint);
+ *hint = (tcp->hint ? strdup(tcp->hint) : NULL);
+ }
+ return tcp->type;
+ }
+ return LABEL;
+}
+
+/*
+ * return index into tclist of given TC descriptor
+ * return -1 if not found
+ */
+static int
+get_tc_index(const char *descriptor, int modid)
+{
+ int i;
+ struct tc *tcp;
+ struct module *mp;
+ struct module_import *mip;
+
+ /*
+ * Check that the descriptor isn't imported
+ * by searching the import list
+ */
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->modid == modid)
+ break;
+ if (mp)
+ for (i = 0, mip = mp->imports; i < mp->no_imports; ++i, ++mip) {
+ if (!label_compare(mip->label, descriptor)) {
+ /*
+ * Found it - so amend the module ID
+ */
+ modid = mip->modid;
+ break;
+ }
+ }
+
+
+ for (i = 0, tcp = tclist; i < MAXTC; i++, tcp++) {
+ if (tcp->type == 0)
+ break;
+ if (!label_compare(descriptor, tcp->descriptor) &&
+ ((modid == tcp->modid) || (modid == -1))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * translate integer tc_index to string identifier from tclist
+ * *
+ * * Returns pointer to string in table (should not be modified) or NULL
+ */
+const char *
+get_tc_descriptor(int tc_index)
+{
+ if (tc_index < 0 || tc_index >= MAXTC)
+ return NULL;
+ return (tclist[tc_index].descriptor);
+}
+
+
+/*
+ * Parses an enumeration list of the form:
+ * { label(value) label(value) ... }
+ * The initial { has already been parsed.
+ * Returns NULL on error.
+ */
+
+static struct enum_list *
+parse_enumlist(FILE * fp, struct enum_list **retp)
+{
+ register int type;
+ char token[MAXTOKEN];
+ struct enum_list *ep = NULL, **epp = &ep;
+
+ free_enums(retp);
+
+ while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE) {
+ if (type == RIGHTBRACKET)
+ break;
+ if (type == LABEL) {
+ /*
+ * this is an enumerated label
+ */
+ *epp =
+ (struct enum_list *) calloc(1, sizeof(struct enum_list));
+ if (*epp == NULL)
+ return (NULL);
+ /*
+ * a reasonable approximation for the length
+ */
+ (*epp)->label = strdup(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTPAREN) {
+ print_error("Expected \"(\"", token, type);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != NUMBER) {
+ print_error("Expected integer", token, type);
+ return NULL;
+ }
+ (*epp)->value = strtol(token, NULL, 10);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != RIGHTPAREN) {
+ print_error("Expected \")\"", token, type);
+ return NULL;
+ }
+ epp = &(*epp)->next;
+ }
+ }
+ if (type == ENDOFFILE) {
+ print_error("Expected \"}\"", token, type);
+ return NULL;
+ }
+ *retp = ep;
+ return ep;
+}
+
+static struct range_list *
+parse_ranges(FILE * fp, struct range_list **retp)
+{
+ int low, high;
+ char nexttoken[MAXTOKEN];
+ int nexttype;
+ struct range_list *rp = NULL, **rpp = &rp;
+ int size = 0, taken = 1;
+
+ free_ranges(retp);
+
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == SIZE) {
+ size = 1;
+ taken = 0;
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype != LEFTPAREN)
+ print_error("Expected \"(\" after SIZE", nexttoken, nexttype);
+ }
+
+ do {
+ if (!taken)
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ else
+ taken = 0;
+ high = low = strtol(nexttoken, NULL, 10);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == RANGE) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ high = strtol(nexttoken, NULL, 10);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ }
+ *rpp = (struct range_list *) calloc(1, sizeof(struct range_list));
+ if (*rpp == NULL)
+ break;
+ (*rpp)->low = low;
+ (*rpp)->high = high;
+ rpp = &(*rpp)->next;
+
+ } while (nexttype == BAR);
+ if (size) {
+ if (nexttype != RIGHTPAREN)
+ print_error("Expected \")\" after SIZE", nexttoken, nexttype);
+ nexttype = get_token(fp, nexttoken, nexttype);
+ }
+ if (nexttype != RIGHTPAREN)
+ print_error("Expected \")\"", nexttoken, nexttype);
+
+ *retp = rp;
+ return rp;
+}
+
+/*
+ * Parses an asn type. Structures are ignored by this parser.
+ * Returns NULL on error.
+ */
+static struct node *
+parse_asntype(FILE * fp, char *name, int *ntype, char *ntoken)
+{
+ int type, i;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ char *hint = NULL;
+ struct tc *tcp;
+ int level;
+
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == SEQUENCE || type == CHOICE) {
+ level = 0;
+ while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE) {
+ if (type == LEFTBRACKET) {
+ level++;
+ } else if (type == RIGHTBRACKET && --level == 0) {
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ return NULL;
+ }
+ }
+ print_error("Expected \"}\"", token, type);
+ return NULL;
+ } else if (type == LEFTBRACKET) {
+ struct node *np;
+ int ch_next = '{';
+ ungetc(ch_next, fp);
+ np = parse_objectid(fp, name);
+ if (np != NULL) {
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ return np;
+ }
+ return NULL;
+ } else if (type == LEFTSQBRACK) {
+ int size = 0;
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type != ENDOFFILE && type != RIGHTSQBRACK);
+ if (type != RIGHTSQBRACK) {
+ print_error("Expected \"]\"", token, type);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == IMPLICIT)
+ type = get_token(fp, token, MAXTOKEN);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype == LEFTPAREN) {
+ switch (type) {
+ case OCTETSTR:
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype != SIZE) {
+ print_error("Expected SIZE", ntoken, *ntype);
+ return NULL;
+ }
+ size = 1;
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype != LEFTPAREN) {
+ print_error("Expected \"(\" after SIZE", ntoken,
+ *ntype);
+ return NULL;
+ }
+ /*
+ * fall through
+ */
+ case INTEGER:
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ do {
+ if (*ntype != NUMBER)
+ print_error("Expected NUMBER", ntoken, *ntype);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype == RANGE) {
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype != NUMBER)
+ print_error("Expected NUMBER", ntoken, *ntype);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ }
+ } while (*ntype == BAR);
+ if (*ntype != RIGHTPAREN) {
+ print_error("Expected \")\"", ntoken, *ntype);
+ return NULL;
+ }
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (size) {
+ if (*ntype != RIGHTPAREN) {
+ print_error("Expected \")\" to terminate SIZE",
+ ntoken, *ntype);
+ return NULL;
+ }
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ }
+ }
+ }
+ return NULL;
+ } else {
+ if (type == CONVENTION) {
+ while (type != SYNTAX && type != ENDOFFILE) {
+ if (type == DISPLAYHINT) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING)
+ print_error("DISPLAY-HINT must be string", token,
+ type);
+ else
+ hint = strdup(token);
+ } else
+ type =
+ get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == OBJECT) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != IDENTIFIER) {
+ print_error("Expected IDENTIFIER", token, type);
+ SNMP_FREE(hint);
+ return NULL;
+ }
+ type = OBJID;
+ }
+ } else if (type == OBJECT) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != IDENTIFIER) {
+ print_error("Expected IDENTIFIER", token, type);
+ return NULL;
+ }
+ type = OBJID;
+ }
+
+ if (type == LABEL) {
+ type = get_tc(token, current_module, NULL, NULL, NULL, NULL);
+ }
+
+ /*
+ * textual convention
+ */
+ for (i = 0; i < MAXTC; i++) {
+ if (tclist[i].type == 0)
+ break;
+ }
+
+ if (i == MAXTC) {
+ print_error("Too many textual conventions", token, type);
+ SNMP_FREE(hint);
+ return NULL;
+ }
+ if (!(type & SYNTAX_MASK)) {
+ print_error("Textual convention doesn't map to real type",
+ token, type);
+ SNMP_FREE(hint);
+ return NULL;
+ }
+ tcp = &tclist[i];
+ tcp->modid = current_module;
+ tcp->descriptor = strdup(name);
+ tcp->hint = hint;
+ tcp->type = type;
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype == LEFTPAREN) {
+ tcp->ranges = parse_ranges(fp, &tcp->ranges);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ } else if (*ntype == LEFTBRACKET) {
+ /*
+ * if there is an enumeration list, parse it
+ */
+ tcp->enums = parse_enumlist(fp, &tcp->enums);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ }
+ return NULL;
+ }
+}
+
+
+/*
+ * Parses an OBJECT TYPE macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_objecttype(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char nexttoken[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ int nexttype, tctype;
+ register struct node *np;
+
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != SYNTAX) {
+ print_error("Bad format for OBJECT-TYPE", token, type);
+ return NULL;
+ }
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == OBJECT) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != IDENTIFIER) {
+ print_error("Expected IDENTIFIER", token, type);
+ free_node(np);
+ return NULL;
+ }
+ type = OBJID;
+ }
+ if (type == LABEL) {
+ int tmp_index;
+ tctype = get_tc(token, current_module, &tmp_index,
+ &np->enums, &np->ranges, &np->hint);
+ if (tctype == LABEL &&
+ netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS) > 1) {
+ print_error("Warning: No known translation for type", token,
+ type);
+ }
+ type = tctype;
+ np->tc_index = tmp_index; /* store TC for later reference */
+ }
+ np->type = type;
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ switch (type) {
+ case SEQUENCE:
+ if (nexttype == OF) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+
+ }
+ break;
+ case INTEGER:
+ case INTEGER32:
+ case UINTEGER32:
+ case UNSIGNED32:
+ case COUNTER:
+ case GAUGE:
+ case BITSTRING:
+ case LABEL:
+ if (nexttype == LEFTBRACKET) {
+ /*
+ * if there is an enumeration list, parse it
+ */
+ np->enums = parse_enumlist(fp, &np->enums);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ } else if (nexttype == LEFTPAREN) {
+ /*
+ * if there is a range list, parse it
+ */
+ np->ranges = parse_ranges(fp, &np->ranges);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ }
+ break;
+ case OCTETSTR:
+ case KW_OPAQUE:
+ /*
+ * parse any SIZE specification
+ */
+ if (nexttype == LEFTPAREN) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == SIZE) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == LEFTPAREN) {
+ np->ranges = parse_ranges(fp, &np->ranges);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN); /* ) */
+ if (nexttype == RIGHTPAREN) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ break;
+ }
+ }
+ }
+ print_error("Bad SIZE syntax", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ case OBJID:
+ case NETADDR:
+ case IPADDR:
+ case TIMETICKS:
+ case NUL:
+ case NSAPADDRESS:
+ case COUNTER64:
+ break;
+ default:
+ print_error("Bad syntax", token, type);
+ free_node(np);
+ return NULL;
+ }
+ if (nexttype == UNITS) {
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad UNITS", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ np->units = strdup(quoted_string_buffer);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ }
+ if (nexttype != ACCESS) {
+ print_error("Should be ACCESS", nexttoken, nexttype);
+ free_node(np);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != READONLY && type != READWRITE && type != WRITEONLY
+ && type != NOACCESS && type != READCREATE && type != ACCNOTIFY) {
+ print_error("Bad ACCESS type", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->access = type;
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != STATUS) {
+ print_error("Should be STATUS", token, type);
+ free_node(np);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != MANDATORY && type != CURRENT && type != KW_OPTIONAL &&
+ type != OBSOLETE && type != DEPRECATED) {
+ print_error("Bad STATUS", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->status = type;
+ /*
+ * Optional parts of the OBJECT-TYPE macro
+ */
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != EQUALS && type != ENDOFFILE) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ break;
+
+ case REFERENCE:
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad REFERENCE", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ case INDEX:
+ if (np->augments) {
+ print_error("Cannot have both INDEX and AUGMENTS", token,
+ type);
+ free_node(np);
+ return NULL;
+ }
+ np->indexes = getIndexes(fp, &np->indexes);
+ if (np->indexes == NULL) {
+ print_error("Bad INDEX list", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ case AUGMENTS:
+ if (np->indexes) {
+ print_error("Cannot have both INDEX and AUGMENTS", token,
+ type);
+ free_node(np);
+ return NULL;
+ }
+ np->indexes = getIndexes(fp, &np->indexes);
+ if (np->indexes == NULL) {
+ print_error("Bad AUGMENTS list", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->augments = strdup(np->indexes->ilabel);
+ free_indexes(&np->indexes);
+ break;
+ case DEFVAL:
+ /*
+ * Mark's defVal section
+ */
+ type = get_token(fp, quoted_string_buffer, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Bad DEFAULTVALUE", quoted_string_buffer,
+ type);
+ free_node(np);
+ return NULL;
+ }
+
+ {
+ int level = 1;
+ char defbuf[512];
+
+ defbuf[0] = 0;
+ while (1) {
+ type = get_token(fp, quoted_string_buffer, MAXTOKEN);
+ if ((type == RIGHTBRACKET && --level == 0)
+ || type == ENDOFFILE)
+ break;
+ else if (type == LEFTBRACKET)
+ level++;
+ if (type == QUOTESTRING) {
+ if (strlen(defbuf)+2 < sizeof(defbuf)) {
+ defbuf[ strlen(defbuf)+2 ] = 0;
+ defbuf[ strlen(defbuf)+1 ] = '"';
+ defbuf[ strlen(defbuf) ] = '\\';
+ }
+ defbuf[ sizeof(defbuf)-1 ] = 0;
+ }
+ strncat(defbuf, quoted_string_buffer,
+ sizeof(defbuf)-strlen(defbuf));
+ defbuf[ sizeof(defbuf)-1 ] = 0;
+ if (type == QUOTESTRING) {
+ if (strlen(defbuf)+2 < sizeof(defbuf)) {
+ defbuf[ strlen(defbuf)+2 ] = 0;
+ defbuf[ strlen(defbuf)+1 ] = '"';
+ defbuf[ strlen(defbuf) ] = '\\';
+ }
+ defbuf[ sizeof(defbuf)-1 ] = 0;
+ }
+ if (strlen(defbuf)+1 < sizeof(defbuf)) {
+ defbuf[ strlen(defbuf)+1 ] = 0;
+ defbuf[ strlen(defbuf) ] = ' ';
+ }
+ }
+
+ if (type != RIGHTBRACKET) {
+ print_error("Bad DEFAULTVALUE", quoted_string_buffer,
+ type);
+ free_node(np);
+ return NULL;
+ }
+
+ defbuf[strlen(defbuf) - 1] = 0;
+ np->defaultValue = strdup(defbuf);
+ }
+
+ break;
+
+ case NUM_ENTRIES:
+ if (tossObjectIdentifier(fp) != OBJID) {
+ print_error("Bad Object Identifier", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+
+ default:
+ print_error("Bad format of optional clauses", token, type);
+ free_node(np);
+ return NULL;
+
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != EQUALS) {
+ print_error("Bad format", token, type);
+ free_node(np);
+ return NULL;
+ }
+ return merge_parse_objectid(np, fp, name);
+}
+
+/*
+ * Parses an OBJECT GROUP macro.
+ * Returns 0 on error.
+ *
+ * Also parses object-identity, since they are similar (ignore STATUS).
+ * - WJH 10/96
+ */
+static struct node *
+parse_objectgroup(FILE * fp, char *name, int what, struct objgroup **ol)
+{
+ int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == what) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ goto skip;
+ }
+ do {
+ struct objgroup *o;
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad identifier", token, type);
+ goto skip;
+ }
+ o = (struct objgroup *) malloc(sizeof(struct objgroup));
+ o->line = mibLine;
+ o->name = strdup(token);
+ o->next = *ol;
+ *ol = o;
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type == COMMA);
+ if (type != RIGHTBRACKET) {
+ print_error("Expected \"}\" after list", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, type);
+ }
+ if (type != STATUS) {
+ print_error("Expected STATUS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) {
+ print_error("Bad STATUS value", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == REFERENCE) {
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad REFERENCE", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != EQUALS)
+ print_error("Expected \"::=\"", token, type);
+ skip:
+ while (type != EQUALS && type != ENDOFFILE)
+ type = get_token(fp, token, MAXTOKEN);
+
+ return merge_parse_objectid(np, fp, name);
+}
+
+/*
+ * Parses a NOTIFICATION-TYPE macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_notificationDefinition(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ register struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != EQUALS && type != ENDOFFILE) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ break;
+ case OBJECTS:
+ np->varbinds = getVarbinds(fp, &np->varbinds);
+ if (!np->varbinds) {
+ print_error("Bad OBJECTS list", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ default:
+ /*
+ * NOTHING
+ */
+ break;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ return merge_parse_objectid(np, fp, name);
+}
+
+/*
+ * Parses a TRAP-TYPE macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_trapDefinition(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ register struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != EQUALS && type != ENDOFFILE) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ break;
+ case ENTERPRISE:
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LEFTBRACKET) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad Trap Format", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->parent = strdup(token);
+ /*
+ * Get right bracket
+ */
+ type = get_token(fp, token, MAXTOKEN);
+ } else if (type == LABEL)
+ np->parent = strdup(token);
+ break;
+ case VARIABLES:
+ np->varbinds = getVarbinds(fp, &np->varbinds);
+ if (!np->varbinds) {
+ print_error("Bad VARIABLES list", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ default:
+ /*
+ * NOTHING
+ */
+ break;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+
+ np->label = strdup(name);
+
+ if (type != NUMBER) {
+ print_error("Expected a Number", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->subid = strtoul(token, NULL, 10);
+ np->next = alloc_node(current_module);
+ if (np->next == NULL) {
+ free_node(np);
+ return (NULL);
+ }
+ np->next->parent = np->parent;
+ np->parent = (char *) malloc(strlen(np->parent) + 2);
+ if (np->parent == NULL) {
+ free_node(np->next);
+ free_node(np);
+ return (NULL);
+ }
+ strcpy(np->parent, np->next->parent);
+ strcat(np->parent, "#");
+ np->next->label = strdup(np->parent);
+ return np;
+}
+
+
+/*
+ * Parses a compliance macro
+ * Returns 0 on error.
+ */
+static int
+eat_syntax(FILE * fp, char *token, int maxtoken)
+{
+ int type, nexttype;
+ struct node *np = alloc_node(current_module);
+ char nexttoken[MAXTOKEN];
+
+ type = get_token(fp, token, maxtoken);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ switch (type) {
+ case INTEGER:
+ case INTEGER32:
+ case UINTEGER32:
+ case UNSIGNED32:
+ case COUNTER:
+ case GAUGE:
+ case BITSTRING:
+ case LABEL:
+ if (nexttype == LEFTBRACKET) {
+ /*
+ * if there is an enumeration list, parse it
+ */
+ np->enums = parse_enumlist(fp, &np->enums);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ } else if (nexttype == LEFTPAREN) {
+ /*
+ * if there is a range list, parse it
+ */
+ np->ranges = parse_ranges(fp, &np->ranges);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ }
+ break;
+ case OCTETSTR:
+ case KW_OPAQUE:
+ /*
+ * parse any SIZE specification
+ */
+ if (nexttype == LEFTPAREN) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == SIZE) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == LEFTPAREN) {
+ np->ranges = parse_ranges(fp, &np->ranges);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN); /* ) */
+ if (nexttype == RIGHTPAREN) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ break;
+ }
+ }
+ }
+ print_error("Bad SIZE syntax", token, type);
+ free_node(np);
+ return nexttype;
+ }
+ break;
+ case OBJID:
+ case NETADDR:
+ case IPADDR:
+ case TIMETICKS:
+ case NUL:
+ case NSAPADDRESS:
+ case COUNTER64:
+ break;
+ default:
+ print_error("Bad syntax", token, type);
+ free_node(np);
+ return nexttype;
+ }
+ free_node(np);
+ return nexttype;
+}
+
+static int
+compliance_lookup(const char *name, int modid)
+{
+ if (modid == -1) {
+ struct objgroup *op =
+ (struct objgroup *) malloc(sizeof(struct objgroup));
+ op->next = objgroups;
+ op->name = strdup(name);
+ op->line = mibLine;
+ objgroups = op;
+ return 1;
+ } else
+ return find_tree_node(name, modid) != NULL;
+}
+
+static struct node *
+parse_compliance(FILE * fp, char *name)
+{
+ int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != STATUS) {
+ print_error("Expected STATUS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) {
+ print_error("Bad STATUS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ goto skip;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS))
+ np->description = strdup(quoted_string_buffer);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == REFERENCE) {
+ type = get_token(fp, quoted_string_buffer, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad REFERENCE", quoted_string_buffer, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != MODULE) {
+ print_error("Expected MODULE", token, type);
+ goto skip;
+ }
+ while (type == MODULE) {
+ int modid = -1;
+ char modname[MAXTOKEN];
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LABEL
+ && strcmp(token, module_name(current_module, modname))) {
+ modid = read_module_internal(token);
+ if (modid != MODULE_LOADED_OK
+ && modid != MODULE_ALREADY_LOADED) {
+ print_error("Unknown module", token, type);
+ goto skip;
+ }
+ modid = which_module(token);
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type == MANDATORYGROUPS) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ goto skip;
+ }
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad group name", token, type);
+ goto skip;
+ }
+ if (!compliance_lookup(token, modid))
+ print_error("Unknown group", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type == COMMA);
+ if (type != RIGHTBRACKET) {
+ print_error("Expected \"}\"", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ while (type == GROUP || type == OBJECT) {
+ if (type == GROUP) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad group name", token, type);
+ goto skip;
+ }
+ if (!compliance_lookup(token, modid))
+ print_error("Unknown group", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ } else {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad object name", token, type);
+ goto skip;
+ }
+ if (!compliance_lookup(token, modid))
+ print_error("Unknown group", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == SYNTAX)
+ type = eat_syntax(fp, token, MAXTOKEN);
+ if (type == WRSYNTAX)
+ type = eat_syntax(fp, token, MAXTOKEN);
+ if (type == MINACCESS) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != NOACCESS && type != ACCNOTIFY
+ && type != READONLY && type != WRITEONLY
+ && type != READCREATE && type != READWRITE) {
+ print_error("Bad MIN-ACCESS spec", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ }
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ }
+ skip:
+ while (type != EQUALS && type != ENDOFFILE)
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+
+ return merge_parse_objectid(np, fp, name);
+}
+
+
+/*
+ * Parses a capabilities macro
+ * Returns 0 on error.
+ */
+static struct node *
+parse_capabilities(FILE * fp, char *name)
+{
+ int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != PRODREL) {
+ print_error("Expected PRODUCT-RELEASE", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Expected STRING after PRODUCT-RELEASE", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != STATUS) {
+ print_error("Expected STATUS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != CURRENT && type != OBSOLETE) {
+ print_error("STATUS should be current or obsolete", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ goto skip;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(token);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == REFERENCE) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad REFERENCE", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, type);
+ }
+ while (type == SUPPORTS) {
+ int modid;
+ struct tree *tp;
+
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad module name", token, type);
+ goto skip;
+ }
+ modid = read_module_internal(token);
+ if (modid != MODULE_LOADED_OK && modid != MODULE_ALREADY_LOADED) {
+ print_error("Module not found", token, type);
+ goto skip;
+ }
+ modid = which_module(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != INCLUDES) {
+ print_error("Expected INCLUDES", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ goto skip;
+ }
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Expected group name", token, type);
+ goto skip;
+ }
+ tp = find_tree_node(token, modid);
+ if (!tp)
+ print_error("Group not found in module", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type == COMMA);
+ if (type != RIGHTBRACKET) {
+ print_error("Expected \"}\" after group list", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ while (type == VARIATION) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad object name", token, type);
+ goto skip;
+ }
+ tp = find_tree_node(token, modid);
+ if (!tp)
+ print_error("Object not found in module", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == SYNTAX) {
+ type = eat_syntax(fp, token, MAXTOKEN);
+ }
+ if (type == WRSYNTAX) {
+ type = eat_syntax(fp, token, MAXTOKEN);
+ }
+ if (type == ACCESS) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != ACCNOTIFY && type != READONLY
+ && type != READWRITE && type != READCREATE
+ && type != WRITEONLY && type != NOTIMPL) {
+ print_error("Bad ACCESS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type == CREATEREQ) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ goto skip;
+ }
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad object name in list", token,
+ type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type == COMMA);
+ if (type != RIGHTBRACKET) {
+ print_error("Expected \"}\" after list", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type == DEFVAL) {
+ int level = 1;
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\" after DEFVAL", token,
+ type);
+ goto skip;
+ }
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LEFTBRACKET)
+ level++;
+ else if (type == RIGHTBRACKET)
+ level--;
+ } while (type != RIGHTBRACKET && type != ENDOFFILE
+ && level != 0);
+ if (type != RIGHTBRACKET) {
+ print_error("Missing \"}\" after DEFVAL", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ }
+ if (type != EQUALS)
+ print_error("Expected \"::=\"", token, type);
+ skip:
+ while (type != EQUALS && type != ENDOFFILE) {
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ }
+ return merge_parse_objectid(np, fp, name);
+}
+
+/*
+ * Parses a module identity macro
+ * Returns 0 on error.
+ */
+static void
+check_utc(const char *utc)
+{
+ int len, year, month, day, hour, minute;
+
+ len = strlen(utc);
+ if (utc[len - 1] != 'Z' && utc[len - 1] != 'z') {
+ print_error("Timestamp should end with Z", utc, QUOTESTRING);
+ return;
+ }
+ if (len == 11) {
+ len =
+ sscanf(utc, "%2d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
+ &minute);
+ year += 1900;
+ } else if (len == 13)
+ len =
+ sscanf(utc, "%4d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
+ &minute);
+ else {
+ print_error("Bad timestamp format (11 or 13 characters)",
+ utc, QUOTESTRING);
+ return;
+ }
+ if (len != 5) {
+ print_error("Bad timestamp format", utc, QUOTESTRING);
+ return;
+ }
+ if (month < 1 || month > 12)
+ print_error("Bad month in timestamp", utc, QUOTESTRING);
+ if (day < 1 || day > 31)
+ print_error("Bad day in timestamp", utc, QUOTESTRING);
+ if (hour < 0 || hour > 23)
+ print_error("Bad hour in timestamp", utc, QUOTESTRING);
+ if (minute < 0 || minute > 59)
+ print_error("Bad minute in timestamp", utc, QUOTESTRING);
+}
+
+static struct node *
+parse_moduleIdentity(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ register struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LASTUPDATED) {
+ print_error("Expected LAST-UPDATED", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Need STRING for LAST-UPDATED", token, type);
+ goto skip;
+ }
+ check_utc(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != ORGANIZATION) {
+ print_error("Expected ORGANIZATION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad ORGANIZATION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != CONTACTINFO) {
+ print_error("Expected CONTACT-INFO", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad CONTACT-INFO", quoted_string_buffer, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ goto skip;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ while (type == REVISION) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad REVISION", token, type);
+ goto skip;
+ }
+ check_utc(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != EQUALS)
+ print_error("Expected \"::=\"", token, type);
+ skip:
+ while (type != EQUALS && type != ENDOFFILE) {
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ }
+ return merge_parse_objectid(np, fp, name);
+}
+
+
+/*
+ * Parses a MACRO definition
+ * Expect BEGIN, discard everything to end.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_macro(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ struct node *np;
+ int iLine = mibLine;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, sizeof(token));
+ while (type != EQUALS && type != ENDOFFILE) {
+ type = get_token(fp, token, sizeof(token));
+ }
+ if (type != EQUALS)
+ return NULL;
+ while (type != BEGIN && type != ENDOFFILE) {
+ type = get_token(fp, token, sizeof(token));
+ }
+ if (type != BEGIN)
+ return NULL;
+ while (type != END && type != ENDOFFILE) {
+ type = get_token(fp, token, sizeof(token));
+ }
+ if (type != END)
+ return NULL;
+
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "%s MACRO (lines %d..%d parsed and ignored).\n", name,
+ iLine, mibLine);
+ }
+
+ return np;
+}
+
+/*
+ * Parses a module import clause
+ * loading any modules referenced
+ */
+static void
+parse_imports(FILE * fp)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char modbuf[256];
+#define MAX_IMPORTS 256
+ struct module_import import_list[MAX_IMPORTS];
+ int this_module;
+ struct module *mp;
+
+ int import_count = 0; /* Total number of imported descriptors */
+ int i = 0, old_i; /* index of first import from each module */
+
+ type = get_token(fp, token, MAXTOKEN);
+
+ /*
+ * Parse the IMPORTS clause
+ */
+ while (type != SEMI && type != ENDOFFILE) {
+ if (type == LABEL) {
+ if (import_count == MAX_IMPORTS) {
+ print_error("Too many imported symbols", token, type);
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type != SEMI && type != ENDOFFILE);
+ return;
+ }
+ import_list[import_count++].label = strdup(token);
+ } else if (type == FROM) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (import_count == i) { /* All imports are handled internally */
+ type = get_token(fp, token, MAXTOKEN);
+ continue;
+ }
+ this_module = which_module(token);
+
+ for (old_i = i; i < import_count; ++i)
+ import_list[i].modid = this_module;
+
+ /*
+ * Recursively read any pre-requisite modules
+ */
+ if (read_module_internal(token) == MODULE_NOT_FOUND) {
+ for (; old_i < import_count; ++old_i) {
+ read_import_replacements(token, &import_list[old_i]);
+ }
+ }
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+
+ /*
+ * Save the import information
+ * in the global module table
+ */
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->modid == current_module) {
+ if (import_count == 0)
+ return;
+ if (mp->imports && (mp->imports != root_imports)) {
+ /*
+ * this can happen if all modules are in one source file.
+ */
+ for (i = 0; i < mp->no_imports; ++i) {
+ DEBUGMSGTL(("parse-mibs",
+ "#### freeing Module %d '%s' %d\n",
+ mp->modid, mp->imports[i].label,
+ mp->imports[i].modid));
+ free((char *) mp->imports[i].label);
+ }
+ free((char *) mp->imports);
+ }
+ mp->imports = (struct module_import *)
+ calloc(import_count, sizeof(struct module_import));
+ if (mp->imports == NULL)
+ return;
+ for (i = 0; i < import_count; ++i) {
+ mp->imports[i].label = import_list[i].label;
+ mp->imports[i].modid = import_list[i].modid;
+ DEBUGMSGTL(("parse-mibs",
+ "#### adding Module %d '%s' %d\n", mp->modid,
+ mp->imports[i].label, mp->imports[i].modid));
+ }
+ mp->no_imports = import_count;
+ return;
+ }
+
+ /*
+ * Shouldn't get this far
+ */
+ print_module_not_found(module_name(current_module, modbuf));
+ return;
+}
+
+
+
+/*
+ * MIB module handling routines
+ */
+
+static void
+dump_module_list(void)
+{
+ struct module *mp = module_head;
+
+ DEBUGMSGTL(("parse-mibs", "Module list:\n"));
+ while (mp) {
+ DEBUGMSGTL(("parse-mibs", " %s %d %s %d\n", mp->name, mp->modid,
+ mp->file, mp->no_imports));
+ mp = mp->next;
+ }
+}
+
+int
+which_module(const char *name)
+{
+ struct module *mp;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (!label_compare(mp->name, name))
+ return (mp->modid);
+
+ DEBUGMSGTL(("parse-mibs", "Module %s not found\n", name));
+ return (-1);
+}
+
+/*
+ * module_name - copy module name to user buffer, return ptr to same.
+ */
+char *
+module_name(int modid, char *cp)
+{
+ struct module *mp;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->modid == modid) {
+ strcpy(cp, mp->name);
+ return (cp);
+ }
+
+ DEBUGMSGTL(("parse-mibs", "Module %d not found\n", modid));
+ sprintf(cp, "#%d", modid);
+ return (cp);
+}
+
+/*
+ * Backwards compatability
+ * Read newer modules that replace the one specified:-
+ * either all of them (read_module_replacements),
+ * or those relating to a specified identifier (read_import_replacements)
+ * plus an interface to add new replacement requirements
+ */
+void
+add_module_replacement(const char *old_module,
+ const char *new_module_name,
+ const char *tag, int len)
+{
+ struct module_compatability *mcp;
+
+ mcp = (struct module_compatability *)
+ calloc(1, sizeof(struct module_compatability));
+ if (mcp == NULL)
+ return;
+
+ mcp->old_module = strdup(old_module);
+ mcp->new_module = strdup(new_module_name);
+ if (tag)
+ mcp->tag = strdup(tag);
+ mcp->tag_len = len;
+
+ mcp->next = module_map_head;
+ module_map_head = mcp;
+}
+
+static void
+read_module_replacements(const char *name)
+{
+ struct module_compatability *mcp;
+
+ for (mcp = module_map_head; mcp; mcp = mcp->next) {
+ if (!label_compare(mcp->old_module, name)) {
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Loading replacement module %s for %s (%s)\n",
+ mcp->new_module, name, File);
+ }
+ (void) read_module(mcp->new_module);
+ return;
+ }
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_ERRORS)) {
+ print_module_not_found(name);
+ }
+}
+
+static void
+read_import_replacements(const char *old_module_name,
+ struct module_import *identifier)
+{
+ struct module_compatability *mcp;
+
+ /*
+ * Look for matches first
+ */
+ for (mcp = module_map_head; mcp; mcp = mcp->next) {
+ if (!label_compare(mcp->old_module, old_module_name)) {
+
+ if ( /* exact match */
+ (mcp->tag_len == 0 &&
+ (mcp->tag == NULL ||
+ !label_compare(mcp->tag, identifier->label))) ||
+ /*
+ * prefix match
+ */
+ (mcp->tag_len != 0 &&
+ !strncmp(mcp->tag, identifier->label, mcp->tag_len))
+ ) {
+
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Importing %s from replacement module %s instead of %s (%s)\n",
+ identifier->label, mcp->new_module,
+ old_module_name, File);
+ }
+ (void) read_module(mcp->new_module);
+ identifier->modid = which_module(mcp->new_module);
+ return; /* finished! */
+ }
+ }
+ }
+
+ /*
+ * If no exact match, load everything relevant
+ */
+ read_module_replacements(old_module_name);
+}
+
+
+/*
+ * Read in the named module
+ * Returns the root of the whole tree
+ * (by analogy with 'read_mib')
+ */
+static int
+read_module_internal(const char *name)
+{
+ struct module *mp;
+ FILE *fp;
+ struct node *np;
+
+ init_mib_internals();
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (!label_compare(mp->name, name)) {
+ const char *oldFile = File;
+ int oldLine = mibLine;
+ int oldModule = current_module;
+
+ if (mp->no_imports != -1) {
+ DEBUGMSGTL(("parse-mibs", "Module %s already loaded\n",
+ name));
+ return MODULE_ALREADY_LOADED;
+ }
+ if ((fp = fopen(mp->file, "r")) == NULL) {
+ snmp_log_perror(mp->file);
+ return MODULE_LOAD_FAILED;
+ }
+ mp->no_imports = 0; /* Note that we've read the file */
+ File = mp->file;
+ mibLine = 1;
+ current_module = mp->modid;
+ /*
+ * Parse the file
+ */
+ np = parse(fp, NULL);
+ fclose(fp);
+ File = oldFile;
+ mibLine = oldLine;
+ current_module = oldModule;
+ return MODULE_LOADED_OK;
+ }
+
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS) > 1) {
+ snmp_log(LOG_WARNING, "Module %s not found\n", name);
+ }
+ return MODULE_NOT_FOUND;
+}
+
+void
+adopt_orphans(void)
+{
+ struct node *np, *onp;
+ struct tree *tp;
+ int i, adopted = 1;
+
+ if (!orphan_nodes)
+ return;
+ init_node_hash(orphan_nodes);
+ orphan_nodes = NULL;
+
+ while (adopted) {
+ adopted = 0;
+ for (i = 0; i < NHASHSIZE; i++)
+ if (nbuckets[i]) {
+ for (np = nbuckets[i]; np != NULL; np = np->next) {
+ tp = find_tree_node(np->parent, -1);
+ if (tp) {
+ do_subtree(tp, &np);
+ adopted = 1;
+ /*
+ * if do_subtree adopted the entire bucket, stop
+ */
+ if(NULL == nbuckets[i])
+ break;
+
+ /*
+ * do_subtree may modify nbuckets, and if np
+ * was adopted, np->next probably isn't an orphan
+ * anymore. if np is still in the bucket (do_subtree
+ * didn't adopt it) keep on plugging. otherwise
+ * start over, at the top of the bucket.
+ */
+ for(onp = nbuckets[i]; onp; onp = onp->next)
+ if(onp == np)
+ break;
+ if(NULL == onp) { /* not in the list */
+ np = nbuckets[i]; /* start over */
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Report on outstanding orphans
+ * and link them back into the orphan list
+ */
+ for (i = 0; i < NHASHSIZE; i++)
+ if (nbuckets[i]) {
+ if (orphan_nodes)
+ onp = np->next = nbuckets[i];
+ else
+ onp = orphan_nodes = nbuckets[i];
+ nbuckets[i] = NULL;
+ while (onp) {
+ char modbuf[256];
+ snmp_log(LOG_WARNING,
+ "Cannot adopt OID in %s: %s ::= { %s %ld }\n",
+ module_name(onp->modid, modbuf),
+ (onp->label ? onp->label : "<no label>"),
+ (onp->parent ? onp->parent : "<no parent>"),
+ onp->subid);
+
+ np = onp;
+ onp = onp->next;
+ }
+ }
+}
+
+struct tree *
+read_module(const char *name)
+{
+ if (read_module_internal(name) == MODULE_NOT_FOUND)
+ read_module_replacements(name);
+ return tree_head;
+}
+
+/*
+ * Prototype definition
+ */
+void unload_module_by_ID(int modID, struct tree *tree_top);
+
+void
+unload_module_by_ID(int modID, struct tree *tree_top)
+{
+ struct tree *tp, *next;
+ int i;
+
+ for (tp = tree_top; tp; tp = next) {
+ /*
+ * Essentially, this is equivalent to the code fragment:
+ * if (tp->modID == modID)
+ * tp->number_modules--;
+ * but handles one tree node being part of several modules,
+ * and possible multiple copies of the same module ID.
+ */
+ int nmod = tp->number_modules;
+ if (nmod > 0) { /* in some module */
+ /*
+ * Remove all copies of this module ID
+ */
+ int cnt = 0, *pi1, *pi2 = tp->module_list;
+ for (i = 0, pi1 = pi2; i < nmod; i++, pi2++) {
+ if (*pi2 == modID)
+ continue;
+ cnt++;
+ *pi1++ = *pi2;
+ }
+ if (nmod != cnt) { /* in this module */
+ /*
+ * if ( (nmod - cnt) > 1)
+ * printf("Dup modid %d, %d times, '%s'\n", tp->modid, (nmod-cnt), tp->label); fflush(stdout); ?* XXDEBUG
+ */
+ tp->number_modules = cnt;
+ switch (cnt) {
+ case 0:
+ tp->module_list[0] = -1; /* Mark unused, and FALL THROUGH */
+
+ case 1: /* save the remaining module */
+ if (&(tp->modid) != tp->module_list) {
+ tp->modid = tp->module_list[0];
+ free(tp->module_list);
+ tp->module_list = &(tp->modid);
+ }
+ break;
+
+ default:
+ break;
+ }
+ } /* if tree node is in this module */
+ }
+ /*
+ * if tree node is in some module
+ */
+ next = tp->next_peer;
+
+
+ /*
+ * OK - that's dealt with *this* node.
+ * Now let's look at the children.
+ * (Isn't recursion wonderful!)
+ */
+ if (tp->child_list)
+ unload_module_by_ID(modID, tp->child_list);
+
+
+ if (tp->number_modules == 0) {
+ /*
+ * This node isn't needed any more (except perhaps
+ * for the sake of the children)
+ */
+ if (tp->child_list == NULL) {
+ unlink_tree(tp);
+ free_tree(tp);
+ } else {
+ free_partial_tree(tp, TRUE);
+ }
+ }
+ }
+}
+
+int
+unload_module(const char *name)
+{
+ struct module *mp;
+ int modID = -1;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (!label_compare(mp->name, name)) {
+ modID = mp->modid;
+ break;
+ }
+
+ if (modID == -1) {
+ DEBUGMSGTL(("unload-mib", "Module %s not found to unload\n",
+ name));
+ return MODULE_NOT_FOUND;
+ }
+ unload_module_by_ID(modID, tree_head);
+ mp->no_imports = -1; /* mark as unloaded */
+ return MODULE_LOADED_OK; /* Well, you know what I mean! */
+}
+
+/*
+ * Clear module map, tree nodes, textual convention table.
+ */
+void
+unload_all_mibs()
+{
+ struct module *mp;
+ struct module_compatability *mcp;
+ struct tc *ptc;
+ int i;
+
+ for (mcp = module_map_head; mcp; mcp = module_map_head) {
+ if (mcp == module_map)
+ break;
+ module_map_head = mcp->next;
+ if (mcp->tag) free((char *) mcp->tag);
+ free((char *) mcp->old_module);
+ free((char *) mcp->new_module);
+ free(mcp);
+ }
+
+ for (mp = module_head; mp; mp = module_head) {
+ struct module_import *mi = mp->imports;
+ if (mi) {
+ for (i = 0; i < mp->no_imports; ++i) {
+ SNMP_FREE((mi + i)->label);
+ }
+ mp->no_imports = 0;
+ if (mi == root_imports)
+ memset(mi, 0, sizeof(*mi));
+ else
+ free(mi);
+ }
+
+ unload_module_by_ID(mp->modid, tree_head);
+ module_head = mp->next;
+ free(mp->name);
+ free(mp->file);
+ free(mp);
+ }
+ unload_module_by_ID(-1, tree_head);
+ /*
+ * tree nodes are cleared
+ */
+
+ for (i = 0, ptc = tclist; i < MAXTC; i++, ptc++) {
+ if (ptc->type == 0)
+ continue;
+ free_enums(&ptc->enums);
+ free_ranges(&ptc->ranges);
+ free(ptc->descriptor);
+ if (ptc->hint)
+ free(ptc->hint);
+ }
+ memset(tclist, 0, MAXTC * sizeof(struct tc));
+
+ memset(buckets, 0, sizeof(buckets));
+ memset(nbuckets, 0, sizeof(nbuckets));
+ memset(tbuckets, 0, sizeof(tbuckets));
+
+ for (i = 0; i < sizeof(root_imports) / sizeof(root_imports[0]); i++) {
+ SNMP_FREE(root_imports[i].label);
+ }
+
+ max_module = 0;
+ current_module = 0;
+ module_map_head = NULL;
+ SNMP_FREE(last_err_module);
+}
+
+static void
+new_module(const char *name, const char *file)
+{
+ struct module *mp;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (!label_compare(mp->name, name)) {
+ DEBUGMSGTL(("parse-mibs", "Module %s already noted\n", name));
+ /*
+ * Not the same file
+ */
+ if (label_compare(mp->file, file)) {
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Warning: Module %s was in %s now is %s\n",
+ name, mp->file, file);
+ }
+
+ /*
+ * Use the new one in preference
+ */
+ free(mp->file);
+ mp->file = strdup(file);
+ }
+ return;
+ }
+
+ /*
+ * Add this module to the list
+ */
+ DEBUGMSGTL(("parse-mibs", " Module %d %s is in %s\n", max_module,
+ name, file));
+ mp = (struct module *) calloc(1, sizeof(struct module));
+ if (mp == NULL)
+ return;
+ mp->name = strdup(name);
+ mp->file = strdup(file);
+ mp->imports = NULL;
+ mp->no_imports = -1; /* Not yet loaded */
+ mp->modid = max_module;
+ ++max_module;
+
+ mp->next = module_head; /* Or add to the *end* of the list? */
+ module_head = mp;
+}
+
+
+static void
+scan_objlist(struct node *root, struct objgroup *list, const char *error)
+{
+ int oLine = mibLine;
+
+ while (list) {
+ struct objgroup *gp = list;
+ struct node *np;
+ list = list->next;
+ np = root;
+ while (np)
+ if (label_compare(np->label, gp->name))
+ np = np->next;
+ else
+ break;
+ if (!np) {
+ mibLine = gp->line;
+ print_error(error, gp->name, QUOTESTRING);
+ }
+ free(gp->name);
+ free(gp);
+ }
+ mibLine = oLine;
+}
+
+/*
+ * Parses a mib file and returns a linked list of nodes found in the file.
+ * Returns NULL on error.
+ */
+static struct node *
+parse(FILE * fp, struct node *root)
+{
+ char token[MAXTOKEN];
+ char name[MAXTOKEN];
+ int type = LABEL;
+ int lasttype = LABEL;
+
+#define BETWEEN_MIBS 1
+#define IN_MIB 2
+ int state = BETWEEN_MIBS;
+ struct node *np, *nnp;
+ struct objgroup *oldgroups = NULL, *oldobjects = NULL, *oldnotifs =
+ NULL;
+
+ DEBUGMSGTL(("parse-file", "Parsing file: %s...\n", File));
+
+ if (last_err_module)
+ free(last_err_module);
+ last_err_module = 0;
+
+ np = root;
+ if (np != NULL) {
+ /*
+ * now find end of chain
+ */
+ while (np->next)
+ np = np->next;
+ }
+
+ while (type != ENDOFFILE) {
+ if (lasttype == CONTINUE)
+ lasttype = type;
+ else
+ type = lasttype = get_token(fp, token, MAXTOKEN);
+
+ switch (type) {
+ case END:
+ if (state != IN_MIB) {
+ print_error("Error, END before start of MIB", NULL, type);
+ return NULL;
+ } else {
+ struct module *mp;
+#ifdef TEST
+ printf("\nNodes for Module %s:\n", name);
+ print_nodes(stdout, root);
+#endif
+ scan_objlist(root, objgroups, "Undefined OBJECT-GROUP");
+ scan_objlist(root, objects, "Undefined OBJECT");
+ scan_objlist(root, notifs, "Undefined NOTIFICATION");
+ objgroups = oldgroups;
+ objects = oldobjects;
+ notifs = oldnotifs;
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->modid == current_module)
+ break;
+ do_linkup(mp, root);
+ np = root = NULL;
+ }
+ state = BETWEEN_MIBS;
+#ifdef TEST
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ xmalloc_stats(stderr);
+ }
+#endif
+ continue;
+ case IMPORTS:
+ parse_imports(fp);
+ continue;
+ case EXPORTS:
+ while (type != SEMI && type != ENDOFFILE)
+ type = get_token(fp, token, MAXTOKEN);
+ continue;
+ case LABEL:
+ case INTEGER:
+ case INTEGER32:
+ case UINTEGER32:
+ case UNSIGNED32:
+ case COUNTER:
+ case COUNTER64:
+ case GAUGE:
+ case IPADDR:
+ case NETADDR:
+ case NSAPADDRESS:
+ case OBJSYNTAX:
+ case APPSYNTAX:
+ case SIMPLESYNTAX:
+ case OBJNAME:
+ case NOTIFNAME:
+ case KW_OPAQUE:
+ case TIMETICKS:
+ break;
+ case ENDOFFILE:
+ continue;
+ default:
+ strcpy(name, token);
+ type = get_token(fp, token, MAXTOKEN);
+ nnp = NULL;
+ if (type == MACRO) {
+ nnp = parse_macro(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of MACRO", NULL, type);
+ /*
+ * return NULL;
+ */
+ }
+ free_node(nnp); /* IGNORE */
+ nnp = NULL;
+ } else
+ print_error(name, "is a reserved word", lasttype);
+ continue; /* see if we can parse the rest of the file */
+ }
+ strcpy(name, token);
+ type = get_token(fp, token, MAXTOKEN);
+ nnp = NULL;
+
+ /*
+ * Handle obsolete method to assign an object identifier to a
+ * module
+ */
+ if (lasttype == LABEL && type == LEFTBRACKET) {
+ while (type != RIGHTBRACKET && type != ENDOFFILE)
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == ENDOFFILE) {
+ print_error("Expected \"}\"", token, type);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+
+ switch (type) {
+ case DEFINITIONS:
+ if (state != BETWEEN_MIBS) {
+ print_error("Error, nested MIBS", NULL, type);
+ return NULL;
+ }
+ state = IN_MIB;
+ current_module = which_module(name);
+ oldgroups = objgroups;
+ objgroups = NULL;
+ oldobjects = objects;
+ objects = NULL;
+ oldnotifs = notifs;
+ notifs = NULL;
+ if (current_module == -1) {
+ new_module(name, File);
+ current_module = which_module(name);
+ }
+ DEBUGMSGTL(("parse-mibs", "Parsing MIB: %d %s\n",
+ current_module, name));
+ while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE)
+ if (type == BEGIN)
+ break;
+ break;
+ case OBJTYPE:
+ nnp = parse_objecttype(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of OBJECT-TYPE", NULL, type);
+ return NULL;
+ }
+ break;
+ case OBJGROUP:
+ nnp = parse_objectgroup(fp, name, OBJECTS, &objects);
+ if (nnp == NULL) {
+ print_error("Bad parse of OBJECT-GROUP", NULL, type);
+ return NULL;
+ }
+ break;
+ case NOTIFGROUP:
+ nnp = parse_objectgroup(fp, name, NOTIFICATIONS, &notifs);
+ if (nnp == NULL) {
+ print_error("Bad parse of NOTIFICATION-GROUP", NULL, type);
+ return NULL;
+ }
+ break;
+ case TRAPTYPE:
+ nnp = parse_trapDefinition(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of TRAP-TYPE", NULL, type);
+ return NULL;
+ }
+ break;
+ case NOTIFTYPE:
+ nnp = parse_notificationDefinition(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of NOTIFICATION-TYPE", NULL, type);
+ return NULL;
+ }
+ break;
+ case COMPLIANCE:
+ nnp = parse_compliance(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of MODULE-COMPLIANCE", NULL, type);
+ return NULL;
+ }
+ break;
+ case AGENTCAP:
+ nnp = parse_capabilities(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of AGENT-CAPABILITIES", NULL, type);
+ return NULL;
+ }
+ break;
+ case MACRO:
+ nnp = parse_macro(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of MACRO", NULL, type);
+ /*
+ * return NULL;
+ */
+ }
+ free_node(nnp); /* IGNORE */
+ nnp = NULL;
+ break;
+ case MODULEIDENTITY:
+ nnp = parse_moduleIdentity(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of MODULE-IDENTITY", NULL, type);
+ return NULL;
+ }
+ break;
+ case OBJECT:
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != IDENTIFIER) {
+ print_error("Expected IDENTIFIER", token, type);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != EQUALS) {
+ print_error("Expected \"::=\"", token, type);
+ return NULL;
+ }
+ nnp = parse_objectid(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of OBJECT IDENTIFIER", NULL, type);
+ return NULL;
+ }
+ break;
+ case EQUALS:
+ nnp = parse_asntype(fp, name, &type, token);
+ lasttype = CONTINUE;
+ break;
+ case ENDOFFILE:
+ break;
+ default:
+ print_error("Bad operator", token, type);
+ return NULL;
+ }
+ if (nnp) {
+ if (nnp->type == TYPE_OTHER)
+ nnp->type = type;
+ if (np)
+ np->next = nnp;
+ else
+ np = root = nnp;
+ while (np->next)
+ np = np->next;
+ }
+ }
+ DEBUGMSGTL(("parse-file", "End of file (%s)\n", File));
+ return root;
+}
+
+/*
+ * return zero if character is not a label character.
+ */
+static int
+is_labelchar(int ich)
+{
+ if ((isalnum(ich)) || (ich == '-'))
+ return 1;
+ if (ich == '_' && netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_PARSE_LABEL)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Parses a token from the file. The type of the token parsed is returned,
+ * and the text is placed in the string pointed to by token.
+ * Warning: this method may recurse.
+ */
+static int
+get_token(FILE * fp, char *token, int maxtlen)
+{
+ register int ch, ch_next;
+ register char *cp = token;
+ register int hash = 0;
+ register struct tok *tp;
+ int too_long = 0;
+
+ /*
+ * skip all white space
+ */
+ do {
+ ch = getc(fp);
+ if (ch == '\n')
+ mibLine++;
+ }
+ while (isspace(ch) && ch != EOF);
+ *cp++ = ch;
+ *cp = '\0';
+ switch (ch) {
+ case EOF:
+ return ENDOFFILE;
+ case '"':
+ return parseQuoteString(fp, token, maxtlen);
+ case '\'': /* binary or hex constant */
+ while ((ch = getc(fp)) != EOF && ch != '\''
+ && cp - token < maxtlen - 2)
+ *cp++ = ch;
+ if (ch == '\'') {
+ unsigned long val = 0;
+ *cp++ = '\'';
+ *cp++ = ch = getc(fp);
+ *cp = 0;
+ cp = token + 1;
+ switch (ch) {
+ case EOF:
+ return ENDOFFILE;
+ case 'b':
+ case 'B':
+ while ((ch = *cp++) != '\'')
+ if (ch != '0' && ch != '1')
+ return LABEL;
+ else
+ val = val * 2 + ch - '0';
+ break;
+ case 'h':
+ case 'H':
+ while ((ch = *cp++) != '\'')
+ if ('0' <= ch && ch <= '9')
+ val = val * 16 + ch - '0';
+ else if ('a' <= ch && ch <= 'f')
+ val = val * 16 + ch - 'a' + 10;
+ else if ('A' <= ch && ch <= 'F')
+ val = val * 16 + ch - 'A' + 10;
+ else
+ return LABEL;
+ break;
+ default:
+ return LABEL;
+ }
+ sprintf(token, "%ld", val);
+ return NUMBER;
+ } else
+ return LABEL;
+ case '(':
+ return LEFTPAREN;
+ case ')':
+ return RIGHTPAREN;
+ case '{':
+ return LEFTBRACKET;
+ case '}':
+ return RIGHTBRACKET;
+ case '[':
+ return LEFTSQBRACK;
+ case ']':
+ return RIGHTSQBRACK;
+ case ';':
+ return SEMI;
+ case ',':
+ return COMMA;
+ case '|':
+ return BAR;
+ case '.':
+ ch_next = getc(fp);
+ if (ch_next == '.')
+ return RANGE;
+ ungetc(ch_next, fp);
+ return LABEL;
+ case ':':
+ ch_next = getc(fp);
+ if (ch_next != ':') {
+ ungetc(ch_next, fp);
+ return LABEL;
+ }
+ ch_next = getc(fp);
+ if (ch_next != '=') {
+ ungetc(ch_next, fp);
+ return LABEL;
+ }
+ return EQUALS;
+ case '-':
+ ch_next = getc(fp);
+ if (ch_next == '-') {
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_COMMENT_TERM)) {
+ /*
+ * Treat the rest of this line as a comment.
+ */
+ while ((ch_next != EOF) && (ch_next != '\n'))
+ ch_next = getc(fp);
+ } else {
+ /*
+ * Treat the rest of the line or until another '--' as a comment
+ */
+ /*
+ * (this is the "technically" correct way to parse comments)
+ */
+ ch = ' ';
+ ch_next = getc(fp);
+ while (ch_next != EOF && ch_next != '\n' &&
+ (ch != '-' || ch_next != '-')) {
+ ch = ch_next;
+ ch_next = getc(fp);
+ }
+ }
+ if (ch_next == EOF)
+ return ENDOFFILE;
+ if (ch_next == '\n')
+ mibLine++;
+ return get_token(fp, token, maxtlen);
+ }
+ ungetc(ch_next, fp);
+ default:
+ /*
+ * Accumulate characters until end of token is found. Then attempt to
+ * match this token as a reserved word. If a match is found, return the
+ * type. Else it is a label.
+ */
+ if (!is_labelchar(ch))
+ return LABEL;
+ hash += tolower(ch);
+ more:
+ while (is_labelchar(ch_next = getc(fp))) {
+ hash += tolower(ch_next);
+ if (cp - token < maxtlen - 1)
+ *cp++ = ch_next;
+ else
+ too_long = 1;
+ }
+ ungetc(ch_next, fp);
+ *cp = '\0';
+
+ if (too_long)
+ print_error("Warning: token too long", token, CONTINUE);
+ for (tp = buckets[BUCKET(hash)]; tp; tp = tp->next) {
+ if ((tp->hash == hash) && (!label_compare(tp->name, token)))
+ break;
+ }
+ if (tp) {
+ if (tp->token != CONTINUE)
+ return (tp->token);
+ while (isspace((ch_next = getc(fp))))
+ if (ch_next == '\n')
+ mibLine++;
+ if (ch_next == EOF)
+ return ENDOFFILE;
+ if (isalnum(ch_next)) {
+ *cp++ = ch_next;
+ hash += tolower(ch_next);
+ goto more;
+ }
+ }
+ if (token[0] == '-' || isdigit(token[0])) {
+ for (cp = token + 1; *cp; cp++)
+ if (!isdigit(*cp))
+ return LABEL;
+ return NUMBER;
+ }
+ return LABEL;
+ }
+}
+
+int
+snmp_get_token(FILE * fp, char *token, int maxtlen)
+{
+ return get_token(fp, token, maxtlen);
+}
+
+int
+add_mibdir(const char *dirname)
+{
+ FILE *fp, *ip;
+ DIR *dir, *dir2;
+ const char *oldFile = File;
+ struct dirent *file;
+ char token[MAXTOKEN], token2[MAXTOKEN];
+ char tmpstr[300];
+ int count = 0;
+
+ DEBUGMSGTL(("parse-mibs", "Scanning directory %s\n", dirname));
+
+ if ((dir = opendir(dirname))) {
+ snprintf(tmpstr, sizeof(tmpstr), "%s/.index", dirname);
+ tmpstr[ sizeof(tmpstr)-1 ] = 0;
+ ip = fopen(tmpstr, "w");
+ while ((file = readdir(dir))) {
+ /*
+ * Only parse file names not beginning with a '.'
+ */
+ if (file->d_name != NULL && file->d_name[0] != '.') {
+ snprintf(tmpstr, sizeof(tmpstr), "%s/%s", dirname, file->d_name);
+ tmpstr[ sizeof(tmpstr)-1 ] = 0;
+ if ((dir2 = opendir(tmpstr))) {
+ /*
+ * file is a directory, don't read it
+ */
+ closedir(dir2);
+ } else {
+ /*
+ * which module is this
+ */
+ if ((fp = fopen(tmpstr, "r")) == NULL) {
+ snmp_log_perror(tmpstr);
+ continue;
+ }
+ DEBUGMSGTL(("parse-mibs", "Checking file: %s...\n",
+ tmpstr));
+ mibLine = 1;
+ File = tmpstr;
+ get_token(fp, token, MAXTOKEN);
+ /*
+ * simple test for this being a MIB
+ */
+ if (get_token(fp, token2, MAXTOKEN) == DEFINITIONS) {
+ new_module(token, tmpstr);
+ count++;
+ if (ip)
+ fprintf(ip, "%s %s\n", token, file->d_name);
+ }
+ fclose(fp);
+ }
+ }
+ }
+ File = oldFile;
+ closedir(dir);
+ if (ip)
+ fclose(ip);
+ return (count);
+ }
+ return (-1);
+}
+
+
+/*
+ * Returns the root of the whole tree
+ * (for backwards compatability)
+ */
+struct tree *
+read_mib(const char *filename)
+{
+ FILE *fp;
+ char token[MAXTOKEN];
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ snmp_log_perror(filename);
+ return NULL;
+ }
+ mibLine = 1;
+ File = filename;
+ DEBUGMSGTL(("parse-mibs", "Parsing file: %s...\n", filename));
+ get_token(fp, token, MAXTOKEN);
+ fclose(fp);
+ new_module(token, filename);
+ (void) read_module(token);
+
+ return tree_head;
+}
+
+
+struct tree *
+read_all_mibs()
+{
+ struct module *mp;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->no_imports == -1)
+ read_module(mp->name);
+ adopt_orphans();
+
+ return tree_head;
+}
+
+
+#ifdef TEST
+main(int argc, char *argv[])
+{
+ int i;
+ struct tree *tp;
+ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIB_WARNINGS, 2);
+
+ init_mib();
+
+ if (argc == 1)
+ (void) read_all_mibs();
+ else
+ for (i = 1; i < argc; i++)
+ read_mib(argv[i]);
+
+ for (tp = tree_head; tp; tp = tp->next_peer)
+ print_subtree(stdout, tp, 0);
+ free_tree(tree_head);
+
+ return 0;
+}
+#endif /* TEST */
+
+static int
+parseQuoteString(FILE * fp, char *token, int maxtlen)
+{
+ register int ch;
+ int count = 0;
+ int too_long = 0;
+ char *token_start = token;
+
+ for (ch = getc(fp); ch != EOF; ch = getc(fp)) {
+ if (ch == '\r')
+ continue;
+ if (ch == '\n') {
+ mibLine++;
+ } else if (ch == '"') {
+ *token = '\0';
+ if (too_long && netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS) > 1) {
+ /*
+ * show short form for brevity sake
+ */
+ char ch_save = *(token_start + 50);
+ *(token_start + 50) = '\0';
+ print_error("Warning: string too long",
+ token_start, QUOTESTRING);
+ *(token_start + 50) = ch_save;
+ }
+ return QUOTESTRING;
+ }
+ /*
+ * maximum description length check. If greater, keep parsing
+ * but truncate the string
+ */
+ if (++count < maxtlen)
+ *token++ = ch;
+ else
+ too_long = 1;
+ }
+
+ return 0;
+}
+
+/*
+ * struct index_list *
+ * getIndexes(FILE *fp):
+ * This routine parses a string like { blah blah blah } and returns a
+ * list of the strings enclosed within it.
+ *
+ */
+static struct index_list *
+getIndexes(FILE * fp, struct index_list **retp)
+{
+ int type;
+ char token[MAXTOKEN];
+ char nextIsImplied = 0;
+
+ struct index_list *mylist = NULL;
+ struct index_list **mypp = &mylist;
+
+ free_indexes(retp);
+
+ type = get_token(fp, token, MAXTOKEN);
+
+ if (type != LEFTBRACKET) {
+ return NULL;
+ }
+
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != RIGHTBRACKET && type != ENDOFFILE) {
+ if ((type == LABEL) || (type & SYNTAX_MASK)) {
+ *mypp =
+ (struct index_list *) calloc(1, sizeof(struct index_list));
+ if (*mypp) {
+ (*mypp)->ilabel = strdup(token);
+ (*mypp)->isimplied = nextIsImplied;
+ mypp = &(*mypp)->next;
+ nextIsImplied = 0;
+ }
+ } else if (type == IMPLIED) {
+ nextIsImplied = 1;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+
+ *retp = mylist;
+ return mylist;
+}
+
+static struct varbind_list *
+getVarbinds(FILE * fp, struct varbind_list **retp)
+{
+ int type;
+ char token[MAXTOKEN];
+
+ struct varbind_list *mylist = NULL;
+ struct varbind_list **mypp = &mylist;
+
+ free_varbinds(retp);
+
+ type = get_token(fp, token, MAXTOKEN);
+
+ if (type != LEFTBRACKET) {
+ return NULL;
+ }
+
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != RIGHTBRACKET && type != ENDOFFILE) {
+ if ((type == LABEL) || (type & SYNTAX_MASK)) {
+ *mypp =
+ (struct varbind_list *) calloc(1,
+ sizeof(struct
+ varbind_list));
+ if (*mypp) {
+ (*mypp)->vblabel = strdup(token);
+ mypp = &(*mypp)->next;
+ }
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+
+ *retp = mylist;
+ return mylist;
+}
+
+static void
+free_indexes(struct index_list **spp)
+{
+ if (spp && *spp) {
+ struct index_list *pp, *npp;
+
+ pp = *spp;
+ *spp = NULL;
+
+ while (pp) {
+ npp = pp->next;
+ if (pp->ilabel)
+ free(pp->ilabel);
+ free(pp);
+ pp = npp;
+ }
+ }
+}
+
+static void
+free_varbinds(struct varbind_list **spp)
+{
+ if (spp && *spp) {
+ struct varbind_list *pp, *npp;
+
+ pp = *spp;
+ *spp = NULL;
+
+ while (pp) {
+ npp = pp->next;
+ if (pp->vblabel)
+ free(pp->vblabel);
+ free(pp);
+ pp = npp;
+ }
+ }
+}
+
+static void
+free_ranges(struct range_list **spp)
+{
+ if (spp && *spp) {
+ struct range_list *pp, *npp;
+
+ pp = *spp;
+ *spp = NULL;
+
+ while (pp) {
+ npp = pp->next;
+ free(pp);
+ pp = npp;
+ }
+ }
+}
+
+static void
+free_enums(struct enum_list **spp)
+{
+ if (spp && *spp) {
+ struct enum_list *pp, *npp;
+
+ pp = *spp;
+ *spp = NULL;
+
+ while (pp) {
+ npp = pp->next;
+ if (pp->label)
+ free(pp->label);
+ free(pp);
+ pp = npp;
+ }
+ }
+}
+
+static struct enum_list *
+copy_enums(struct enum_list *sp)
+{
+ struct enum_list *xp = NULL, **spp = &xp;
+
+ while (sp) {
+ *spp = (struct enum_list *) calloc(1, sizeof(struct enum_list));
+ if (!*spp)
+ break;
+ (*spp)->label = strdup(sp->label);
+ (*spp)->value = sp->value;
+ spp = &(*spp)->next;
+ sp = sp->next;
+ }
+ return (xp);
+}
+
+static struct range_list *
+copy_ranges(struct range_list *sp)
+{
+ struct range_list *xp = NULL, **spp = &xp;
+
+ while (sp) {
+ *spp = (struct range_list *) calloc(1, sizeof(struct range_list));
+ if (!*spp)
+ break;
+ (*spp)->low = sp->low;
+ (*spp)->high = sp->high;
+ spp = &(*spp)->next;
+ sp = sp->next;
+ }
+ return (xp);
+}
+
+/*
+ * This routine parses a string like { blah blah blah } and returns OBJID if
+ * it is well formed, and NULL if not.
+ */
+static int
+tossObjectIdentifier(FILE * fp)
+{
+ int type;
+ char token[MAXTOKEN];
+ int bracketcount = 1;
+
+ type = get_token(fp, token, MAXTOKEN);
+
+ if (type != LEFTBRACKET)
+ return 0;
+ while ((type != RIGHTBRACKET || bracketcount > 0) && type != ENDOFFILE) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LEFTBRACKET)
+ bracketcount++;
+ else if (type == RIGHTBRACKET)
+ bracketcount--;
+ }
+
+ if (type == RIGHTBRACKET)
+ return OBJID;
+ else
+ return 0;
+}
+
+struct tree *
+find_node(const char *name, struct tree *subtree)
+{ /* Unused */
+ return (find_tree_node(name, -1));
+}
+
+struct module *
+find_module(int mid)
+{
+ struct module *mp;
+
+ for (mp = module_head; mp != NULL; mp = mp->next) {
+ if (mp->modid == mid)
+ break;
+ }
+ if (mp != 0)
+ return mp;
+ return NULL;
+}
+
+
+static char leave_indent[256];
+static int leave_was_simple;
+
+static void
+print_mib_leaves(FILE * f, struct tree *tp, int width)
+{
+ struct tree *ntp;
+ char *ip = leave_indent + strlen(leave_indent) - 1;
+ char last_ipch = *ip;
+
+ *ip = '+';
+ if (tp->type == TYPE_OTHER || tp->type > TYPE_SIMPLE_LAST) {
+ fprintf(f, "%s--%s(%ld)\n", leave_indent, tp->label, tp->subid);
+ if (tp->indexes) {
+ struct index_list *xp = tp->indexes;
+ int first = 1, cpos = 0, len, cmax =
+ width - strlen(leave_indent) - 12;
+ *ip = last_ipch;
+ fprintf(f, "%s | Index: ", leave_indent);
+ while (xp) {
+ if (first)
+ first = 0;
+ else
+ fprintf(f, ", ");
+ cpos += (len = strlen(xp->ilabel) + 2);
+ if (cpos > cmax) {
+ fprintf(f, "\n");
+ fprintf(f, "%s | ", leave_indent);
+ cpos = len;
+ }
+ fprintf(f, "%s", xp->ilabel);
+ xp = xp->next;
+ }
+ fprintf(f, "\n");
+ *ip = '+';
+ }
+ } else {
+ const char *acc, *typ;
+ int size = 0;
+ switch (tp->access) {
+ case MIB_ACCESS_NOACCESS:
+ acc = "----";
+ break;
+ case MIB_ACCESS_READONLY:
+ acc = "-R--";
+ break;
+ case MIB_ACCESS_WRITEONLY:
+ acc = "--W-";
+ break;
+ case MIB_ACCESS_READWRITE:
+ acc = "-RW-";
+ break;
+ case MIB_ACCESS_NOTIFY:
+ acc = "---N";
+ break;
+ case MIB_ACCESS_CREATE:
+ acc = "CR--";
+ break;
+ default:
+ acc = " ";
+ break;
+ }
+ switch (tp->type) {
+ case TYPE_OBJID:
+ typ = "ObjID ";
+ break;
+ case TYPE_OCTETSTR:
+ typ = "String ";
+ size = 1;
+ break;
+ case TYPE_INTEGER:
+ if (tp->enums)
+ typ = "EnumVal ";
+ else
+ typ = "INTEGER ";
+ break;
+ case TYPE_NETADDR:
+ typ = "NetAddr ";
+ break;
+ case TYPE_IPADDR:
+ typ = "IpAddr ";
+ break;
+ case TYPE_COUNTER:
+ typ = "Counter ";
+ break;
+ case TYPE_GAUGE:
+ typ = "Gauge ";
+ break;
+ case TYPE_TIMETICKS:
+ typ = "TimeTicks";
+ break;
+ case TYPE_OPAQUE:
+ typ = "Opaque ";
+ size = 1;
+ break;
+ case TYPE_NULL:
+ typ = "Null ";
+ break;
+ case TYPE_COUNTER64:
+ typ = "Counter64";
+ break;
+ case TYPE_BITSTRING:
+ typ = "BitString";
+ break;
+ case TYPE_NSAPADDRESS:
+ typ = "NsapAddr ";
+ break;
+ case TYPE_UNSIGNED32:
+ typ = "Unsigned ";
+ break;
+ case TYPE_UINTEGER:
+ typ = "UInteger ";
+ break;
+ case TYPE_INTEGER32:
+ typ = "Integer32";
+ break;
+ default:
+ typ = " ";
+ break;
+ }
+ fprintf(f, "%s-- %s %s %s(%ld)\n", leave_indent, acc, typ,
+ tp->label, tp->subid);
+ *ip = last_ipch;
+ if (tp->tc_index >= 0)
+ fprintf(f, "%s Textual Convention: %s\n", leave_indent,
+ tclist[tp->tc_index].descriptor);
+ if (tp->enums) {
+ struct enum_list *ep = tp->enums;
+ int cpos = 0, cmax =
+ width - strlen(leave_indent) - 16;
+ fprintf(f, "%s Values: ", leave_indent);
+ while (ep) {
+ char buf[80];
+ int bufw;
+ if (ep != tp->enums)
+ fprintf(f, ", ");
+ snprintf(buf, sizeof(buf), "%s(%d)", ep->label, ep->value);
+ buf[ sizeof(buf)-1 ] = 0;
+ cpos += (bufw = strlen(buf) + 2);
+ if (cpos >= cmax) {
+ fprintf(f, "\n%s ", leave_indent);
+ cpos = bufw;
+ }
+ fprintf(f, "%s", buf);
+ ep = ep->next;
+ }
+ fprintf(f, "\n");
+ }
+ if (tp->ranges) {
+ struct range_list *rp = tp->ranges;
+ if (size)
+ fprintf(f, "%s Size: ", leave_indent);
+ else
+ fprintf(f, "%s Range: ", leave_indent);
+ while (rp) {
+ if (rp != tp->ranges)
+ fprintf(f, " | ");
+ if (rp->low == rp->high)
+ fprintf(f, "%d", rp->low);
+ else
+ fprintf(f, "%d..%d", rp->low, rp->high);
+ rp = rp->next;
+ }
+ fprintf(f, "\n");
+ }
+ }
+ *ip = last_ipch;
+ strcat(leave_indent, " |");
+ leave_was_simple = tp->type != TYPE_OTHER;
+
+ {
+ int i, j, count = 0;
+ struct leave {
+ oid id;
+ struct tree *tp;
+ } *leaves, *lp;
+
+ for (ntp = tp->child_list; ntp; ntp = ntp->next_peer)
+ count++;
+ if (count) {
+ leaves = (struct leave *) calloc(count, sizeof(struct leave));
+ if (!leaves)
+ return;
+ for (ntp = tp->child_list, count = 0; ntp;
+ ntp = ntp->next_peer) {
+ for (i = 0, lp = leaves; i < count; i++, lp++)
+ if (lp->id >= ntp->subid)
+ break;
+ for (j = count; j > i; j--)
+ leaves[j] = leaves[j - 1];
+ lp->id = ntp->subid;
+ lp->tp = ntp;
+ count++;
+ }
+ for (i = 1, lp = leaves; i <= count; i++, lp++) {
+ if (!leave_was_simple || lp->tp->type == 0)
+ fprintf(f, "%s\n", leave_indent);
+ if (i == count)
+ ip[3] = ' ';
+ print_mib_leaves(f, lp->tp, width);
+ }
+ free(leaves);
+ leave_was_simple = 0;
+ }
+ }
+ ip[1] = 0;
+}
+
+void
+print_mib_tree(FILE * f, struct tree *tp, int width)
+{
+ leave_indent[0] = ' ';
+ leave_indent[1] = 0;
+ leave_was_simple = 1;
+ print_mib_leaves(f, tp, width);
+}
+
+
+/*
+ * Merge the parsed object identifier with the existing node.
+ * If there is a problem with the identifier, release the existing node.
+ */
+static struct node *
+merge_parse_objectid(struct node *np, FILE * fp, char *name)
+{
+ struct node *nnp;
+ /*
+ * printf("merge defval --> %s\n",np->defaultValue);
+ */
+ nnp = parse_objectid(fp, name);
+ if (nnp) {
+
+ /*
+ * apply last OID sub-identifier data to the information
+ */
+ /*
+ * already collected for this node.
+ */
+ struct node *headp, *nextp;
+ int ncount = 0;
+ nextp = headp = nnp;
+ while (nnp->next) {
+ nextp = nnp;
+ ncount++;
+ nnp = nnp->next;
+ }
+
+ np->label = nnp->label;
+ np->subid = nnp->subid;
+ np->modid = nnp->modid;
+ np->parent = nnp->parent;
+ if (nnp->filename != NULL) {
+ free(nnp->filename);
+ }
+ free(nnp);
+
+ if (ncount) {
+ nextp->next = np;
+ np = headp;
+ }
+ } else {
+ free_node(np);
+ np = NULL;
+ }
+
+ return np;
+}
+
+/*
+ * transfer data to tree from node
+ *
+ * move pointers for alloc'd data from np to tp.
+ * this prevents them from being freed when np is released.
+ * parent member is not moved.
+ *
+ * CAUTION: nodes may be repeats of existing tree nodes.
+ * This can happen especially when resolving IMPORT clauses.
+ *
+ */
+static void
+tree_from_node(struct tree *tp, struct node *np)
+{
+ free_partial_tree(tp, FALSE);
+
+ tp->label = np->label;
+ np->label = NULL;
+ tp->enums = np->enums;
+ np->enums = NULL;
+ tp->ranges = np->ranges;
+ np->ranges = NULL;
+ tp->indexes = np->indexes;
+ np->indexes = NULL;
+ tp->augments = np->augments;
+ np->augments = NULL;
+ tp->varbinds = np->varbinds;
+ np->varbinds = NULL;
+ tp->hint = np->hint;
+ np->hint = NULL;
+ tp->units = np->units;
+ np->units = NULL;
+ tp->description = np->description;
+ np->description = NULL;
+ tp->defaultValue = np->defaultValue;
+ np->defaultValue = NULL;
+ tp->subid = np->subid;
+ tp->tc_index = np->tc_index;
+ tp->type = translation_table[np->type];
+ tp->access = np->access;
+ tp->status = np->status;
+
+ set_function(tp);
+}
diff --git a/mib/parse.c.orig b/mib/parse.c.orig
new file mode 100644
index 0000000..e4268d4
--- /dev/null
+++ b/mib/parse.c.orig
@@ -0,0 +1,5326 @@
+/*
+ * parse.c
+ *
+ * Update: 1998-09-22 <mslifcak@iss.net>
+ * Clear nbuckets in init_node_hash.
+ * New method xcalloc returns zeroed data structures.
+ * New method alloc_node encapsulates common node creation.
+ * New method to configure terminate comment at end of line.
+ * New method to configure accept underscore in labels.
+ *
+ * Update: 1998-10-10 <daves@csc.liv.ac.uk>
+ * fully qualified OID parsing patch
+ *
+ * Update: 1998-10-20 <daves@csc.liv.ac.uk>
+ * merge_anon_children patch
+ *
+ * Update: 1998-10-21 <mslifcak@iss.net>
+ * Merge_parse_objectid associates information with last node in chain.
+ */
+/* Portions of this file are subject to the following copyrights. See
+ * the Net-SNMP's COPYING file for more details and other copyrights
+ * that may apply:
+ */
+/******************************************************************
+ Copyright 1989, 1991, 1992 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of CMU not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+******************************************************************/
+/*
+ * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms specified in the COPYING file
+ * distributed with the Net-SNMP package.
+ */
+#include <net-snmp/net-snmp-config.h>
+
+#include <stdio.h>
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*
+ * Wow. This is ugly. -- Wes
+ */
+#if HAVE_DIRENT_H
+# include <dirent.h>
+# define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+# define dirent direct
+# define NAMLEN(dirent) (dirent)->d_namlen
+# if HAVE_SYS_NDIR_H
+# include <sys/ndir.h>
+# endif
+# if HAVE_SYS_DIR_H
+# include <sys/dir.h>
+# endif
+# if HAVE_NDIR_H
+# include <ndir.h>
+# endif
+#endif
+#if TIME_WITH_SYS_TIME
+# ifdef WIN32
+# include <sys/timeb.h>
+# else
+# include <sys/time.h>
+# endif
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#if HAVE_WINSOCK_H
+#include <winsock.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
+#include <regex.h>
+#endif
+#if HAVE_DMALLOC_H
+#include <dmalloc.h>
+#endif
+
+#include <net-snmp/types.h>
+#include <net-snmp/output_api.h>
+#include <net-snmp/config_api.h>
+#include <net-snmp/utilities.h>
+
+#include <net-snmp/library/parse.h>
+#include <net-snmp/library/mib.h>
+#include <net-snmp/library/snmp_api.h>
+
+/*
+ * This is one element of an object identifier with either an integer
+ * subidentifier, or a textual string label, or both.
+ * The subid is -1 if not present, and label is NULL if not present.
+ */
+struct subid_s {
+ int subid;
+ int modid;
+ char *label;
+};
+
+#define MAXTC 4096
+struct tc { /* textual conventions */
+ int type;
+ int modid;
+ char *descriptor;
+ char *hint;
+ struct enum_list *enums;
+ struct range_list *ranges;
+} tclist[MAXTC];
+
+int mibLine = 0;
+const char *File = "(none)";
+static int anonymous = 0;
+
+struct objgroup {
+ char *name;
+ int line;
+ struct objgroup *next;
+} *objgroups = NULL, *objects = NULL, *notifs = NULL;
+
+#define SYNTAX_MASK 0x80
+/*
+ * types of tokens
+ * Tokens wiht the SYNTAX_MASK bit set are syntax tokens
+ */
+#define CONTINUE -1
+#define ENDOFFILE 0
+#define LABEL 1
+#define SUBTREE 2
+#define SYNTAX 3
+#define OBJID (4 | SYNTAX_MASK)
+#define OCTETSTR (5 | SYNTAX_MASK)
+#define INTEGER (6 | SYNTAX_MASK)
+#define NETADDR (7 | SYNTAX_MASK)
+#define IPADDR (8 | SYNTAX_MASK)
+#define COUNTER (9 | SYNTAX_MASK)
+#define GAUGE (10 | SYNTAX_MASK)
+#define TIMETICKS (11 | SYNTAX_MASK)
+#define KW_OPAQUE (12 | SYNTAX_MASK)
+#define NUL (13 | SYNTAX_MASK)
+#define SEQUENCE 14
+#define OF 15 /* SEQUENCE OF */
+#define OBJTYPE 16
+#define ACCESS 17
+#define READONLY 18
+#define READWRITE 19
+#define WRITEONLY 20
+#ifdef NOACCESS
+#undef NOACCESS /* agent 'NOACCESS' token */
+#endif
+#define NOACCESS 21
+#define STATUS 22
+#define MANDATORY 23
+#define KW_OPTIONAL 24
+#define OBSOLETE 25
+/*
+ * #define RECOMMENDED 26
+ */
+#define PUNCT 27
+#define EQUALS 28
+#define NUMBER 29
+#define LEFTBRACKET 30
+#define RIGHTBRACKET 31
+#define LEFTPAREN 32
+#define RIGHTPAREN 33
+#define COMMA 34
+#define DESCRIPTION 35
+#define QUOTESTRING 36
+#define INDEX 37
+#define DEFVAL 38
+#define DEPRECATED 39
+#define SIZE 40
+#define BITSTRING (41 | SYNTAX_MASK)
+#define NSAPADDRESS (42 | SYNTAX_MASK)
+#define COUNTER64 (43 | SYNTAX_MASK)
+#define OBJGROUP 44
+#define NOTIFTYPE 45
+#define AUGMENTS 46
+#define COMPLIANCE 47
+#define READCREATE 48
+#define UNITS 49
+#define REFERENCE 50
+#define NUM_ENTRIES 51
+#define MODULEIDENTITY 52
+#define LASTUPDATED 53
+#define ORGANIZATION 54
+#define CONTACTINFO 55
+#define UINTEGER32 (56 | SYNTAX_MASK)
+#define CURRENT 57
+#define DEFINITIONS 58
+#define END 59
+#define SEMI 60
+#define TRAPTYPE 61
+#define ENTERPRISE 62
+/*
+ * #define DISPLAYSTR (63 | SYNTAX_MASK)
+ */
+#define BEGIN 64
+#define IMPORTS 65
+#define EXPORTS 66
+#define ACCNOTIFY 67
+#define BAR 68
+#define RANGE 69
+#define CONVENTION 70
+#define DISPLAYHINT 71
+#define FROM 72
+#define AGENTCAP 73
+#define MACRO 74
+#define IMPLIED 75
+#define SUPPORTS 76
+#define INCLUDES 77
+#define VARIATION 78
+#define REVISION 79
+#define NOTIMPL 80
+#define OBJECTS 81
+#define NOTIFICATIONS 82
+#define MODULE 83
+#define MINACCESS 84
+#define PRODREL 85
+#define WRSYNTAX 86
+#define CREATEREQ 87
+#define NOTIFGROUP 88
+#define MANDATORYGROUPS 89
+#define GROUP 90
+#define OBJECT 91
+#define IDENTIFIER 92
+#define CHOICE 93
+#define LEFTSQBRACK 95
+#define RIGHTSQBRACK 96
+#define IMPLICIT 97
+#define APPSYNTAX (98 | SYNTAX_MASK)
+#define OBJSYNTAX (99 | SYNTAX_MASK)
+#define SIMPLESYNTAX (100 | SYNTAX_MASK)
+#define OBJNAME (101 | SYNTAX_MASK)
+#define NOTIFNAME (102 | SYNTAX_MASK)
+#define VARIABLES 103
+#define UNSIGNED32 (104 | SYNTAX_MASK)
+#define INTEGER32 (105 | SYNTAX_MASK)
+/*
+ * Beware of reaching SYNTAX_MASK (0x80)
+ */
+
+struct tok {
+ const char *name; /* token name */
+ int len; /* length not counting nul */
+ int token; /* value */
+ int hash; /* hash of name */
+ struct tok *next; /* pointer to next in hash table */
+};
+
+
+static struct tok tokens[] = {
+ {"obsolete", sizeof("obsolete") - 1, OBSOLETE}
+ ,
+ {"Opaque", sizeof("Opaque") - 1, KW_OPAQUE}
+ ,
+ {"optional", sizeof("optional") - 1, KW_OPTIONAL}
+ ,
+ {"LAST-UPDATED", sizeof("LAST-UPDATED") - 1, LASTUPDATED}
+ ,
+ {"ORGANIZATION", sizeof("ORGANIZATION") - 1, ORGANIZATION}
+ ,
+ {"CONTACT-INFO", sizeof("CONTACT-INFO") - 1, CONTACTINFO}
+ ,
+ {"MODULE-IDENTITY", sizeof("MODULE-IDENTITY") - 1, MODULEIDENTITY}
+ ,
+ {"MODULE-COMPLIANCE", sizeof("MODULE-COMPLIANCE") - 1, COMPLIANCE}
+ ,
+ {"DEFINITIONS", sizeof("DEFINITIONS") - 1, DEFINITIONS}
+ ,
+ {"END", sizeof("END") - 1, END}
+ ,
+ {"AUGMENTS", sizeof("AUGMENTS") - 1, AUGMENTS}
+ ,
+ {"not-accessible", sizeof("not-accessible") - 1, NOACCESS}
+ ,
+ {"write-only", sizeof("write-only") - 1, WRITEONLY}
+ ,
+ {"NsapAddress", sizeof("NsapAddress") - 1, NSAPADDRESS}
+ ,
+ {"UNITS", sizeof("Units") - 1, UNITS}
+ ,
+ {"REFERENCE", sizeof("REFERENCE") - 1, REFERENCE}
+ ,
+ {"NUM-ENTRIES", sizeof("NUM-ENTRIES") - 1, NUM_ENTRIES}
+ ,
+ {"BITSTRING", sizeof("BITSTRING") - 1, BITSTRING}
+ ,
+ {"BIT", sizeof("BIT") - 1, CONTINUE}
+ ,
+ {"BITS", sizeof("BITS") - 1, BITSTRING}
+ ,
+ {"Counter64", sizeof("Counter64") - 1, COUNTER64}
+ ,
+ {"TimeTicks", sizeof("TimeTicks") - 1, TIMETICKS}
+ ,
+ {"NOTIFICATION-TYPE", sizeof("NOTIFICATION-TYPE") - 1, NOTIFTYPE}
+ ,
+ {"OBJECT-GROUP", sizeof("OBJECT-GROUP") - 1, OBJGROUP}
+ ,
+ {"OBJECT-IDENTITY", sizeof("OBJECT-IDENTITY") - 1, OBJGROUP}
+ ,
+ {"IDENTIFIER", sizeof("IDENTIFIER") - 1, IDENTIFIER}
+ ,
+ {"OBJECT", sizeof("OBJECT") - 1, OBJECT}
+ ,
+ {"NetworkAddress", sizeof("NetworkAddress") - 1, NETADDR}
+ ,
+ {"Gauge", sizeof("Gauge") - 1, GAUGE}
+ ,
+ {"Gauge32", sizeof("Gauge32") - 1, GAUGE}
+ ,
+ {"Unsigned32", sizeof("Unsigned32") - 1, UNSIGNED32}
+ ,
+ {"read-write", sizeof("read-write") - 1, READWRITE}
+ ,
+ {"read-create", sizeof("read-create") - 1, READCREATE}
+ ,
+ {"OCTETSTRING", sizeof("OCTETSTRING") - 1, OCTETSTR}
+ ,
+ {"OCTET", sizeof("OCTET") - 1, CONTINUE}
+ ,
+ {"OF", sizeof("OF") - 1, OF}
+ ,
+ {"SEQUENCE", sizeof("SEQUENCE") - 1, SEQUENCE}
+ ,
+ {"NULL", sizeof("NULL") - 1, NUL}
+ ,
+ {"IpAddress", sizeof("IpAddress") - 1, IPADDR}
+ ,
+ {"UInteger32", sizeof("UInteger32") - 1, UINTEGER32}
+ ,
+ {"INTEGER", sizeof("INTEGER") - 1, INTEGER}
+ ,
+ {"Integer32", sizeof("Integer32") - 1, INTEGER32}
+ ,
+ {"Counter", sizeof("Counter") - 1, COUNTER}
+ ,
+ {"Counter32", sizeof("Counter32") - 1, COUNTER}
+ ,
+ {"read-only", sizeof("read-only") - 1, READONLY}
+ ,
+ {"DESCRIPTION", sizeof("DESCRIPTION") - 1, DESCRIPTION}
+ ,
+ {"INDEX", sizeof("INDEX") - 1, INDEX}
+ ,
+ {"DEFVAL", sizeof("DEFVAL") - 1, DEFVAL}
+ ,
+ {"deprecated", sizeof("deprecated") - 1, DEPRECATED}
+ ,
+ {"SIZE", sizeof("SIZE") - 1, SIZE}
+ ,
+ {"MAX-ACCESS", sizeof("MAX-ACCESS") - 1, ACCESS}
+ ,
+ {"ACCESS", sizeof("ACCESS") - 1, ACCESS}
+ ,
+ {"mandatory", sizeof("mandatory") - 1, MANDATORY}
+ ,
+ {"current", sizeof("current") - 1, CURRENT}
+ ,
+ {"STATUS", sizeof("STATUS") - 1, STATUS}
+ ,
+ {"SYNTAX", sizeof("SYNTAX") - 1, SYNTAX}
+ ,
+ {"OBJECT-TYPE", sizeof("OBJECT-TYPE") - 1, OBJTYPE}
+ ,
+ {"TRAP-TYPE", sizeof("TRAP-TYPE") - 1, TRAPTYPE}
+ ,
+ {"ENTERPRISE", sizeof("ENTERPRISE") - 1, ENTERPRISE}
+ ,
+ {"BEGIN", sizeof("BEGIN") - 1, BEGIN}
+ ,
+ {"IMPORTS", sizeof("IMPORTS") - 1, IMPORTS}
+ ,
+ {"EXPORTS", sizeof("EXPORTS") - 1, EXPORTS}
+ ,
+ {"accessible-for-notify", sizeof("accessible-for-notify") - 1,
+ ACCNOTIFY}
+ ,
+ {"TEXTUAL-CONVENTION", sizeof("TEXTUAL-CONVENTION") - 1, CONVENTION}
+ ,
+ {"NOTIFICATION-GROUP", sizeof("NOTIFICATION-GROUP") - 1, NOTIFGROUP}
+ ,
+ {"DISPLAY-HINT", sizeof("DISPLAY-HINT") - 1, DISPLAYHINT}
+ ,
+ {"FROM", sizeof("FROM") - 1, FROM}
+ ,
+ {"AGENT-CAPABILITIES", sizeof("AGENT-CAPABILITIES") - 1, AGENTCAP}
+ ,
+ {"MACRO", sizeof("MACRO") - 1, MACRO}
+ ,
+ {"IMPLIED", sizeof("IMPLIED") - 1, IMPLIED}
+ ,
+ {"SUPPORTS", sizeof("SUPPORTS") - 1, SUPPORTS}
+ ,
+ {"INCLUDES", sizeof("INCLUDES") - 1, INCLUDES}
+ ,
+ {"VARIATION", sizeof("VARIATION") - 1, VARIATION}
+ ,
+ {"REVISION", sizeof("REVISION") - 1, REVISION}
+ ,
+ {"not-implemented", sizeof("not-implemented") - 1, NOTIMPL}
+ ,
+ {"OBJECTS", sizeof("OBJECTS") - 1, OBJECTS}
+ ,
+ {"NOTIFICATIONS", sizeof("NOTIFICATIONS") - 1, NOTIFICATIONS}
+ ,
+ {"MODULE", sizeof("MODULE") - 1, MODULE}
+ ,
+ {"MIN-ACCESS", sizeof("MIN-ACCESS") - 1, MINACCESS}
+ ,
+ {"PRODUCT-RELEASE", sizeof("PRODUCT-RELEASE") - 1, PRODREL}
+ ,
+ {"WRITE-SYNTAX", sizeof("WRITE-SYNTAX") - 1, WRSYNTAX}
+ ,
+ {"CREATION-REQUIRES", sizeof("CREATION-REQUIRES") - 1, CREATEREQ}
+ ,
+ {"MANDATORY-GROUPS", sizeof("MANDATORY-GROUPS") - 1, MANDATORYGROUPS}
+ ,
+ {"GROUP", sizeof("GROUP") - 1, GROUP}
+ ,
+ {"CHOICE", sizeof("CHOICE") - 1, CHOICE}
+ ,
+ {"IMPLICIT", sizeof("IMPLICIT") - 1, IMPLICIT}
+ ,
+ {"ObjectSyntax", sizeof("ObjectSyntax") - 1, OBJSYNTAX}
+ ,
+ {"SimpleSyntax", sizeof("SimpleSyntax") - 1, SIMPLESYNTAX}
+ ,
+ {"ApplicationSyntax", sizeof("ApplicationSyntax") - 1, APPSYNTAX}
+ ,
+ {"ObjectName", sizeof("ObjectName") - 1, OBJNAME}
+ ,
+ {"NotificationName", sizeof("NotificationName") - 1, NOTIFNAME}
+ ,
+ {"VARIABLES", sizeof("VARIABLES") - 1, VARIABLES}
+ ,
+ {NULL}
+};
+
+static struct module_compatability *module_map_head;
+static struct module_compatability module_map[] = {
+ {"RFC1065-SMI", "RFC1155-SMI", NULL, 0},
+ {"RFC1066-MIB", "RFC1156-MIB", NULL, 0},
+ /*
+ * 'mib' -> 'mib-2'
+ */
+ {"RFC1156-MIB", "RFC1158-MIB", NULL, 0},
+ /*
+ * 'snmpEnableAuthTraps' -> 'snmpEnableAuthenTraps'
+ */
+ {"RFC1158-MIB", "RFC1213-MIB", NULL, 0},
+ /*
+ * 'nullOID' -> 'zeroDotZero'
+ */
+ {"RFC1155-SMI", "SNMPv2-SMI", NULL, 0},
+ {"RFC1213-MIB", "SNMPv2-SMI", "mib-2", 0},
+ {"RFC1213-MIB", "SNMPv2-MIB", "sys", 3},
+ {"RFC1213-MIB", "IF-MIB", "if", 2},
+ {"RFC1213-MIB", "IP-MIB", "ip", 2},
+ {"RFC1213-MIB", "IP-MIB", "icmp", 4},
+ {"RFC1213-MIB", "TCP-MIB", "tcp", 3},
+ {"RFC1213-MIB", "UDP-MIB", "udp", 3},
+ {"RFC1213-MIB", "SNMPv2-SMI", "transmission", 0},
+ {"RFC1213-MIB", "SNMPv2-MIB", "snmp", 4},
+ {"RFC1231-MIB", "TOKENRING-MIB", NULL, 0},
+ {"RFC1271-MIB", "RMON-MIB", NULL, 0},
+ {"RFC1286-MIB", "SOURCE-ROUTING-MIB", "dot1dSr", 7},
+ {"RFC1286-MIB", "BRIDGE-MIB", NULL, 0},
+ {"RFC1315-MIB", "FRAME-RELAY-DTE-MIB", NULL, 0},
+ {"RFC1316-MIB", "CHARACTER-MIB", NULL, 0},
+ {"RFC1406-MIB", "DS1-MIB", NULL, 0},
+ {"RFC-1213", "RFC1213-MIB", NULL, 0},
+};
+
+#define MODULE_NOT_FOUND 0
+#define MODULE_LOADED_OK 1
+#define MODULE_ALREADY_LOADED 2
+/*
+ * #define MODULE_LOAD_FAILED 3
+ */
+#define MODULE_LOAD_FAILED MODULE_NOT_FOUND
+
+
+#define HASHSIZE 32
+#define BUCKET(x) (x & (HASHSIZE-1))
+
+#define NHASHSIZE 128
+#define NBUCKET(x) (x & (NHASHSIZE-1))
+
+static struct tok *buckets[HASHSIZE];
+
+static struct node *nbuckets[NHASHSIZE];
+static struct tree *tbuckets[NHASHSIZE];
+static struct module *module_head = NULL;
+
+struct node *orphan_nodes = NULL;
+struct tree *tree_head = NULL;
+
+#define NUMBER_OF_ROOT_NODES 3
+static struct module_import root_imports[NUMBER_OF_ROOT_NODES];
+
+static int current_module = 0;
+static int max_module = 0;
+static char *last_err_module = 0; /* no repeats on "Cannot find module..." */
+
+static void tree_from_node(struct tree *tp, struct node *np);
+static void do_subtree(struct tree *, struct node **);
+static void do_linkup(struct module *, struct node *);
+static void dump_module_list(void);
+static int get_token(FILE *, char *, int);
+static int parseQuoteString(FILE *, char *, int);
+static int tossObjectIdentifier(FILE *);
+static int name_hash(const char *);
+static void init_node_hash(struct node *);
+static void print_error(const char *, const char *, int);
+static void free_tree(struct tree *);
+static void free_partial_tree(struct tree *, int);
+static void free_node(struct node *);
+static void build_translation_table(void);
+static void init_tree_roots(void);
+static void merge_anon_children(struct tree *, struct tree *);
+static void unlink_tbucket(struct tree *);
+static void unlink_tree(struct tree *);
+static int getoid(FILE *, struct subid_s *, int);
+static struct node *parse_objectid(FILE *, char *);
+static int get_tc(const char *, int, int *, struct enum_list **,
+ struct range_list **, char **);
+static int get_tc_index(const char *, int);
+static struct enum_list *parse_enumlist(FILE *, struct enum_list **);
+static struct range_list *parse_ranges(FILE * fp, struct range_list **);
+static struct node *parse_asntype(FILE *, char *, int *, char *);
+static struct node *parse_objecttype(FILE *, char *);
+static struct node *parse_objectgroup(FILE *, char *, int,
+ struct objgroup **);
+static struct node *parse_notificationDefinition(FILE *, char *);
+static struct node *parse_trapDefinition(FILE *, char *);
+static struct node *parse_compliance(FILE *, char *);
+static struct node *parse_capabilities(FILE *, char *);
+static struct node *parse_moduleIdentity(FILE *, char *);
+static struct node *parse_macro(FILE *, char *);
+static void parse_imports(FILE *);
+static struct node *parse(FILE *, struct node *);
+
+static int read_module_internal(const char *);
+static void read_module_replacements(const char *);
+static void read_import_replacements(const char *,
+ struct module_import *);
+
+static void new_module(const char *, const char *);
+
+static struct node *merge_parse_objectid(struct node *, FILE *, char *);
+static struct index_list *getIndexes(FILE * fp, struct index_list **);
+static struct varbind_list *getVarbinds(FILE * fp, struct varbind_list **);
+static void free_indexes(struct index_list **);
+static void free_varbinds(struct varbind_list **);
+static void free_ranges(struct range_list **);
+static void free_enums(struct enum_list **);
+static struct range_list *copy_ranges(struct range_list *);
+static struct enum_list *copy_enums(struct enum_list *);
+
+static u_int compute_match(const char *search_base, const char *key);
+
+void
+snmp_mib_toggle_options_usage(const char *lead, FILE * outf)
+{
+ fprintf(outf, "%su: %sallow the use of underlines in MIB symbols\n",
+ lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_PARSE_LABEL)) ?
+ "dis" : ""));
+ fprintf(outf, "%sc: %sallow the use of \"--\" to terminate comments\n",
+ lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_COMMENT_TERM)) ?
+ "" : "dis"));
+
+ fprintf(outf, "%sd: %ssave the DESCRIPTIONs of the MIB objects\n",
+ lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) ?
+ "do not " : ""));
+
+ fprintf(outf, "%se: disable errors when MIB symbols conflict\n", lead);
+
+ fprintf(outf, "%sw: enable warnings when MIB symbols conflict\n", lead);
+
+ fprintf(outf, "%sW: enable detailed warnings when MIB symbols conflict\n",
+ lead);
+
+ fprintf(outf, "%sR: replace MIB symbols from latest module\n", lead);
+}
+
+char *
+snmp_mib_toggle_options(char *options)
+{
+ if (options) {
+ while (*options) {
+ switch (*options) {
+ case 'u':
+ netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIB_PARSE_LABEL,
+ !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_PARSE_LABEL));
+ break;
+
+ case 'c':
+ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_COMMENT_TERM);
+ break;
+
+ case 'e':
+ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_ERRORS);
+ break;
+
+ case 'w':
+ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS, 1);
+ break;
+
+ case 'W':
+ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS, 2);
+ break;
+
+ case 'd':
+ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS);
+ break;
+
+ case 'R':
+ netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_REPLACE);
+ break;
+
+ default:
+ /*
+ * return at the unknown option
+ */
+ return options;
+ }
+ options++;
+ }
+ }
+ return NULL;
+}
+
+static int
+name_hash(const char *name)
+{
+ int hash = 0;
+ const char *cp;
+
+ if (!name)
+ return 0;
+ for (cp = name; *cp; cp++)
+ hash += tolower(*cp);
+ return (hash);
+}
+
+void
+init_mib_internals(void)
+{
+ register struct tok *tp;
+ register int b, i;
+ int max_modc;
+
+ if (tree_head)
+ return;
+
+ /*
+ * Set up hash list of pre-defined tokens
+ */
+ memset(buckets, 0, sizeof(buckets));
+ for (tp = tokens; tp->name; tp++) {
+ tp->hash = name_hash(tp->name);
+ b = BUCKET(tp->hash);
+ if (buckets[b])
+ tp->next = buckets[b]; /* BUG ??? */
+ buckets[b] = tp;
+ }
+
+ /*
+ * Initialise other internal structures
+ */
+
+ max_modc = sizeof(module_map) / sizeof(module_map[0]) - 1;
+ for (i = 0; i < max_modc; ++i)
+ module_map[i].next = &(module_map[i + 1]);
+ module_map[max_modc].next = NULL;
+ module_map_head = module_map;
+
+ memset(nbuckets, 0, sizeof(nbuckets));
+ memset(tbuckets, 0, sizeof(tbuckets));
+ memset(tclist, 0, MAXTC * sizeof(struct tc));
+ build_translation_table();
+ init_tree_roots(); /* Set up initial roots */
+ /*
+ * Relies on 'add_mibdir' having set up the modules
+ */
+}
+
+static void
+init_node_hash(struct node *nodes)
+{
+ struct node *np, *nextp;
+ int hash;
+
+ memset(nbuckets, 0, sizeof(nbuckets));
+ for (np = nodes; np;) {
+ nextp = np->next;
+ hash = NBUCKET(name_hash(np->parent));
+ np->next = nbuckets[hash];
+ nbuckets[hash] = np;
+ np = nextp;
+ }
+}
+
+static int erroneousMibs = 0;
+
+int
+get_mib_parse_error_count(void)
+{
+ return erroneousMibs;
+}
+
+
+static void
+print_error(const char *string, const char *token, int type)
+{
+ erroneousMibs++;
+ DEBUGMSGTL(("parse-mibs", "\n"));
+ if (type == ENDOFFILE)
+ snmp_log(LOG_ERR, "%s (EOF): At line %d in %s\n", string, mibLine,
+ File);
+ else if (token && *token)
+ snmp_log(LOG_ERR, "%s (%s): At line %d in %s\n", string, token,
+ mibLine, File);
+ else
+ snmp_log(LOG_ERR, "%s: At line %d in %s\n", string, mibLine, File);
+}
+
+static void
+print_module_not_found(const char *cp)
+{
+ if (!last_err_module || strcmp(cp, last_err_module))
+ print_error("Cannot find module", cp, CONTINUE);
+ if (last_err_module)
+ free(last_err_module);
+ last_err_module = strdup(cp);
+}
+
+static struct node *
+alloc_node(int modid)
+{
+ struct node *np;
+ np = (struct node *) calloc(1, sizeof(struct node));
+ if (np) {
+ np->tc_index = -1;
+ np->modid = modid;
+ np->filename = strdup(File);
+ np->lineno = mibLine;
+ }
+ return np;
+}
+
+static void
+unlink_tbucket(struct tree *tp)
+{
+ int hash = NBUCKET(name_hash(tp->label));
+ struct tree *otp = NULL, *ntp = tbuckets[hash];
+
+ while (ntp && ntp != tp) {
+ otp = ntp;
+ ntp = ntp->next;
+ }
+ if (!ntp)
+ snmp_log(LOG_EMERG, "Can't find %s in tbuckets\n", tp->label);
+ else if (otp)
+ otp->next = ntp->next;
+ else
+ tbuckets[hash] = tp->next;
+}
+
+static void
+unlink_tree(struct tree *tp)
+{
+ struct tree *otp = NULL, *ntp = tp->parent;
+
+ if (!ntp) { /* this tree has no parent */
+ DEBUGMSGTL(("unlink_tree", "Tree node %s has no parent\n",
+ tp->label));
+ } else {
+ ntp = ntp->child_list;
+
+ while (ntp && ntp != tp) {
+ otp = ntp;
+ ntp = ntp->next_peer;
+ }
+ if (!ntp)
+ snmp_log(LOG_EMERG, "Can't find %s in %s's children\n",
+ tp->label, tp->parent->label);
+ else if (otp)
+ otp->next_peer = ntp->next_peer;
+ else
+ tp->parent->child_list = tp->next_peer;
+ }
+
+ if (tree_head == tp)
+ tree_head = tp->next_peer;
+}
+
+static void
+free_partial_tree(struct tree *tp, int keep_label)
+{
+ if (!tp)
+ return;
+
+ /*
+ * remove the data from this tree node
+ */
+ free_enums(&tp->enums);
+ free_ranges(&tp->ranges);
+ free_indexes(&tp->indexes);
+ free_varbinds(&tp->varbinds);
+ if (!keep_label)
+ SNMP_FREE(tp->label);
+ SNMP_FREE(tp->hint);
+ SNMP_FREE(tp->units);
+ SNMP_FREE(tp->description);
+ SNMP_FREE(tp->augments);
+ SNMP_FREE(tp->defaultValue);
+}
+
+/*
+ * free a tree node. Note: the node must already have been unlinked
+ * from the tree when calling this routine
+ */
+static void
+free_tree(struct tree *Tree)
+{
+ if (!Tree)
+ return;
+
+ unlink_tbucket(Tree);
+ free_partial_tree(Tree, FALSE);
+ if (Tree->number_modules > 1)
+ free((char *) Tree->module_list);
+ free((char *) Tree);
+}
+
+static void
+free_node(struct node *np)
+{
+ if (!np)
+ return;
+
+ free_enums(&np->enums);
+ free_ranges(&np->ranges);
+ free_indexes(&np->indexes);
+ free_varbinds(&np->varbinds);
+ if (np->label)
+ free(np->label);
+ if (np->hint)
+ free(np->hint);
+ if (np->units)
+ free(np->units);
+ if (np->description)
+ free(np->description);
+ if (np->defaultValue)
+ free(np->defaultValue);
+ if (np->parent)
+ free(np->parent);
+ if (np->augments)
+ free(np->augments);
+ if (np->filename)
+ free(np->filename);
+ free((char *) np);
+}
+
+#ifdef TEST
+static void
+print_nodes(FILE * fp, struct node *root)
+{
+ extern void xmalloc_stats(FILE *);
+ struct enum_list *ep;
+ struct index_list *ip;
+ struct range_list *rp;
+ struct varbind_list *vp;
+ struct node *np;
+
+ for (np = root; np; np = np->next) {
+ fprintf(fp, "%s ::= { %s %ld } (%d)\n", np->label, np->parent,
+ np->subid, np->type);
+ if (np->tc_index >= 0)
+ fprintf(fp, " TC = %s\n", tclist[np->tc_index].descriptor);
+ if (np->enums) {
+ fprintf(fp, " Enums: \n");
+ for (ep = np->enums; ep; ep = ep->next) {
+ fprintf(fp, " %s(%d)\n", ep->label, ep->value);
+ }
+ }
+ if (np->ranges) {
+ fprintf(fp, " Ranges: \n");
+ for (rp = np->ranges; rp; rp = rp->next) {
+ fprintf(fp, " %d..%d\n", rp->low, rp->high);
+ }
+ }
+ if (np->indexes) {
+ fprintf(fp, " Indexes: \n");
+ for (ip = np->indexes; ip; ip = ip->next) {
+ fprintf(fp, " %s\n", ip->ilabel);
+ }
+ }
+ if (np->augments)
+ fprintf(fp, " Augments: %s\n", np->augments);
+ if (np->varbinds) {
+ fprintf(fp, " Varbinds: \n");
+ for (vp = np->varbinds; vp; vp = vp->next) {
+ fprintf(fp, " %s\n", vp->vblabel);
+ }
+ }
+ if (np->hint)
+ fprintf(fp, " Hint: %s\n", np->hint);
+ if (np->units)
+ fprintf(fp, " Units: %s\n", np->units);
+ if (np->defaultValue)
+ fprintf(fp, " DefaultValue: %s\n", np->defaultValue);
+ }
+}
+#endif
+
+void
+print_subtree(FILE * f, struct tree *tree, int count)
+{
+ struct tree *tp;
+ int i;
+ char modbuf[256];
+
+ for (i = 0; i < count; i++)
+ fprintf(f, " ");
+ fprintf(f, "Children of %s(%ld):\n", tree->label, tree->subid);
+ count++;
+ for (tp = tree->child_list; tp; tp = tp->next_peer) {
+ for (i = 0; i < count; i++)
+ fprintf(f, " ");
+ fprintf(f, "%s:%s(%ld) type=%d",
+ module_name(tp->module_list[0], modbuf),
+ tp->label, tp->subid, tp->type);
+ if (tp->tc_index != -1)
+ fprintf(f, " tc=%d", tp->tc_index);
+ if (tp->hint)
+ fprintf(f, " hint=%s", tp->hint);
+ if (tp->units)
+ fprintf(f, " units=%s", tp->units);
+ if (tp->number_modules > 1) {
+ fprintf(f, " modules:");
+ for (i = 1; i < tp->number_modules; i++)
+ fprintf(f, " %s", module_name(tp->module_list[i], modbuf));
+ }
+ fprintf(f, "\n");
+ }
+ for (tp = tree->child_list; tp; tp = tp->next_peer) {
+ if (tp->child_list)
+ print_subtree(f, tp, count);
+ }
+}
+
+void
+print_ascii_dump_tree(FILE * f, struct tree *tree, int count)
+{
+ struct tree *tp;
+
+ count++;
+ for (tp = tree->child_list; tp; tp = tp->next_peer) {
+ fprintf(f, "%s OBJECT IDENTIFIER ::= { %s %ld }\n", tp->label,
+ tree->label, tp->subid);
+ }
+ for (tp = tree->child_list; tp; tp = tp->next_peer) {
+ if (tp->child_list)
+ print_ascii_dump_tree(f, tp, count);
+ }
+}
+
+static int translation_table[256];
+
+static void
+build_translation_table()
+{
+ int count;
+
+ for (count = 0; count < 256; count++) {
+ switch (count) {
+ case OBJID:
+ translation_table[count] = TYPE_OBJID;
+ break;
+ case OCTETSTR:
+ translation_table[count] = TYPE_OCTETSTR;
+ break;
+ case INTEGER:
+ translation_table[count] = TYPE_INTEGER;
+ break;
+ case NETADDR:
+ translation_table[count] = TYPE_NETADDR;
+ break;
+ case IPADDR:
+ translation_table[count] = TYPE_IPADDR;
+ break;
+ case COUNTER:
+ translation_table[count] = TYPE_COUNTER;
+ break;
+ case GAUGE:
+ translation_table[count] = TYPE_GAUGE;
+ break;
+ case TIMETICKS:
+ translation_table[count] = TYPE_TIMETICKS;
+ break;
+ case KW_OPAQUE:
+ translation_table[count] = TYPE_OPAQUE;
+ break;
+ case NUL:
+ translation_table[count] = TYPE_NULL;
+ break;
+ case COUNTER64:
+ translation_table[count] = TYPE_COUNTER64;
+ break;
+ case BITSTRING:
+ translation_table[count] = TYPE_BITSTRING;
+ break;
+ case NSAPADDRESS:
+ translation_table[count] = TYPE_NSAPADDRESS;
+ break;
+ case INTEGER32:
+ translation_table[count] = TYPE_INTEGER32;
+ break;
+ case UINTEGER32:
+ translation_table[count] = TYPE_UINTEGER;
+ break;
+ case UNSIGNED32:
+ translation_table[count] = TYPE_UNSIGNED32;
+ break;
+ case TRAPTYPE:
+ translation_table[count] = TYPE_TRAPTYPE;
+ break;
+ case NOTIFTYPE:
+ translation_table[count] = TYPE_NOTIFTYPE;
+ break;
+ case OBJGROUP:
+ translation_table[count] = TYPE_OBJGROUP;
+ break;
+ case MODULEIDENTITY:
+ translation_table[count] = TYPE_MODID;
+ break;
+ case AGENTCAP:
+ translation_table[count] = TYPE_AGENTCAP;
+ break;
+ case COMPLIANCE:
+ translation_table[count] = TYPE_MODCOMP;
+ break;
+ default:
+ translation_table[count] = TYPE_OTHER;
+ break;
+ }
+ }
+}
+
+static void
+init_tree_roots()
+{
+ struct tree *tp, *lasttp;
+ int base_modid;
+ int hash;
+
+ base_modid = which_module("SNMPv2-SMI");
+ if (base_modid == -1)
+ base_modid = which_module("RFC1155-SMI");
+ if (base_modid == -1)
+ base_modid = which_module("RFC1213-MIB");
+
+ /*
+ * build root node
+ */
+ tp = (struct tree *) calloc(1, sizeof(struct tree));
+ if (tp == NULL)
+ return;
+ tp->label = strdup("joint-iso-ccitt");
+ tp->modid = base_modid;
+ tp->number_modules = 1;
+ tp->module_list = &(tp->modid);
+ tp->subid = 2;
+ tp->tc_index = -1;
+ set_function(tp); /* from mib.c */
+ hash = NBUCKET(name_hash(tp->label));
+ tp->next = tbuckets[hash];
+ tbuckets[hash] = tp;
+ lasttp = tp;
+ root_imports[0].label = strdup(tp->label);
+ root_imports[0].modid = base_modid;
+
+ /*
+ * build root node
+ */
+ tp = (struct tree *) calloc(1, sizeof(struct tree));
+ if (tp == NULL)
+ return;
+ tp->next_peer = lasttp;
+ tp->label = strdup("ccitt");
+ tp->modid = base_modid;
+ tp->number_modules = 1;
+ tp->module_list = &(tp->modid);
+ tp->subid = 0;
+ tp->tc_index = -1;
+ set_function(tp); /* from mib.c */
+ hash = NBUCKET(name_hash(tp->label));
+ tp->next = tbuckets[hash];
+ tbuckets[hash] = tp;
+ lasttp = tp;
+ root_imports[1].label = strdup(tp->label);
+ root_imports[1].modid = base_modid;
+
+ /*
+ * build root node
+ */
+ tp = (struct tree *) calloc(1, sizeof(struct tree));
+ if (tp == NULL)
+ return;
+ tp->next_peer = lasttp;
+ tp->label = strdup("iso");
+ tp->modid = base_modid;
+ tp->number_modules = 1;
+ tp->module_list = &(tp->modid);
+ tp->subid = 1;
+ tp->tc_index = -1;
+ set_function(tp); /* from mib.c */
+ hash = NBUCKET(name_hash(tp->label));
+ tp->next = tbuckets[hash];
+ tbuckets[hash] = tp;
+ lasttp = tp;
+ root_imports[2].label = strdup(tp->label);
+ root_imports[2].modid = base_modid;
+
+ tree_head = tp;
+}
+
+#ifdef STRICT_MIB_PARSEING
+#define label_compare strcasecmp
+#else
+#define label_compare strcmp
+#endif
+
+
+struct tree *
+find_tree_node(const char *name, int modid)
+{
+ struct tree *tp, *headtp;
+ int count, *int_p;
+
+ if (!name || !*name)
+ return (NULL);
+
+ headtp = tbuckets[NBUCKET(name_hash(name))];
+ for (tp = headtp; tp; tp = tp->next) {
+ if (tp->label && !label_compare(tp->label, name)) {
+
+ if (modid == -1) /* Any module */
+ return (tp);
+
+ for (int_p = tp->module_list, count = 0;
+ count < tp->number_modules; ++count, ++int_p)
+ if (*int_p == modid)
+ return (tp);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * computes a value which represents how close name1 is to name2.
+ * * high scores mean a worse match.
+ * * (yes, the algorithm sucks!)
+ */
+#define MAX_BAD 0xffffff
+
+static u_int
+compute_match(const char *search_base, const char *key)
+{
+#if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
+ int rc;
+ regex_t parsetree;
+ regmatch_t pmatch;
+
+ rc = regcomp(&parsetree, key, REG_ICASE | REG_EXTENDED);
+ if (rc == 0)
+ rc = regexec(&parsetree, search_base, 1, &pmatch, 0);
+ regfree(&parsetree);
+ if (rc == 0) {
+ /*
+ * found
+ */
+ return pmatch.rm_so;
+ }
+#else /* use our own wildcard matcher */
+ /*
+ * first find the longest matching substring (ick)
+ */
+ char *first = NULL, *result = NULL, *entry;
+ const char *position;
+ char *newkey = strdup(key);
+ char *st;
+
+
+ entry = strtok_r(newkey, "*", &st);
+ position = search_base;
+ while (entry) {
+ result = strcasestr(position, entry);
+
+ if (result == NULL) {
+ free(newkey);
+ return MAX_BAD;
+ }
+
+ if (first == NULL)
+ first = result;
+
+ position = result + strlen(entry);
+ entry = strtok_r(NULL, "*", &st);
+ }
+ free(newkey);
+ if (result)
+ return (first - search_base);
+#endif
+
+ /*
+ * not found
+ */
+ return MAX_BAD;
+}
+
+/*
+ * Find the tree node that best matches the pattern string.
+ * Use the "reported" flag such that only one match
+ * is attempted for every node.
+ *
+ * Warning! This function may recurse.
+ *
+ * Caller _must_ invoke clear_tree_flags before first call
+ * to this function. This function may be called multiple times
+ * to ensure that the entire tree is traversed.
+ */
+
+struct tree *
+find_best_tree_node(const char *pattrn, struct tree *tree_top,
+ u_int * match)
+{
+ struct tree *tp, *best_so_far = NULL, *retptr;
+ u_int old_match = MAX_BAD, new_match = MAX_BAD;
+
+ if (!pattrn || !*pattrn)
+ return (NULL);
+
+ if (!tree_top)
+ tree_top = get_tree_head();
+
+ for (tp = tree_top; tp; tp = tp->next_peer) {
+ if (!tp->reported && tp->label)
+ new_match = compute_match(tp->label, pattrn);
+ tp->reported = 1;
+
+ if (new_match < old_match) {
+ best_so_far = tp;
+ old_match = new_match;
+ }
+ if (new_match == 0)
+ break; /* this is the best result we can get */
+ if (tp->child_list) {
+ retptr =
+ find_best_tree_node(pattrn, tp->child_list, &new_match);
+ if (new_match < old_match) {
+ best_so_far = retptr;
+ old_match = new_match;
+ }
+ if (new_match == 0)
+ break; /* this is the best result we can get */
+ }
+ }
+
+ if (match)
+ *match = old_match;
+ return (best_so_far);
+}
+
+
+static void
+merge_anon_children(struct tree *tp1, struct tree *tp2)
+ /*
+ * NB: tp1 is the 'anonymous' node
+ */
+{
+ struct tree *child1, *child2, *previous;
+
+ for (child1 = tp1->child_list; child1;) {
+
+ for (child2 = tp2->child_list, previous = NULL;
+ child2; previous = child2, child2 = child2->next_peer) {
+
+ if (child1->subid == child2->subid) {
+ /*
+ * Found 'matching' children,
+ * so merge them
+ */
+ if (!strncmp(child1->label, ANON, ANON_LEN)) {
+ merge_anon_children(child1, child2);
+
+ child1->child_list = NULL;
+ previous = child1; /* Finished with 'child1' */
+ child1 = child1->next_peer;
+ free_tree(previous);
+ goto next;
+ }
+
+ else if (!strncmp(child2->label, ANON, ANON_LEN)) {
+ merge_anon_children(child2, child1);
+
+ if (previous)
+ previous->next_peer = child2->next_peer;
+ else
+ tp2->child_list = child2->next_peer;
+ free_tree(child2);
+
+ previous = child1; /* Move 'child1' to 'tp2' */
+ child1 = child1->next_peer;
+ previous->next_peer = tp2->child_list;
+ tp2->child_list = previous;
+ for (previous = tp2->child_list;
+ previous; previous = previous->next_peer)
+ previous->parent = tp2;
+ goto next;
+ } else if (!label_compare(child1->label, child2->label)) {
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Warning: %s.%ld is both %s and %s (%s)\n",
+ tp2->label, child1->subid, child1->label,
+ child2->label, File);
+ }
+ continue;
+ } else {
+ /*
+ * Two copies of the same node.
+ * 'child2' adopts the children of 'child1'
+ */
+
+ if (child2->child_list) {
+ for (previous = child2->child_list; previous->next_peer; previous = previous->next_peer); /* Find the end of the list */
+ previous->next_peer = child1->child_list;
+ } else
+ child2->child_list = child1->child_list;
+ for (previous = child1->child_list;
+ previous; previous = previous->next_peer)
+ previous->parent = child2;
+ child1->child_list = NULL;
+
+ previous = child1; /* Finished with 'child1' */
+ child1 = child1->next_peer;
+ free_tree(previous);
+ goto next;
+ }
+ }
+ }
+ /*
+ * If no match, move 'child1' to 'tp2' child_list
+ */
+ if (child1) {
+ previous = child1;
+ child1 = child1->next_peer;
+ previous->parent = tp2;
+ previous->next_peer = tp2->child_list;
+ tp2->child_list = previous;
+ }
+ next:;
+ }
+}
+
+
+/*
+ * Find all the children of root in the list of nodes. Link them into the
+ * tree and out of the nodes list.
+ */
+static void
+do_subtree(struct tree *root, struct node **nodes)
+{
+ struct tree *tp, *anon_tp = NULL;
+ struct tree *xroot = root;
+ struct node *np, **headp;
+ struct node *oldnp = NULL, *child_list = NULL, *childp = NULL;
+ int hash;
+ int *int_p;
+
+ while (xroot->next_peer && xroot->next_peer->subid == root->subid) {
+#if 0
+ printf("xroot: %s.%s => %s\n", xroot->parent->label, xroot->label,
+ xroot->next_peer->label);
+#endif
+ xroot = xroot->next_peer;
+ }
+
+ tp = root;
+ headp = &nbuckets[NBUCKET(name_hash(tp->label))];
+ /*
+ * Search each of the nodes for one whose parent is root, and
+ * move each into a separate list.
+ */
+ for (np = *headp; np; np = np->next) {
+ if (!label_compare(tp->label, np->parent)) {
+ /*
+ * take this node out of the node list
+ */
+ if (oldnp == NULL) {
+ *headp = np->next; /* fix root of node list */
+ } else {
+ oldnp->next = np->next; /* link around this node */
+ }
+ if (child_list)
+ childp->next = np;
+ else
+ child_list = np;
+ childp = np;
+ } else {
+ oldnp = np;
+ }
+
+ }
+ if (childp)
+ childp->next = NULL;
+ /*
+ * Take each element in the child list and place it into the tree.
+ */
+ for (np = child_list; np; np = np->next) {
+ struct tree *otp = NULL;
+ struct tree *xxroot = xroot;
+ anon_tp = NULL;
+ tp = xroot->child_list;
+
+ if (np->subid == -1) {
+ /*
+ * name ::= { parent }
+ */
+ np->subid = xroot->subid;
+ tp = xroot;
+ xxroot = xroot->parent;
+ }
+
+ while (tp) {
+ if (tp->subid == np->subid)
+ break;
+ else {
+ otp = tp;
+ tp = tp->next_peer;
+ }
+ }
+ if (tp) {
+ if (!label_compare(tp->label, np->label)) {
+ /*
+ * Update list of modules
+ */
+ int_p =
+ (int *) malloc((tp->number_modules + 1) * sizeof(int));
+ if (int_p == NULL)
+ return;
+ memcpy(int_p, tp->module_list,
+ tp->number_modules * sizeof(int));
+ int_p[tp->number_modules] = np->modid;
+ if (tp->number_modules > 1)
+ free((char *) tp->module_list);
+ ++tp->number_modules;
+ tp->module_list = int_p;
+
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_REPLACE)) {
+ /*
+ * Replace from node
+ */
+ tree_from_node(tp, np);
+ }
+ /*
+ * Handle children
+ */
+ do_subtree(tp, nodes);
+ continue;
+ }
+ if (!strncmp(np->label, ANON, ANON_LEN) ||
+ !strncmp(tp->label, ANON, ANON_LEN)) {
+ anon_tp = tp; /* Need to merge these two trees later */
+ } else if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Warning: %s.%ld is both %s and %s (%s)\n",
+ root->label, np->subid, tp->label, np->label,
+ File);
+ }
+ }
+
+ tp = (struct tree *) calloc(1, sizeof(struct tree));
+ if (tp == NULL)
+ return;
+ tp->parent = xxroot;
+ tp->modid = np->modid;
+ tp->number_modules = 1;
+ tp->module_list = &(tp->modid);
+ tree_from_node(tp, np);
+ tp->next_peer = otp ? otp->next_peer : xxroot->child_list;
+ if (otp)
+ otp->next_peer = tp;
+ else
+ xxroot->child_list = tp;
+ hash = NBUCKET(name_hash(tp->label));
+ tp->next = tbuckets[hash];
+ tbuckets[hash] = tp;
+ do_subtree(tp, nodes);
+
+ if (anon_tp) {
+ if (!strncmp(tp->label, ANON, ANON_LEN)) {
+ /*
+ * The new node is anonymous,
+ * so merge it with the existing one.
+ */
+ merge_anon_children(tp, anon_tp);
+
+ /*
+ * unlink and destroy tp
+ */
+ unlink_tree(tp);
+ free_tree(tp);
+ } else if (!strncmp(anon_tp->label, ANON, ANON_LEN)) {
+ struct tree *ntp;
+ /*
+ * The old node was anonymous,
+ * so merge it with the existing one,
+ * and fill in the full information.
+ */
+ merge_anon_children(anon_tp, tp);
+
+ /*
+ * unlink anon_tp from the hash
+ */
+ unlink_tbucket(anon_tp);
+
+ /*
+ * get rid of old contents of anon_tp
+ */
+ free_partial_tree(anon_tp, FALSE);
+
+ /*
+ * put in the current information
+ */
+ anon_tp->label = tp->label;
+ anon_tp->child_list = tp->child_list;
+ anon_tp->modid = tp->modid;
+ anon_tp->tc_index = tp->tc_index;
+ anon_tp->type = tp->type;
+ anon_tp->enums = tp->enums;
+ anon_tp->indexes = tp->indexes;
+ anon_tp->augments = tp->augments;
+ anon_tp->varbinds = tp->varbinds;
+ anon_tp->ranges = tp->ranges;
+ anon_tp->hint = tp->hint;
+ anon_tp->units = tp->units;
+ anon_tp->description = tp->description;
+ anon_tp->defaultValue = tp->defaultValue;
+ anon_tp->parent = tp->parent;
+
+ set_function(anon_tp);
+
+ /*
+ * update parent pointer in moved children
+ */
+ ntp = anon_tp->child_list;
+ while (ntp) {
+ ntp->parent = anon_tp;
+ ntp = ntp->next_peer;
+ }
+
+ /*
+ * hash in anon_tp in its new place
+ */
+ hash = NBUCKET(name_hash(anon_tp->label));
+ anon_tp->next = tbuckets[hash];
+ tbuckets[hash] = anon_tp;
+
+ /*
+ * unlink and destroy tp
+ */
+ unlink_tbucket(tp);
+ unlink_tree(tp);
+ free(tp);
+ } else {
+ /*
+ * Uh? One of these two should have been anonymous!
+ */
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Warning: expected anonymous node (either %s or %s) in %s\n",
+ tp->label, anon_tp->label, File);
+ }
+ }
+ anon_tp = NULL;
+ }
+ }
+ /*
+ * free all nodes that were copied into tree
+ */
+ oldnp = NULL;
+ for (np = child_list; np; np = np->next) {
+ if (oldnp)
+ free_node(oldnp);
+ oldnp = np;
+ }
+ if (oldnp)
+ free_node(oldnp);
+}
+
+static void
+do_linkup(struct module *mp, struct node *np)
+{
+ struct module_import *mip;
+ struct node *onp, *oldp, *newp;
+ struct tree *tp;
+ int i, more;
+ /*
+ * All modules implicitly import
+ * the roots of the tree
+ */
+ if (snmp_get_do_debugging() > 1)
+ dump_module_list();
+ DEBUGMSGTL(("parse-mibs", "Processing IMPORTS for module %d %s\n",
+ mp->modid, mp->name));
+ if (mp->no_imports == 0) {
+ mp->no_imports = NUMBER_OF_ROOT_NODES;
+ mp->imports = root_imports;
+ }
+
+ /*
+ * Build the tree
+ */
+ init_node_hash(np);
+ for (i = 0, mip = mp->imports; i < mp->no_imports; ++i, ++mip) {
+ char modbuf[256];
+ DEBUGMSGTL(("parse-mibs", " Processing import: %s\n",
+ mip->label));
+ if (get_tc_index(mip->label, mip->modid) != -1)
+ continue;
+ tp = find_tree_node(mip->label, mip->modid);
+ if (!tp) {
+ if (mip->modid != -1)
+ snmp_log(LOG_WARNING,
+ "Did not find '%s' in module %s (%s)\n",
+ mip->label, module_name(mip->modid, modbuf),
+ File);
+ continue;
+ }
+ do_subtree(tp, &np);
+ }
+
+ /*
+ * If any nodes left over,
+ * check that they're not the result of a "fully qualified"
+ * name, and then add them to the list of orphans
+ */
+
+ if (!np)
+ return;
+ for (tp = tree_head; tp; tp = tp->next_peer)
+ do_subtree(tp, &np);
+ if (!np)
+ return;
+
+ /*
+ * quietly move all internal references to the orphan list
+ */
+ oldp = orphan_nodes;
+ do {
+ for (i = 0; i < NHASHSIZE; i++)
+ for (onp = nbuckets[i]; onp; onp = onp->next) {
+ struct node *op = NULL;
+ int hash = NBUCKET(name_hash(onp->label));
+ np = nbuckets[hash];
+ while (np) {
+ if (label_compare(onp->label, np->parent)) {
+ op = np;
+ np = np->next;
+ } else {
+ if (op)
+ op->next = np->next;
+ else
+ nbuckets[hash] = np->next;
+ np->next = orphan_nodes;
+ orphan_nodes = np;
+ op = NULL;
+ np = nbuckets[hash];
+ }
+ }
+ }
+ newp = orphan_nodes;
+ more = 0;
+ for (onp = orphan_nodes; onp != oldp; onp = onp->next) {
+ struct node *op = NULL;
+ int hash = NBUCKET(name_hash(onp->label));
+ np = nbuckets[hash];
+ while (np) {
+ if (label_compare(onp->label, np->parent)) {
+ op = np;
+ np = np->next;
+ } else {
+ if (op)
+ op->next = np->next;
+ else
+ nbuckets[hash] = np->next;
+ np->next = orphan_nodes;
+ orphan_nodes = np;
+ op = NULL;
+ np = nbuckets[hash];
+ more = 1;
+ }
+ }
+ }
+ oldp = newp;
+ } while (more);
+
+ /*
+ * complain about left over nodes
+ */
+ for (np = orphan_nodes; np && np->next; np = np->next); /* find the end of the orphan list */
+ for (i = 0; i < NHASHSIZE; i++)
+ if (nbuckets[i]) {
+ if (orphan_nodes)
+ onp = np->next = nbuckets[i];
+ else
+ onp = orphan_nodes = nbuckets[i];
+ nbuckets[i] = NULL;
+ while (onp) {
+ snmp_log(LOG_WARNING,
+ "Unlinked OID in %s: %s ::= { %s %ld }\n",
+ (mp->name ? mp->name : "<no module>"),
+ (onp->label ? onp->label : "<no label>"),
+ (onp->parent ? onp->parent : "<no parent>"),
+ onp->subid);
+ snmp_log(LOG_WARNING,
+ "Undefined identifier: %s near line %d of %s\n",
+ (onp->parent ? onp->parent : "<no parent>"),
+ onp->lineno, onp->filename);
+ np = onp;
+ onp = onp->next;
+ }
+ }
+ return;
+}
+
+
+/*
+ * Takes a list of the form:
+ * { iso org(3) dod(6) 1 }
+ * and creates several nodes, one for each parent-child pair.
+ * Returns 0 on error.
+ */
+static int
+getoid(FILE * fp, struct subid_s *id, /* an array of subids */
+ int length)
+{ /* the length of the array */
+ register int count;
+ int type;
+ char token[MAXTOKEN];
+
+ if ((type = get_token(fp, token, MAXTOKEN)) != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ return 0;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ for (count = 0; count < length; count++, id++) {
+ id->label = NULL;
+ id->modid = current_module;
+ id->subid = -1;
+ if (type == RIGHTBRACKET)
+ return count;
+ if (type == LABEL) {
+ /*
+ * this entry has a label
+ */
+ id->label = strdup(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LEFTPAREN) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == NUMBER) {
+ id->subid = strtoul(token, NULL, 10);
+ if ((type =
+ get_token(fp, token, MAXTOKEN)) != RIGHTPAREN) {
+ print_error("Expected a closing parenthesis",
+ token, type);
+ return 0;
+ }
+ } else {
+ print_error("Expected a number", token, type);
+ return 0;
+ }
+ } else {
+ continue;
+ }
+ } else if (type == NUMBER) {
+ /*
+ * this entry has just an integer sub-identifier
+ */
+ id->subid = strtoul(token, NULL, 10);
+ } else {
+ print_error("Expected label or number", token, type);
+ return 0;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ print_error("Too long OID", token, type);
+ return 0;
+}
+
+/*
+ * Parse a sequence of object subidentifiers for the given name.
+ * The "label OBJECT IDENTIFIER ::=" portion has already been parsed.
+ *
+ * The majority of cases take this form :
+ * label OBJECT IDENTIFIER ::= { parent 2 }
+ * where a parent label and a child subidentifier number are specified.
+ *
+ * Variations on the theme include cases where a number appears with
+ * the parent, or intermediate subidentifiers are specified by label,
+ * by number, or both.
+ *
+ * Here are some representative samples :
+ * internet OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 }
+ * mgmt OBJECT IDENTIFIER ::= { internet 2 }
+ * rptrInfoHealth OBJECT IDENTIFIER ::= { snmpDot3RptrMgt 0 4 }
+ *
+ * Here is a very rare form :
+ * iso OBJECT IDENTIFIER ::= { 1 }
+ *
+ * Returns NULL on error. When this happens, memory may be leaked.
+ */
+static struct node *
+parse_objectid(FILE * fp, char *name)
+{
+ register int count;
+ register struct subid_s *op, *nop;
+ int length;
+ struct subid_s loid[32];
+ struct node *np, *root = NULL, *oldnp = NULL;
+ struct tree *tp;
+
+ if ((length = getoid(fp, loid, 32)) == 0) {
+ print_error("Bad object identifier", NULL, CONTINUE);
+ return NULL;
+ }
+
+ /*
+ * Handle numeric-only object identifiers,
+ * by labelling the first sub-identifier
+ */
+ op = loid;
+ if (!op->label)
+ for (tp = tree_head; tp; tp = tp->next_peer)
+ if ((int) tp->subid == op->subid) {
+ op->label = strdup(tp->label);
+ break;
+ }
+
+ /*
+ * Handle "label OBJECT-IDENTIFIER ::= { subid }"
+ */
+ if (length == 1) {
+ op = loid;
+ np = alloc_node(op->modid);
+ if (np == NULL)
+ return (NULL);
+ np->subid = op->subid;
+ np->label = strdup(name);
+ np->parent = op->label;
+ return np;
+ }
+
+ /*
+ * For each parent-child subid pair in the subid array,
+ * create a node and link it into the node list.
+ */
+ for (count = 0, op = loid, nop = loid + 1; count < (length - 1);
+ count++, op++, nop++) {
+ /*
+ * every node must have parent's name and child's name or number
+ */
+ /*
+ * XX the next statement is always true -- does it matter ??
+ */
+ if (op->label && (nop->label || (nop->subid != -1))) {
+ np = alloc_node(nop->modid);
+ if (np == NULL)
+ return (NULL);
+ if (root == NULL)
+ root = np;
+
+ np->parent = strdup(op->label);
+ if (count == (length - 2)) {
+ /*
+ * The name for this node is the label for this entry
+ */
+ np->label = strdup(name);
+ } else {
+ if (!nop->label) {
+ nop->label = (char *) malloc(20 + ANON_LEN);
+ if (nop->label == NULL)
+ return (NULL);
+ sprintf(nop->label, "%s%d", ANON, anonymous++);
+ }
+ np->label = strdup(nop->label);
+ }
+ if (nop->subid != -1)
+ np->subid = nop->subid;
+ else
+ print_error("Warning: This entry is pretty silly",
+ np->label, CONTINUE);
+
+ /*
+ * set up next entry
+ */
+ if (oldnp)
+ oldnp->next = np;
+ oldnp = np;
+ } /* end if(op->label... */
+ }
+
+ /*
+ * free the loid array
+ */
+ for (count = 0, op = loid; count < length; count++, op++) {
+ if (op->label)
+ free(op->label);
+ }
+
+ return root;
+}
+
+static int
+get_tc(const char *descriptor,
+ int modid,
+ int *tc_index,
+ struct enum_list **ep, struct range_list **rp, char **hint)
+{
+ int i;
+ struct tc *tcp;
+
+ i = get_tc_index(descriptor, modid);
+ if (tc_index)
+ *tc_index = i;
+ if (i != -1) {
+ tcp = &tclist[i];
+ if (ep) {
+ free_enums(ep);
+ *ep = copy_enums(tcp->enums);
+ }
+ if (rp) {
+ free_ranges(rp);
+ *rp = copy_ranges(tcp->ranges);
+ }
+ if (hint) {
+ if (*hint)
+ free(*hint);
+ *hint = (tcp->hint ? strdup(tcp->hint) : NULL);
+ }
+ return tcp->type;
+ }
+ return LABEL;
+}
+
+/*
+ * return index into tclist of given TC descriptor
+ * return -1 if not found
+ */
+static int
+get_tc_index(const char *descriptor, int modid)
+{
+ int i;
+ struct tc *tcp;
+ struct module *mp;
+ struct module_import *mip;
+
+ /*
+ * Check that the descriptor isn't imported
+ * by searching the import list
+ */
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->modid == modid)
+ break;
+ if (mp)
+ for (i = 0, mip = mp->imports; i < mp->no_imports; ++i, ++mip) {
+ if (!label_compare(mip->label, descriptor)) {
+ /*
+ * Found it - so amend the module ID
+ */
+ modid = mip->modid;
+ break;
+ }
+ }
+
+
+ for (i = 0, tcp = tclist; i < MAXTC; i++, tcp++) {
+ if (tcp->type == 0)
+ break;
+ if (!label_compare(descriptor, tcp->descriptor) &&
+ ((modid == tcp->modid) || (modid == -1))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/*
+ * translate integer tc_index to string identifier from tclist
+ * *
+ * * Returns pointer to string in table (should not be modified) or NULL
+ */
+const char *
+get_tc_descriptor(int tc_index)
+{
+ if (tc_index < 0 || tc_index >= MAXTC)
+ return NULL;
+ return (tclist[tc_index].descriptor);
+}
+
+
+/*
+ * Parses an enumeration list of the form:
+ * { label(value) label(value) ... }
+ * The initial { has already been parsed.
+ * Returns NULL on error.
+ */
+
+static struct enum_list *
+parse_enumlist(FILE * fp, struct enum_list **retp)
+{
+ register int type;
+ char token[MAXTOKEN];
+ struct enum_list *ep = NULL, **epp = &ep;
+
+ free_enums(retp);
+
+ while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE) {
+ if (type == RIGHTBRACKET)
+ break;
+ if (type == LABEL) {
+ /*
+ * this is an enumerated label
+ */
+ *epp =
+ (struct enum_list *) calloc(1, sizeof(struct enum_list));
+ if (*epp == NULL)
+ return (NULL);
+ /*
+ * a reasonable approximation for the length
+ */
+ (*epp)->label = strdup(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTPAREN) {
+ print_error("Expected \"(\"", token, type);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != NUMBER) {
+ print_error("Expected integer", token, type);
+ return NULL;
+ }
+ (*epp)->value = strtol(token, NULL, 10);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != RIGHTPAREN) {
+ print_error("Expected \")\"", token, type);
+ return NULL;
+ }
+ epp = &(*epp)->next;
+ }
+ }
+ if (type == ENDOFFILE) {
+ print_error("Expected \"}\"", token, type);
+ return NULL;
+ }
+ *retp = ep;
+ return ep;
+}
+
+static struct range_list *
+parse_ranges(FILE * fp, struct range_list **retp)
+{
+ int low, high;
+ char nexttoken[MAXTOKEN];
+ int nexttype;
+ struct range_list *rp = NULL, **rpp = &rp;
+ int size = 0, taken = 1;
+
+ free_ranges(retp);
+
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == SIZE) {
+ size = 1;
+ taken = 0;
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype != LEFTPAREN)
+ print_error("Expected \"(\" after SIZE", nexttoken, nexttype);
+ }
+
+ do {
+ if (!taken)
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ else
+ taken = 0;
+ high = low = strtol(nexttoken, NULL, 10);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == RANGE) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ high = strtol(nexttoken, NULL, 10);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ }
+ *rpp = (struct range_list *) calloc(1, sizeof(struct range_list));
+ if (*rpp == NULL)
+ break;
+ (*rpp)->low = low;
+ (*rpp)->high = high;
+ rpp = &(*rpp)->next;
+
+ } while (nexttype == BAR);
+ if (size) {
+ if (nexttype != RIGHTPAREN)
+ print_error("Expected \")\" after SIZE", nexttoken, nexttype);
+ nexttype = get_token(fp, nexttoken, nexttype);
+ }
+ if (nexttype != RIGHTPAREN)
+ print_error("Expected \")\"", nexttoken, nexttype);
+
+ *retp = rp;
+ return rp;
+}
+
+/*
+ * Parses an asn type. Structures are ignored by this parser.
+ * Returns NULL on error.
+ */
+static struct node *
+parse_asntype(FILE * fp, char *name, int *ntype, char *ntoken)
+{
+ int type, i;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ char *hint = NULL;
+ struct tc *tcp;
+ int level;
+
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == SEQUENCE || type == CHOICE) {
+ level = 0;
+ while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE) {
+ if (type == LEFTBRACKET) {
+ level++;
+ } else if (type == RIGHTBRACKET && --level == 0) {
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ return NULL;
+ }
+ }
+ print_error("Expected \"}\"", token, type);
+ return NULL;
+ } else if (type == LEFTBRACKET) {
+ struct node *np;
+ int ch_next = '{';
+ ungetc(ch_next, fp);
+ np = parse_objectid(fp, name);
+ if (np != NULL) {
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ return np;
+ }
+ return NULL;
+ } else if (type == LEFTSQBRACK) {
+ int size = 0;
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type != ENDOFFILE && type != RIGHTSQBRACK);
+ if (type != RIGHTSQBRACK) {
+ print_error("Expected \"]\"", token, type);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == IMPLICIT)
+ type = get_token(fp, token, MAXTOKEN);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype == LEFTPAREN) {
+ switch (type) {
+ case OCTETSTR:
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype != SIZE) {
+ print_error("Expected SIZE", ntoken, *ntype);
+ return NULL;
+ }
+ size = 1;
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype != LEFTPAREN) {
+ print_error("Expected \"(\" after SIZE", ntoken,
+ *ntype);
+ return NULL;
+ }
+ /*
+ * fall through
+ */
+ case INTEGER:
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ do {
+ if (*ntype != NUMBER)
+ print_error("Expected NUMBER", ntoken, *ntype);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype == RANGE) {
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype != NUMBER)
+ print_error("Expected NUMBER", ntoken, *ntype);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ }
+ } while (*ntype == BAR);
+ if (*ntype != RIGHTPAREN) {
+ print_error("Expected \")\"", ntoken, *ntype);
+ return NULL;
+ }
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (size) {
+ if (*ntype != RIGHTPAREN) {
+ print_error("Expected \")\" to terminate SIZE",
+ ntoken, *ntype);
+ return NULL;
+ }
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ }
+ }
+ }
+ return NULL;
+ } else {
+ if (type == CONVENTION) {
+ while (type != SYNTAX && type != ENDOFFILE) {
+ if (type == DISPLAYHINT) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING)
+ print_error("DISPLAY-HINT must be string", token,
+ type);
+ else
+ hint = strdup(token);
+ } else
+ type =
+ get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == OBJECT) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != IDENTIFIER) {
+ print_error("Expected IDENTIFIER", token, type);
+ SNMP_FREE(hint);
+ return NULL;
+ }
+ type = OBJID;
+ }
+ } else if (type == OBJECT) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != IDENTIFIER) {
+ print_error("Expected IDENTIFIER", token, type);
+ return NULL;
+ }
+ type = OBJID;
+ }
+
+ if (type == LABEL) {
+ type = get_tc(token, current_module, NULL, NULL, NULL, NULL);
+ }
+
+ /*
+ * textual convention
+ */
+ for (i = 0; i < MAXTC; i++) {
+ if (tclist[i].type == 0)
+ break;
+ }
+
+ if (i == MAXTC) {
+ print_error("Too many textual conventions", token, type);
+ SNMP_FREE(hint);
+ return NULL;
+ }
+ if (!(type & SYNTAX_MASK)) {
+ print_error("Textual convention doesn't map to real type",
+ token, type);
+ SNMP_FREE(hint);
+ return NULL;
+ }
+ tcp = &tclist[i];
+ tcp->modid = current_module;
+ tcp->descriptor = strdup(name);
+ tcp->hint = hint;
+ tcp->type = type;
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ if (*ntype == LEFTPAREN) {
+ tcp->ranges = parse_ranges(fp, &tcp->ranges);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ } else if (*ntype == LEFTBRACKET) {
+ /*
+ * if there is an enumeration list, parse it
+ */
+ tcp->enums = parse_enumlist(fp, &tcp->enums);
+ *ntype = get_token(fp, ntoken, MAXTOKEN);
+ }
+ return NULL;
+ }
+}
+
+
+/*
+ * Parses an OBJECT TYPE macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_objecttype(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char nexttoken[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ int nexttype, tctype;
+ register struct node *np;
+
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != SYNTAX) {
+ print_error("Bad format for OBJECT-TYPE", token, type);
+ return NULL;
+ }
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == OBJECT) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != IDENTIFIER) {
+ print_error("Expected IDENTIFIER", token, type);
+ free_node(np);
+ return NULL;
+ }
+ type = OBJID;
+ }
+ if (type == LABEL) {
+ int tmp_index;
+ tctype = get_tc(token, current_module, &tmp_index,
+ &np->enums, &np->ranges, &np->hint);
+ if (tctype == LABEL &&
+ netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS) > 1) {
+ print_error("Warning: No known translation for type", token,
+ type);
+ }
+ type = tctype;
+ np->tc_index = tmp_index; /* store TC for later reference */
+ }
+ np->type = type;
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ switch (type) {
+ case SEQUENCE:
+ if (nexttype == OF) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+
+ }
+ break;
+ case INTEGER:
+ case INTEGER32:
+ case UINTEGER32:
+ case UNSIGNED32:
+ case COUNTER:
+ case GAUGE:
+ case BITSTRING:
+ case LABEL:
+ if (nexttype == LEFTBRACKET) {
+ /*
+ * if there is an enumeration list, parse it
+ */
+ np->enums = parse_enumlist(fp, &np->enums);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ } else if (nexttype == LEFTPAREN) {
+ /*
+ * if there is a range list, parse it
+ */
+ np->ranges = parse_ranges(fp, &np->ranges);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ }
+ break;
+ case OCTETSTR:
+ case KW_OPAQUE:
+ /*
+ * parse any SIZE specification
+ */
+ if (nexttype == LEFTPAREN) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == SIZE) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == LEFTPAREN) {
+ np->ranges = parse_ranges(fp, &np->ranges);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN); /* ) */
+ if (nexttype == RIGHTPAREN) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ break;
+ }
+ }
+ }
+ print_error("Bad SIZE syntax", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ case OBJID:
+ case NETADDR:
+ case IPADDR:
+ case TIMETICKS:
+ case NUL:
+ case NSAPADDRESS:
+ case COUNTER64:
+ break;
+ default:
+ print_error("Bad syntax", token, type);
+ free_node(np);
+ return NULL;
+ }
+ if (nexttype == UNITS) {
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad UNITS", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ np->units = strdup(quoted_string_buffer);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ }
+ if (nexttype != ACCESS) {
+ print_error("Should be ACCESS", nexttoken, nexttype);
+ free_node(np);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != READONLY && type != READWRITE && type != WRITEONLY
+ && type != NOACCESS && type != READCREATE && type != ACCNOTIFY) {
+ print_error("Bad ACCESS type", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->access = type;
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != STATUS) {
+ print_error("Should be STATUS", token, type);
+ free_node(np);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != MANDATORY && type != CURRENT && type != KW_OPTIONAL &&
+ type != OBSOLETE && type != DEPRECATED) {
+ print_error("Bad STATUS", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->status = type;
+ /*
+ * Optional parts of the OBJECT-TYPE macro
+ */
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != EQUALS && type != ENDOFFILE) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ break;
+
+ case REFERENCE:
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad REFERENCE", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ case INDEX:
+ if (np->augments) {
+ print_error("Cannot have both INDEX and AUGMENTS", token,
+ type);
+ free_node(np);
+ return NULL;
+ }
+ np->indexes = getIndexes(fp, &np->indexes);
+ if (np->indexes == NULL) {
+ print_error("Bad INDEX list", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ case AUGMENTS:
+ if (np->indexes) {
+ print_error("Cannot have both INDEX and AUGMENTS", token,
+ type);
+ free_node(np);
+ return NULL;
+ }
+ np->indexes = getIndexes(fp, &np->indexes);
+ if (np->indexes == NULL) {
+ print_error("Bad AUGMENTS list", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->augments = strdup(np->indexes->ilabel);
+ free_indexes(&np->indexes);
+ break;
+ case DEFVAL:
+ /*
+ * Mark's defVal section
+ */
+ type = get_token(fp, quoted_string_buffer, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Bad DEFAULTVALUE", quoted_string_buffer,
+ type);
+ free_node(np);
+ return NULL;
+ }
+
+ {
+ int level = 1;
+ char defbuf[512];
+
+ defbuf[0] = 0;
+ while (1) {
+ type = get_token(fp, quoted_string_buffer, MAXTOKEN);
+ if ((type == RIGHTBRACKET && --level == 0)
+ || type == ENDOFFILE)
+ break;
+ else if (type == LEFTBRACKET)
+ level++;
+ if (type == QUOTESTRING) {
+ if (strlen(defbuf)+2 < sizeof(defbuf)) {
+ defbuf[ strlen(defbuf)+2 ] = 0;
+ defbuf[ strlen(defbuf)+1 ] = '"';
+ defbuf[ strlen(defbuf) ] = '\\';
+ }
+ defbuf[ sizeof(defbuf)-1 ] = 0;
+ }
+ strncat(defbuf, quoted_string_buffer,
+ sizeof(defbuf)-strlen(defbuf));
+ defbuf[ sizeof(defbuf)-1 ] = 0;
+ if (type == QUOTESTRING) {
+ if (strlen(defbuf)+2 < sizeof(defbuf)) {
+ defbuf[ strlen(defbuf)+2 ] = 0;
+ defbuf[ strlen(defbuf)+1 ] = '"';
+ defbuf[ strlen(defbuf) ] = '\\';
+ }
+ defbuf[ sizeof(defbuf)-1 ] = 0;
+ }
+ if (strlen(defbuf)+1 < sizeof(defbuf)) {
+ defbuf[ strlen(defbuf)+1 ] = 0;
+ defbuf[ strlen(defbuf) ] = ' ';
+ }
+ }
+
+ if (type != RIGHTBRACKET) {
+ print_error("Bad DEFAULTVALUE", quoted_string_buffer,
+ type);
+ free_node(np);
+ return NULL;
+ }
+
+ defbuf[strlen(defbuf) - 1] = 0;
+ np->defaultValue = strdup(defbuf);
+ }
+
+ break;
+
+ case NUM_ENTRIES:
+ if (tossObjectIdentifier(fp) != OBJID) {
+ print_error("Bad Object Identifier", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+
+ default:
+ print_error("Bad format of optional clauses", token, type);
+ free_node(np);
+ return NULL;
+
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != EQUALS) {
+ print_error("Bad format", token, type);
+ free_node(np);
+ return NULL;
+ }
+ return merge_parse_objectid(np, fp, name);
+}
+
+/*
+ * Parses an OBJECT GROUP macro.
+ * Returns 0 on error.
+ *
+ * Also parses object-identity, since they are similar (ignore STATUS).
+ * - WJH 10/96
+ */
+static struct node *
+parse_objectgroup(FILE * fp, char *name, int what, struct objgroup **ol)
+{
+ int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == what) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ goto skip;
+ }
+ do {
+ struct objgroup *o;
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad identifier", token, type);
+ goto skip;
+ }
+ o = (struct objgroup *) malloc(sizeof(struct objgroup));
+ o->line = mibLine;
+ o->name = strdup(token);
+ o->next = *ol;
+ *ol = o;
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type == COMMA);
+ if (type != RIGHTBRACKET) {
+ print_error("Expected \"}\" after list", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, type);
+ }
+ if (type != STATUS) {
+ print_error("Expected STATUS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) {
+ print_error("Bad STATUS value", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == REFERENCE) {
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad REFERENCE", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != EQUALS)
+ print_error("Expected \"::=\"", token, type);
+ skip:
+ while (type != EQUALS && type != ENDOFFILE)
+ type = get_token(fp, token, MAXTOKEN);
+
+ return merge_parse_objectid(np, fp, name);
+}
+
+/*
+ * Parses a NOTIFICATION-TYPE macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_notificationDefinition(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ register struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != EQUALS && type != ENDOFFILE) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ break;
+ case OBJECTS:
+ np->varbinds = getVarbinds(fp, &np->varbinds);
+ if (!np->varbinds) {
+ print_error("Bad OBJECTS list", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ default:
+ /*
+ * NOTHING
+ */
+ break;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ return merge_parse_objectid(np, fp, name);
+}
+
+/*
+ * Parses a TRAP-TYPE macro.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_trapDefinition(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ register struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != EQUALS && type != ENDOFFILE) {
+ switch (type) {
+ case DESCRIPTION:
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ free_node(np);
+ return NULL;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ break;
+ case ENTERPRISE:
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LEFTBRACKET) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad Trap Format", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->parent = strdup(token);
+ /*
+ * Get right bracket
+ */
+ type = get_token(fp, token, MAXTOKEN);
+ } else if (type == LABEL)
+ np->parent = strdup(token);
+ break;
+ case VARIABLES:
+ np->varbinds = getVarbinds(fp, &np->varbinds);
+ if (!np->varbinds) {
+ print_error("Bad VARIABLES list", token, type);
+ free_node(np);
+ return NULL;
+ }
+ break;
+ default:
+ /*
+ * NOTHING
+ */
+ break;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+
+ np->label = strdup(name);
+
+ if (type != NUMBER) {
+ print_error("Expected a Number", token, type);
+ free_node(np);
+ return NULL;
+ }
+ np->subid = strtoul(token, NULL, 10);
+ np->next = alloc_node(current_module);
+ if (np->next == NULL) {
+ free_node(np);
+ return (NULL);
+ }
+ np->next->parent = np->parent;
+ np->parent = (char *) malloc(strlen(np->parent) + 2);
+ if (np->parent == NULL) {
+ free_node(np->next);
+ free_node(np);
+ return (NULL);
+ }
+ strcpy(np->parent, np->next->parent);
+ strcat(np->parent, "#");
+ np->next->label = strdup(np->parent);
+ return np;
+}
+
+
+/*
+ * Parses a compliance macro
+ * Returns 0 on error.
+ */
+static int
+eat_syntax(FILE * fp, char *token, int maxtoken)
+{
+ int type, nexttype;
+ struct node *np = alloc_node(current_module);
+ char nexttoken[MAXTOKEN];
+
+ type = get_token(fp, token, maxtoken);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ switch (type) {
+ case INTEGER:
+ case INTEGER32:
+ case UINTEGER32:
+ case UNSIGNED32:
+ case COUNTER:
+ case GAUGE:
+ case BITSTRING:
+ case LABEL:
+ if (nexttype == LEFTBRACKET) {
+ /*
+ * if there is an enumeration list, parse it
+ */
+ np->enums = parse_enumlist(fp, &np->enums);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ } else if (nexttype == LEFTPAREN) {
+ /*
+ * if there is a range list, parse it
+ */
+ np->ranges = parse_ranges(fp, &np->ranges);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ }
+ break;
+ case OCTETSTR:
+ case KW_OPAQUE:
+ /*
+ * parse any SIZE specification
+ */
+ if (nexttype == LEFTPAREN) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == SIZE) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ if (nexttype == LEFTPAREN) {
+ np->ranges = parse_ranges(fp, &np->ranges);
+ nexttype = get_token(fp, nexttoken, MAXTOKEN); /* ) */
+ if (nexttype == RIGHTPAREN) {
+ nexttype = get_token(fp, nexttoken, MAXTOKEN);
+ break;
+ }
+ }
+ }
+ print_error("Bad SIZE syntax", token, type);
+ free_node(np);
+ return nexttype;
+ }
+ break;
+ case OBJID:
+ case NETADDR:
+ case IPADDR:
+ case TIMETICKS:
+ case NUL:
+ case NSAPADDRESS:
+ case COUNTER64:
+ break;
+ default:
+ print_error("Bad syntax", token, type);
+ free_node(np);
+ return nexttype;
+ }
+ free_node(np);
+ return nexttype;
+}
+
+static int
+compliance_lookup(const char *name, int modid)
+{
+ if (modid == -1) {
+ struct objgroup *op =
+ (struct objgroup *) malloc(sizeof(struct objgroup));
+ op->next = objgroups;
+ op->name = strdup(name);
+ op->line = mibLine;
+ objgroups = op;
+ return 1;
+ } else
+ return find_tree_node(name, modid) != NULL;
+}
+
+static struct node *
+parse_compliance(FILE * fp, char *name)
+{
+ int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != STATUS) {
+ print_error("Expected STATUS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) {
+ print_error("Bad STATUS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ goto skip;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS))
+ np->description = strdup(quoted_string_buffer);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == REFERENCE) {
+ type = get_token(fp, quoted_string_buffer, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad REFERENCE", quoted_string_buffer, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != MODULE) {
+ print_error("Expected MODULE", token, type);
+ goto skip;
+ }
+ while (type == MODULE) {
+ int modid = -1;
+ char modname[MAXTOKEN];
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LABEL
+ && strcmp(token, module_name(current_module, modname))) {
+ modid = read_module_internal(token);
+ if (modid != MODULE_LOADED_OK
+ && modid != MODULE_ALREADY_LOADED) {
+ print_error("Unknown module", token, type);
+ goto skip;
+ }
+ modid = which_module(token);
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type == MANDATORYGROUPS) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ goto skip;
+ }
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad group name", token, type);
+ goto skip;
+ }
+ if (!compliance_lookup(token, modid))
+ print_error("Unknown group", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type == COMMA);
+ if (type != RIGHTBRACKET) {
+ print_error("Expected \"}\"", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ while (type == GROUP || type == OBJECT) {
+ if (type == GROUP) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad group name", token, type);
+ goto skip;
+ }
+ if (!compliance_lookup(token, modid))
+ print_error("Unknown group", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ } else {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad object name", token, type);
+ goto skip;
+ }
+ if (!compliance_lookup(token, modid))
+ print_error("Unknown group", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == SYNTAX)
+ type = eat_syntax(fp, token, MAXTOKEN);
+ if (type == WRSYNTAX)
+ type = eat_syntax(fp, token, MAXTOKEN);
+ if (type == MINACCESS) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != NOACCESS && type != ACCNOTIFY
+ && type != READONLY && type != WRITEONLY
+ && type != READCREATE && type != READWRITE) {
+ print_error("Bad MIN-ACCESS spec", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ }
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ }
+ skip:
+ while (type != EQUALS && type != ENDOFFILE)
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+
+ return merge_parse_objectid(np, fp, name);
+}
+
+
+/*
+ * Parses a capabilities macro
+ * Returns 0 on error.
+ */
+static struct node *
+parse_capabilities(FILE * fp, char *name)
+{
+ int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != PRODREL) {
+ print_error("Expected PRODUCT-RELEASE", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Expected STRING after PRODUCT-RELEASE", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != STATUS) {
+ print_error("Expected STATUS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != CURRENT && type != OBSOLETE) {
+ print_error("STATUS should be current or obsolete", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ goto skip;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(token);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == REFERENCE) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad REFERENCE", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, type);
+ }
+ while (type == SUPPORTS) {
+ int modid;
+ struct tree *tp;
+
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad module name", token, type);
+ goto skip;
+ }
+ modid = read_module_internal(token);
+ if (modid != MODULE_LOADED_OK && modid != MODULE_ALREADY_LOADED) {
+ print_error("Module not found", token, type);
+ goto skip;
+ }
+ modid = which_module(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != INCLUDES) {
+ print_error("Expected INCLUDES", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ goto skip;
+ }
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Expected group name", token, type);
+ goto skip;
+ }
+ tp = find_tree_node(token, modid);
+ if (!tp)
+ print_error("Group not found in module", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type == COMMA);
+ if (type != RIGHTBRACKET) {
+ print_error("Expected \"}\" after group list", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ while (type == VARIATION) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad object name", token, type);
+ goto skip;
+ }
+ tp = find_tree_node(token, modid);
+ if (!tp)
+ print_error("Object not found in module", token, type);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == SYNTAX) {
+ type = eat_syntax(fp, token, MAXTOKEN);
+ }
+ if (type == WRSYNTAX) {
+ type = eat_syntax(fp, token, MAXTOKEN);
+ }
+ if (type == ACCESS) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != ACCNOTIFY && type != READONLY
+ && type != READWRITE && type != READCREATE
+ && type != WRITEONLY && type != NOTIMPL) {
+ print_error("Bad ACCESS", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type == CREATEREQ) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\"", token, type);
+ goto skip;
+ }
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LABEL) {
+ print_error("Bad object name in list", token,
+ type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type == COMMA);
+ if (type != RIGHTBRACKET) {
+ print_error("Expected \"}\" after list", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type == DEFVAL) {
+ int level = 1;
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LEFTBRACKET) {
+ print_error("Expected \"{\" after DEFVAL", token,
+ type);
+ goto skip;
+ }
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LEFTBRACKET)
+ level++;
+ else if (type == RIGHTBRACKET)
+ level--;
+ } while (type != RIGHTBRACKET && type != ENDOFFILE
+ && level != 0);
+ if (type != RIGHTBRACKET) {
+ print_error("Missing \"}\" after DEFVAL", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ }
+ if (type != EQUALS)
+ print_error("Expected \"::=\"", token, type);
+ skip:
+ while (type != EQUALS && type != ENDOFFILE) {
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ }
+ return merge_parse_objectid(np, fp, name);
+}
+
+/*
+ * Parses a module identity macro
+ * Returns 0 on error.
+ */
+static void
+check_utc(const char *utc)
+{
+ int len, year, month, day, hour, minute;
+
+ len = strlen(utc);
+ if (utc[len - 1] != 'Z' && utc[len - 1] != 'z') {
+ print_error("Timestamp should end with Z", utc, QUOTESTRING);
+ return;
+ }
+ if (len == 11) {
+ len =
+ sscanf(utc, "%2d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
+ &minute);
+ year += 1900;
+ } else if (len == 13)
+ len =
+ sscanf(utc, "%4d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
+ &minute);
+ else {
+ print_error("Bad timestamp format (11 or 13 characters)",
+ utc, QUOTESTRING);
+ return;
+ }
+ if (len != 5) {
+ print_error("Bad timestamp format", utc, QUOTESTRING);
+ return;
+ }
+ if (month < 1 || month > 12)
+ print_error("Bad month in timestamp", utc, QUOTESTRING);
+ if (day < 1 || day > 31)
+ print_error("Bad day in timestamp", utc, QUOTESTRING);
+ if (hour < 0 || hour > 23)
+ print_error("Bad hour in timestamp", utc, QUOTESTRING);
+ if (minute < 0 || minute > 59)
+ print_error("Bad minute in timestamp", utc, QUOTESTRING);
+}
+
+static struct node *
+parse_moduleIdentity(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char quoted_string_buffer[MAXQUOTESTR];
+ register struct node *np;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != LASTUPDATED) {
+ print_error("Expected LAST-UPDATED", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Need STRING for LAST-UPDATED", token, type);
+ goto skip;
+ }
+ check_utc(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != ORGANIZATION) {
+ print_error("Expected ORGANIZATION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad ORGANIZATION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != CONTACTINFO) {
+ print_error("Expected CONTACT-INFO", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad CONTACT-INFO", quoted_string_buffer, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ goto skip;
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
+ np->description = strdup(quoted_string_buffer);
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ while (type == REVISION) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != QUOTESTRING) {
+ print_error("Bad REVISION", token, type);
+ goto skip;
+ }
+ check_utc(token);
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != DESCRIPTION) {
+ print_error("Expected DESCRIPTION", token, type);
+ goto skip;
+ }
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ if (type != QUOTESTRING) {
+ print_error("Bad DESCRIPTION", quoted_string_buffer, type);
+ goto skip;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+ if (type != EQUALS)
+ print_error("Expected \"::=\"", token, type);
+ skip:
+ while (type != EQUALS && type != ENDOFFILE) {
+ type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
+ }
+ return merge_parse_objectid(np, fp, name);
+}
+
+
+/*
+ * Parses a MACRO definition
+ * Expect BEGIN, discard everything to end.
+ * Returns 0 on error.
+ */
+static struct node *
+parse_macro(FILE * fp, char *name)
+{
+ register int type;
+ char token[MAXTOKEN];
+ struct node *np;
+ int iLine = mibLine;
+
+ np = alloc_node(current_module);
+ if (np == NULL)
+ return (NULL);
+ type = get_token(fp, token, sizeof(token));
+ while (type != EQUALS && type != ENDOFFILE) {
+ type = get_token(fp, token, sizeof(token));
+ }
+ if (type != EQUALS)
+ return NULL;
+ while (type != BEGIN && type != ENDOFFILE) {
+ type = get_token(fp, token, sizeof(token));
+ }
+ if (type != BEGIN)
+ return NULL;
+ while (type != END && type != ENDOFFILE) {
+ type = get_token(fp, token, sizeof(token));
+ }
+ if (type != END)
+ return NULL;
+
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "%s MACRO (lines %d..%d parsed and ignored).\n", name,
+ iLine, mibLine);
+ }
+
+ return np;
+}
+
+/*
+ * Parses a module import clause
+ * loading any modules referenced
+ */
+static void
+parse_imports(FILE * fp)
+{
+ register int type;
+ char token[MAXTOKEN];
+ char modbuf[256];
+#define MAX_IMPORTS 256
+ struct module_import import_list[MAX_IMPORTS];
+ int this_module;
+ struct module *mp;
+
+ int import_count = 0; /* Total number of imported descriptors */
+ int i = 0, old_i; /* index of first import from each module */
+
+ type = get_token(fp, token, MAXTOKEN);
+
+ /*
+ * Parse the IMPORTS clause
+ */
+ while (type != SEMI && type != ENDOFFILE) {
+ if (type == LABEL) {
+ if (import_count == MAX_IMPORTS) {
+ print_error("Too many imported symbols", token, type);
+ do {
+ type = get_token(fp, token, MAXTOKEN);
+ } while (type != SEMI && type != ENDOFFILE);
+ return;
+ }
+ import_list[import_count++].label = strdup(token);
+ } else if (type == FROM) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (import_count == i) { /* All imports are handled internally */
+ type = get_token(fp, token, MAXTOKEN);
+ continue;
+ }
+ this_module = which_module(token);
+
+ for (old_i = i; i < import_count; ++i)
+ import_list[i].modid = this_module;
+
+ /*
+ * Recursively read any pre-requisite modules
+ */
+ if (read_module_internal(token) == MODULE_NOT_FOUND) {
+ for (; old_i < import_count; ++old_i) {
+ read_import_replacements(token, &import_list[old_i]);
+ }
+ }
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+
+ /*
+ * Save the import information
+ * in the global module table
+ */
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->modid == current_module) {
+ if (import_count == 0)
+ return;
+ if (mp->imports && (mp->imports != root_imports)) {
+ /*
+ * this can happen if all modules are in one source file.
+ */
+ for (i = 0; i < mp->no_imports; ++i) {
+ DEBUGMSGTL(("parse-mibs",
+ "#### freeing Module %d '%s' %d\n",
+ mp->modid, mp->imports[i].label,
+ mp->imports[i].modid));
+ free((char *) mp->imports[i].label);
+ }
+ free((char *) mp->imports);
+ }
+ mp->imports = (struct module_import *)
+ calloc(import_count, sizeof(struct module_import));
+ if (mp->imports == NULL)
+ return;
+ for (i = 0; i < import_count; ++i) {
+ mp->imports[i].label = import_list[i].label;
+ mp->imports[i].modid = import_list[i].modid;
+ DEBUGMSGTL(("parse-mibs",
+ "#### adding Module %d '%s' %d\n", mp->modid,
+ mp->imports[i].label, mp->imports[i].modid));
+ }
+ mp->no_imports = import_count;
+ return;
+ }
+
+ /*
+ * Shouldn't get this far
+ */
+ print_module_not_found(module_name(current_module, modbuf));
+ return;
+}
+
+
+
+/*
+ * MIB module handling routines
+ */
+
+static void
+dump_module_list(void)
+{
+ struct module *mp = module_head;
+
+ DEBUGMSGTL(("parse-mibs", "Module list:\n"));
+ while (mp) {
+ DEBUGMSGTL(("parse-mibs", " %s %d %s %d\n", mp->name, mp->modid,
+ mp->file, mp->no_imports));
+ mp = mp->next;
+ }
+}
+
+int
+which_module(const char *name)
+{
+ struct module *mp;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (!label_compare(mp->name, name))
+ return (mp->modid);
+
+ DEBUGMSGTL(("parse-mibs", "Module %s not found\n", name));
+ return (-1);
+}
+
+/*
+ * module_name - copy module name to user buffer, return ptr to same.
+ */
+char *
+module_name(int modid, char *cp)
+{
+ struct module *mp;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->modid == modid) {
+ strcpy(cp, mp->name);
+ return (cp);
+ }
+
+ DEBUGMSGTL(("parse-mibs", "Module %d not found\n", modid));
+ sprintf(cp, "#%d", modid);
+ return (cp);
+}
+
+/*
+ * Backwards compatability
+ * Read newer modules that replace the one specified:-
+ * either all of them (read_module_replacements),
+ * or those relating to a specified identifier (read_import_replacements)
+ * plus an interface to add new replacement requirements
+ */
+void
+add_module_replacement(const char *old_module,
+ const char *new_module_name,
+ const char *tag, int len)
+{
+ struct module_compatability *mcp;
+
+ mcp = (struct module_compatability *)
+ calloc(1, sizeof(struct module_compatability));
+ if (mcp == NULL)
+ return;
+
+ mcp->old_module = strdup(old_module);
+ mcp->new_module = strdup(new_module_name);
+ if (tag)
+ mcp->tag = strdup(tag);
+ mcp->tag_len = len;
+
+ mcp->next = module_map_head;
+ module_map_head = mcp;
+}
+
+static void
+read_module_replacements(const char *name)
+{
+ struct module_compatability *mcp;
+
+ for (mcp = module_map_head; mcp; mcp = mcp->next) {
+ if (!label_compare(mcp->old_module, name)) {
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Loading replacement module %s for %s (%s)\n",
+ mcp->new_module, name, File);
+ }
+ (void) read_module(mcp->new_module);
+ return;
+ }
+ }
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_ERRORS)) {
+ print_module_not_found(name);
+ }
+}
+
+static void
+read_import_replacements(const char *old_module_name,
+ struct module_import *identifier)
+{
+ struct module_compatability *mcp;
+
+ /*
+ * Look for matches first
+ */
+ for (mcp = module_map_head; mcp; mcp = mcp->next) {
+ if (!label_compare(mcp->old_module, old_module_name)) {
+
+ if ( /* exact match */
+ (mcp->tag_len == 0 &&
+ (mcp->tag == NULL ||
+ !label_compare(mcp->tag, identifier->label))) ||
+ /*
+ * prefix match
+ */
+ (mcp->tag_len != 0 &&
+ !strncmp(mcp->tag, identifier->label, mcp->tag_len))
+ ) {
+
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Importing %s from replacement module %s instead of %s (%s)\n",
+ identifier->label, mcp->new_module,
+ old_module_name, File);
+ }
+ (void) read_module(mcp->new_module);
+ identifier->modid = which_module(mcp->new_module);
+ return; /* finished! */
+ }
+ }
+ }
+
+ /*
+ * If no exact match, load everything relevant
+ */
+ read_module_replacements(old_module_name);
+}
+
+
+/*
+ * Read in the named module
+ * Returns the root of the whole tree
+ * (by analogy with 'read_mib')
+ */
+static int
+read_module_internal(const char *name)
+{
+ struct module *mp;
+ FILE *fp;
+ struct node *np;
+
+ init_mib_internals();
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (!label_compare(mp->name, name)) {
+ const char *oldFile = File;
+ int oldLine = mibLine;
+ int oldModule = current_module;
+
+ if (mp->no_imports != -1) {
+ DEBUGMSGTL(("parse-mibs", "Module %s already loaded\n",
+ name));
+ return MODULE_ALREADY_LOADED;
+ }
+ if ((fp = fopen(mp->file, "r")) == NULL) {
+ snmp_log_perror(mp->file);
+ return MODULE_LOAD_FAILED;
+ }
+ mp->no_imports = 0; /* Note that we've read the file */
+ File = mp->file;
+ mibLine = 1;
+ current_module = mp->modid;
+ /*
+ * Parse the file
+ */
+ np = parse(fp, NULL);
+ fclose(fp);
+ File = oldFile;
+ mibLine = oldLine;
+ current_module = oldModule;
+ return MODULE_LOADED_OK;
+ }
+
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS) > 1) {
+ snmp_log(LOG_WARNING, "Module %s not found\n", name);
+ }
+ return MODULE_NOT_FOUND;
+}
+
+void
+adopt_orphans(void)
+{
+ struct node *np, *onp;
+ struct tree *tp;
+ int i, adopted = 1;
+
+ if (!orphan_nodes)
+ return;
+ init_node_hash(orphan_nodes);
+ orphan_nodes = NULL;
+
+ while (adopted) {
+ adopted = 0;
+ for (i = 0; i < NHASHSIZE; i++)
+ if (nbuckets[i]) {
+ for (np = nbuckets[i]; np != NULL; np = np->next) {
+ tp = find_tree_node(np->parent, -1);
+ if (tp) {
+ do_subtree(tp, &np);
+ adopted = 1;
+ /*
+ * if do_subtree adopted the entire bucket, stop
+ */
+ if(NULL == nbuckets[i])
+ break;
+
+ /*
+ * do_subtree may modify nbuckets, and if np
+ * was adopted, np->next probably isn't an orphan
+ * anymore. if np is still in the bucket (do_subtree
+ * didn't adopt it) keep on plugging. otherwise
+ * start over, at the top of the bucket.
+ */
+ for(onp = nbuckets[i]; onp; onp = onp->next)
+ if(onp == np)
+ break;
+ if(NULL == onp) { /* not in the list */
+ np = nbuckets[i]; /* start over */
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Report on outstanding orphans
+ * and link them back into the orphan list
+ */
+ for (i = 0; i < NHASHSIZE; i++)
+ if (nbuckets[i]) {
+ if (orphan_nodes)
+ onp = np->next = nbuckets[i];
+ else
+ onp = orphan_nodes = nbuckets[i];
+ nbuckets[i] = NULL;
+ while (onp) {
+ char modbuf[256];
+ snmp_log(LOG_WARNING,
+ "Cannot adopt OID in %s: %s ::= { %s %ld }\n",
+ module_name(onp->modid, modbuf),
+ (onp->label ? onp->label : "<no label>"),
+ (onp->parent ? onp->parent : "<no parent>"),
+ onp->subid);
+
+ np = onp;
+ onp = onp->next;
+ }
+ }
+}
+
+struct tree *
+read_module(const char *name)
+{
+ if (read_module_internal(name) == MODULE_NOT_FOUND)
+ read_module_replacements(name);
+ return tree_head;
+}
+
+/*
+ * Prototype definition
+ */
+void unload_module_by_ID(int modID, struct tree *tree_top);
+
+void
+unload_module_by_ID(int modID, struct tree *tree_top)
+{
+ struct tree *tp, *next;
+ int i;
+
+ for (tp = tree_top; tp; tp = next) {
+ /*
+ * Essentially, this is equivalent to the code fragment:
+ * if (tp->modID == modID)
+ * tp->number_modules--;
+ * but handles one tree node being part of several modules,
+ * and possible multiple copies of the same module ID.
+ */
+ int nmod = tp->number_modules;
+ if (nmod > 0) { /* in some module */
+ /*
+ * Remove all copies of this module ID
+ */
+ int cnt = 0, *pi1, *pi2 = tp->module_list;
+ for (i = 0, pi1 = pi2; i < nmod; i++, pi2++) {
+ if (*pi2 == modID)
+ continue;
+ cnt++;
+ *pi1++ = *pi2;
+ }
+ if (nmod != cnt) { /* in this module */
+ /*
+ * if ( (nmod - cnt) > 1)
+ * printf("Dup modid %d, %d times, '%s'\n", tp->modid, (nmod-cnt), tp->label); fflush(stdout); ?* XXDEBUG
+ */
+ tp->number_modules = cnt;
+ switch (cnt) {
+ case 0:
+ tp->module_list[0] = -1; /* Mark unused, and FALL THROUGH */
+
+ case 1: /* save the remaining module */
+ if (&(tp->modid) != tp->module_list) {
+ tp->modid = tp->module_list[0];
+ free(tp->module_list);
+ tp->module_list = &(tp->modid);
+ }
+ break;
+
+ default:
+ break;
+ }
+ } /* if tree node is in this module */
+ }
+ /*
+ * if tree node is in some module
+ */
+ next = tp->next_peer;
+
+
+ /*
+ * OK - that's dealt with *this* node.
+ * Now let's look at the children.
+ * (Isn't recursion wonderful!)
+ */
+ if (tp->child_list)
+ unload_module_by_ID(modID, tp->child_list);
+
+
+ if (tp->number_modules == 0) {
+ /*
+ * This node isn't needed any more (except perhaps
+ * for the sake of the children)
+ */
+ if (tp->child_list == NULL) {
+ unlink_tree(tp);
+ free_tree(tp);
+ } else {
+ free_partial_tree(tp, TRUE);
+ }
+ }
+ }
+}
+
+int
+unload_module(const char *name)
+{
+ struct module *mp;
+ int modID = -1;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (!label_compare(mp->name, name)) {
+ modID = mp->modid;
+ break;
+ }
+
+ if (modID == -1) {
+ DEBUGMSGTL(("unload-mib", "Module %s not found to unload\n",
+ name));
+ return MODULE_NOT_FOUND;
+ }
+ unload_module_by_ID(modID, tree_head);
+ mp->no_imports = -1; /* mark as unloaded */
+ return MODULE_LOADED_OK; /* Well, you know what I mean! */
+}
+
+/*
+ * Clear module map, tree nodes, textual convention table.
+ */
+void
+unload_all_mibs()
+{
+ struct module *mp;
+ struct module_compatability *mcp;
+ struct tc *ptc;
+ int i;
+
+ for (mcp = module_map_head; mcp; mcp = module_map_head) {
+ if (mcp == module_map)
+ break;
+ module_map_head = mcp->next;
+ if (mcp->tag) free((char *) mcp->tag);
+ free((char *) mcp->old_module);
+ free((char *) mcp->new_module);
+ free(mcp);
+ }
+
+ for (mp = module_head; mp; mp = module_head) {
+ struct module_import *mi = mp->imports;
+ if (mi) {
+ for (i = 0; i < mp->no_imports; ++i) {
+ SNMP_FREE((mi + i)->label);
+ }
+ mp->no_imports = 0;
+ if (mi == root_imports)
+ memset(mi, 0, sizeof(*mi));
+ else
+ free(mi);
+ }
+
+ unload_module_by_ID(mp->modid, tree_head);
+ module_head = mp->next;
+ free(mp->name);
+ free(mp->file);
+ free(mp);
+ }
+ unload_module_by_ID(-1, tree_head);
+ /*
+ * tree nodes are cleared
+ */
+
+ for (i = 0, ptc = tclist; i < MAXTC; i++, ptc++) {
+ if (ptc->type == 0)
+ continue;
+ free_enums(&ptc->enums);
+ free_ranges(&ptc->ranges);
+ free(ptc->descriptor);
+ if (ptc->hint)
+ free(ptc->hint);
+ }
+ memset(tclist, 0, MAXTC * sizeof(struct tc));
+
+ memset(buckets, 0, sizeof(buckets));
+ memset(nbuckets, 0, sizeof(nbuckets));
+ memset(tbuckets, 0, sizeof(tbuckets));
+
+ for (i = 0; i < sizeof(root_imports) / sizeof(root_imports[0]); i++) {
+ SNMP_FREE(root_imports[i].label);
+ }
+
+ max_module = 0;
+ current_module = 0;
+ module_map_head = NULL;
+ SNMP_FREE(last_err_module);
+}
+
+static void
+new_module(const char *name, const char *file)
+{
+ struct module *mp;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (!label_compare(mp->name, name)) {
+ DEBUGMSGTL(("parse-mibs", "Module %s already noted\n", name));
+ /*
+ * Not the same file
+ */
+ if (label_compare(mp->file, file)) {
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ snmp_log(LOG_WARNING,
+ "Warning: Module %s was in %s now is %s\n",
+ name, mp->file, file);
+ }
+
+ /*
+ * Use the new one in preference
+ */
+ free(mp->file);
+ mp->file = strdup(file);
+ }
+ return;
+ }
+
+ /*
+ * Add this module to the list
+ */
+ DEBUGMSGTL(("parse-mibs", " Module %d %s is in %s\n", max_module,
+ name, file));
+ mp = (struct module *) calloc(1, sizeof(struct module));
+ if (mp == NULL)
+ return;
+ mp->name = strdup(name);
+ mp->file = strdup(file);
+ mp->imports = NULL;
+ mp->no_imports = -1; /* Not yet loaded */
+ mp->modid = max_module;
+ ++max_module;
+
+ mp->next = module_head; /* Or add to the *end* of the list? */
+ module_head = mp;
+}
+
+
+static void
+scan_objlist(struct node *root, struct objgroup *list, const char *error)
+{
+ int oLine = mibLine;
+
+ while (list) {
+ struct objgroup *gp = list;
+ struct node *np;
+ list = list->next;
+ np = root;
+ while (np)
+ if (label_compare(np->label, gp->name))
+ np = np->next;
+ else
+ break;
+ if (!np) {
+ mibLine = gp->line;
+ print_error(error, gp->name, QUOTESTRING);
+ }
+ free(gp->name);
+ free(gp);
+ }
+ mibLine = oLine;
+}
+
+/*
+ * Parses a mib file and returns a linked list of nodes found in the file.
+ * Returns NULL on error.
+ */
+static struct node *
+parse(FILE * fp, struct node *root)
+{
+ char token[MAXTOKEN];
+ char name[MAXTOKEN];
+ int type = LABEL;
+ int lasttype = LABEL;
+
+#define BETWEEN_MIBS 1
+#define IN_MIB 2
+ int state = BETWEEN_MIBS;
+ struct node *np, *nnp;
+ struct objgroup *oldgroups = NULL, *oldobjects = NULL, *oldnotifs =
+ NULL;
+
+ DEBUGMSGTL(("parse-file", "Parsing file: %s...\n", File));
+
+ if (last_err_module)
+ free(last_err_module);
+ last_err_module = 0;
+
+ np = root;
+ if (np != NULL) {
+ /*
+ * now find end of chain
+ */
+ while (np->next)
+ np = np->next;
+ }
+
+ while (type != ENDOFFILE) {
+ if (lasttype == CONTINUE)
+ lasttype = type;
+ else
+ type = lasttype = get_token(fp, token, MAXTOKEN);
+
+ switch (type) {
+ case END:
+ if (state != IN_MIB) {
+ print_error("Error, END before start of MIB", NULL, type);
+ return NULL;
+ } else {
+ struct module *mp;
+#ifdef TEST
+ printf("\nNodes for Module %s:\n", name);
+ print_nodes(stdout, root);
+#endif
+ scan_objlist(root, objgroups, "Undefined OBJECT-GROUP");
+ scan_objlist(root, objects, "Undefined OBJECT");
+ scan_objlist(root, notifs, "Undefined NOTIFICATION");
+ objgroups = oldgroups;
+ objects = oldobjects;
+ notifs = oldnotifs;
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->modid == current_module)
+ break;
+ do_linkup(mp, root);
+ np = root = NULL;
+ }
+ state = BETWEEN_MIBS;
+#ifdef TEST
+ if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS)) {
+ xmalloc_stats(stderr);
+ }
+#endif
+ continue;
+ case IMPORTS:
+ parse_imports(fp);
+ continue;
+ case EXPORTS:
+ while (type != SEMI && type != ENDOFFILE)
+ type = get_token(fp, token, MAXTOKEN);
+ continue;
+ case LABEL:
+ case INTEGER:
+ case INTEGER32:
+ case UINTEGER32:
+ case UNSIGNED32:
+ case COUNTER:
+ case COUNTER64:
+ case GAUGE:
+ case IPADDR:
+ case NETADDR:
+ case NSAPADDRESS:
+ case OBJSYNTAX:
+ case APPSYNTAX:
+ case SIMPLESYNTAX:
+ case OBJNAME:
+ case NOTIFNAME:
+ case KW_OPAQUE:
+ case TIMETICKS:
+ break;
+ case ENDOFFILE:
+ continue;
+ default:
+ strcpy(name, token);
+ type = get_token(fp, token, MAXTOKEN);
+ nnp = NULL;
+ if (type == MACRO) {
+ nnp = parse_macro(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of MACRO", NULL, type);
+ /*
+ * return NULL;
+ */
+ }
+ free_node(nnp); /* IGNORE */
+ nnp = NULL;
+ } else
+ print_error(name, "is a reserved word", lasttype);
+ continue; /* see if we can parse the rest of the file */
+ }
+ strcpy(name, token);
+ type = get_token(fp, token, MAXTOKEN);
+ nnp = NULL;
+
+ /*
+ * Handle obsolete method to assign an object identifier to a
+ * module
+ */
+ if (lasttype == LABEL && type == LEFTBRACKET) {
+ while (type != RIGHTBRACKET && type != ENDOFFILE)
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == ENDOFFILE) {
+ print_error("Expected \"}\"", token, type);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+
+ switch (type) {
+ case DEFINITIONS:
+ if (state != BETWEEN_MIBS) {
+ print_error("Error, nested MIBS", NULL, type);
+ return NULL;
+ }
+ state = IN_MIB;
+ current_module = which_module(name);
+ oldgroups = objgroups;
+ objgroups = NULL;
+ oldobjects = objects;
+ objects = NULL;
+ oldnotifs = notifs;
+ notifs = NULL;
+ if (current_module == -1) {
+ new_module(name, File);
+ current_module = which_module(name);
+ }
+ DEBUGMSGTL(("parse-mibs", "Parsing MIB: %d %s\n",
+ current_module, name));
+ while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE)
+ if (type == BEGIN)
+ break;
+ break;
+ case OBJTYPE:
+ nnp = parse_objecttype(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of OBJECT-TYPE", NULL, type);
+ return NULL;
+ }
+ break;
+ case OBJGROUP:
+ nnp = parse_objectgroup(fp, name, OBJECTS, &objects);
+ if (nnp == NULL) {
+ print_error("Bad parse of OBJECT-GROUP", NULL, type);
+ return NULL;
+ }
+ break;
+ case NOTIFGROUP:
+ nnp = parse_objectgroup(fp, name, NOTIFICATIONS, &notifs);
+ if (nnp == NULL) {
+ print_error("Bad parse of NOTIFICATION-GROUP", NULL, type);
+ return NULL;
+ }
+ break;
+ case TRAPTYPE:
+ nnp = parse_trapDefinition(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of TRAP-TYPE", NULL, type);
+ return NULL;
+ }
+ break;
+ case NOTIFTYPE:
+ nnp = parse_notificationDefinition(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of NOTIFICATION-TYPE", NULL, type);
+ return NULL;
+ }
+ break;
+ case COMPLIANCE:
+ nnp = parse_compliance(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of MODULE-COMPLIANCE", NULL, type);
+ return NULL;
+ }
+ break;
+ case AGENTCAP:
+ nnp = parse_capabilities(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of AGENT-CAPABILITIES", NULL, type);
+ return NULL;
+ }
+ break;
+ case MACRO:
+ nnp = parse_macro(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of MACRO", NULL, type);
+ /*
+ * return NULL;
+ */
+ }
+ free_node(nnp); /* IGNORE */
+ nnp = NULL;
+ break;
+ case MODULEIDENTITY:
+ nnp = parse_moduleIdentity(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of MODULE-IDENTITY", NULL, type);
+ return NULL;
+ }
+ break;
+ case OBJECT:
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != IDENTIFIER) {
+ print_error("Expected IDENTIFIER", token, type);
+ return NULL;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ if (type != EQUALS) {
+ print_error("Expected \"::=\"", token, type);
+ return NULL;
+ }
+ nnp = parse_objectid(fp, name);
+ if (nnp == NULL) {
+ print_error("Bad parse of OBJECT IDENTIFIER", NULL, type);
+ return NULL;
+ }
+ break;
+ case EQUALS:
+ nnp = parse_asntype(fp, name, &type, token);
+ lasttype = CONTINUE;
+ break;
+ case ENDOFFILE:
+ break;
+ default:
+ print_error("Bad operator", token, type);
+ return NULL;
+ }
+ if (nnp) {
+ if (nnp->type == TYPE_OTHER)
+ nnp->type = type;
+ if (np)
+ np->next = nnp;
+ else
+ np = root = nnp;
+ while (np->next)
+ np = np->next;
+ }
+ }
+ DEBUGMSGTL(("parse-file", "End of file (%s)\n", File));
+ return root;
+}
+
+/*
+ * return zero if character is not a label character.
+ */
+static int
+is_labelchar(int ich)
+{
+ if ((isalnum(ich)) || (ich == '-'))
+ return 1;
+ if (ich == '_' && netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_PARSE_LABEL)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Parses a token from the file. The type of the token parsed is returned,
+ * and the text is placed in the string pointed to by token.
+ * Warning: this method may recurse.
+ */
+static int
+get_token(FILE * fp, char *token, int maxtlen)
+{
+ register int ch, ch_next;
+ register char *cp = token;
+ register int hash = 0;
+ register struct tok *tp;
+ int too_long = 0;
+
+ /*
+ * skip all white space
+ */
+ do {
+ ch = getc(fp);
+ if (ch == '\n')
+ mibLine++;
+ }
+ while (isspace(ch) && ch != EOF);
+ *cp++ = ch;
+ *cp = '\0';
+ switch (ch) {
+ case EOF:
+ return ENDOFFILE;
+ case '"':
+ return parseQuoteString(fp, token, maxtlen);
+ case '\'': /* binary or hex constant */
+ while ((ch = getc(fp)) != EOF && ch != '\''
+ && cp - token < maxtlen - 2)
+ *cp++ = ch;
+ if (ch == '\'') {
+ unsigned long val = 0;
+ *cp++ = '\'';
+ *cp++ = ch = getc(fp);
+ *cp = 0;
+ cp = token + 1;
+ switch (ch) {
+ case EOF:
+ return ENDOFFILE;
+ case 'b':
+ case 'B':
+ while ((ch = *cp++) != '\'')
+ if (ch != '0' && ch != '1')
+ return LABEL;
+ else
+ val = val * 2 + ch - '0';
+ break;
+ case 'h':
+ case 'H':
+ while ((ch = *cp++) != '\'')
+ if ('0' <= ch && ch <= '9')
+ val = val * 16 + ch - '0';
+ else if ('a' <= ch && ch <= 'f')
+ val = val * 16 + ch - 'a' + 10;
+ else if ('A' <= ch && ch <= 'F')
+ val = val * 16 + ch - 'A' + 10;
+ else
+ return LABEL;
+ break;
+ default:
+ return LABEL;
+ }
+ sprintf(token, "%ld", val);
+ return NUMBER;
+ } else
+ return LABEL;
+ case '(':
+ return LEFTPAREN;
+ case ')':
+ return RIGHTPAREN;
+ case '{':
+ return LEFTBRACKET;
+ case '}':
+ return RIGHTBRACKET;
+ case '[':
+ return LEFTSQBRACK;
+ case ']':
+ return RIGHTSQBRACK;
+ case ';':
+ return SEMI;
+ case ',':
+ return COMMA;
+ case '|':
+ return BAR;
+ case '.':
+ ch_next = getc(fp);
+ if (ch_next == '.')
+ return RANGE;
+ ungetc(ch_next, fp);
+ return LABEL;
+ case ':':
+ ch_next = getc(fp);
+ if (ch_next != ':') {
+ ungetc(ch_next, fp);
+ return LABEL;
+ }
+ ch_next = getc(fp);
+ if (ch_next != '=') {
+ ungetc(ch_next, fp);
+ return LABEL;
+ }
+ return EQUALS;
+ case '-':
+ ch_next = getc(fp);
+ if (ch_next == '-') {
+ if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_COMMENT_TERM)) {
+ /*
+ * Treat the rest of this line as a comment.
+ */
+ while ((ch_next != EOF) && (ch_next != '\n'))
+ ch_next = getc(fp);
+ } else {
+ /*
+ * Treat the rest of the line or until another '--' as a comment
+ */
+ /*
+ * (this is the "technically" correct way to parse comments)
+ */
+ ch = ' ';
+ ch_next = getc(fp);
+ while (ch_next != EOF && ch_next != '\n' &&
+ (ch != '-' || ch_next != '-')) {
+ ch = ch_next;
+ ch_next = getc(fp);
+ }
+ }
+ if (ch_next == EOF)
+ return ENDOFFILE;
+ if (ch_next == '\n')
+ mibLine++;
+ return get_token(fp, token, maxtlen);
+ }
+ ungetc(ch_next, fp);
+ default:
+ /*
+ * Accumulate characters until end of token is found. Then attempt to
+ * match this token as a reserved word. If a match is found, return the
+ * type. Else it is a label.
+ */
+ if (!is_labelchar(ch))
+ return LABEL;
+ hash += tolower(ch);
+ more:
+ while (is_labelchar(ch_next = getc(fp))) {
+ hash += tolower(ch_next);
+ if (cp - token < maxtlen - 1)
+ *cp++ = ch_next;
+ else
+ too_long = 1;
+ }
+ ungetc(ch_next, fp);
+ *cp = '\0';
+
+ if (too_long)
+ print_error("Warning: token too long", token, CONTINUE);
+ for (tp = buckets[BUCKET(hash)]; tp; tp = tp->next) {
+ if ((tp->hash == hash) && (!label_compare(tp->name, token)))
+ break;
+ }
+ if (tp) {
+ if (tp->token != CONTINUE)
+ return (tp->token);
+ while (isspace((ch_next = getc(fp))))
+ if (ch_next == '\n')
+ mibLine++;
+ if (ch_next == EOF)
+ return ENDOFFILE;
+ if (isalnum(ch_next)) {
+ *cp++ = ch_next;
+ hash += tolower(ch_next);
+ goto more;
+ }
+ }
+ if (token[0] == '-' || isdigit(token[0])) {
+ for (cp = token + 1; *cp; cp++)
+ if (!isdigit(*cp))
+ return LABEL;
+ return NUMBER;
+ }
+ return LABEL;
+ }
+}
+
+int
+snmp_get_token(FILE * fp, char *token, int maxtlen)
+{
+ return get_token(fp, token, maxtlen);
+}
+
+int
+add_mibdir(const char *dirname)
+{
+ FILE *fp, *ip;
+ DIR *dir, *dir2;
+ const char *oldFile = File;
+ struct dirent *file;
+ char token[MAXTOKEN], token2[MAXTOKEN];
+ char tmpstr[300];
+ int count = 0;
+#if !(defined(WIN32) || defined(cygwin))
+ char space;
+ char newline;
+ struct stat dir_stat, idx_stat;
+ char tmpstr1[300];
+#endif
+
+ DEBUGMSGTL(("parse-mibs", "Scanning directory %s\n", dirname));
+#if !(defined(WIN32) || defined(cygwin))
+ snprintf(token, sizeof(token), "%s/%s", dirname, ".index");
+ token[ sizeof(token)-1 ] = 0;
+ if (stat(token, &idx_stat) == 0 && stat(dirname, &dir_stat) == 0) {
+ if (dir_stat.st_mtime < idx_stat.st_mtime) {
+ DEBUGMSGTL(("parse-mibs", "The index is good\n"));
+ if ((ip = fopen(token, "r")) != NULL) {
+ while (fscanf(ip, "%127s%c%299s%c", token, &space, tmpstr,
+ &newline) == 4) {
+
+ /*
+ * If an overflow of the token or tmpstr buffers has been
+ * found log a message and break out of the while loop,
+ * thus the rest of the file tokens will be ignored.
+ */
+ if (space != ' ' || newline != '\n') {
+ snmp_log(LOG_ERR,
+ "add_mibdir: strings scanned in from %s/%s " \
+ "are too large. count = %d\n ", dirname,
+ ".index", count);
+ break;
+ }
+
+ snprintf(tmpstr1, sizeof(tmpstr1), "%s/%s", dirname, tmpstr);
+ tmpstr1[ sizeof(tmpstr1)-1 ] = 0;
+ new_module(token, tmpstr1);
+ count++;
+ }
+ fclose(ip);
+ return count;
+ } else
+ DEBUGMSGTL(("parse-mibs", "Can't read index\n"));
+ } else
+ DEBUGMSGTL(("parse-mibs", "Index outdated\n"));
+ } else
+ DEBUGMSGTL(("parse-mibs", "No index\n"));
+#endif
+
+ if ((dir = opendir(dirname))) {
+ snprintf(tmpstr, sizeof(tmpstr), "%s/.index", dirname);
+ tmpstr[ sizeof(tmpstr)-1 ] = 0;
+ ip = fopen(tmpstr, "w");
+ while ((file = readdir(dir))) {
+ /*
+ * Only parse file names not beginning with a '.'
+ */
+ if (file->d_name != NULL && file->d_name[0] != '.') {
+ snprintf(tmpstr, sizeof(tmpstr), "%s/%s", dirname, file->d_name);
+ tmpstr[ sizeof(tmpstr)-1 ] = 0;
+ if ((dir2 = opendir(tmpstr))) {
+ /*
+ * file is a directory, don't read it
+ */
+ closedir(dir2);
+ } else {
+ /*
+ * which module is this
+ */
+ if ((fp = fopen(tmpstr, "r")) == NULL) {
+ snmp_log_perror(tmpstr);
+ continue;
+ }
+ DEBUGMSGTL(("parse-mibs", "Checking file: %s...\n",
+ tmpstr));
+ mibLine = 1;
+ File = tmpstr;
+ get_token(fp, token, MAXTOKEN);
+ /*
+ * simple test for this being a MIB
+ */
+ if (get_token(fp, token2, MAXTOKEN) == DEFINITIONS) {
+ new_module(token, tmpstr);
+ count++;
+ if (ip)
+ fprintf(ip, "%s %s\n", token, file->d_name);
+ }
+ fclose(fp);
+ }
+ }
+ }
+ File = oldFile;
+ closedir(dir);
+ if (ip)
+ fclose(ip);
+ return (count);
+ }
+ return (-1);
+}
+
+
+/*
+ * Returns the root of the whole tree
+ * (for backwards compatability)
+ */
+struct tree *
+read_mib(const char *filename)
+{
+ FILE *fp;
+ char token[MAXTOKEN];
+
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ snmp_log_perror(filename);
+ return NULL;
+ }
+ mibLine = 1;
+ File = filename;
+ DEBUGMSGTL(("parse-mibs", "Parsing file: %s...\n", filename));
+ get_token(fp, token, MAXTOKEN);
+ fclose(fp);
+ new_module(token, filename);
+ (void) read_module(token);
+
+ return tree_head;
+}
+
+
+struct tree *
+read_all_mibs()
+{
+ struct module *mp;
+
+ for (mp = module_head; mp; mp = mp->next)
+ if (mp->no_imports == -1)
+ read_module(mp->name);
+ adopt_orphans();
+
+ return tree_head;
+}
+
+
+#ifdef TEST
+main(int argc, char *argv[])
+{
+ int i;
+ struct tree *tp;
+ netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIB_WARNINGS, 2);
+
+ init_mib();
+
+ if (argc == 1)
+ (void) read_all_mibs();
+ else
+ for (i = 1; i < argc; i++)
+ read_mib(argv[i]);
+
+ for (tp = tree_head; tp; tp = tp->next_peer)
+ print_subtree(stdout, tp, 0);
+ free_tree(tree_head);
+
+ return 0;
+}
+#endif /* TEST */
+
+static int
+parseQuoteString(FILE * fp, char *token, int maxtlen)
+{
+ register int ch;
+ int count = 0;
+ int too_long = 0;
+ char *token_start = token;
+
+ for (ch = getc(fp); ch != EOF; ch = getc(fp)) {
+ if (ch == '\r')
+ continue;
+ if (ch == '\n') {
+ mibLine++;
+ } else if (ch == '"') {
+ *token = '\0';
+ if (too_long && netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_MIB_WARNINGS) > 1) {
+ /*
+ * show short form for brevity sake
+ */
+ char ch_save = *(token_start + 50);
+ *(token_start + 50) = '\0';
+ print_error("Warning: string too long",
+ token_start, QUOTESTRING);
+ *(token_start + 50) = ch_save;
+ }
+ return QUOTESTRING;
+ }
+ /*
+ * maximum description length check. If greater, keep parsing
+ * but truncate the string
+ */
+ if (++count < maxtlen)
+ *token++ = ch;
+ else
+ too_long = 1;
+ }
+
+ return 0;
+}
+
+/*
+ * struct index_list *
+ * getIndexes(FILE *fp):
+ * This routine parses a string like { blah blah blah } and returns a
+ * list of the strings enclosed within it.
+ *
+ */
+static struct index_list *
+getIndexes(FILE * fp, struct index_list **retp)
+{
+ int type;
+ char token[MAXTOKEN];
+ char nextIsImplied = 0;
+
+ struct index_list *mylist = NULL;
+ struct index_list **mypp = &mylist;
+
+ free_indexes(retp);
+
+ type = get_token(fp, token, MAXTOKEN);
+
+ if (type != LEFTBRACKET) {
+ return NULL;
+ }
+
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != RIGHTBRACKET && type != ENDOFFILE) {
+ if ((type == LABEL) || (type & SYNTAX_MASK)) {
+ *mypp =
+ (struct index_list *) calloc(1, sizeof(struct index_list));
+ if (*mypp) {
+ (*mypp)->ilabel = strdup(token);
+ (*mypp)->isimplied = nextIsImplied;
+ mypp = &(*mypp)->next;
+ nextIsImplied = 0;
+ }
+ } else if (type == IMPLIED) {
+ nextIsImplied = 1;
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+
+ *retp = mylist;
+ return mylist;
+}
+
+static struct varbind_list *
+getVarbinds(FILE * fp, struct varbind_list **retp)
+{
+ int type;
+ char token[MAXTOKEN];
+
+ struct varbind_list *mylist = NULL;
+ struct varbind_list **mypp = &mylist;
+
+ free_varbinds(retp);
+
+ type = get_token(fp, token, MAXTOKEN);
+
+ if (type != LEFTBRACKET) {
+ return NULL;
+ }
+
+ type = get_token(fp, token, MAXTOKEN);
+ while (type != RIGHTBRACKET && type != ENDOFFILE) {
+ if ((type == LABEL) || (type & SYNTAX_MASK)) {
+ *mypp =
+ (struct varbind_list *) calloc(1,
+ sizeof(struct
+ varbind_list));
+ if (*mypp) {
+ (*mypp)->vblabel = strdup(token);
+ mypp = &(*mypp)->next;
+ }
+ }
+ type = get_token(fp, token, MAXTOKEN);
+ }
+
+ *retp = mylist;
+ return mylist;
+}
+
+static void
+free_indexes(struct index_list **spp)
+{
+ if (spp && *spp) {
+ struct index_list *pp, *npp;
+
+ pp = *spp;
+ *spp = NULL;
+
+ while (pp) {
+ npp = pp->next;
+ if (pp->ilabel)
+ free(pp->ilabel);
+ free(pp);
+ pp = npp;
+ }
+ }
+}
+
+static void
+free_varbinds(struct varbind_list **spp)
+{
+ if (spp && *spp) {
+ struct varbind_list *pp, *npp;
+
+ pp = *spp;
+ *spp = NULL;
+
+ while (pp) {
+ npp = pp->next;
+ if (pp->vblabel)
+ free(pp->vblabel);
+ free(pp);
+ pp = npp;
+ }
+ }
+}
+
+static void
+free_ranges(struct range_list **spp)
+{
+ if (spp && *spp) {
+ struct range_list *pp, *npp;
+
+ pp = *spp;
+ *spp = NULL;
+
+ while (pp) {
+ npp = pp->next;
+ free(pp);
+ pp = npp;
+ }
+ }
+}
+
+static void
+free_enums(struct enum_list **spp)
+{
+ if (spp && *spp) {
+ struct enum_list *pp, *npp;
+
+ pp = *spp;
+ *spp = NULL;
+
+ while (pp) {
+ npp = pp->next;
+ if (pp->label)
+ free(pp->label);
+ free(pp);
+ pp = npp;
+ }
+ }
+}
+
+static struct enum_list *
+copy_enums(struct enum_list *sp)
+{
+ struct enum_list *xp = NULL, **spp = &xp;
+
+ while (sp) {
+ *spp = (struct enum_list *) calloc(1, sizeof(struct enum_list));
+ if (!*spp)
+ break;
+ (*spp)->label = strdup(sp->label);
+ (*spp)->value = sp->value;
+ spp = &(*spp)->next;
+ sp = sp->next;
+ }
+ return (xp);
+}
+
+static struct range_list *
+copy_ranges(struct range_list *sp)
+{
+ struct range_list *xp = NULL, **spp = &xp;
+
+ while (sp) {
+ *spp = (struct range_list *) calloc(1, sizeof(struct range_list));
+ if (!*spp)
+ break;
+ (*spp)->low = sp->low;
+ (*spp)->high = sp->high;
+ spp = &(*spp)->next;
+ sp = sp->next;
+ }
+ return (xp);
+}
+
+/*
+ * This routine parses a string like { blah blah blah } and returns OBJID if
+ * it is well formed, and NULL if not.
+ */
+static int
+tossObjectIdentifier(FILE * fp)
+{
+ int type;
+ char token[MAXTOKEN];
+ int bracketcount = 1;
+
+ type = get_token(fp, token, MAXTOKEN);
+
+ if (type != LEFTBRACKET)
+ return 0;
+ while ((type != RIGHTBRACKET || bracketcount > 0) && type != ENDOFFILE) {
+ type = get_token(fp, token, MAXTOKEN);
+ if (type == LEFTBRACKET)
+ bracketcount++;
+ else if (type == RIGHTBRACKET)
+ bracketcount--;
+ }
+
+ if (type == RIGHTBRACKET)
+ return OBJID;
+ else
+ return 0;
+}
+
+struct tree *
+find_node(const char *name, struct tree *subtree)
+{ /* Unused */
+ return (find_tree_node(name, -1));
+}
+
+struct module *
+find_module(int mid)
+{
+ struct module *mp;
+
+ for (mp = module_head; mp != NULL; mp = mp->next) {
+ if (mp->modid == mid)
+ break;
+ }
+ if (mp != 0)
+ return mp;
+ return NULL;
+}
+
+
+static char leave_indent[256];
+static int leave_was_simple;
+
+static void
+print_mib_leaves(FILE * f, struct tree *tp, int width)
+{
+ struct tree *ntp;
+ char *ip = leave_indent + strlen(leave_indent) - 1;
+ char last_ipch = *ip;
+
+ *ip = '+';
+ if (tp->type == TYPE_OTHER || tp->type > TYPE_SIMPLE_LAST) {
+ fprintf(f, "%s--%s(%ld)\n", leave_indent, tp->label, tp->subid);
+ if (tp->indexes) {
+ struct index_list *xp = tp->indexes;
+ int first = 1, cpos = 0, len, cmax =
+ width - strlen(leave_indent) - 12;
+ *ip = last_ipch;
+ fprintf(f, "%s | Index: ", leave_indent);
+ while (xp) {
+ if (first)
+ first = 0;
+ else
+ fprintf(f, ", ");
+ cpos += (len = strlen(xp->ilabel) + 2);
+ if (cpos > cmax) {
+ fprintf(f, "\n");
+ fprintf(f, "%s | ", leave_indent);
+ cpos = len;
+ }
+ fprintf(f, "%s", xp->ilabel);
+ xp = xp->next;
+ }
+ fprintf(f, "\n");
+ *ip = '+';
+ }
+ } else {
+ const char *acc, *typ;
+ int size = 0;
+ switch (tp->access) {
+ case MIB_ACCESS_NOACCESS:
+ acc = "----";
+ break;
+ case MIB_ACCESS_READONLY:
+ acc = "-R--";
+ break;
+ case MIB_ACCESS_WRITEONLY:
+ acc = "--W-";
+ break;
+ case MIB_ACCESS_READWRITE:
+ acc = "-RW-";
+ break;
+ case MIB_ACCESS_NOTIFY:
+ acc = "---N";
+ break;
+ case MIB_ACCESS_CREATE:
+ acc = "CR--";
+ break;
+ default:
+ acc = " ";
+ break;
+ }
+ switch (tp->type) {
+ case TYPE_OBJID:
+ typ = "ObjID ";
+ break;
+ case TYPE_OCTETSTR:
+ typ = "String ";
+ size = 1;
+ break;
+ case TYPE_INTEGER:
+ if (tp->enums)
+ typ = "EnumVal ";
+ else
+ typ = "INTEGER ";
+ break;
+ case TYPE_NETADDR:
+ typ = "NetAddr ";
+ break;
+ case TYPE_IPADDR:
+ typ = "IpAddr ";
+ break;
+ case TYPE_COUNTER:
+ typ = "Counter ";
+ break;
+ case TYPE_GAUGE:
+ typ = "Gauge ";
+ break;
+ case TYPE_TIMETICKS:
+ typ = "TimeTicks";
+ break;
+ case TYPE_OPAQUE:
+ typ = "Opaque ";
+ size = 1;
+ break;
+ case TYPE_NULL:
+ typ = "Null ";
+ break;
+ case TYPE_COUNTER64:
+ typ = "Counter64";
+ break;
+ case TYPE_BITSTRING:
+ typ = "BitString";
+ break;
+ case TYPE_NSAPADDRESS:
+ typ = "NsapAddr ";
+ break;
+ case TYPE_UNSIGNED32:
+ typ = "Unsigned ";
+ break;
+ case TYPE_UINTEGER:
+ typ = "UInteger ";
+ break;
+ case TYPE_INTEGER32:
+ typ = "Integer32";
+ break;
+ default:
+ typ = " ";
+ break;
+ }
+ fprintf(f, "%s-- %s %s %s(%ld)\n", leave_indent, acc, typ,
+ tp->label, tp->subid);
+ *ip = last_ipch;
+ if (tp->tc_index >= 0)
+ fprintf(f, "%s Textual Convention: %s\n", leave_indent,
+ tclist[tp->tc_index].descriptor);
+ if (tp->enums) {
+ struct enum_list *ep = tp->enums;
+ int cpos = 0, cmax =
+ width - strlen(leave_indent) - 16;
+ fprintf(f, "%s Values: ", leave_indent);
+ while (ep) {
+ char buf[80];
+ int bufw;
+ if (ep != tp->enums)
+ fprintf(f, ", ");
+ snprintf(buf, sizeof(buf), "%s(%d)", ep->label, ep->value);
+ buf[ sizeof(buf)-1 ] = 0;
+ cpos += (bufw = strlen(buf) + 2);
+ if (cpos >= cmax) {
+ fprintf(f, "\n%s ", leave_indent);
+ cpos = bufw;
+ }
+ fprintf(f, "%s", buf);
+ ep = ep->next;
+ }
+ fprintf(f, "\n");
+ }
+ if (tp->ranges) {
+ struct range_list *rp = tp->ranges;
+ if (size)
+ fprintf(f, "%s Size: ", leave_indent);
+ else
+ fprintf(f, "%s Range: ", leave_indent);
+ while (rp) {
+ if (rp != tp->ranges)
+ fprintf(f, " | ");
+ if (rp->low == rp->high)
+ fprintf(f, "%d", rp->low);
+ else
+ fprintf(f, "%d..%d", rp->low, rp->high);
+ rp = rp->next;
+ }
+ fprintf(f, "\n");
+ }
+ }
+ *ip = last_ipch;
+ strcat(leave_indent, " |");
+ leave_was_simple = tp->type != TYPE_OTHER;
+
+ {
+ int i, j, count = 0;
+ struct leave {
+ oid id;
+ struct tree *tp;
+ } *leaves, *lp;
+
+ for (ntp = tp->child_list; ntp; ntp = ntp->next_peer)
+ count++;
+ if (count) {
+ leaves = (struct leave *) calloc(count, sizeof(struct leave));
+ if (!leaves)
+ return;
+ for (ntp = tp->child_list, count = 0; ntp;
+ ntp = ntp->next_peer) {
+ for (i = 0, lp = leaves; i < count; i++, lp++)
+ if (lp->id >= ntp->subid)
+ break;
+ for (j = count; j > i; j--)
+ leaves[j] = leaves[j - 1];
+ lp->id = ntp->subid;
+ lp->tp = ntp;
+ count++;
+ }
+ for (i = 1, lp = leaves; i <= count; i++, lp++) {
+ if (!leave_was_simple || lp->tp->type == 0)
+ fprintf(f, "%s\n", leave_indent);
+ if (i == count)
+ ip[3] = ' ';
+ print_mib_leaves(f, lp->tp, width);
+ }
+ free(leaves);
+ leave_was_simple = 0;
+ }
+ }
+ ip[1] = 0;
+}
+
+void
+print_mib_tree(FILE * f, struct tree *tp, int width)
+{
+ leave_indent[0] = ' ';
+ leave_indent[1] = 0;
+ leave_was_simple = 1;
+ print_mib_leaves(f, tp, width);
+}
+
+
+/*
+ * Merge the parsed object identifier with the existing node.
+ * If there is a problem with the identifier, release the existing node.
+ */
+static struct node *
+merge_parse_objectid(struct node *np, FILE * fp, char *name)
+{
+ struct node *nnp;
+ /*
+ * printf("merge defval --> %s\n",np->defaultValue);
+ */
+ nnp = parse_objectid(fp, name);
+ if (nnp) {
+
+ /*
+ * apply last OID sub-identifier data to the information
+ */
+ /*
+ * already collected for this node.
+ */
+ struct node *headp, *nextp;
+ int ncount = 0;
+ nextp = headp = nnp;
+ while (nnp->next) {
+ nextp = nnp;
+ ncount++;
+ nnp = nnp->next;
+ }
+
+ np->label = nnp->label;
+ np->subid = nnp->subid;
+ np->modid = nnp->modid;
+ np->parent = nnp->parent;
+ if (nnp->filename != NULL) {
+ free(nnp->filename);
+ }
+ free(nnp);
+
+ if (ncount) {
+ nextp->next = np;
+ np = headp;
+ }
+ } else {
+ free_node(np);
+ np = NULL;
+ }
+
+ return np;
+}
+
+/*
+ * transfer data to tree from node
+ *
+ * move pointers for alloc'd data from np to tp.
+ * this prevents them from being freed when np is released.
+ * parent member is not moved.
+ *
+ * CAUTION: nodes may be repeats of existing tree nodes.
+ * This can happen especially when resolving IMPORT clauses.
+ *
+ */
+static void
+tree_from_node(struct tree *tp, struct node *np)
+{
+ free_partial_tree(tp, FALSE);
+
+ tp->label = np->label;
+ np->label = NULL;
+ tp->enums = np->enums;
+ np->enums = NULL;
+ tp->ranges = np->ranges;
+ np->ranges = NULL;
+ tp->indexes = np->indexes;
+ np->indexes = NULL;
+ tp->augments = np->augments;
+ np->augments = NULL;
+ tp->varbinds = np->varbinds;
+ np->varbinds = NULL;
+ tp->hint = np->hint;
+ np->hint = NULL;
+ tp->units = np->units;
+ np->units = NULL;
+ tp->description = np->description;
+ np->description = NULL;
+ tp->defaultValue = np->defaultValue;
+ np->defaultValue = NULL;
+ tp->subid = np->subid;
+ tp->tc_index = np->tc_index;
+ tp->type = translation_table[np->type];
+ tp->access = np->access;
+ tp->status = np->status;
+
+ set_function(tp);
+}
diff --git a/mib/parse.h b/mib/parse.h
new file mode 100644
index 0000000..9c1994d
--- /dev/null
+++ b/mib/parse.h
@@ -0,0 +1,241 @@
+#ifndef PARSE_H
+#define PARSE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ /*
+ * parse.h
+ */
+/***********************************************************
+ Copyright 1989 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of CMU not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+******************************************************************/
+
+#define MAXLABEL 64 /* maximum characters in a label */
+#define MAXTOKEN 128 /* maximum characters in a token */
+#define MAXQUOTESTR 4096 /* maximum characters in a quoted string */
+
+ struct variable_list;
+
+ /*
+ * A linked list of tag-value pairs for enumerated integers.
+ */
+ struct enum_list {
+ struct enum_list *next;
+ int value;
+ char *label;
+ };
+
+ /*
+ * A linked list of ranges
+ */
+ struct range_list {
+ struct range_list *next;
+ int low, high;
+ };
+
+ /*
+ * A linked list of indexes
+ */
+ struct index_list {
+ struct index_list *next;
+ char *ilabel;
+ char isimplied;
+ };
+
+ /*
+ * A linked list of varbinds
+ */
+ struct varbind_list {
+ struct varbind_list *next;
+ char *vblabel;
+ };
+
+ /*
+ * A linked list of nodes.
+ */
+ struct node {
+ struct node *next;
+ char *label; /* This node's (unique) textual name */
+ u_long subid; /* This node's integer subidentifier */
+ int modid; /* The module containing this node */
+ char *parent; /* The parent's textual name */
+ int tc_index; /* index into tclist (-1 if NA) */
+ int type; /* The type of object this represents */
+ int access;
+ int status;
+ struct enum_list *enums; /* (optional) list of enumerated integers */
+ struct range_list *ranges;
+ struct index_list *indexes;
+ char *augments;
+ struct varbind_list *varbinds;
+ char *hint;
+ char *units;
+ char *description; /* description (a quoted string) */
+ char *defaultValue;
+ char *filename;
+ int lineno;
+ };
+
+ /*
+ * A tree in the format of the tree structure of the MIB.
+ */
+ struct tree {
+ struct tree *child_list; /* list of children of this node */
+ struct tree *next_peer; /* Next node in list of peers */
+ struct tree *next; /* Next node in hashed list of names */
+ struct tree *parent;
+ char *label; /* This node's textual name */
+ u_long subid; /* This node's integer subidentifier */
+ int modid; /* The module containing this node */
+ int number_modules;
+ int *module_list; /* To handle multiple modules */
+ int tc_index; /* index into tclist (-1 if NA) */
+ int type; /* This node's object type */
+ int access; /* This nodes access */
+ int status; /* This nodes status */
+ struct enum_list *enums; /* (optional) list of enumerated integers */
+ struct range_list *ranges;
+ struct index_list *indexes;
+ char *augments;
+ struct varbind_list *varbinds;
+ char *hint;
+ char *units;
+ char *description; /* description (a quoted string) */
+ int reported; /* 1=report started in print_subtree... */
+ char *defaultValue;
+ };
+
+ /*
+ * Information held about each MIB module
+ */
+ struct module_import {
+ char *label; /* The descriptor being imported */
+ int modid; /* The module imported from */
+ };
+
+ struct module {
+ char *name; /* This module's name */
+ char *file; /* The file containing the module */
+ struct module_import *imports; /* List of descriptors being imported */
+ int no_imports; /* The number of such import descriptors */
+ /*
+ * -1 implies the module hasn't been read in yet
+ */
+ int modid; /* The index number of this module */
+ struct module *next; /* Linked list pointer */
+ };
+
+ struct module_compatability {
+ const char *old_module;
+ const char *new_module;
+ const char *tag; /* NULL implies unconditional replacement,
+ * otherwise node identifier or prefix */
+ size_t tag_len; /* 0 implies exact match (or unconditional) */
+ struct module_compatability *next; /* linked list */
+ };
+
+
+ /*
+ * non-aggregate types for tree end nodes
+ */
+#define TYPE_OTHER 0
+#define TYPE_OBJID 1
+#define TYPE_OCTETSTR 2
+#define TYPE_INTEGER 3
+#define TYPE_NETADDR 4
+#define TYPE_IPADDR 5
+#define TYPE_COUNTER 6
+#define TYPE_GAUGE 7
+#define TYPE_TIMETICKS 8
+#define TYPE_OPAQUE 9
+#define TYPE_NULL 10
+#define TYPE_COUNTER64 11
+#define TYPE_BITSTRING 12
+#define TYPE_NSAPADDRESS 13
+#define TYPE_UINTEGER 14
+#define TYPE_UNSIGNED32 15
+#define TYPE_INTEGER32 16
+
+#define TYPE_SIMPLE_LAST 16
+
+#define TYPE_TRAPTYPE 20
+#define TYPE_NOTIFTYPE 21
+#define TYPE_OBJGROUP 22
+#define TYPE_NOTIFGROUP 23
+#define TYPE_MODID 24
+#define TYPE_AGENTCAP 25
+#define TYPE_MODCOMP 26
+
+#define MIB_ACCESS_READONLY 18
+#define MIB_ACCESS_READWRITE 19
+#define MIB_ACCESS_WRITEONLY 20
+#define MIB_ACCESS_NOACCESS 21
+#define MIB_ACCESS_NOTIFY 67
+#define MIB_ACCESS_CREATE 48
+
+#define MIB_STATUS_MANDATORY 23
+#define MIB_STATUS_OPTIONAL 24
+#define MIB_STATUS_OBSOLETE 25
+#define MIB_STATUS_DEPRECATED 39
+#define MIB_STATUS_CURRENT 57
+
+#define ANON "anonymous#"
+#define ANON_LEN strlen(ANON)
+
+ struct tree *read_module(const char *);
+ struct tree *read_mib(const char *);
+ struct tree *read_all_mibs(void);
+ int unload_module(const char *name);
+ void unload_all_mibs(void);
+ void init_mib_internals(void);
+ int add_mibdir(const char *);
+ void add_module_replacement(const char *, const char *,
+ const char *, int);
+ int which_module(const char *);
+ char *module_name(int, char *);
+ void print_subtree(FILE *, struct tree *, int);
+ void print_ascii_dump_tree(FILE *, struct tree *, int);
+ struct tree *find_tree_node(const char *, int);
+ const char *get_tc_descriptor(int);
+ struct tree *find_best_tree_node(const char *, struct tree *,
+ u_int *);
+ /*
+ * backwards compatability
+ */
+ struct tree *find_node(const char *, struct tree *);
+ struct module *find_module(int);
+ void adopt_orphans(void);
+ char *snmp_mib_toggle_options(char *options);
+ void snmp_mib_toggle_options_usage(const char *lead,
+ FILE * outf);
+ void print_mib(FILE *);
+ void print_mib_tree(FILE *, struct tree *, int);
+ int get_mib_parse_error_count(void);
+ int snmp_get_token(FILE * fp, char *token, int maxtlen);
+ struct tree *find_best_tree_node(const char *name,
+ struct tree *tree_top,
+ u_int * match);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* PARSE_H */
diff --git a/mib/parse.h.orig b/mib/parse.h.orig
new file mode 100644
index 0000000..5582891
--- /dev/null
+++ b/mib/parse.h.orig
@@ -0,0 +1,246 @@
+#ifndef PARSE_H
+#define PARSE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ /*
+ * parse.h
+ */
+/***********************************************************
+ Copyright 1989 by Carnegie Mellon University
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of CMU not be
+used in advertising or publicity pertaining to distribution of the
+software without specific, written prior permission.
+
+CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
+CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
+ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+SOFTWARE.
+******************************************************************/
+
+#define MAXLABEL 64 /* maximum characters in a label */
+#define MAXTOKEN 128 /* maximum characters in a token */
+#define MAXQUOTESTR 4096 /* maximum characters in a quoted string */
+
+ struct variable_list;
+
+ /*
+ * A linked list of tag-value pairs for enumerated integers.
+ */
+ struct enum_list {
+ struct enum_list *next;
+ int value;
+ char *label;
+ };
+
+ /*
+ * A linked list of ranges
+ */
+ struct range_list {
+ struct range_list *next;
+ int low, high;
+ };
+
+ /*
+ * A linked list of indexes
+ */
+ struct index_list {
+ struct index_list *next;
+ char *ilabel;
+ char isimplied;
+ };
+
+ /*
+ * A linked list of varbinds
+ */
+ struct varbind_list {
+ struct varbind_list *next;
+ char *vblabel;
+ };
+
+ /*
+ * A linked list of nodes.
+ */
+ struct node {
+ struct node *next;
+ char *label; /* This node's (unique) textual name */
+ u_long subid; /* This node's integer subidentifier */
+ int modid; /* The module containing this node */
+ char *parent; /* The parent's textual name */
+ int tc_index; /* index into tclist (-1 if NA) */
+ int type; /* The type of object this represents */
+ int access;
+ int status;
+ struct enum_list *enums; /* (optional) list of enumerated integers */
+ struct range_list *ranges;
+ struct index_list *indexes;
+ char *augments;
+ struct varbind_list *varbinds;
+ char *hint;
+ char *units;
+ char *description; /* description (a quoted string) */
+ char *defaultValue;
+ char *filename;
+ int lineno;
+ };
+
+ /*
+ * A tree in the format of the tree structure of the MIB.
+ */
+ struct tree {
+ struct tree *child_list; /* list of children of this node */
+ struct tree *next_peer; /* Next node in list of peers */
+ struct tree *next; /* Next node in hashed list of names */
+ struct tree *parent;
+ char *label; /* This node's textual name */
+ u_long subid; /* This node's integer subidentifier */
+ int modid; /* The module containing this node */
+ int number_modules;
+ int *module_list; /* To handle multiple modules */
+ int tc_index; /* index into tclist (-1 if NA) */
+ int type; /* This node's object type */
+ int access; /* This nodes access */
+ int status; /* This nodes status */
+ struct enum_list *enums; /* (optional) list of enumerated integers */
+ struct range_list *ranges;
+ struct index_list *indexes;
+ char *augments;
+ struct varbind_list *varbinds;
+ char *hint;
+ char *units;
+ int (*printomat) (u_char **, size_t *, size_t *, int,
+ const netsnmp_variable_list *,
+ const struct enum_list *, const char *,
+ const char *);
+ void (*printer) (char *, const netsnmp_variable_list *, const struct enum_list *, const char *, const char *); /* Value printing function */
+ char *description; /* description (a quoted string) */
+ int reported; /* 1=report started in print_subtree... */
+ char *defaultValue;
+ };
+
+ /*
+ * Information held about each MIB module
+ */
+ struct module_import {
+ char *label; /* The descriptor being imported */
+ int modid; /* The module imported from */
+ };
+
+ struct module {
+ char *name; /* This module's name */
+ char *file; /* The file containing the module */
+ struct module_import *imports; /* List of descriptors being imported */
+ int no_imports; /* The number of such import descriptors */
+ /*
+ * -1 implies the module hasn't been read in yet
+ */
+ int modid; /* The index number of this module */
+ struct module *next; /* Linked list pointer */
+ };
+
+ struct module_compatability {
+ const char *old_module;
+ const char *new_module;
+ const char *tag; /* NULL implies unconditional replacement,
+ * otherwise node identifier or prefix */
+ size_t tag_len; /* 0 implies exact match (or unconditional) */
+ struct module_compatability *next; /* linked list */
+ };
+
+
+ /*
+ * non-aggregate types for tree end nodes
+ */
+#define TYPE_OTHER 0
+#define TYPE_OBJID 1
+#define TYPE_OCTETSTR 2
+#define TYPE_INTEGER 3
+#define TYPE_NETADDR 4
+#define TYPE_IPADDR 5
+#define TYPE_COUNTER 6
+#define TYPE_GAUGE 7
+#define TYPE_TIMETICKS 8
+#define TYPE_OPAQUE 9
+#define TYPE_NULL 10
+#define TYPE_COUNTER64 11
+#define TYPE_BITSTRING 12
+#define TYPE_NSAPADDRESS 13
+#define TYPE_UINTEGER 14
+#define TYPE_UNSIGNED32 15
+#define TYPE_INTEGER32 16
+
+#define TYPE_SIMPLE_LAST 16
+
+#define TYPE_TRAPTYPE 20
+#define TYPE_NOTIFTYPE 21
+#define TYPE_OBJGROUP 22
+#define TYPE_NOTIFGROUP 23
+#define TYPE_MODID 24
+#define TYPE_AGENTCAP 25
+#define TYPE_MODCOMP 26
+
+#define MIB_ACCESS_READONLY 18
+#define MIB_ACCESS_READWRITE 19
+#define MIB_ACCESS_WRITEONLY 20
+#define MIB_ACCESS_NOACCESS 21
+#define MIB_ACCESS_NOTIFY 67
+#define MIB_ACCESS_CREATE 48
+
+#define MIB_STATUS_MANDATORY 23
+#define MIB_STATUS_OPTIONAL 24
+#define MIB_STATUS_OBSOLETE 25
+#define MIB_STATUS_DEPRECATED 39
+#define MIB_STATUS_CURRENT 57
+
+#define ANON "anonymous#"
+#define ANON_LEN strlen(ANON)
+
+ struct tree *read_module(const char *);
+ struct tree *read_mib(const char *);
+ struct tree *read_all_mibs(void);
+ int unload_module(const char *name);
+ void unload_all_mibs(void);
+ void init_mib_internals(void);
+ int add_mibdir(const char *);
+ void add_module_replacement(const char *, const char *,
+ const char *, int);
+ int which_module(const char *);
+ char *module_name(int, char *);
+ void print_subtree(FILE *, struct tree *, int);
+ void print_ascii_dump_tree(FILE *, struct tree *, int);
+ struct tree *find_tree_node(const char *, int);
+ const char *get_tc_descriptor(int);
+ struct tree *find_best_tree_node(const char *, struct tree *,
+ u_int *);
+ /*
+ * backwards compatability
+ */
+ struct tree *find_node(const char *, struct tree *);
+ struct module *find_module(int);
+ void adopt_orphans(void);
+ char *snmp_mib_toggle_options(char *options);
+ void snmp_mib_toggle_options_usage(const char *lead,
+ FILE * outf);
+ void print_mib(FILE *);
+ void print_mib_tree(FILE *, struct tree *, int);
+ int get_mib_parse_error_count(void);
+ int snmp_get_token(FILE * fp, char *token, int maxtlen);
+ struct tree *find_best_tree_node(const char *name,
+ struct tree *tree_top,
+ u_int * match);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* PARSE_H */