diff options
| author | Stef Walter <stef@memberwebs.com> | 2006-01-27 21:36:35 +0000 | 
|---|---|---|
| committer | Stef Walter <stef@memberwebs.com> | 2006-01-27 21:36:35 +0000 | 
| commit | cadd830e5aca1f208541ea6d38da5b4a863db5cc (patch) | |
| tree | 597b1bd012ddd2fb1950b4bdc84c17b69ed37e2c | |
| parent | 36f86d822d09ec0d91839ee68178d8602e1970e2 (diff) | |
Added textual MIB parsing support. See #45
| -rw-r--r-- | daemon/Makefile.am | 3 | ||||
| -rw-r--r-- | daemon/config.c | 10 | ||||
| -rw-r--r-- | daemon/rrdbotd.c | 67 | ||||
| -rw-r--r-- | daemon/rrdbotd.h | 19 | ||||
| -rw-r--r-- | daemon/snmp-help.c | 99 | ||||
| -rw-r--r-- | mib/parse-compat.inc.c | 241 | ||||
| -rw-r--r-- | mib/parse-net-snmp.patch | 184 | ||||
| -rw-r--r-- | mib/parse.c | 5244 | ||||
| -rw-r--r-- | mib/parse.c.orig | 5326 | ||||
| -rw-r--r-- | mib/parse.h | 241 | ||||
| -rw-r--r-- | mib/parse.h.orig | 246 | 
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, ¬ifs); +            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, ¬ifs); +            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 */ | 
