net-snmp  5.4.1
parse.c
00001 /*
00002  * parse.c
00003  *
00004  */
00005 /* Portions of this file are subject to the following copyrights.  See
00006  * the Net-SNMP's COPYING file for more details and other copyrights
00007  * that may apply:
00008  */
00009 /******************************************************************
00010         Copyright 1989, 1991, 1992 by Carnegie Mellon University
00011 
00012                       All Rights Reserved
00013 
00014 Permission to use, copy, modify, and distribute this software and its
00015 documentation for any purpose and without fee is hereby granted,
00016 provided that the above copyright notice appear in all copies and that
00017 both that copyright notice and this permission notice appear in
00018 supporting documentation, and that the name of CMU not be
00019 used in advertising or publicity pertaining to distribution of the
00020 software without specific, written prior permission.
00021 
00022 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
00023 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
00024 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
00025 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
00026 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
00027 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
00028 SOFTWARE.
00029 ******************************************************************/
00030 /*
00031  * Copyright © 2003 Sun Microsystems, Inc. All rights reserved.
00032  * Use is subject to license terms specified in the COPYING file
00033  * distributed with the Net-SNMP package.
00034  */
00035 #include <net-snmp/net-snmp-config.h>
00036 
00037 #ifndef NETSNMP_DISABLE_MIB_LOADING
00038 
00039 #if HAVE_LIMITS_H
00040 #include <limits.h>
00041 #endif
00042 #include <stdio.h>
00043 #if HAVE_STDLIB_H
00044 #include <stdlib.h>
00045 #endif
00046 #if HAVE_STRING_H
00047 #include <string.h>
00048 #else
00049 #include <strings.h>
00050 #endif
00051 #include <ctype.h>
00052 #include <sys/types.h>
00053 #if HAVE_SYS_STAT_H
00054 #include <sys/stat.h>
00055 #endif
00056 
00057 /*
00058  * Wow.  This is ugly.  -- Wes 
00059  */
00060 #if HAVE_DIRENT_H
00061 # include <dirent.h>
00062 # define NAMLEN(dirent) strlen((dirent)->d_name)
00063 #else
00064 # define dirent direct
00065 # define NAMLEN(dirent) (dirent)->d_namlen
00066 # if HAVE_SYS_NDIR_H
00067 #  include <sys/ndir.h>
00068 # endif
00069 # if HAVE_SYS_DIR_H
00070 #  include <sys/dir.h>
00071 # endif
00072 # if HAVE_NDIR_H
00073 #  include <ndir.h>
00074 # endif
00075 #endif
00076 #if TIME_WITH_SYS_TIME
00077 # ifdef WIN32
00078 #  include <sys/timeb.h>
00079 # else
00080 #  include <sys/time.h>
00081 # endif
00082 # include <time.h>
00083 #else
00084 # if HAVE_SYS_TIME_H
00085 #  include <sys/time.h>
00086 # else
00087 #  include <time.h>
00088 # endif
00089 #endif
00090 #if HAVE_WINSOCK_H
00091 #include <winsock.h>
00092 #endif
00093 #if HAVE_NETINET_IN_H
00094 #include <netinet/in.h>
00095 #endif
00096 #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
00097 #include <regex.h>
00098 #endif
00099 #if HAVE_DMALLOC_H
00100 #include <dmalloc.h>
00101 #endif
00102 
00103 #include <errno.h>
00104 
00105 #include <net-snmp/types.h>
00106 #include <net-snmp/output_api.h>
00107 #include <net-snmp/config_api.h>
00108 #include <net-snmp/utilities.h>
00109 
00110 #include <net-snmp/library/parse.h>
00111 #include <net-snmp/library/mib.h>
00112 #include <net-snmp/library/snmp_api.h>
00113 
00114 /*
00115  * This is one element of an object identifier with either an integer
00116  * subidentifier, or a textual string label, or both.
00117  * The subid is -1 if not present, and label is NULL if not present.
00118  */
00119 struct subid_s {
00120     int             subid;
00121     int             modid;
00122     char           *label;
00123 };
00124 
00125 #define MAXTC   4096
00126 struct tc {                     /* textual conventions */
00127     int             type;
00128     int             modid;
00129     char           *descriptor;
00130     char           *hint;
00131     struct enum_list *enums;
00132     struct range_list *ranges;
00133     char           *description;
00134 } tclist[MAXTC];
00135 
00136 int             mibLine = 0;
00137 const char     *File = "(none)";
00138 static int      anonymous = 0;
00139 
00140 struct objgroup {
00141     char           *name;
00142     int             line;
00143     struct objgroup *next;
00144 }              *objgroups = NULL, *objects = NULL, *notifs = NULL;
00145 
00146 #define SYNTAX_MASK     0x80
00147 /*
00148  * types of tokens
00149  * Tokens wiht the SYNTAX_MASK bit set are syntax tokens 
00150  */
00151 #define CONTINUE    -1
00152 #define ENDOFFILE   0
00153 #define LABEL       1
00154 #define SUBTREE     2
00155 #define SYNTAX      3
00156 #define OBJID       (4 | SYNTAX_MASK)
00157 #define OCTETSTR    (5 | SYNTAX_MASK)
00158 #define INTEGER     (6 | SYNTAX_MASK)
00159 #define NETADDR     (7 | SYNTAX_MASK)
00160 #define IPADDR      (8 | SYNTAX_MASK)
00161 #define COUNTER     (9 | SYNTAX_MASK)
00162 #define GAUGE       (10 | SYNTAX_MASK)
00163 #define TIMETICKS   (11 | SYNTAX_MASK)
00164 #define KW_OPAQUE   (12 | SYNTAX_MASK)
00165 #define NUL         (13 | SYNTAX_MASK)
00166 #define SEQUENCE    14
00167 #define OF          15          /* SEQUENCE OF */
00168 #define OBJTYPE     16
00169 #define ACCESS      17
00170 #define READONLY    18
00171 #define READWRITE   19
00172 #define WRITEONLY   20
00173 #ifdef NOACCESS
00174 #undef NOACCESS                 /* agent 'NOACCESS' token */
00175 #endif
00176 #define NOACCESS    21
00177 #define STATUS      22
00178 #define MANDATORY   23
00179 #define KW_OPTIONAL    24
00180 #define OBSOLETE    25
00181 /*
00182  * #define RECOMMENDED 26 
00183  */
00184 #define PUNCT       27
00185 #define EQUALS      28
00186 #define NUMBER      29
00187 #define LEFTBRACKET 30
00188 #define RIGHTBRACKET 31
00189 #define LEFTPAREN   32
00190 #define RIGHTPAREN  33
00191 #define COMMA       34
00192 #define DESCRIPTION 35
00193 #define QUOTESTRING 36
00194 #define INDEX       37
00195 #define DEFVAL      38
00196 #define DEPRECATED  39
00197 #define SIZE        40
00198 #define BITSTRING   (41 | SYNTAX_MASK)
00199 #define NSAPADDRESS (42 | SYNTAX_MASK)
00200 #define COUNTER64   (43 | SYNTAX_MASK)
00201 #define OBJGROUP    44
00202 #define NOTIFTYPE   45
00203 #define AUGMENTS    46
00204 #define COMPLIANCE  47
00205 #define READCREATE  48
00206 #define UNITS       49
00207 #define REFERENCE   50
00208 #define NUM_ENTRIES 51
00209 #define MODULEIDENTITY 52
00210 #define LASTUPDATED 53
00211 #define ORGANIZATION 54
00212 #define CONTACTINFO 55
00213 #define UINTEGER32 (56 | SYNTAX_MASK)
00214 #define CURRENT     57
00215 #define DEFINITIONS 58
00216 #define END         59
00217 #define SEMI        60
00218 #define TRAPTYPE    61
00219 #define ENTERPRISE  62
00220 /*
00221  * #define DISPLAYSTR (63 | SYNTAX_MASK) 
00222  */
00223 #define BEGIN       64
00224 #define IMPORTS     65
00225 #define EXPORTS     66
00226 #define ACCNOTIFY   67
00227 #define BAR         68
00228 #define RANGE       69
00229 #define CONVENTION  70
00230 #define DISPLAYHINT 71
00231 #define FROM        72
00232 #define AGENTCAP    73
00233 #define MACRO       74
00234 #define IMPLIED     75
00235 #define SUPPORTS    76
00236 #define INCLUDES    77
00237 #define VARIATION   78
00238 #define REVISION    79
00239 #define NOTIMPL     80
00240 #define OBJECTS     81
00241 #define NOTIFICATIONS   82
00242 #define MODULE      83
00243 #define MINACCESS   84
00244 #define PRODREL     85
00245 #define WRSYNTAX    86
00246 #define CREATEREQ   87
00247 #define NOTIFGROUP  88
00248 #define MANDATORYGROUPS 89
00249 #define GROUP       90
00250 #define OBJECT      91
00251 #define IDENTIFIER  92
00252 #define CHOICE      93
00253 #define LEFTSQBRACK     95
00254 #define RIGHTSQBRACK    96
00255 #define IMPLICIT    97
00256 #define APPSYNTAX       (98 | SYNTAX_MASK)
00257 #define OBJSYNTAX       (99 | SYNTAX_MASK)
00258 #define SIMPLESYNTAX    (100 | SYNTAX_MASK)
00259 #define OBJNAME         (101 | SYNTAX_MASK)
00260 #define NOTIFNAME       (102 | SYNTAX_MASK)
00261 #define VARIABLES       103
00262 #define UNSIGNED32      (104 | SYNTAX_MASK)
00263 #define INTEGER32       (105 | SYNTAX_MASK)
00264 #define OBJIDENTITY     106
00265 /*
00266  * Beware of reaching SYNTAX_MASK (0x80) 
00267  */
00268 
00269 struct tok {
00270     const char     *name;       /* token name */
00271     int             len;        /* length not counting nul */
00272     int             token;      /* value */
00273     int             hash;       /* hash of name */
00274     struct tok     *next;       /* pointer to next in hash table */
00275 };
00276 
00277 
00278 static struct tok tokens[] = {
00279     {"obsolete", sizeof("obsolete") - 1, OBSOLETE}
00280     ,
00281     {"Opaque", sizeof("Opaque") - 1, KW_OPAQUE}
00282     ,
00283     {"optional", sizeof("optional") - 1, KW_OPTIONAL}
00284     ,
00285     {"LAST-UPDATED", sizeof("LAST-UPDATED") - 1, LASTUPDATED}
00286     ,
00287     {"ORGANIZATION", sizeof("ORGANIZATION") - 1, ORGANIZATION}
00288     ,
00289     {"CONTACT-INFO", sizeof("CONTACT-INFO") - 1, CONTACTINFO}
00290     ,
00291     {"MODULE-IDENTITY", sizeof("MODULE-IDENTITY") - 1, MODULEIDENTITY}
00292     ,
00293     {"MODULE-COMPLIANCE", sizeof("MODULE-COMPLIANCE") - 1, COMPLIANCE}
00294     ,
00295     {"DEFINITIONS", sizeof("DEFINITIONS") - 1, DEFINITIONS}
00296     ,
00297     {"END", sizeof("END") - 1, END}
00298     ,
00299     {"AUGMENTS", sizeof("AUGMENTS") - 1, AUGMENTS}
00300     ,
00301     {"not-accessible", sizeof("not-accessible") - 1, NOACCESS}
00302     ,
00303     {"write-only", sizeof("write-only") - 1, WRITEONLY}
00304     ,
00305     {"NsapAddress", sizeof("NsapAddress") - 1, NSAPADDRESS}
00306     ,
00307     {"UNITS", sizeof("Units") - 1, UNITS}
00308     ,
00309     {"REFERENCE", sizeof("REFERENCE") - 1, REFERENCE}
00310     ,
00311     {"NUM-ENTRIES", sizeof("NUM-ENTRIES") - 1, NUM_ENTRIES}
00312     ,
00313     {"BITSTRING", sizeof("BITSTRING") - 1, BITSTRING}
00314     ,
00315     {"BIT", sizeof("BIT") - 1, CONTINUE}
00316     ,
00317     {"BITS", sizeof("BITS") - 1, BITSTRING}
00318     ,
00319     {"Counter64", sizeof("Counter64") - 1, COUNTER64}
00320     ,
00321     {"TimeTicks", sizeof("TimeTicks") - 1, TIMETICKS}
00322     ,
00323     {"NOTIFICATION-TYPE", sizeof("NOTIFICATION-TYPE") - 1, NOTIFTYPE}
00324     ,
00325     {"OBJECT-GROUP", sizeof("OBJECT-GROUP") - 1, OBJGROUP}
00326     ,
00327     {"OBJECT-IDENTITY", sizeof("OBJECT-IDENTITY") - 1, OBJIDENTITY}
00328     ,
00329     {"IDENTIFIER", sizeof("IDENTIFIER") - 1, IDENTIFIER}
00330     ,
00331     {"OBJECT", sizeof("OBJECT") - 1, OBJECT}
00332     ,
00333     {"NetworkAddress", sizeof("NetworkAddress") - 1, NETADDR}
00334     ,
00335     {"Gauge", sizeof("Gauge") - 1, GAUGE}
00336     ,
00337     {"Gauge32", sizeof("Gauge32") - 1, GAUGE}
00338     ,
00339     {"Unsigned32", sizeof("Unsigned32") - 1, UNSIGNED32}
00340     ,
00341     {"read-write", sizeof("read-write") - 1, READWRITE}
00342     ,
00343     {"read-create", sizeof("read-create") - 1, READCREATE}
00344     ,
00345     {"OCTETSTRING", sizeof("OCTETSTRING") - 1, OCTETSTR}
00346     ,
00347     {"OCTET", sizeof("OCTET") - 1, CONTINUE}
00348     ,
00349     {"OF", sizeof("OF") - 1, OF}
00350     ,
00351     {"SEQUENCE", sizeof("SEQUENCE") - 1, SEQUENCE}
00352     ,
00353     {"NULL", sizeof("NULL") - 1, NUL}
00354     ,
00355     {"IpAddress", sizeof("IpAddress") - 1, IPADDR}
00356     ,
00357     {"UInteger32", sizeof("UInteger32") - 1, UINTEGER32}
00358     ,
00359     {"INTEGER", sizeof("INTEGER") - 1, INTEGER}
00360     ,
00361     {"Integer32", sizeof("Integer32") - 1, INTEGER32}
00362     ,
00363     {"Counter", sizeof("Counter") - 1, COUNTER}
00364     ,
00365     {"Counter32", sizeof("Counter32") - 1, COUNTER}
00366     ,
00367     {"read-only", sizeof("read-only") - 1, READONLY}
00368     ,
00369     {"DESCRIPTION", sizeof("DESCRIPTION") - 1, DESCRIPTION}
00370     ,
00371     {"INDEX", sizeof("INDEX") - 1, INDEX}
00372     ,
00373     {"DEFVAL", sizeof("DEFVAL") - 1, DEFVAL}
00374     ,
00375     {"deprecated", sizeof("deprecated") - 1, DEPRECATED}
00376     ,
00377     {"SIZE", sizeof("SIZE") - 1, SIZE}
00378     ,
00379     {"MAX-ACCESS", sizeof("MAX-ACCESS") - 1, ACCESS}
00380     ,
00381     {"ACCESS", sizeof("ACCESS") - 1, ACCESS}
00382     ,
00383     {"mandatory", sizeof("mandatory") - 1, MANDATORY}
00384     ,
00385     {"current", sizeof("current") - 1, CURRENT}
00386     ,
00387     {"STATUS", sizeof("STATUS") - 1, STATUS}
00388     ,
00389     {"SYNTAX", sizeof("SYNTAX") - 1, SYNTAX}
00390     ,
00391     {"OBJECT-TYPE", sizeof("OBJECT-TYPE") - 1, OBJTYPE}
00392     ,
00393     {"TRAP-TYPE", sizeof("TRAP-TYPE") - 1, TRAPTYPE}
00394     ,
00395     {"ENTERPRISE", sizeof("ENTERPRISE") - 1, ENTERPRISE}
00396     ,
00397     {"BEGIN", sizeof("BEGIN") - 1, BEGIN}
00398     ,
00399     {"IMPORTS", sizeof("IMPORTS") - 1, IMPORTS}
00400     ,
00401     {"EXPORTS", sizeof("EXPORTS") - 1, EXPORTS}
00402     ,
00403     {"accessible-for-notify", sizeof("accessible-for-notify") - 1,
00404      ACCNOTIFY}
00405     ,
00406     {"TEXTUAL-CONVENTION", sizeof("TEXTUAL-CONVENTION") - 1, CONVENTION}
00407     ,
00408     {"NOTIFICATION-GROUP", sizeof("NOTIFICATION-GROUP") - 1, NOTIFGROUP}
00409     ,
00410     {"DISPLAY-HINT", sizeof("DISPLAY-HINT") - 1, DISPLAYHINT}
00411     ,
00412     {"FROM", sizeof("FROM") - 1, FROM}
00413     ,
00414     {"AGENT-CAPABILITIES", sizeof("AGENT-CAPABILITIES") - 1, AGENTCAP}
00415     ,
00416     {"MACRO", sizeof("MACRO") - 1, MACRO}
00417     ,
00418     {"IMPLIED", sizeof("IMPLIED") - 1, IMPLIED}
00419     ,
00420     {"SUPPORTS", sizeof("SUPPORTS") - 1, SUPPORTS}
00421     ,
00422     {"INCLUDES", sizeof("INCLUDES") - 1, INCLUDES}
00423     ,
00424     {"VARIATION", sizeof("VARIATION") - 1, VARIATION}
00425     ,
00426     {"REVISION", sizeof("REVISION") - 1, REVISION}
00427     ,
00428     {"not-implemented", sizeof("not-implemented") - 1, NOTIMPL}
00429     ,
00430     {"OBJECTS", sizeof("OBJECTS") - 1, OBJECTS}
00431     ,
00432     {"NOTIFICATIONS", sizeof("NOTIFICATIONS") - 1, NOTIFICATIONS}
00433     ,
00434     {"MODULE", sizeof("MODULE") - 1, MODULE}
00435     ,
00436     {"MIN-ACCESS", sizeof("MIN-ACCESS") - 1, MINACCESS}
00437     ,
00438     {"PRODUCT-RELEASE", sizeof("PRODUCT-RELEASE") - 1, PRODREL}
00439     ,
00440     {"WRITE-SYNTAX", sizeof("WRITE-SYNTAX") - 1, WRSYNTAX}
00441     ,
00442     {"CREATION-REQUIRES", sizeof("CREATION-REQUIRES") - 1, CREATEREQ}
00443     ,
00444     {"MANDATORY-GROUPS", sizeof("MANDATORY-GROUPS") - 1, MANDATORYGROUPS}
00445     ,
00446     {"GROUP", sizeof("GROUP") - 1, GROUP}
00447     ,
00448     {"CHOICE", sizeof("CHOICE") - 1, CHOICE}
00449     ,
00450     {"IMPLICIT", sizeof("IMPLICIT") - 1, IMPLICIT}
00451     ,
00452     {"ObjectSyntax", sizeof("ObjectSyntax") - 1, OBJSYNTAX}
00453     ,
00454     {"SimpleSyntax", sizeof("SimpleSyntax") - 1, SIMPLESYNTAX}
00455     ,
00456     {"ApplicationSyntax", sizeof("ApplicationSyntax") - 1, APPSYNTAX}
00457     ,
00458     {"ObjectName", sizeof("ObjectName") - 1, OBJNAME}
00459     ,
00460     {"NotificationName", sizeof("NotificationName") - 1, NOTIFNAME}
00461     ,
00462     {"VARIABLES", sizeof("VARIABLES") - 1, VARIABLES}
00463     ,
00464     {NULL}
00465 };
00466 
00467 static struct module_compatability *module_map_head;
00468 static struct module_compatability module_map[] = {
00469     {"RFC1065-SMI", "RFC1155-SMI", NULL, 0},
00470     {"RFC1066-MIB", "RFC1156-MIB", NULL, 0},
00471     /*
00472      * 'mib' -> 'mib-2' 
00473      */
00474     {"RFC1156-MIB", "RFC1158-MIB", NULL, 0},
00475     /*
00476      * 'snmpEnableAuthTraps' -> 'snmpEnableAuthenTraps' 
00477      */
00478     {"RFC1158-MIB", "RFC1213-MIB", NULL, 0},
00479     /*
00480      * 'nullOID' -> 'zeroDotZero' 
00481      */
00482     {"RFC1155-SMI", "SNMPv2-SMI", NULL, 0},
00483     {"RFC1213-MIB", "SNMPv2-SMI", "mib-2", 0},
00484     {"RFC1213-MIB", "SNMPv2-MIB", "sys", 3},
00485     {"RFC1213-MIB", "IF-MIB", "if", 2},
00486     {"RFC1213-MIB", "IP-MIB", "ip", 2},
00487     {"RFC1213-MIB", "IP-MIB", "icmp", 4},
00488     {"RFC1213-MIB", "TCP-MIB", "tcp", 3},
00489     {"RFC1213-MIB", "UDP-MIB", "udp", 3},
00490     {"RFC1213-MIB", "SNMPv2-SMI", "transmission", 0},
00491     {"RFC1213-MIB", "SNMPv2-MIB", "snmp", 4},
00492     {"RFC1231-MIB", "TOKENRING-MIB", NULL, 0},
00493     {"RFC1271-MIB", "RMON-MIB", NULL, 0},
00494     {"RFC1286-MIB", "SOURCE-ROUTING-MIB", "dot1dSr", 7},
00495     {"RFC1286-MIB", "BRIDGE-MIB", NULL, 0},
00496     {"RFC1315-MIB", "FRAME-RELAY-DTE-MIB", NULL, 0},
00497     {"RFC1316-MIB", "CHARACTER-MIB", NULL, 0},
00498     {"RFC1406-MIB", "DS1-MIB", NULL, 0},
00499     {"RFC-1213", "RFC1213-MIB", NULL, 0},
00500 };
00501 
00502 #define MODULE_NOT_FOUND        0
00503 #define MODULE_LOADED_OK        1
00504 #define MODULE_ALREADY_LOADED   2
00505 /*
00506  * #define MODULE_LOAD_FAILED   3       
00507  */
00508 #define MODULE_LOAD_FAILED      MODULE_NOT_FOUND
00509 
00510 
00511 #define HASHSIZE        32
00512 #define BUCKET(x)       (x & (HASHSIZE-1))
00513 
00514 #define NHASHSIZE    128
00515 #define NBUCKET(x)   (x & (NHASHSIZE-1))
00516 
00517 static struct tok *buckets[HASHSIZE];
00518 
00519 static struct node *nbuckets[NHASHSIZE];
00520 static struct tree *tbuckets[NHASHSIZE];
00521 static struct module *module_head = NULL;
00522 
00523 struct node    *orphan_nodes = NULL;
00524 struct tree    *tree_head = NULL;
00525 
00526 #define NUMBER_OF_ROOT_NODES    3
00527 static struct module_import root_imports[NUMBER_OF_ROOT_NODES];
00528 
00529 static int      current_module = 0;
00530 static int      max_module = 0;
00531 static char    *last_err_module = 0;    /* no repeats on "Cannot find module..." */
00532 
00533 static void     tree_from_node(struct tree *tp, struct node *np);
00534 static void     do_subtree(struct tree *, struct node **);
00535 static void     do_linkup(struct module *, struct node *);
00536 static void     dump_module_list(void);
00537 static int      get_token(FILE *, char *, int);
00538 static int      parseQuoteString(FILE *, char *, int);
00539 static int      tossObjectIdentifier(FILE *);
00540 static int      name_hash(const char *);
00541 static void     init_node_hash(struct node *);
00542 static void     print_error(const char *, const char *, int);
00543 static void     free_tree(struct tree *);
00544 static void     free_partial_tree(struct tree *, int);
00545 static void     free_node(struct node *);
00546 static void     build_translation_table(void);
00547 static void     init_tree_roots(void);
00548 static void     merge_anon_children(struct tree *, struct tree *);
00549 static void     unlink_tbucket(struct tree *);
00550 static void     unlink_tree(struct tree *);
00551 static int      getoid(FILE *, struct subid_s *, int);
00552 static struct node *parse_objectid(FILE *, char *);
00553 static int      get_tc(const char *, int, int *, struct enum_list **,
00554                        struct range_list **, char **);
00555 static int      get_tc_index(const char *, int);
00556 static struct enum_list *parse_enumlist(FILE *, struct enum_list **);
00557 static struct range_list *parse_ranges(FILE * fp, struct range_list **);
00558 static struct node *parse_asntype(FILE *, char *, int *, char *);
00559 static struct node *parse_objecttype(FILE *, char *);
00560 static struct node *parse_objectgroup(FILE *, char *, int,
00561                                       struct objgroup **);
00562 static struct node *parse_notificationDefinition(FILE *, char *);
00563 static struct node *parse_trapDefinition(FILE *, char *);
00564 static struct node *parse_compliance(FILE *, char *);
00565 static struct node *parse_capabilities(FILE *, char *);
00566 static struct node *parse_moduleIdentity(FILE *, char *);
00567 static struct node *parse_macro(FILE *, char *);
00568 static void     parse_imports(FILE *);
00569 static struct node *parse(FILE *, struct node *);
00570 
00571 static int     read_module_internal(const char *);
00572 static int     read_module_replacements(const char *);
00573 static int     read_import_replacements(const char *,
00574                                          struct module_import *);
00575 
00576 static void     new_module(const char *, const char *);
00577 
00578 static struct node *merge_parse_objectid(struct node *, FILE *, char *);
00579 static struct index_list *getIndexes(FILE * fp, struct index_list **);
00580 static struct varbind_list *getVarbinds(FILE * fp, struct varbind_list **);
00581 static void     free_indexes(struct index_list **);
00582 static void     free_varbinds(struct varbind_list **);
00583 static void     free_ranges(struct range_list **);
00584 static void     free_enums(struct enum_list **);
00585 static struct range_list *copy_ranges(struct range_list *);
00586 static struct enum_list *copy_enums(struct enum_list *);
00587 
00588 static u_int    compute_match(const char *search_base, const char *key);
00589 
00590 void
00591 snmp_mib_toggle_options_usage(const char *lead, FILE * outf)
00592 {
00593     fprintf(outf, "%su:  %sallow the use of underlines in MIB symbols\n",
00594             lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
00595                                            NETSNMP_DS_LIB_MIB_PARSE_LABEL)) ?
00596                    "dis" : ""));
00597     fprintf(outf, "%sc:  %sallow the use of \"--\" to terminate comments\n",
00598             lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
00599                                            NETSNMP_DS_LIB_MIB_COMMENT_TERM)) ?
00600                    "" : "dis"));
00601 
00602     fprintf(outf, "%sd:  %ssave the DESCRIPTIONs of the MIB objects\n",
00603             lead, ((netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
00604                                            NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) ?
00605                    "do not " : ""));
00606 
00607     fprintf(outf, "%se:  disable errors when MIB symbols conflict\n", lead);
00608 
00609     fprintf(outf, "%sw:  enable warnings when MIB symbols conflict\n", lead);
00610 
00611     fprintf(outf, "%sW:  enable detailed warnings when MIB symbols conflict\n",
00612             lead);
00613 
00614     fprintf(outf, "%sR:  replace MIB symbols from latest module\n", lead);
00615 }
00616 
00617 char           *
00618 snmp_mib_toggle_options(char *options)
00619 {
00620     if (options) {
00621         while (*options) {
00622             switch (*options) {
00623             case 'u':
00624                 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIB_PARSE_LABEL,
00625                                !netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
00626                                                NETSNMP_DS_LIB_MIB_PARSE_LABEL));
00627                 break;
00628 
00629             case 'c':
00630                 netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
00631                                           NETSNMP_DS_LIB_MIB_COMMENT_TERM);
00632                 break;
00633 
00634             case 'e':
00635                 netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID,
00636                                           NETSNMP_DS_LIB_MIB_ERRORS);
00637                 break;
00638 
00639             case 'w':
00640                 netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
00641                                    NETSNMP_DS_LIB_MIB_WARNINGS, 1);
00642                 break;
00643 
00644             case 'W':
00645                 netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID,
00646                                    NETSNMP_DS_LIB_MIB_WARNINGS, 2);
00647                 break;
00648 
00649             case 'd':
00650                 netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID, 
00651                                           NETSNMP_DS_LIB_SAVE_MIB_DESCRS);
00652                 break;
00653 
00654             case 'R':
00655                 netsnmp_ds_toggle_boolean(NETSNMP_DS_LIBRARY_ID, 
00656                                           NETSNMP_DS_LIB_MIB_REPLACE);
00657                 break;
00658 
00659             default:
00660                 /*
00661                  * return at the unknown option 
00662                  */
00663                 return options;
00664             }
00665             options++;
00666         }
00667     }
00668     return NULL;
00669 }
00670 
00671 static int
00672 name_hash(const char *name)
00673 {
00674     int             hash = 0;
00675     const char     *cp;
00676 
00677     if (!name)
00678         return 0;
00679     for (cp = name; *cp; cp++)
00680         hash += tolower(*cp);
00681     return (hash);
00682 }
00683 
00684 void
00685 netsnmp_init_mib_internals(void)
00686 {
00687     register struct tok *tp;
00688     register int    b, i;
00689     int             max_modc;
00690 
00691     if (tree_head)
00692         return;
00693 
00694     /*
00695      * Set up hash list of pre-defined tokens
00696      */
00697     memset(buckets, 0, sizeof(buckets));
00698     for (tp = tokens; tp->name; tp++) {
00699         tp->hash = name_hash(tp->name);
00700         b = BUCKET(tp->hash);
00701         if (buckets[b])
00702             tp->next = buckets[b];      /* BUG ??? */
00703         buckets[b] = tp;
00704     }
00705 
00706     /*
00707      * Initialise other internal structures
00708      */
00709 
00710     max_modc = sizeof(module_map) / sizeof(module_map[0]) - 1;
00711     for (i = 0; i < max_modc; ++i)
00712         module_map[i].next = &(module_map[i + 1]);
00713     module_map[max_modc].next = NULL;
00714     module_map_head = module_map;
00715 
00716     memset(nbuckets, 0, sizeof(nbuckets));
00717     memset(tbuckets, 0, sizeof(tbuckets));
00718     memset(tclist, 0, MAXTC * sizeof(struct tc));
00719     build_translation_table();
00720     init_tree_roots();          /* Set up initial roots */
00721     /*
00722      * Relies on 'add_mibdir' having set up the modules 
00723      */
00724 }
00725 
00726 #ifndef NETSNMP_CLEAN_NAMESPACE
00727 void
00728 init_mib_internals(void)
00729 {
00730     netsnmp_init_mib_internals();
00731 }
00732 #endif
00733 
00734 static void
00735 init_node_hash(struct node *nodes)
00736 {
00737     struct node    *np, *nextp;
00738     int             hash;
00739 
00740     memset(nbuckets, 0, sizeof(nbuckets));
00741     for (np = nodes; np;) {
00742         nextp = np->next;
00743         hash = NBUCKET(name_hash(np->parent));
00744         np->next = nbuckets[hash];
00745         nbuckets[hash] = np;
00746         np = nextp;
00747     }
00748 }
00749 
00750 static int      erroneousMibs = 0;
00751 
00752 int
00753 get_mib_parse_error_count(void)
00754 {
00755     return erroneousMibs;
00756 }
00757 
00758 
00759 static void
00760 print_error(const char *str, const char *token, int type)
00761 {
00762     erroneousMibs++;
00763     DEBUGMSGTL(("parse-mibs", "\n"));
00764     if (type == ENDOFFILE)
00765         snmp_log(LOG_ERR, "%s (EOF): At line %d in %s\n", str, mibLine,
00766                  File);
00767     else if (token && *token)
00768         snmp_log(LOG_ERR, "%s (%s): At line %d in %s\n", str, token,
00769                  mibLine, File);
00770     else
00771         snmp_log(LOG_ERR, "%s: At line %d in %s\n", str, mibLine, File);
00772 }
00773 
00774 static void
00775 print_module_not_found(const char *cp)
00776 {
00777     if (!last_err_module || strcmp(cp, last_err_module))
00778         print_error("Cannot find module", cp, CONTINUE);
00779     if (last_err_module)
00780         free(last_err_module);
00781     last_err_module = strdup(cp);
00782 }
00783 
00784 static struct node *
00785 alloc_node(int modid)
00786 {
00787     struct node    *np;
00788     np = (struct node *) calloc(1, sizeof(struct node));
00789     if (np) {
00790         np->tc_index = -1;
00791         np->modid = modid;
00792         np->filename = strdup(File);
00793         np->lineno = mibLine;
00794     }
00795     return np;
00796 }
00797 
00798 static void
00799 unlink_tbucket(struct tree *tp)
00800 {
00801     int             hash = NBUCKET(name_hash(tp->label));
00802     struct tree    *otp = NULL, *ntp = tbuckets[hash];
00803 
00804     while (ntp && ntp != tp) {
00805         otp = ntp;
00806         ntp = ntp->next;
00807     }
00808     if (!ntp)
00809         snmp_log(LOG_EMERG, "Can't find %s in tbuckets\n", tp->label);
00810     else if (otp)
00811         otp->next = ntp->next;
00812     else
00813         tbuckets[hash] = tp->next;
00814 }
00815 
00816 static void
00817 unlink_tree(struct tree *tp)
00818 {
00819     struct tree    *otp = NULL, *ntp = tp->parent;
00820 
00821     if (!ntp) {                 /* this tree has no parent */
00822         DEBUGMSGTL(("unlink_tree", "Tree node %s has no parent\n",
00823                     tp->label));
00824     } else {
00825         ntp = ntp->child_list;
00826 
00827         while (ntp && ntp != tp) {
00828             otp = ntp;
00829             ntp = ntp->next_peer;
00830         }
00831         if (!ntp)
00832             snmp_log(LOG_EMERG, "Can't find %s in %s's children\n",
00833                      tp->label, tp->parent->label);
00834         else if (otp)
00835             otp->next_peer = ntp->next_peer;
00836         else
00837             tp->parent->child_list = tp->next_peer;
00838     }
00839 
00840     if (tree_head == tp)
00841         tree_head = tp->next_peer;
00842 }
00843 
00844 static void
00845 free_partial_tree(struct tree *tp, int keep_label)
00846 {
00847     if (!tp)
00848         return;
00849 
00850     /*
00851      * remove the data from this tree node 
00852      */
00853     free_enums(&tp->enums);
00854     free_ranges(&tp->ranges);
00855     free_indexes(&tp->indexes);
00856     free_varbinds(&tp->varbinds);
00857     if (!keep_label)
00858         SNMP_FREE(tp->label);
00859     SNMP_FREE(tp->hint);
00860     SNMP_FREE(tp->units);
00861     SNMP_FREE(tp->description);
00862     SNMP_FREE(tp->reference);
00863     SNMP_FREE(tp->augments);
00864     SNMP_FREE(tp->defaultValue);
00865 }
00866 
00867 /*
00868  * free a tree node. Note: the node must already have been unlinked
00869  * from the tree when calling this routine
00870  */
00871 static void
00872 free_tree(struct tree *Tree)
00873 {
00874     if (!Tree)
00875         return;
00876 
00877     unlink_tbucket(Tree);
00878     free_partial_tree(Tree, FALSE);
00879     if (Tree->number_modules > 1)
00880         free((char *) Tree->module_list);
00881     free((char *) Tree);
00882 }
00883 
00884 static void
00885 free_node(struct node *np)
00886 {
00887     if (!np)
00888         return;
00889 
00890     free_enums(&np->enums);
00891     free_ranges(&np->ranges);
00892     free_indexes(&np->indexes);
00893     free_varbinds(&np->varbinds);
00894     if (np->label)
00895         free(np->label);
00896     if (np->hint)
00897         free(np->hint);
00898     if (np->units)
00899         free(np->units);
00900     if (np->description)
00901         free(np->description);
00902     if (np->reference)
00903         free(np->reference);
00904     if (np->defaultValue)
00905         free(np->defaultValue);
00906     if (np->parent)
00907         free(np->parent);
00908     if (np->augments)
00909         free(np->augments);
00910     if (np->filename)
00911         free(np->filename);
00912     free((char *) np);
00913 }
00914 
00915 #ifdef TEST
00916 static void
00917 print_nodes(FILE * fp, struct node *root)
00918 {
00919     extern void     xmalloc_stats(FILE *);
00920     struct enum_list *ep;
00921     struct index_list *ip;
00922     struct range_list *rp;
00923     struct varbind_list *vp;
00924     struct node    *np;
00925 
00926     for (np = root; np; np = np->next) {
00927         fprintf(fp, "%s ::= { %s %ld } (%d)\n", np->label, np->parent,
00928                 np->subid, np->type);
00929         if (np->tc_index >= 0)
00930             fprintf(fp, "  TC = %s\n", tclist[np->tc_index].descriptor);
00931         if (np->enums) {
00932             fprintf(fp, "  Enums: \n");
00933             for (ep = np->enums; ep; ep = ep->next) {
00934                 fprintf(fp, "    %s(%d)\n", ep->label, ep->value);
00935             }
00936         }
00937         if (np->ranges) {
00938             fprintf(fp, "  Ranges: \n");
00939             for (rp = np->ranges; rp; rp = rp->next) {
00940                 fprintf(fp, "    %d..%d\n", rp->low, rp->high);
00941             }
00942         }
00943         if (np->indexes) {
00944             fprintf(fp, "  Indexes: \n");
00945             for (ip = np->indexes; ip; ip = ip->next) {
00946                 fprintf(fp, "    %s\n", ip->ilabel);
00947             }
00948         }
00949         if (np->augments)
00950             fprintf(fp, "  Augments: %s\n", np->augments);
00951         if (np->varbinds) {
00952             fprintf(fp, "  Varbinds: \n");
00953             for (vp = np->varbinds; vp; vp = vp->next) {
00954                 fprintf(fp, "    %s\n", vp->vblabel);
00955             }
00956         }
00957         if (np->hint)
00958             fprintf(fp, "  Hint: %s\n", np->hint);
00959         if (np->units)
00960             fprintf(fp, "  Units: %s\n", np->units);
00961         if (np->defaultValue)
00962             fprintf(fp, "  DefaultValue: %s\n", np->defaultValue);
00963     }
00964 }
00965 #endif
00966 
00967 void
00968 print_subtree(FILE * f, struct tree *tree, int count)
00969 {
00970     struct tree    *tp;
00971     int             i;
00972     char            modbuf[256];
00973 
00974     for (i = 0; i < count; i++)
00975         fprintf(f, "  ");
00976     fprintf(f, "Children of %s(%ld):\n", tree->label, tree->subid);
00977     count++;
00978     for (tp = tree->child_list; tp; tp = tp->next_peer) {
00979         for (i = 0; i < count; i++)
00980             fprintf(f, "  ");
00981         fprintf(f, "%s:%s(%ld) type=%d",
00982                 module_name(tp->module_list[0], modbuf),
00983                 tp->label, tp->subid, tp->type);
00984         if (tp->tc_index != -1)
00985             fprintf(f, " tc=%d", tp->tc_index);
00986         if (tp->hint)
00987             fprintf(f, " hint=%s", tp->hint);
00988         if (tp->units)
00989             fprintf(f, " units=%s", tp->units);
00990         if (tp->number_modules > 1) {
00991             fprintf(f, " modules:");
00992             for (i = 1; i < tp->number_modules; i++)
00993                 fprintf(f, " %s", module_name(tp->module_list[i], modbuf));
00994         }
00995         fprintf(f, "\n");
00996     }
00997     for (tp = tree->child_list; tp; tp = tp->next_peer) {
00998         if (tp->child_list)
00999             print_subtree(f, tp, count);
01000     }
01001 }
01002 
01003 void
01004 print_ascii_dump_tree(FILE * f, struct tree *tree, int count)
01005 {
01006     struct tree    *tp;
01007 
01008     count++;
01009     for (tp = tree->child_list; tp; tp = tp->next_peer) {
01010         fprintf(f, "%s OBJECT IDENTIFIER ::= { %s %ld }\n", tp->label,
01011                 tree->label, tp->subid);
01012     }
01013     for (tp = tree->child_list; tp; tp = tp->next_peer) {
01014         if (tp->child_list)
01015             print_ascii_dump_tree(f, tp, count);
01016     }
01017 }
01018 
01019 static int      translation_table[256];
01020 
01021 static void
01022 build_translation_table()
01023 {
01024     int             count;
01025 
01026     for (count = 0; count < 256; count++) {
01027         switch (count) {
01028         case OBJID:
01029             translation_table[count] = TYPE_OBJID;
01030             break;
01031         case OCTETSTR:
01032             translation_table[count] = TYPE_OCTETSTR;
01033             break;
01034         case INTEGER:
01035             translation_table[count] = TYPE_INTEGER;
01036             break;
01037         case NETADDR:
01038             translation_table[count] = TYPE_NETADDR;
01039             break;
01040         case IPADDR:
01041             translation_table[count] = TYPE_IPADDR;
01042             break;
01043         case COUNTER:
01044             translation_table[count] = TYPE_COUNTER;
01045             break;
01046         case GAUGE:
01047             translation_table[count] = TYPE_GAUGE;
01048             break;
01049         case TIMETICKS:
01050             translation_table[count] = TYPE_TIMETICKS;
01051             break;
01052         case KW_OPAQUE:
01053             translation_table[count] = TYPE_OPAQUE;
01054             break;
01055         case NUL:
01056             translation_table[count] = TYPE_NULL;
01057             break;
01058         case COUNTER64:
01059             translation_table[count] = TYPE_COUNTER64;
01060             break;
01061         case BITSTRING:
01062             translation_table[count] = TYPE_BITSTRING;
01063             break;
01064         case NSAPADDRESS:
01065             translation_table[count] = TYPE_NSAPADDRESS;
01066             break;
01067         case INTEGER32:
01068             translation_table[count] = TYPE_INTEGER32;
01069             break;
01070         case UINTEGER32:
01071             translation_table[count] = TYPE_UINTEGER;
01072             break;
01073         case UNSIGNED32:
01074             translation_table[count] = TYPE_UNSIGNED32;
01075             break;
01076         case TRAPTYPE:
01077             translation_table[count] = TYPE_TRAPTYPE;
01078             break;
01079         case NOTIFTYPE:
01080             translation_table[count] = TYPE_NOTIFTYPE;
01081             break;
01082         case NOTIFGROUP:
01083             translation_table[count] = TYPE_NOTIFGROUP;
01084             break;
01085         case OBJGROUP:
01086             translation_table[count] = TYPE_OBJGROUP;
01087             break;
01088         case MODULEIDENTITY:
01089             translation_table[count] = TYPE_MODID;
01090             break;
01091         case OBJIDENTITY:
01092             translation_table[count] = TYPE_OBJIDENTITY;
01093             break;
01094         case AGENTCAP:
01095             translation_table[count] = TYPE_AGENTCAP;
01096             break;
01097         case COMPLIANCE:
01098             translation_table[count] = TYPE_MODCOMP;
01099             break;
01100         default:
01101             translation_table[count] = TYPE_OTHER;
01102             break;
01103         }
01104     }
01105 }
01106 
01107 static void
01108 init_tree_roots()
01109 {
01110     struct tree    *tp, *lasttp;
01111     int             base_modid;
01112     int             hash;
01113 
01114     base_modid = which_module("SNMPv2-SMI");
01115     if (base_modid == -1)
01116         base_modid = which_module("RFC1155-SMI");
01117     if (base_modid == -1)
01118         base_modid = which_module("RFC1213-MIB");
01119 
01120     /*
01121      * build root node 
01122      */
01123     tp = (struct tree *) calloc(1, sizeof(struct tree));
01124     if (tp == NULL)
01125         return;
01126     tp->label = strdup("joint-iso-ccitt");
01127     tp->modid = base_modid;
01128     tp->number_modules = 1;
01129     tp->module_list = &(tp->modid);
01130     tp->subid = 2;
01131     tp->tc_index = -1;
01132     set_function(tp);           /* from mib.c */
01133     hash = NBUCKET(name_hash(tp->label));
01134     tp->next = tbuckets[hash];
01135     tbuckets[hash] = tp;
01136     lasttp = tp;
01137     root_imports[0].label = strdup(tp->label);
01138     root_imports[0].modid = base_modid;
01139 
01140     /*
01141      * build root node 
01142      */
01143     tp = (struct tree *) calloc(1, sizeof(struct tree));
01144     if (tp == NULL)
01145         return;
01146     tp->next_peer = lasttp;
01147     tp->label = strdup("ccitt");
01148     tp->modid = base_modid;
01149     tp->number_modules = 1;
01150     tp->module_list = &(tp->modid);
01151     tp->subid = 0;
01152     tp->tc_index = -1;
01153     set_function(tp);           /* from mib.c */
01154     hash = NBUCKET(name_hash(tp->label));
01155     tp->next = tbuckets[hash];
01156     tbuckets[hash] = tp;
01157     lasttp = tp;
01158     root_imports[1].label = strdup(tp->label);
01159     root_imports[1].modid = base_modid;
01160 
01161     /*
01162      * build root node 
01163      */
01164     tp = (struct tree *) calloc(1, sizeof(struct tree));
01165     if (tp == NULL)
01166         return;
01167     tp->next_peer = lasttp;
01168     tp->label = strdup("iso");
01169     tp->modid = base_modid;
01170     tp->number_modules = 1;
01171     tp->module_list = &(tp->modid);
01172     tp->subid = 1;
01173     tp->tc_index = -1;
01174     set_function(tp);           /* from mib.c */
01175     hash = NBUCKET(name_hash(tp->label));
01176     tp->next = tbuckets[hash];
01177     tbuckets[hash] = tp;
01178     lasttp = tp;
01179     root_imports[2].label = strdup(tp->label);
01180     root_imports[2].modid = base_modid;
01181 
01182     tree_head = tp;
01183 }
01184 
01185 #ifdef STRICT_MIB_PARSEING
01186 #define label_compare   strcasecmp
01187 #else
01188 #define label_compare   strcmp
01189 #endif
01190 
01191 
01192 struct tree    *
01193 find_tree_node(const char *name, int modid)
01194 {
01195     struct tree    *tp, *headtp;
01196     int             count, *int_p;
01197 
01198     if (!name || !*name)
01199         return (NULL);
01200 
01201     headtp = tbuckets[NBUCKET(name_hash(name))];
01202     for (tp = headtp; tp; tp = tp->next) {
01203         if (tp->label && !label_compare(tp->label, name)) {
01204 
01205             if (modid == -1)    /* Any module */
01206                 return (tp);
01207 
01208             for (int_p = tp->module_list, count = 0;
01209                  count < tp->number_modules; ++count, ++int_p)
01210                 if (*int_p == modid)
01211                     return (tp);
01212         }
01213     }
01214 
01215     return (NULL);
01216 }
01217 
01218 /*
01219  * computes a value which represents how close name1 is to name2.
01220  * * high scores mean a worse match.
01221  * * (yes, the algorithm sucks!)
01222  */
01223 #define MAX_BAD 0xffffff
01224 
01225 static          u_int
01226 compute_match(const char *search_base, const char *key)
01227 {
01228 #if defined(HAVE_REGEX_H) && defined(HAVE_REGCOMP)
01229     int             rc;
01230     regex_t         parsetree;
01231     regmatch_t      pmatch;
01232     rc = regcomp(&parsetree, key, REG_ICASE | REG_EXTENDED);
01233     if (rc == 0)
01234         rc = regexec(&parsetree, search_base, 1, &pmatch, 0);
01235     regfree(&parsetree);
01236     if (rc == 0) {
01237         /*
01238          * found 
01239          */
01240         return pmatch.rm_so;
01241     }
01242 #else                           /* use our own wildcard matcher */
01243     /*
01244      * first find the longest matching substring (ick) 
01245      */
01246     char           *first = NULL, *result = NULL, *entry;
01247     const char     *position;
01248     char           *newkey = strdup(key);
01249     char           *st;
01250 
01251 
01252     entry = strtok_r(newkey, "*", &st);
01253     position = search_base;
01254     while (entry) {
01255         result = strcasestr(position, entry);
01256 
01257         if (result == NULL) {
01258             free(newkey);
01259             return MAX_BAD;
01260         }
01261 
01262         if (first == NULL)
01263             first = result;
01264 
01265         position = result + strlen(entry);
01266         entry = strtok_r(NULL, "*", &st);
01267     }
01268     free(newkey);
01269     if (result)
01270         return (first - search_base);
01271 #endif
01272 
01273     /*
01274      * not found 
01275      */
01276     return MAX_BAD;
01277 }
01278 
01279 /*
01280  * Find the tree node that best matches the pattern string.
01281  * Use the "reported" flag such that only one match
01282  * is attempted for every node.
01283  *
01284  * Warning! This function may recurse.
01285  *
01286  * Caller _must_ invoke clear_tree_flags before first call
01287  * to this function.  This function may be called multiple times
01288  * to ensure that the entire tree is traversed.
01289  */
01290 
01291 struct tree    *
01292 find_best_tree_node(const char *pattrn, struct tree *tree_top,
01293                     u_int * match)
01294 {
01295     struct tree    *tp, *best_so_far = NULL, *retptr;
01296     u_int           old_match = MAX_BAD, new_match = MAX_BAD;
01297 
01298     if (!pattrn || !*pattrn)
01299         return (NULL);
01300 
01301     if (!tree_top)
01302         tree_top = get_tree_head();
01303 
01304     for (tp = tree_top; tp; tp = tp->next_peer) {
01305         if (!tp->reported && tp->label)
01306             new_match = compute_match(tp->label, pattrn);
01307         tp->reported = 1;
01308 
01309         if (new_match < old_match) {
01310             best_so_far = tp;
01311             old_match = new_match;
01312         }
01313         if (new_match == 0)
01314             break;              /* this is the best result we can get */
01315         if (tp->child_list) {
01316             retptr =
01317                 find_best_tree_node(pattrn, tp->child_list, &new_match);
01318             if (new_match < old_match) {
01319                 best_so_far = retptr;
01320                 old_match = new_match;
01321             }
01322             if (new_match == 0)
01323                 break;          /* this is the best result we can get */
01324         }
01325     }
01326     if (match)
01327         *match = old_match;
01328     return (best_so_far);
01329 }
01330 
01331 
01332 static void
01333 merge_anon_children(struct tree *tp1, struct tree *tp2)
01334                 /*
01335                  * NB: tp1 is the 'anonymous' node 
01336                  */
01337 {
01338     struct tree    *child1, *child2, *previous;
01339 
01340     for (child1 = tp1->child_list; child1;) {
01341 
01342         for (child2 = tp2->child_list, previous = NULL;
01343              child2; previous = child2, child2 = child2->next_peer) {
01344 
01345             if (child1->subid == child2->subid) {
01346                 /*
01347                  * Found 'matching' children,
01348                  *  so merge them
01349                  */
01350                 if (!strncmp(child1->label, ANON, ANON_LEN)) {
01351                     merge_anon_children(child1, child2);
01352 
01353                     child1->child_list = NULL;
01354                     previous = child1;  /* Finished with 'child1' */
01355                     child1 = child1->next_peer;
01356                     free_tree(previous);
01357                     goto next;
01358                 }
01359 
01360                 else if (!strncmp(child2->label, ANON, ANON_LEN)) {
01361                     merge_anon_children(child2, child1);
01362 
01363                     if (previous)
01364                         previous->next_peer = child2->next_peer;
01365                     else
01366                         tp2->child_list = child2->next_peer;
01367                     free_tree(child2);
01368 
01369                     previous = child1;  /* Move 'child1' to 'tp2' */
01370                     child1 = child1->next_peer;
01371                     previous->next_peer = tp2->child_list;
01372                     tp2->child_list = previous;
01373                     for (previous = tp2->child_list;
01374                          previous; previous = previous->next_peer)
01375                         previous->parent = tp2;
01376                     goto next;
01377                 } else if (!label_compare(child1->label, child2->label)) {
01378                     if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
01379                                            NETSNMP_DS_LIB_MIB_WARNINGS)) {
01380                         snmp_log(LOG_WARNING,
01381                                  "Warning: %s.%ld is both %s and %s (%s)\n",
01382                                  tp2->label, child1->subid, child1->label,
01383                                  child2->label, File);
01384                     }
01385                     continue;
01386                 } else {
01387                     /*
01388                      * Two copies of the same node.
01389                      * 'child2' adopts the children of 'child1'
01390                      */
01391 
01392                     if (child2->child_list) {
01393                         for (previous = child2->child_list; previous->next_peer; previous = previous->next_peer);       /* Find the end of the list */
01394                         previous->next_peer = child1->child_list;
01395                     } else
01396                         child2->child_list = child1->child_list;
01397                     for (previous = child1->child_list;
01398                          previous; previous = previous->next_peer)
01399                         previous->parent = child2;
01400                     child1->child_list = NULL;
01401 
01402                     previous = child1;  /* Finished with 'child1' */
01403                     child1 = child1->next_peer;
01404                     free_tree(previous);
01405                     goto next;
01406                 }
01407             }
01408         }
01409         /*
01410          * If no match, move 'child1' to 'tp2' child_list
01411          */
01412         if (child1) {
01413             previous = child1;
01414             child1 = child1->next_peer;
01415             previous->parent = tp2;
01416             previous->next_peer = tp2->child_list;
01417             tp2->child_list = previous;
01418         }
01419       next:;
01420     }
01421 }
01422 
01423 
01424 /*
01425  * Find all the children of root in the list of nodes.  Link them into the
01426  * tree and out of the nodes list.
01427  */
01428 static void
01429 do_subtree(struct tree *root, struct node **nodes)
01430 {
01431     struct tree    *tp, *anon_tp = NULL;
01432     struct tree    *xroot = root;
01433     struct node    *np, **headp;
01434     struct node    *oldnp = NULL, *child_list = NULL, *childp = NULL;
01435     int             hash;
01436     int            *int_p;
01437 
01438     while (xroot->next_peer && xroot->next_peer->subid == root->subid) {
01439 #if 0
01440         printf("xroot: %s.%s => %s\n", xroot->parent->label, xroot->label,
01441                xroot->next_peer->label);
01442 #endif
01443         xroot = xroot->next_peer;
01444     }
01445 
01446     tp = root;
01447     headp = &nbuckets[NBUCKET(name_hash(tp->label))];
01448     /*
01449      * Search each of the nodes for one whose parent is root, and
01450      * move each into a separate list.
01451      */
01452     for (np = *headp; np; np = np->next) {
01453         if (!label_compare(tp->label, np->parent)) {
01454             /*
01455              * take this node out of the node list 
01456              */
01457             if (oldnp == NULL) {
01458                 *headp = np->next;      /* fix root of node list */
01459             } else {
01460                 oldnp->next = np->next; /* link around this node */
01461             }
01462             if (child_list)
01463                 childp->next = np;
01464             else
01465                 child_list = np;
01466             childp = np;
01467         } else {
01468             oldnp = np;
01469         }
01470 
01471     }
01472     if (childp)
01473         childp->next = NULL;
01474     /*
01475      * Take each element in the child list and place it into the tree.
01476      */
01477     for (np = child_list; np; np = np->next) {
01478         struct tree    *otp = NULL;
01479         struct tree    *xxroot = xroot;
01480         anon_tp = NULL;
01481         tp = xroot->child_list;
01482 
01483         if (np->subid == -1) {
01484             /*
01485              * name ::= { parent } 
01486              */
01487             np->subid = xroot->subid;
01488             tp = xroot;
01489             xxroot = xroot->parent;
01490         }
01491 
01492         while (tp) {
01493             if (tp->subid == np->subid)
01494                 break;
01495             else {
01496                 otp = tp;
01497                 tp = tp->next_peer;
01498             }
01499         }
01500         if (tp) {
01501             if (!label_compare(tp->label, np->label)) {
01502                 /*
01503                  * Update list of modules 
01504                  */
01505                 int_p =
01506                     (int *) malloc((tp->number_modules + 1) * sizeof(int));
01507                 if (int_p == NULL)
01508                     return;
01509                 memcpy(int_p, tp->module_list,
01510                        tp->number_modules * sizeof(int));
01511                 int_p[tp->number_modules] = np->modid;
01512                 if (tp->number_modules > 1)
01513                     free((char *) tp->module_list);
01514                 ++tp->number_modules;
01515                 tp->module_list = int_p;
01516 
01517                 if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
01518                                            NETSNMP_DS_LIB_MIB_REPLACE)) {
01519                     /*
01520                      * Replace from node 
01521                      */
01522                     tree_from_node(tp, np);
01523                 }
01524                 /*
01525                  * Handle children 
01526                  */
01527                 do_subtree(tp, nodes);
01528                 continue;
01529             }
01530             if (!strncmp(np->label, ANON, ANON_LEN) ||
01531                 !strncmp(tp->label, ANON, ANON_LEN)) {
01532                 anon_tp = tp;   /* Need to merge these two trees later */
01533             } else if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
01534                                           NETSNMP_DS_LIB_MIB_WARNINGS)) {
01535                 snmp_log(LOG_WARNING,
01536                          "Warning: %s.%ld is both %s and %s (%s)\n",
01537                          root->label, np->subid, tp->label, np->label,
01538                          File);
01539             }
01540         }
01541 
01542         tp = (struct tree *) calloc(1, sizeof(struct tree));
01543         if (tp == NULL)
01544             return;
01545         tp->parent = xxroot;
01546         tp->modid = np->modid;
01547         tp->number_modules = 1;
01548         tp->module_list = &(tp->modid);
01549         tree_from_node(tp, np);
01550         tp->next_peer = otp ? otp->next_peer : xxroot->child_list;
01551         if (otp)
01552             otp->next_peer = tp;
01553         else
01554             xxroot->child_list = tp;
01555         hash = NBUCKET(name_hash(tp->label));
01556         tp->next = tbuckets[hash];
01557         tbuckets[hash] = tp;
01558         do_subtree(tp, nodes);
01559 
01560         if (anon_tp) {
01561             if (!strncmp(tp->label, ANON, ANON_LEN)) {
01562                 /*
01563                  * The new node is anonymous,
01564                  *  so merge it with the existing one.
01565                  */
01566                 merge_anon_children(tp, anon_tp);
01567 
01568                 /*
01569                  * unlink and destroy tp 
01570                  */
01571                 unlink_tree(tp);
01572                 free_tree(tp);
01573             } else if (!strncmp(anon_tp->label, ANON, ANON_LEN)) {
01574                 struct tree    *ntp;
01575                 /*
01576                  * The old node was anonymous,
01577                  *  so merge it with the existing one,
01578                  *  and fill in the full information.
01579                  */
01580                 merge_anon_children(anon_tp, tp);
01581 
01582                 /*
01583                  * unlink anon_tp from the hash 
01584                  */
01585                 unlink_tbucket(anon_tp);
01586 
01587                 /*
01588                  * get rid of old contents of anon_tp 
01589                  */
01590                 free_partial_tree(anon_tp, FALSE);
01591 
01592                 /*
01593                  * put in the current information 
01594                  */
01595                 anon_tp->label = tp->label;
01596                 anon_tp->child_list = tp->child_list;
01597                 anon_tp->modid = tp->modid;
01598                 anon_tp->tc_index = tp->tc_index;
01599                 anon_tp->type = tp->type;
01600                 anon_tp->enums = tp->enums;
01601                 anon_tp->indexes = tp->indexes;
01602                 anon_tp->augments = tp->augments;
01603                 anon_tp->varbinds = tp->varbinds;
01604                 anon_tp->ranges = tp->ranges;
01605                 anon_tp->hint = tp->hint;
01606                 anon_tp->units = tp->units;
01607                 anon_tp->description = tp->description;
01608                 anon_tp->reference = tp->reference;
01609                 anon_tp->defaultValue = tp->defaultValue;
01610                 anon_tp->parent = tp->parent;
01611 
01612                 set_function(anon_tp);
01613 
01614                 /*
01615                  * update parent pointer in moved children 
01616                  */
01617                 ntp = anon_tp->child_list;
01618                 while (ntp) {
01619                     ntp->parent = anon_tp;
01620                     ntp = ntp->next_peer;
01621                 }
01622 
01623                 /*
01624                  * hash in anon_tp in its new place 
01625                  */
01626                 hash = NBUCKET(name_hash(anon_tp->label));
01627                 anon_tp->next = tbuckets[hash];
01628                 tbuckets[hash] = anon_tp;
01629 
01630                 /*
01631                  * unlink and destroy tp 
01632                  */
01633                 unlink_tbucket(tp);
01634                 unlink_tree(tp);
01635                 free(tp);
01636             } else {
01637                 /*
01638                  * Uh?  One of these two should have been anonymous! 
01639                  */
01640                 if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
01641                                        NETSNMP_DS_LIB_MIB_WARNINGS)) {
01642                     snmp_log(LOG_WARNING,
01643                              "Warning: expected anonymous node (either %s or %s) in %s\n",
01644                              tp->label, anon_tp->label, File);
01645                 }
01646             }
01647             anon_tp = NULL;
01648         }
01649     }
01650     /*
01651      * free all nodes that were copied into tree 
01652      */
01653     oldnp = NULL;
01654     for (np = child_list; np; np = np->next) {
01655         if (oldnp)
01656             free_node(oldnp);
01657         oldnp = np;
01658     }
01659     if (oldnp)
01660         free_node(oldnp);
01661 }
01662 
01663 static void
01664 do_linkup(struct module *mp, struct node *np)
01665 {
01666     struct module_import *mip;
01667     struct node    *onp, *oldp, *newp;
01668     struct tree    *tp;
01669     int             i, more;
01670     /*
01671      * All modules implicitly import
01672      *   the roots of the tree
01673      */
01674     if (snmp_get_do_debugging() > 1)
01675         dump_module_list();
01676     DEBUGMSGTL(("parse-mibs", "Processing IMPORTS for module %d %s\n",
01677                 mp->modid, mp->name));
01678     if (mp->no_imports == 0) {
01679         mp->no_imports = NUMBER_OF_ROOT_NODES;
01680         mp->imports = root_imports;
01681     }
01682 
01683     /*
01684      * Build the tree
01685      */
01686     init_node_hash(np);
01687     for (i = 0, mip = mp->imports; i < mp->no_imports; ++i, ++mip) {
01688         char            modbuf[256];
01689         DEBUGMSGTL(("parse-mibs", "  Processing import: %s\n",
01690                     mip->label));
01691         if (get_tc_index(mip->label, mip->modid) != -1)
01692             continue;
01693         tp = find_tree_node(mip->label, mip->modid);
01694         if (!tp) {
01695                 snmp_log(LOG_WARNING,
01696                          "Did not find '%s' in module %s (%s)\n",
01697                          mip->label, module_name(mip->modid, modbuf),
01698                          File);
01699             continue;
01700         }
01701         do_subtree(tp, &np);
01702     }
01703 
01704     /*
01705      * If any nodes left over,
01706      *   check that they're not the result of a "fully qualified"
01707      *   name, and then add them to the list of orphans
01708      */
01709 
01710     if (!np)
01711         return;
01712     for (tp = tree_head; tp; tp = tp->next_peer)
01713         do_subtree(tp, &np);
01714     if (!np)
01715         return;
01716 
01717     /*
01718      * quietly move all internal references to the orphan list 
01719      */
01720     oldp = orphan_nodes;
01721     do {
01722         for (i = 0; i < NHASHSIZE; i++)
01723             for (onp = nbuckets[i]; onp; onp = onp->next) {
01724                 struct node    *op = NULL;
01725                 int             hash = NBUCKET(name_hash(onp->label));
01726                 np = nbuckets[hash];
01727                 while (np) {
01728                     if (label_compare(onp->label, np->parent)) {
01729                         op = np;
01730                         np = np->next;
01731                     } else {
01732                         if (op)
01733                             op->next = np->next;
01734                         else
01735                             nbuckets[hash] = np->next;
01736                         np->next = orphan_nodes;
01737                         orphan_nodes = np;
01738                         op = NULL;
01739                         np = nbuckets[hash];
01740                     }
01741                 }
01742             }
01743         newp = orphan_nodes;
01744         more = 0;
01745         for (onp = orphan_nodes; onp != oldp; onp = onp->next) {
01746             struct node    *op = NULL;
01747             int             hash = NBUCKET(name_hash(onp->label));
01748             np = nbuckets[hash];
01749             while (np) {
01750                 if (label_compare(onp->label, np->parent)) {
01751                     op = np;
01752                     np = np->next;
01753                 } else {
01754                     if (op)
01755                         op->next = np->next;
01756                     else
01757                         nbuckets[hash] = np->next;
01758                     np->next = orphan_nodes;
01759                     orphan_nodes = np;
01760                     op = NULL;
01761                     np = nbuckets[hash];
01762                     more = 1;
01763                 }
01764             }
01765         }
01766         oldp = newp;
01767     } while (more);
01768 
01769     /*
01770      * complain about left over nodes 
01771      */
01772     for (np = orphan_nodes; np && np->next; np = np->next);     /* find the end of the orphan list */
01773     for (i = 0; i < NHASHSIZE; i++)
01774         if (nbuckets[i]) {
01775             if (orphan_nodes)
01776                 onp = np->next = nbuckets[i];
01777             else
01778                 onp = orphan_nodes = nbuckets[i];
01779             nbuckets[i] = NULL;
01780             while (onp) {
01781                 snmp_log(LOG_WARNING,
01782                          "Unlinked OID in %s: %s ::= { %s %ld }\n",
01783                          (mp->name ? mp->name : "<no module>"),
01784                          (onp->label ? onp->label : "<no label>"),
01785                          (onp->parent ? onp->parent : "<no parent>"),
01786                          onp->subid);
01787                  snmp_log(LOG_WARNING,
01788                           "Undefined identifier: %s near line %d of %s\n",
01789                           (onp->parent ? onp->parent : "<no parent>"),
01790                           onp->lineno, onp->filename);
01791                 np = onp;
01792                 onp = onp->next;
01793             }
01794         }
01795     return;
01796 }
01797 
01798 
01799 /*
01800  * Takes a list of the form:
01801  * { iso org(3) dod(6) 1 }
01802  * and creates several nodes, one for each parent-child pair.
01803  * Returns 0 on error.
01804  */
01805 static int
01806 getoid(FILE * fp, struct subid_s *id,   /* an array of subids */
01807        int length)
01808 {                               /* the length of the array */
01809     register int    count;
01810     int             type;
01811     char            token[MAXTOKEN];
01812 
01813     if ((type = get_token(fp, token, MAXTOKEN)) != LEFTBRACKET) {
01814         print_error("Expected \"{\"", token, type);
01815         return 0;
01816     }
01817     type = get_token(fp, token, MAXTOKEN);
01818     for (count = 0; count < length; count++, id++) {
01819         id->label = NULL;
01820         id->modid = current_module;
01821         id->subid = -1;
01822         if (type == RIGHTBRACKET)
01823             return count;
01824         if (type == LABEL) {
01825             /*
01826              * this entry has a label 
01827              */
01828             id->label = strdup(token);
01829             type = get_token(fp, token, MAXTOKEN);
01830             if (type == LEFTPAREN) {
01831                 type = get_token(fp, token, MAXTOKEN);
01832                 if (type == NUMBER) {
01833                     id->subid = strtoul(token, NULL, 10);
01834                     if ((type =
01835                          get_token(fp, token, MAXTOKEN)) != RIGHTPAREN) {
01836                         print_error("Expected a closing parenthesis",
01837                                     token, type);
01838                         return 0;
01839                     }
01840                 } else {
01841                     print_error("Expected a number", token, type);
01842                     return 0;
01843                 }
01844             } else {
01845                 continue;
01846             }
01847         } else if (type == NUMBER) {
01848             /*
01849              * this entry  has just an integer sub-identifier 
01850              */
01851             id->subid = strtoul(token, NULL, 10);
01852         } else {
01853             print_error("Expected label or number", token, type);
01854             return 0;
01855         }
01856         type = get_token(fp, token, MAXTOKEN);
01857     }
01858     print_error("Too long OID", token, type);
01859     return 0;
01860 }
01861 
01862 /*
01863  * Parse a sequence of object subidentifiers for the given name.
01864  * The "label OBJECT IDENTIFIER ::=" portion has already been parsed.
01865  *
01866  * The majority of cases take this form :
01867  * label OBJECT IDENTIFIER ::= { parent 2 }
01868  * where a parent label and a child subidentifier number are specified.
01869  *
01870  * Variations on the theme include cases where a number appears with
01871  * the parent, or intermediate subidentifiers are specified by label,
01872  * by number, or both.
01873  *
01874  * Here are some representative samples :
01875  * internet        OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 }
01876  * mgmt            OBJECT IDENTIFIER ::= { internet 2 }
01877  * rptrInfoHealth  OBJECT IDENTIFIER ::= { snmpDot3RptrMgt 0 4 }
01878  *
01879  * Here is a very rare form :
01880  * iso             OBJECT IDENTIFIER ::= { 1 }
01881  *
01882  * Returns NULL on error.  When this happens, memory may be leaked.
01883  */
01884 static struct node *
01885 parse_objectid(FILE * fp, char *name)
01886 {
01887     register int    count;
01888     register struct subid_s *op, *nop;
01889     int             length;
01890     struct subid_s  loid[32];
01891     struct node    *np, *root = NULL, *oldnp = NULL;
01892     struct tree    *tp;
01893 
01894     if ((length = getoid(fp, loid, 32)) == 0) {
01895         print_error("Bad object identifier", NULL, CONTINUE);
01896         return NULL;
01897     }
01898 
01899     /*
01900      * Handle numeric-only object identifiers,
01901      *  by labelling the first sub-identifier
01902      */
01903     op = loid;
01904     if (!op->label) {
01905         if (length == 1) {
01906             print_error("Attempt to define a root oid", name, OBJECT);
01907             return NULL;
01908         }
01909         for (tp = tree_head; tp; tp = tp->next_peer)
01910             if ((int) tp->subid == op->subid) {
01911                 op->label = strdup(tp->label);
01912                 break;
01913             }
01914     }
01915 
01916     /*
01917      * Handle  "label OBJECT-IDENTIFIER ::= { subid }"
01918      */
01919     if (length == 1) {
01920         op = loid;
01921         np = alloc_node(op->modid);
01922         if (np == NULL)
01923             return (NULL);
01924         np->subid = op->subid;
01925         np->label = strdup(name);
01926         np->parent = op->label;
01927         return np;
01928     }
01929 
01930     /*
01931      * For each parent-child subid pair in the subid array,
01932      * create a node and link it into the node list.
01933      */
01934     for (count = 0, op = loid, nop = loid + 1; count < (length - 1);
01935          count++, op++, nop++) {
01936         /*
01937          * every node must have parent's name and child's name or number 
01938          */
01939         /*
01940          * XX the next statement is always true -- does it matter ?? 
01941          */
01942         if (op->label && (nop->label || (nop->subid != -1))) {
01943             np = alloc_node(nop->modid);
01944             if (np == NULL)
01945                 return (NULL);
01946             if (root == NULL)
01947                 root = np;
01948 
01949             np->parent = strdup(op->label);
01950             if (count == (length - 2)) {
01951                 /*
01952                  * The name for this node is the label for this entry 
01953                  */
01954                 np->label = strdup(name);
01955             } else {
01956                 if (!nop->label) {
01957                     nop->label = (char *) malloc(20 + ANON_LEN);
01958                     if (nop->label == NULL)
01959                         return (NULL);
01960                     sprintf(nop->label, "%s%d", ANON, anonymous++);
01961                 }
01962                 np->label = strdup(nop->label);
01963             }
01964             if (nop->subid != -1)
01965                 np->subid = nop->subid;
01966             else
01967                 print_error("Warning: This entry is pretty silly",
01968                             np->label, CONTINUE);
01969 
01970             /*
01971              * set up next entry 
01972              */
01973             if (oldnp)
01974                 oldnp->next = np;
01975             oldnp = np;
01976         }                       /* end if(op->label... */
01977     }
01978 
01979     /*
01980      * free the loid array 
01981      */
01982     for (count = 0, op = loid; count < length; count++, op++) {
01983         if (op->label)
01984             free(op->label);
01985     }
01986 
01987     return root;
01988 }
01989 
01990 static int
01991 get_tc(const char *descriptor,
01992        int modid,
01993        int *tc_index,
01994        struct enum_list **ep, struct range_list **rp, char **hint)
01995 {
01996     int             i;
01997     struct tc      *tcp;
01998 
01999     i = get_tc_index(descriptor, modid);
02000     if (tc_index)
02001         *tc_index = i;
02002     if (i != -1) {
02003         tcp = &tclist[i];
02004         if (ep) {
02005             free_enums(ep);
02006             *ep = copy_enums(tcp->enums);
02007         }
02008         if (rp) {
02009             free_ranges(rp);
02010             *rp = copy_ranges(tcp->ranges);
02011         }
02012         if (hint) {
02013             if (*hint)
02014                 free(*hint);
02015             *hint = (tcp->hint ? strdup(tcp->hint) : NULL);
02016         }
02017         return tcp->type;
02018     }
02019     return LABEL;
02020 }
02021 
02022 /*
02023  * return index into tclist of given TC descriptor
02024  * return -1 if not found
02025  */
02026 static int
02027 get_tc_index(const char *descriptor, int modid)
02028 {
02029     int             i;
02030     struct tc      *tcp;
02031     struct module  *mp;
02032     struct module_import *mip;
02033 
02034     /*
02035      * Check that the descriptor isn't imported
02036      *  by searching the import list
02037      */
02038 
02039     for (mp = module_head; mp; mp = mp->next)
02040         if (mp->modid == modid)
02041             break;
02042     if (mp)
02043         for (i = 0, mip = mp->imports; i < mp->no_imports; ++i, ++mip) {
02044             if (!label_compare(mip->label, descriptor)) {
02045                 /*
02046                  * Found it - so amend the module ID 
02047                  */
02048                 modid = mip->modid;
02049                 break;
02050             }
02051         }
02052 
02053 
02054     for (i = 0, tcp = tclist; i < MAXTC; i++, tcp++) {
02055         if (tcp->type == 0)
02056             break;
02057         if (!label_compare(descriptor, tcp->descriptor) &&
02058             ((modid == tcp->modid) || (modid == -1))) {
02059             return i;
02060         }
02061     }
02062     return -1;
02063 }
02064 
02065 /*
02066  * translate integer tc_index to string identifier from tclist
02067  * *
02068  * * Returns pointer to string in table (should not be modified) or NULL
02069  */
02070 const char     *
02071 get_tc_descriptor(int tc_index)
02072 {
02073     if (tc_index < 0 || tc_index >= MAXTC)
02074         return NULL;
02075     return (tclist[tc_index].descriptor);
02076 }
02077 
02078 const char     *
02079 get_tc_description(int tc_index)
02080 {
02081     if (tc_index < 0 || tc_index >= MAXTC)
02082         return NULL;
02083     return (tclist[tc_index].description);
02084 }
02085 
02086 
02087 /*
02088  * Parses an enumeration list of the form:
02089  *        { label(value) label(value) ... }
02090  * The initial { has already been parsed.
02091  * Returns NULL on error.
02092  */
02093 
02094 static struct enum_list *
02095 parse_enumlist(FILE * fp, struct enum_list **retp)
02096 {
02097     register int    type;
02098     char            token[MAXTOKEN];
02099     struct enum_list *ep = NULL, **epp = &ep;
02100 
02101     free_enums(retp);
02102 
02103     while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE) {
02104         if (type == RIGHTBRACKET)
02105             break;
02106         /* some enums use "deprecated" to indicate a no longer value label */
02107         /* (EG: IP-MIB's IpAddressStatusTC) */
02108         if (type == LABEL || type == DEPRECATED) {
02109             /*
02110              * this is an enumerated label 
02111              */
02112             *epp =
02113                 (struct enum_list *) calloc(1, sizeof(struct enum_list));
02114             if (*epp == NULL)
02115                 return (NULL);
02116             /*
02117              * a reasonable approximation for the length 
02118              */
02119             (*epp)->label = strdup(token);
02120             type = get_token(fp, token, MAXTOKEN);
02121             if (type != LEFTPAREN) {
02122                 print_error("Expected \"(\"", token, type);
02123                 return NULL;
02124             }
02125             type = get_token(fp, token, MAXTOKEN);
02126             if (type != NUMBER) {
02127                 print_error("Expected integer", token, type);
02128                 return NULL;
02129             }
02130             (*epp)->value = strtol(token, NULL, 10);
02131             type = get_token(fp, token, MAXTOKEN);
02132             if (type != RIGHTPAREN) {
02133                 print_error("Expected \")\"", token, type);
02134                 return NULL;
02135             }
02136             epp = &(*epp)->next;
02137         }
02138     }
02139     if (type == ENDOFFILE) {
02140         print_error("Expected \"}\"", token, type);
02141         return NULL;
02142     }
02143     *retp = ep;
02144     return ep;
02145 }
02146 
02147 static struct range_list *
02148 parse_ranges(FILE * fp, struct range_list **retp)
02149 {
02150     int             low, high;
02151     char            nexttoken[MAXTOKEN];
02152     int             nexttype;
02153     struct range_list *rp = NULL, **rpp = &rp;
02154     int             size = 0, taken = 1;
02155 
02156     free_ranges(retp);
02157 
02158     nexttype = get_token(fp, nexttoken, MAXTOKEN);
02159     if (nexttype == SIZE) {
02160         size = 1;
02161         taken = 0;
02162         nexttype = get_token(fp, nexttoken, MAXTOKEN);
02163         if (nexttype != LEFTPAREN)
02164             print_error("Expected \"(\" after SIZE", nexttoken, nexttype);
02165     }
02166 
02167     do {
02168         if (!taken)
02169             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02170         else
02171             taken = 0;
02172         high = low = strtol(nexttoken, NULL, 10);
02173         nexttype = get_token(fp, nexttoken, MAXTOKEN);
02174         if (nexttype == RANGE) {
02175             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02176             errno = 0;
02177             high = strtol(nexttoken, NULL, 10);
02178             if ( errno == ERANGE ) {
02179                 if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
02180                                        NETSNMP_DS_LIB_MIB_WARNINGS))
02181                     snmp_log(LOG_WARNING,
02182                              "Warning: Upper bound not handled correctly (%s != %d): At line %d in %s\n",
02183                                  nexttoken, high, mibLine, File);
02184             }
02185             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02186         }
02187         *rpp = (struct range_list *) calloc(1, sizeof(struct range_list));
02188         if (*rpp == NULL)
02189             break;
02190         (*rpp)->low = low;
02191         (*rpp)->high = high;
02192         rpp = &(*rpp)->next;
02193 
02194     } while (nexttype == BAR);
02195     if (size) {
02196         if (nexttype != RIGHTPAREN)
02197             print_error("Expected \")\" after SIZE", nexttoken, nexttype);
02198         nexttype = get_token(fp, nexttoken, nexttype);
02199     }
02200     if (nexttype != RIGHTPAREN)
02201         print_error("Expected \")\"", nexttoken, nexttype);
02202 
02203     *retp = rp;
02204     return rp;
02205 }
02206 
02207 /*
02208  * Parses an asn type.  Structures are ignored by this parser.
02209  * Returns NULL on error.
02210  */
02211 static struct node *
02212 parse_asntype(FILE * fp, char *name, int *ntype, char *ntoken)
02213 {
02214     int             type, i;
02215     char            token[MAXTOKEN];
02216     char            quoted_string_buffer[MAXQUOTESTR];
02217     char           *hint = NULL;
02218     char           *descr = NULL;
02219     struct tc      *tcp;
02220     int             level;
02221 
02222     type = get_token(fp, token, MAXTOKEN);
02223     if (type == SEQUENCE || type == CHOICE) {
02224         level = 0;
02225         while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE) {
02226             if (type == LEFTBRACKET) {
02227                 level++;
02228             } else if (type == RIGHTBRACKET && --level == 0) {
02229                 *ntype = get_token(fp, ntoken, MAXTOKEN);
02230                 return NULL;
02231             }
02232         }
02233         print_error("Expected \"}\"", token, type);
02234         return NULL;
02235     } else if (type == LEFTBRACKET) {
02236         struct node    *np;
02237         int             ch_next = '{';
02238         ungetc(ch_next, fp);
02239         np = parse_objectid(fp, name);
02240         if (np != NULL) {
02241             *ntype = get_token(fp, ntoken, MAXTOKEN);
02242             return np;
02243         }
02244         return NULL;
02245     } else if (type == LEFTSQBRACK) {
02246         int             size = 0;
02247         do {
02248             type = get_token(fp, token, MAXTOKEN);
02249         } while (type != ENDOFFILE && type != RIGHTSQBRACK);
02250         if (type != RIGHTSQBRACK) {
02251             print_error("Expected \"]\"", token, type);
02252             return NULL;
02253         }
02254         type = get_token(fp, token, MAXTOKEN);
02255         if (type == IMPLICIT)
02256             type = get_token(fp, token, MAXTOKEN);
02257         *ntype = get_token(fp, ntoken, MAXTOKEN);
02258         if (*ntype == LEFTPAREN) {
02259             switch (type) {
02260             case OCTETSTR:
02261                 *ntype = get_token(fp, ntoken, MAXTOKEN);
02262                 if (*ntype != SIZE) {
02263                     print_error("Expected SIZE", ntoken, *ntype);
02264                     return NULL;
02265                 }
02266                 size = 1;
02267                 *ntype = get_token(fp, ntoken, MAXTOKEN);
02268                 if (*ntype != LEFTPAREN) {
02269                     print_error("Expected \"(\" after SIZE", ntoken,
02270                                 *ntype);
02271                     return NULL;
02272                 }
02273                 /*
02274                  * fall through 
02275                  */
02276             case INTEGER:
02277                 *ntype = get_token(fp, ntoken, MAXTOKEN);
02278                 do {
02279                     if (*ntype != NUMBER)
02280                         print_error("Expected NUMBER", ntoken, *ntype);
02281                     *ntype = get_token(fp, ntoken, MAXTOKEN);
02282                     if (*ntype == RANGE) {
02283                         *ntype = get_token(fp, ntoken, MAXTOKEN);
02284                         if (*ntype != NUMBER)
02285                             print_error("Expected NUMBER", ntoken, *ntype);
02286                         *ntype = get_token(fp, ntoken, MAXTOKEN);
02287                     }
02288                 } while (*ntype == BAR);
02289                 if (*ntype != RIGHTPAREN) {
02290                     print_error("Expected \")\"", ntoken, *ntype);
02291                     return NULL;
02292                 }
02293                 *ntype = get_token(fp, ntoken, MAXTOKEN);
02294                 if (size) {
02295                     if (*ntype != RIGHTPAREN) {
02296                         print_error("Expected \")\" to terminate SIZE",
02297                                     ntoken, *ntype);
02298                         return NULL;
02299                     }
02300                     *ntype = get_token(fp, ntoken, MAXTOKEN);
02301                 }
02302             }
02303         }
02304         return NULL;
02305     } else {
02306         if (type == CONVENTION) {
02307             while (type != SYNTAX && type != ENDOFFILE) {
02308                 if (type == DISPLAYHINT) {
02309                     type = get_token(fp, token, MAXTOKEN);
02310                     if (type != QUOTESTRING)
02311                         print_error("DISPLAY-HINT must be string", token,
02312                                     type);
02313                     else
02314                         hint = strdup(token);
02315                 } else if (type == DESCRIPTION &&
02316                            netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
02317                                                   NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
02318                     type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02319                     if (type != QUOTESTRING)
02320                         print_error("DESCRIPTION must be string", token,
02321                                     type);
02322                     else
02323                         descr = strdup(quoted_string_buffer);
02324                 } else
02325                     type =
02326                         get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02327             }
02328             type = get_token(fp, token, MAXTOKEN);
02329             if (type == OBJECT) {
02330                 type = get_token(fp, token, MAXTOKEN);
02331                 if (type != IDENTIFIER) {
02332                     print_error("Expected IDENTIFIER", token, type);
02333                     SNMP_FREE(hint);
02334                     return NULL;
02335                 }
02336                 type = OBJID;
02337             }
02338         } else if (type == OBJECT) {
02339             type = get_token(fp, token, MAXTOKEN);
02340             if (type != IDENTIFIER) {
02341                 print_error("Expected IDENTIFIER", token, type);
02342                 return NULL;
02343             }
02344             type = OBJID;
02345         }
02346 
02347         if (type == LABEL) {
02348             type = get_tc(token, current_module, NULL, NULL, NULL, NULL);
02349         }
02350 
02351         /*
02352          * textual convention 
02353          */
02354         for (i = 0; i < MAXTC; i++) {
02355             if (tclist[i].type == 0)
02356                 break;
02357         }
02358 
02359         if (i == MAXTC) {
02360             print_error("Too many textual conventions", token, type);
02361             SNMP_FREE(hint);
02362             return NULL;
02363         }
02364         if (!(type & SYNTAX_MASK)) {
02365             print_error("Textual convention doesn't map to real type",
02366                         token, type);
02367             SNMP_FREE(hint);
02368             return NULL;
02369         }
02370         tcp = &tclist[i];
02371         tcp->modid = current_module;
02372         tcp->descriptor = strdup(name);
02373         tcp->hint = hint;
02374         tcp->description = descr;
02375         tcp->type = type;
02376         *ntype = get_token(fp, ntoken, MAXTOKEN);
02377         if (*ntype == LEFTPAREN) {
02378             tcp->ranges = parse_ranges(fp, &tcp->ranges);
02379             *ntype = get_token(fp, ntoken, MAXTOKEN);
02380         } else if (*ntype == LEFTBRACKET) {
02381             /*
02382              * if there is an enumeration list, parse it 
02383              */
02384             tcp->enums = parse_enumlist(fp, &tcp->enums);
02385             *ntype = get_token(fp, ntoken, MAXTOKEN);
02386         }
02387         return NULL;
02388     }
02389 }
02390 
02391 
02392 /*
02393  * Parses an OBJECT TYPE macro.
02394  * Returns 0 on error.
02395  */
02396 static struct node *
02397 parse_objecttype(FILE * fp, char *name)
02398 {
02399     register int    type;
02400     char            token[MAXTOKEN];
02401     char            nexttoken[MAXTOKEN];
02402     char            quoted_string_buffer[MAXQUOTESTR];
02403     int             nexttype, tctype;
02404     register struct node *np;
02405 
02406     type = get_token(fp, token, MAXTOKEN);
02407     if (type != SYNTAX) {
02408         print_error("Bad format for OBJECT-TYPE", token, type);
02409         return NULL;
02410     }
02411     np = alloc_node(current_module);
02412     if (np == NULL)
02413         return (NULL);
02414     type = get_token(fp, token, MAXTOKEN);
02415     if (type == OBJECT) {
02416         type = get_token(fp, token, MAXTOKEN);
02417         if (type != IDENTIFIER) {
02418             print_error("Expected IDENTIFIER", token, type);
02419             free_node(np);
02420             return NULL;
02421         }
02422         type = OBJID;
02423     }
02424     if (type == LABEL) {
02425         int             tmp_index;
02426         tctype = get_tc(token, current_module, &tmp_index,
02427                         &np->enums, &np->ranges, &np->hint);
02428         if (tctype == LABEL &&
02429             netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
02430                                NETSNMP_DS_LIB_MIB_WARNINGS) > 1) {
02431             print_error("Warning: No known translation for type", token,
02432                         type);
02433         }
02434         type = tctype;
02435         np->tc_index = tmp_index;       /* store TC for later reference */
02436     }
02437     np->type = type;
02438     nexttype = get_token(fp, nexttoken, MAXTOKEN);
02439     switch (type) {
02440     case SEQUENCE:
02441         if (nexttype == OF) {
02442             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02443             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02444 
02445         }
02446         break;
02447     case INTEGER:
02448     case INTEGER32:
02449     case UINTEGER32:
02450     case UNSIGNED32:
02451     case COUNTER:
02452     case GAUGE:
02453     case BITSTRING:
02454     case LABEL:
02455         if (nexttype == LEFTBRACKET) {
02456             /*
02457              * if there is an enumeration list, parse it 
02458              */
02459             np->enums = parse_enumlist(fp, &np->enums);
02460             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02461         } else if (nexttype == LEFTPAREN) {
02462             /*
02463              * if there is a range list, parse it 
02464              */
02465             np->ranges = parse_ranges(fp, &np->ranges);
02466             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02467         }
02468         break;
02469     case OCTETSTR:
02470     case KW_OPAQUE:
02471         /*
02472          * parse any SIZE specification 
02473          */
02474         if (nexttype == LEFTPAREN) {
02475             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02476             if (nexttype == SIZE) {
02477                 nexttype = get_token(fp, nexttoken, MAXTOKEN);
02478                 if (nexttype == LEFTPAREN) {
02479                     np->ranges = parse_ranges(fp, &np->ranges);
02480                     nexttype = get_token(fp, nexttoken, MAXTOKEN);      /* ) */
02481                     if (nexttype == RIGHTPAREN) {
02482                         nexttype = get_token(fp, nexttoken, MAXTOKEN);
02483                         break;
02484                     }
02485                 }
02486             }
02487             print_error("Bad SIZE syntax", token, type);
02488             free_node(np);
02489             return NULL;
02490         }
02491         break;
02492     case OBJID:
02493     case NETADDR:
02494     case IPADDR:
02495     case TIMETICKS:
02496     case NUL:
02497     case NSAPADDRESS:
02498     case COUNTER64:
02499         break;
02500     default:
02501         print_error("Bad syntax", token, type);
02502         free_node(np);
02503         return NULL;
02504     }
02505     if (nexttype == UNITS) {
02506         type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02507         if (type != QUOTESTRING) {
02508             print_error("Bad UNITS", quoted_string_buffer, type);
02509             free_node(np);
02510             return NULL;
02511         }
02512         np->units = strdup(quoted_string_buffer);
02513         nexttype = get_token(fp, nexttoken, MAXTOKEN);
02514     }
02515     if (nexttype != ACCESS) {
02516         print_error("Should be ACCESS", nexttoken, nexttype);
02517         free_node(np);
02518         return NULL;
02519     }
02520     type = get_token(fp, token, MAXTOKEN);
02521     if (type != READONLY && type != READWRITE && type != WRITEONLY
02522         && type != NOACCESS && type != READCREATE && type != ACCNOTIFY) {
02523         print_error("Bad ACCESS type", token, type);
02524         free_node(np);
02525         return NULL;
02526     }
02527     np->access = type;
02528     type = get_token(fp, token, MAXTOKEN);
02529     if (type != STATUS) {
02530         print_error("Should be STATUS", token, type);
02531         free_node(np);
02532         return NULL;
02533     }
02534     type = get_token(fp, token, MAXTOKEN);
02535     if (type != MANDATORY && type != CURRENT && type != KW_OPTIONAL &&
02536         type != OBSOLETE && type != DEPRECATED) {
02537         print_error("Bad STATUS", token, type);
02538         free_node(np);
02539         return NULL;
02540     }
02541     np->status = type;
02542     /*
02543      * Optional parts of the OBJECT-TYPE macro
02544      */
02545     type = get_token(fp, token, MAXTOKEN);
02546     while (type != EQUALS && type != ENDOFFILE) {
02547         switch (type) {
02548         case DESCRIPTION:
02549             type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02550 
02551             if (type != QUOTESTRING) {
02552                 print_error("Bad DESCRIPTION", quoted_string_buffer, type);
02553                 free_node(np);
02554                 return NULL;
02555             }
02556             if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
02557                                        NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
02558                 np->description = strdup(quoted_string_buffer);
02559             }
02560             break;
02561 
02562         case REFERENCE:
02563             type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02564             if (type != QUOTESTRING) {
02565                 print_error("Bad REFERENCE", quoted_string_buffer, type);
02566                 free_node(np);
02567                 return NULL;
02568             }
02569             np->reference = strdup(quoted_string_buffer);
02570             break;
02571         case INDEX:
02572             if (np->augments) {
02573                 print_error("Cannot have both INDEX and AUGMENTS", token,
02574                             type);
02575                 free_node(np);
02576                 return NULL;
02577             }
02578             np->indexes = getIndexes(fp, &np->indexes);
02579             if (np->indexes == NULL) {
02580                 print_error("Bad INDEX list", token, type);
02581                 free_node(np);
02582                 return NULL;
02583             }
02584             break;
02585         case AUGMENTS:
02586             if (np->indexes) {
02587                 print_error("Cannot have both INDEX and AUGMENTS", token,
02588                             type);
02589                 free_node(np);
02590                 return NULL;
02591             }
02592             np->indexes = getIndexes(fp, &np->indexes);
02593             if (np->indexes == NULL) {
02594                 print_error("Bad AUGMENTS list", token, type);
02595                 free_node(np);
02596                 return NULL;
02597             }
02598             np->augments = strdup(np->indexes->ilabel);
02599             free_indexes(&np->indexes);
02600             break;
02601         case DEFVAL:
02602             /*
02603              * Mark's defVal section 
02604              */
02605             type = get_token(fp, quoted_string_buffer, MAXTOKEN);
02606             if (type != LEFTBRACKET) {
02607                 print_error("Bad DEFAULTVALUE", quoted_string_buffer,
02608                             type);
02609                 free_node(np);
02610                 return NULL;
02611             }
02612 
02613             {
02614                 int             level = 1;
02615                 char            defbuf[512];
02616 
02617                 defbuf[0] = 0;
02618                 while (1) {
02619                     type = get_token(fp, quoted_string_buffer, MAXTOKEN);
02620                     if ((type == RIGHTBRACKET && --level == 0)
02621                         || type == ENDOFFILE)
02622                         break;
02623                     else if (type == LEFTBRACKET)
02624                         level++;
02625                     if (type == QUOTESTRING) {
02626                         if (strlen(defbuf)+2 < sizeof(defbuf)) {
02627                             defbuf[ strlen(defbuf)+2 ] = 0;
02628                             defbuf[ strlen(defbuf)+1 ] = '"';
02629                             defbuf[ strlen(defbuf)   ] = '\\';
02630                         }
02631                         defbuf[ sizeof(defbuf)-1 ] = 0;
02632                     }
02633                     strncat(defbuf, quoted_string_buffer,
02634                             sizeof(defbuf)-strlen(defbuf));
02635                     defbuf[ sizeof(defbuf)-1 ] = 0;
02636                     if (type == QUOTESTRING) {
02637                         if (strlen(defbuf)+2 < sizeof(defbuf)) {
02638                             defbuf[ strlen(defbuf)+2 ] = 0;
02639                             defbuf[ strlen(defbuf)+1 ] = '"';
02640                             defbuf[ strlen(defbuf)   ] = '\\';
02641                         }
02642                         defbuf[ sizeof(defbuf)-1 ] = 0;
02643                     }
02644                     if (strlen(defbuf)+1 < sizeof(defbuf)) {
02645                         defbuf[ strlen(defbuf)+1 ] = 0;
02646                         defbuf[ strlen(defbuf)   ] = ' ';
02647                     }
02648                 }
02649 
02650                 if (type != RIGHTBRACKET) {
02651                     print_error("Bad DEFAULTVALUE", quoted_string_buffer,
02652                                 type);
02653                     free_node(np);
02654                     return NULL;
02655                 }
02656 
02657                 defbuf[strlen(defbuf) - 1] = 0;
02658                 np->defaultValue = strdup(defbuf);
02659             }
02660 
02661             break;
02662 
02663         case NUM_ENTRIES:
02664             if (tossObjectIdentifier(fp) != OBJID) {
02665                 print_error("Bad Object Identifier", token, type);
02666                 free_node(np);
02667                 return NULL;
02668             }
02669             break;
02670 
02671         default:
02672             print_error("Bad format of optional clauses", token, type);
02673             free_node(np);
02674             return NULL;
02675 
02676         }
02677         type = get_token(fp, token, MAXTOKEN);
02678     }
02679     if (type != EQUALS) {
02680         print_error("Bad format", token, type);
02681         free_node(np);
02682         return NULL;
02683     }
02684     return merge_parse_objectid(np, fp, name);
02685 }
02686 
02687 /*
02688  * Parses an OBJECT GROUP macro.
02689  * Returns 0 on error.
02690  *
02691  * Also parses object-identity, since they are similar (ignore STATUS).
02692  *   - WJH 10/96
02693  */
02694 static struct node *
02695 parse_objectgroup(FILE * fp, char *name, int what, struct objgroup **ol)
02696 {
02697     int             type;
02698     char            token[MAXTOKEN];
02699     char            quoted_string_buffer[MAXQUOTESTR];
02700     struct node    *np;
02701 
02702     np = alloc_node(current_module);
02703     if (np == NULL)
02704         return (NULL);
02705     type = get_token(fp, token, MAXTOKEN);
02706     if (type == what) {
02707         type = get_token(fp, token, MAXTOKEN);
02708         if (type != LEFTBRACKET) {
02709             print_error("Expected \"{\"", token, type);
02710             goto skip;
02711         }
02712         do {
02713             struct objgroup *o;
02714             type = get_token(fp, token, MAXTOKEN);
02715             if (type != LABEL) {
02716                 print_error("Bad identifier", token, type);
02717                 goto skip;
02718             }
02719             o = (struct objgroup *) malloc(sizeof(struct objgroup));
02720             if (!o) {
02721                 print_error("Resource failure", token, type);
02722                 goto skip;
02723             }
02724             o->line = mibLine;
02725             o->name = strdup(token);
02726             o->next = *ol;
02727             *ol = o;
02728             type = get_token(fp, token, MAXTOKEN);
02729         } while (type == COMMA);
02730         if (type != RIGHTBRACKET) {
02731             print_error("Expected \"}\" after list", token, type);
02732             goto skip;
02733         }
02734         type = get_token(fp, token, type);
02735     }
02736     if (type != STATUS) {
02737         print_error("Expected STATUS", token, type);
02738         goto skip;
02739     }
02740     type = get_token(fp, token, MAXTOKEN);
02741     if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) {
02742         print_error("Bad STATUS value", token, type);
02743         goto skip;
02744     }
02745     type = get_token(fp, token, MAXTOKEN);
02746     if (type != DESCRIPTION) {
02747         print_error("Expected DESCRIPTION", token, type);
02748         goto skip;
02749     }
02750     type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02751     if (type != QUOTESTRING) {
02752         print_error("Bad DESCRIPTION", quoted_string_buffer, type);
02753         free_node(np);
02754         return NULL;
02755     }
02756     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
02757                                NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
02758         np->description = strdup(quoted_string_buffer);
02759     }
02760     type = get_token(fp, token, MAXTOKEN);
02761     if (type == REFERENCE) {
02762         type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02763         if (type != QUOTESTRING) {
02764             print_error("Bad REFERENCE", quoted_string_buffer, type);
02765             free_node(np);
02766             return NULL;
02767         }
02768         np->reference = strdup(quoted_string_buffer);
02769         type = get_token(fp, token, MAXTOKEN);
02770     }
02771     if (type != EQUALS)
02772         print_error("Expected \"::=\"", token, type);
02773   skip:
02774     while (type != EQUALS && type != ENDOFFILE)
02775         type = get_token(fp, token, MAXTOKEN);
02776 
02777     return merge_parse_objectid(np, fp, name);
02778 }
02779 
02780 /*
02781  * Parses a NOTIFICATION-TYPE macro.
02782  * Returns 0 on error.
02783  */
02784 static struct node *
02785 parse_notificationDefinition(FILE * fp, char *name)
02786 {
02787     register int    type;
02788     char            token[MAXTOKEN];
02789     char            quoted_string_buffer[MAXQUOTESTR];
02790     register struct node *np;
02791 
02792     np = alloc_node(current_module);
02793     if (np == NULL)
02794         return (NULL);
02795     type = get_token(fp, token, MAXTOKEN);
02796     while (type != EQUALS && type != ENDOFFILE) {
02797         switch (type) {
02798         case DESCRIPTION:
02799             type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02800             if (type != QUOTESTRING) {
02801                 print_error("Bad DESCRIPTION", quoted_string_buffer, type);
02802                 free_node(np);
02803                 return NULL;
02804             }
02805             if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
02806                                        NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
02807                 np->description = strdup(quoted_string_buffer);
02808             }
02809             break;
02810         case REFERENCE:
02811             type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02812             if (type != QUOTESTRING) {
02813                 print_error("Bad REFERENCE", quoted_string_buffer, type);
02814                 free_node(np);
02815                 return NULL;
02816             }
02817             np->reference = strdup(quoted_string_buffer);
02818             break;
02819         case OBJECTS:
02820             np->varbinds = getVarbinds(fp, &np->varbinds);
02821             if (!np->varbinds) {
02822                 print_error("Bad OBJECTS list", token, type);
02823                 free_node(np);
02824                 return NULL;
02825             }
02826             break;
02827         default:
02828             /*
02829              * NOTHING 
02830              */
02831             break;
02832         }
02833         type = get_token(fp, token, MAXTOKEN);
02834     }
02835     return merge_parse_objectid(np, fp, name);
02836 }
02837 
02838 /*
02839  * Parses a TRAP-TYPE macro.
02840  * Returns 0 on error.
02841  */
02842 static struct node *
02843 parse_trapDefinition(FILE * fp, char *name)
02844 {
02845     register int    type;
02846     char            token[MAXTOKEN];
02847     char            quoted_string_buffer[MAXQUOTESTR];
02848     register struct node *np;
02849 
02850     np = alloc_node(current_module);
02851     if (np == NULL)
02852         return (NULL);
02853     type = get_token(fp, token, MAXTOKEN);
02854     while (type != EQUALS && type != ENDOFFILE) {
02855         switch (type) {
02856         case DESCRIPTION:
02857             type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02858             if (type != QUOTESTRING) {
02859                 print_error("Bad DESCRIPTION", quoted_string_buffer, type);
02860                 free_node(np);
02861                 return NULL;
02862             }
02863             if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
02864                                        NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
02865                 np->description = strdup(quoted_string_buffer);
02866             }
02867             break;
02868         case REFERENCE:
02869             /* I'm not sure REFERENCEs are legal in smiv1 traps??? */
02870             type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
02871             if (type != QUOTESTRING) {
02872                 print_error("Bad REFERENCE", quoted_string_buffer, type);
02873                 free_node(np);
02874                 return NULL;
02875             }
02876             np->reference = strdup(quoted_string_buffer);
02877             break;
02878         case ENTERPRISE:
02879             type = get_token(fp, token, MAXTOKEN);
02880             if (type == LEFTBRACKET) {
02881                 type = get_token(fp, token, MAXTOKEN);
02882                 if (type != LABEL) {
02883                     print_error("Bad Trap Format", token, type);
02884                     free_node(np);
02885                     return NULL;
02886                 }
02887                 np->parent = strdup(token);
02888                 /*
02889                  * Get right bracket 
02890                  */
02891                 type = get_token(fp, token, MAXTOKEN);
02892             } else if (type == LABEL)
02893                 np->parent = strdup(token);
02894             break;
02895         case VARIABLES:
02896             np->varbinds = getVarbinds(fp, &np->varbinds);
02897             if (!np->varbinds) {
02898                 print_error("Bad VARIABLES list", token, type);
02899                 free_node(np);
02900                 return NULL;
02901             }
02902             break;
02903         default:
02904             /*
02905              * NOTHING 
02906              */
02907             break;
02908         }
02909         type = get_token(fp, token, MAXTOKEN);
02910     }
02911     type = get_token(fp, token, MAXTOKEN);
02912 
02913     np->label = strdup(name);
02914 
02915     if (type != NUMBER) {
02916         print_error("Expected a Number", token, type);
02917         free_node(np);
02918         return NULL;
02919     }
02920     np->subid = strtoul(token, NULL, 10);
02921     np->next = alloc_node(current_module);
02922     if (np->next == NULL) {
02923         free_node(np);
02924         return (NULL);
02925     }
02926     np->next->parent = np->parent;
02927     np->parent = (char *) malloc(strlen(np->parent) + 2);
02928     if (np->parent == NULL) {
02929         free_node(np->next);
02930         free_node(np);
02931         return (NULL);
02932     }
02933     strcpy(np->parent, np->next->parent);
02934     strcat(np->parent, "#");
02935     np->next->label = strdup(np->parent);
02936     return np;
02937 }
02938 
02939 
02940 /*
02941  * Parses a compliance macro
02942  * Returns 0 on error.
02943  */
02944 static int
02945 eat_syntax(FILE * fp, char *token, int maxtoken)
02946 {
02947     int             type, nexttype;
02948     struct node    *np = alloc_node(current_module);
02949     char            nexttoken[MAXTOKEN];
02950 
02951     type = get_token(fp, token, maxtoken);
02952     nexttype = get_token(fp, nexttoken, MAXTOKEN);
02953     switch (type) {
02954     case INTEGER:
02955     case INTEGER32:
02956     case UINTEGER32:
02957     case UNSIGNED32:
02958     case COUNTER:
02959     case GAUGE:
02960     case BITSTRING:
02961     case LABEL:
02962         if (nexttype == LEFTBRACKET) {
02963             /*
02964              * if there is an enumeration list, parse it 
02965              */
02966             np->enums = parse_enumlist(fp, &np->enums);
02967             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02968         } else if (nexttype == LEFTPAREN) {
02969             /*
02970              * if there is a range list, parse it 
02971              */
02972             np->ranges = parse_ranges(fp, &np->ranges);
02973             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02974         }
02975         break;
02976     case OCTETSTR:
02977     case KW_OPAQUE:
02978         /*
02979          * parse any SIZE specification 
02980          */
02981         if (nexttype == LEFTPAREN) {
02982             nexttype = get_token(fp, nexttoken, MAXTOKEN);
02983             if (nexttype == SIZE) {
02984                 nexttype = get_token(fp, nexttoken, MAXTOKEN);
02985                 if (nexttype == LEFTPAREN) {
02986                     np->ranges = parse_ranges(fp, &np->ranges);
02987                     nexttype = get_token(fp, nexttoken, MAXTOKEN);      /* ) */
02988                     if (nexttype == RIGHTPAREN) {
02989                         nexttype = get_token(fp, nexttoken, MAXTOKEN);
02990                         break;
02991                     }
02992                 }
02993             }
02994             print_error("Bad SIZE syntax", token, type);
02995             free_node(np);
02996             return nexttype;
02997         }
02998         break;
02999     case OBJID:
03000     case NETADDR:
03001     case IPADDR:
03002     case TIMETICKS:
03003     case NUL:
03004     case NSAPADDRESS:
03005     case COUNTER64:
03006         break;
03007     default:
03008         print_error("Bad syntax", token, type);
03009         free_node(np);
03010         return nexttype;
03011     }
03012     free_node(np);
03013     return nexttype;
03014 }
03015 
03016 static int
03017 compliance_lookup(const char *name, int modid)
03018 {
03019     if (modid == -1) {
03020         struct objgroup *op =
03021             (struct objgroup *) malloc(sizeof(struct objgroup));
03022         if (!op)
03023             return 0;
03024         op->next = objgroups;
03025         op->name = strdup(name);
03026         op->line = mibLine;
03027         objgroups = op;
03028         return 1;
03029     } else
03030         return find_tree_node(name, modid) != NULL;
03031 }
03032 
03033 static struct node *
03034 parse_compliance(FILE * fp, char *name)
03035 {
03036     int             type;
03037     char            token[MAXTOKEN];
03038     char            quoted_string_buffer[MAXQUOTESTR];
03039     struct node    *np;
03040 
03041     np = alloc_node(current_module);
03042     if (np == NULL)
03043         return (NULL);
03044     type = get_token(fp, token, MAXTOKEN);
03045     if (type != STATUS) {
03046         print_error("Expected STATUS", token, type);
03047         goto skip;
03048     }
03049     type = get_token(fp, token, MAXTOKEN);
03050     if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) {
03051         print_error("Bad STATUS", token, type);
03052         goto skip;
03053     }
03054     type = get_token(fp, token, MAXTOKEN);
03055     if (type != DESCRIPTION) {
03056         print_error("Expected DESCRIPTION", token, type);
03057         goto skip;
03058     }
03059     type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
03060     if (type != QUOTESTRING) {
03061         print_error("Bad DESCRIPTION", quoted_string_buffer, type);
03062         goto skip;
03063     }
03064     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
03065                                NETSNMP_DS_LIB_SAVE_MIB_DESCRS))
03066         np->description = strdup(quoted_string_buffer);
03067     type = get_token(fp, token, MAXTOKEN);
03068     if (type == REFERENCE) {
03069         type = get_token(fp, quoted_string_buffer, MAXTOKEN);
03070         if (type != QUOTESTRING) {
03071             print_error("Bad REFERENCE", quoted_string_buffer, type);
03072             goto skip;
03073         }
03074         np->reference = strdup(quoted_string_buffer);
03075         type = get_token(fp, token, MAXTOKEN);
03076     }
03077     if (type != MODULE) {
03078         print_error("Expected MODULE", token, type);
03079         goto skip;
03080     }
03081     while (type == MODULE) {
03082         int             modid = -1;
03083         char            modname[MAXTOKEN];
03084         type = get_token(fp, token, MAXTOKEN);
03085         if (type == LABEL
03086             && strcmp(token, module_name(current_module, modname))) {
03087             modid = read_module_internal(token);
03088             if (modid != MODULE_LOADED_OK
03089                 && modid != MODULE_ALREADY_LOADED) {
03090                 print_error("Unknown module", token, type);
03091                 goto skip;
03092             }
03093             modid = which_module(token);
03094             type = get_token(fp, token, MAXTOKEN);
03095         }
03096         if (type == MANDATORYGROUPS) {
03097             type = get_token(fp, token, MAXTOKEN);
03098             if (type != LEFTBRACKET) {
03099                 print_error("Expected \"{\"", token, type);
03100                 goto skip;
03101             }
03102             do {
03103                 type = get_token(fp, token, MAXTOKEN);
03104                 if (type != LABEL) {
03105                     print_error("Bad group name", token, type);
03106                     goto skip;
03107                 }
03108                 if (!compliance_lookup(token, modid))
03109                     print_error("Unknown group", token, type);
03110                 type = get_token(fp, token, MAXTOKEN);
03111             } while (type == COMMA);
03112             if (type != RIGHTBRACKET) {
03113                 print_error("Expected \"}\"", token, type);
03114                 goto skip;
03115             }
03116             type = get_token(fp, token, MAXTOKEN);
03117         }
03118         while (type == GROUP || type == OBJECT) {
03119             if (type == GROUP) {
03120                 type = get_token(fp, token, MAXTOKEN);
03121                 if (type != LABEL) {
03122                     print_error("Bad group name", token, type);
03123                     goto skip;
03124                 }
03125                 if (!compliance_lookup(token, modid))
03126                     print_error("Unknown group", token, type);
03127                 type = get_token(fp, token, MAXTOKEN);
03128             } else {
03129                 type = get_token(fp, token, MAXTOKEN);
03130                 if (type != LABEL) {
03131                     print_error("Bad object name", token, type);
03132                     goto skip;
03133                 }
03134                 if (!compliance_lookup(token, modid))
03135                     print_error("Unknown group", token, type);
03136                 type = get_token(fp, token, MAXTOKEN);
03137                 if (type == SYNTAX)
03138                     type = eat_syntax(fp, token, MAXTOKEN);
03139                 if (type == WRSYNTAX)
03140                     type = eat_syntax(fp, token, MAXTOKEN);
03141                 if (type == MINACCESS) {
03142                     type = get_token(fp, token, MAXTOKEN);
03143                     if (type != NOACCESS && type != ACCNOTIFY
03144                         && type != READONLY && type != WRITEONLY
03145                         && type != READCREATE && type != READWRITE) {
03146                         print_error("Bad MIN-ACCESS spec", token, type);
03147                         goto skip;
03148                     }
03149                     type = get_token(fp, token, MAXTOKEN);
03150                 }
03151             }
03152             if (type != DESCRIPTION) {
03153                 print_error("Expected DESCRIPTION", token, type);
03154                 goto skip;
03155             }
03156             type = get_token(fp, token, MAXTOKEN);
03157             if (type != QUOTESTRING) {
03158                 print_error("Bad DESCRIPTION", token, type);
03159                 goto skip;
03160             }
03161             type = get_token(fp, token, MAXTOKEN);
03162         }
03163     }
03164   skip:
03165     while (type != EQUALS && type != ENDOFFILE)
03166         type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
03167 
03168     return merge_parse_objectid(np, fp, name);
03169 }
03170 
03171 
03172 /*
03173  * Parses a capabilities macro
03174  * Returns 0 on error.
03175  */
03176 static struct node *
03177 parse_capabilities(FILE * fp, char *name)
03178 {
03179     int             type;
03180     char            token[MAXTOKEN];
03181     char            quoted_string_buffer[MAXQUOTESTR];
03182     struct node    *np;
03183 
03184     np = alloc_node(current_module);
03185     if (np == NULL)
03186         return (NULL);
03187     type = get_token(fp, token, MAXTOKEN);
03188     if (type != PRODREL) {
03189         print_error("Expected PRODUCT-RELEASE", token, type);
03190         goto skip;
03191     }
03192     type = get_token(fp, token, MAXTOKEN);
03193     if (type != QUOTESTRING) {
03194         print_error("Expected STRING after PRODUCT-RELEASE", token, type);
03195         goto skip;
03196     }
03197     type = get_token(fp, token, MAXTOKEN);
03198     if (type != STATUS) {
03199         print_error("Expected STATUS", token, type);
03200         goto skip;
03201     }
03202     type = get_token(fp, token, MAXTOKEN);
03203     if (type != CURRENT && type != OBSOLETE) {
03204         print_error("STATUS should be current or obsolete", token, type);
03205         goto skip;
03206     }
03207     type = get_token(fp, token, MAXTOKEN);
03208     if (type != DESCRIPTION) {
03209         print_error("Expected DESCRIPTION", token, type);
03210         goto skip;
03211     }
03212     type = get_token(fp, token, MAXTOKEN);
03213     if (type != QUOTESTRING) {
03214         print_error("Bad DESCRIPTION", token, type);
03215         goto skip;
03216     }
03217     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
03218                                NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
03219         np->description = strdup(token);
03220     }
03221     type = get_token(fp, token, MAXTOKEN);
03222     if (type == REFERENCE) {
03223         type = get_token(fp, token, MAXTOKEN);
03224         if (type != QUOTESTRING) {
03225             print_error("Bad REFERENCE", token, type);
03226             goto skip;
03227         }
03228         np->reference = strdup(token);
03229         type = get_token(fp, token, type);
03230     }
03231     while (type == SUPPORTS) {
03232         int             modid;
03233         struct tree    *tp;
03234 
03235         type = get_token(fp, token, MAXTOKEN);
03236         if (type != LABEL) {
03237             print_error("Bad module name", token, type);
03238             goto skip;
03239         }
03240         modid = read_module_internal(token);
03241         if (modid != MODULE_LOADED_OK && modid != MODULE_ALREADY_LOADED) {
03242             print_error("Module not found", token, type);
03243             goto skip;
03244         }
03245         modid = which_module(token);
03246         type = get_token(fp, token, MAXTOKEN);
03247         if (type != INCLUDES) {
03248             print_error("Expected INCLUDES", token, type);
03249             goto skip;
03250         }
03251         type = get_token(fp, token, MAXTOKEN);
03252         if (type != LEFTBRACKET) {
03253             print_error("Expected \"{\"", token, type);
03254             goto skip;
03255         }
03256         do {
03257             type = get_token(fp, token, MAXTOKEN);
03258             if (type != LABEL) {
03259                 print_error("Expected group name", token, type);
03260                 goto skip;
03261             }
03262             tp = find_tree_node(token, modid);
03263             if (!tp)
03264                 print_error("Group not found in module", token, type);
03265             type = get_token(fp, token, MAXTOKEN);
03266         } while (type == COMMA);
03267         if (type != RIGHTBRACKET) {
03268             print_error("Expected \"}\" after group list", token, type);
03269             goto skip;
03270         }
03271         type = get_token(fp, token, MAXTOKEN);
03272         while (type == VARIATION) {
03273             type = get_token(fp, token, MAXTOKEN);
03274             if (type != LABEL) {
03275                 print_error("Bad object name", token, type);
03276                 goto skip;
03277             }
03278             tp = find_tree_node(token, modid);
03279             if (!tp)
03280                 print_error("Object not found in module", token, type);
03281             type = get_token(fp, token, MAXTOKEN);
03282             if (type == SYNTAX) {
03283                 type = eat_syntax(fp, token, MAXTOKEN);
03284             }
03285             if (type == WRSYNTAX) {
03286                 type = eat_syntax(fp, token, MAXTOKEN);
03287             }
03288             if (type == ACCESS) {
03289                 type = get_token(fp, token, MAXTOKEN);
03290                 if (type != ACCNOTIFY && type != READONLY
03291                     && type != READWRITE && type != READCREATE
03292                     && type != WRITEONLY && type != NOTIMPL) {
03293                     print_error("Bad ACCESS", token, type);
03294                     goto skip;
03295                 }
03296                 type = get_token(fp, token, MAXTOKEN);
03297             }
03298             if (type == CREATEREQ) {
03299                 type = get_token(fp, token, MAXTOKEN);
03300                 if (type != LEFTBRACKET) {
03301                     print_error("Expected \"{\"", token, type);
03302                     goto skip;
03303                 }
03304                 do {
03305                     type = get_token(fp, token, MAXTOKEN);
03306                     if (type != LABEL) {
03307                         print_error("Bad object name in list", token,
03308                                     type);
03309                         goto skip;
03310                     }
03311                     type = get_token(fp, token, MAXTOKEN);
03312                 } while (type == COMMA);
03313                 if (type != RIGHTBRACKET) {
03314                     print_error("Expected \"}\" after list", token, type);
03315                     goto skip;
03316                 }
03317                 type = get_token(fp, token, MAXTOKEN);
03318             }
03319             if (type == DEFVAL) {
03320                 int             level = 1;
03321                 type = get_token(fp, token, MAXTOKEN);
03322                 if (type != LEFTBRACKET) {
03323                     print_error("Expected \"{\" after DEFVAL", token,
03324                                 type);
03325                     goto skip;
03326                 }
03327                 do {
03328                     type = get_token(fp, token, MAXTOKEN);
03329                     if (type == LEFTBRACKET)
03330                         level++;
03331                     else if (type == RIGHTBRACKET)
03332                         level--;
03333                 } while (type != RIGHTBRACKET && type != ENDOFFILE
03334                          && level != 0);
03335                 if (type != RIGHTBRACKET) {
03336                     print_error("Missing \"}\" after DEFVAL", token, type);
03337                     goto skip;
03338                 }
03339                 type = get_token(fp, token, MAXTOKEN);
03340             }
03341             if (type != DESCRIPTION) {
03342                 print_error("Expected DESCRIPTION", token, type);
03343                 goto skip;
03344             }
03345             type = get_token(fp, token, MAXTOKEN);
03346             if (type != QUOTESTRING) {
03347                 print_error("Bad DESCRIPTION", token, type);
03348                 goto skip;
03349             }
03350             type = get_token(fp, token, MAXTOKEN);
03351         }
03352     }
03353     if (type != EQUALS)
03354         print_error("Expected \"::=\"", token, type);
03355   skip:
03356     while (type != EQUALS && type != ENDOFFILE) {
03357         type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
03358     }
03359     return merge_parse_objectid(np, fp, name);
03360 }
03361 
03362 /*
03363  * Parses a module identity macro
03364  * Returns 0 on error.
03365  */
03366 static void
03367 check_utc(const char *utc)
03368 {
03369     int             len, year, month, day, hour, minute;
03370 
03371     len = strlen(utc);
03372     if (utc[len - 1] != 'Z' && utc[len - 1] != 'z') {
03373         print_error("Timestamp should end with Z", utc, QUOTESTRING);
03374         return;
03375     }
03376     if (len == 11) {
03377         len =
03378             sscanf(utc, "%2d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
03379                    &minute);
03380         year += 1900;
03381     } else if (len == 13)
03382         len =
03383             sscanf(utc, "%4d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
03384                    &minute);
03385     else {
03386         print_error("Bad timestamp format (11 or 13 characters)",
03387                     utc, QUOTESTRING);
03388         return;
03389     }
03390     if (len != 5) {
03391         print_error("Bad timestamp format", utc, QUOTESTRING);
03392         return;
03393     }
03394     if (month < 1 || month > 12)
03395         print_error("Bad month in timestamp", utc, QUOTESTRING);
03396     if (day < 1 || day > 31)
03397         print_error("Bad day in timestamp", utc, QUOTESTRING);
03398     if (hour < 0 || hour > 23)
03399         print_error("Bad hour in timestamp", utc, QUOTESTRING);
03400     if (minute < 0 || minute > 59)
03401         print_error("Bad minute in timestamp", utc, QUOTESTRING);
03402 }
03403 
03404 static struct node *
03405 parse_moduleIdentity(FILE * fp, char *name)
03406 {
03407     register int    type;
03408     char            token[MAXTOKEN];
03409     char            quoted_string_buffer[MAXQUOTESTR];
03410     register struct node *np;
03411 
03412     np = alloc_node(current_module);
03413     if (np == NULL)
03414         return (NULL);
03415     type = get_token(fp, token, MAXTOKEN);
03416     if (type != LASTUPDATED) {
03417         print_error("Expected LAST-UPDATED", token, type);
03418         goto skip;
03419     }
03420     type = get_token(fp, token, MAXTOKEN);
03421     if (type != QUOTESTRING) {
03422         print_error("Need STRING for LAST-UPDATED", token, type);
03423         goto skip;
03424     }
03425     check_utc(token);
03426     type = get_token(fp, token, MAXTOKEN);
03427     if (type != ORGANIZATION) {
03428         print_error("Expected ORGANIZATION", token, type);
03429         goto skip;
03430     }
03431     type = get_token(fp, token, MAXTOKEN);
03432     if (type != QUOTESTRING) {
03433         print_error("Bad ORGANIZATION", token, type);
03434         goto skip;
03435     }
03436     type = get_token(fp, token, MAXTOKEN);
03437     if (type != CONTACTINFO) {
03438         print_error("Expected CONTACT-INFO", token, type);
03439         goto skip;
03440     }
03441     type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
03442     if (type != QUOTESTRING) {
03443         print_error("Bad CONTACT-INFO", quoted_string_buffer, type);
03444         goto skip;
03445     }
03446     type = get_token(fp, token, MAXTOKEN);
03447     if (type != DESCRIPTION) {
03448         print_error("Expected DESCRIPTION", token, type);
03449         goto skip;
03450     }
03451     type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
03452     if (type != QUOTESTRING) {
03453         print_error("Bad DESCRIPTION", quoted_string_buffer, type);
03454         goto skip;
03455     }
03456     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
03457                                NETSNMP_DS_LIB_SAVE_MIB_DESCRS)) {
03458         np->description = strdup(quoted_string_buffer);
03459     }
03460     type = get_token(fp, token, MAXTOKEN);
03461     while (type == REVISION) {
03462         type = get_token(fp, token, MAXTOKEN);
03463         if (type != QUOTESTRING) {
03464             print_error("Bad REVISION", token, type);
03465             goto skip;
03466         }
03467         check_utc(token);
03468         type = get_token(fp, token, MAXTOKEN);
03469         if (type != DESCRIPTION) {
03470             print_error("Expected DESCRIPTION", token, type);
03471             goto skip;
03472         }
03473         type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
03474         if (type != QUOTESTRING) {
03475             print_error("Bad DESCRIPTION", quoted_string_buffer, type);
03476             goto skip;
03477         }
03478         type = get_token(fp, token, MAXTOKEN);
03479     }
03480     if (type != EQUALS)
03481         print_error("Expected \"::=\"", token, type);
03482   skip:
03483     while (type != EQUALS && type != ENDOFFILE) {
03484         type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
03485     }
03486     return merge_parse_objectid(np, fp, name);
03487 }
03488 
03489 
03490 /*
03491  * Parses a MACRO definition
03492  * Expect BEGIN, discard everything to end.
03493  * Returns 0 on error.
03494  */
03495 static struct node *
03496 parse_macro(FILE * fp, char *name)
03497 {
03498     register int    type;
03499     char            token[MAXTOKEN];
03500     struct node    *np;
03501     int             iLine = mibLine;
03502 
03503     np = alloc_node(current_module);
03504     if (np == NULL)
03505         return (NULL);
03506     type = get_token(fp, token, sizeof(token));
03507     while (type != EQUALS && type != ENDOFFILE) {
03508         type = get_token(fp, token, sizeof(token));
03509     }
03510     if (type != EQUALS) {
03511         if (np)
03512             free_node(np);
03513         return NULL;
03514     }
03515     while (type != BEGIN && type != ENDOFFILE) {
03516         type = get_token(fp, token, sizeof(token));
03517     }
03518     if (type != BEGIN) {
03519         if (np)
03520             free_node(np);
03521         return NULL;
03522     }
03523     while (type != END && type != ENDOFFILE) {
03524         type = get_token(fp, token, sizeof(token));
03525     }
03526     if (type != END) {
03527         if (np)
03528             free_node(np);
03529         return NULL;
03530     }
03531 
03532     if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
03533                            NETSNMP_DS_LIB_MIB_WARNINGS)) {
03534         snmp_log(LOG_WARNING,
03535                  "%s MACRO (lines %d..%d parsed and ignored).\n", name,
03536                  iLine, mibLine);
03537     }
03538 
03539     return np;
03540 }
03541 
03542 /*
03543  * Parses a module import clause
03544  *   loading any modules referenced
03545  */
03546 static void
03547 parse_imports(FILE * fp)
03548 {
03549     register int    type;
03550     char            token[MAXTOKEN];
03551     char            modbuf[256];
03552 #define MAX_IMPORTS     256
03553     struct module_import import_list[MAX_IMPORTS];
03554     int             this_module;
03555     struct module  *mp;
03556 
03557     int             import_count = 0;   /* Total number of imported descriptors */
03558     int             i = 0, old_i;       /* index of first import from each module */
03559 
03560     type = get_token(fp, token, MAXTOKEN);
03561 
03562     /*
03563      * Parse the IMPORTS clause
03564      */
03565     while (type != SEMI && type != ENDOFFILE) {
03566         if (type == LABEL) {
03567             if (import_count == MAX_IMPORTS) {
03568                 print_error("Too many imported symbols", token, type);
03569                 do {
03570                     type = get_token(fp, token, MAXTOKEN);
03571                 } while (type != SEMI && type != ENDOFFILE);
03572                 return;
03573             }
03574             import_list[import_count++].label = strdup(token);
03575         } else if (type == FROM) {
03576             type = get_token(fp, token, MAXTOKEN);
03577             if (import_count == i) {    /* All imports are handled internally */
03578                 type = get_token(fp, token, MAXTOKEN);
03579                 continue;
03580             }
03581             this_module = which_module(token);
03582 
03583             for (old_i = i; i < import_count; ++i)
03584                 import_list[i].modid = this_module;
03585 
03586             /*
03587              * Recursively read any pre-requisite modules
03588              */
03589             if (read_module_internal(token) == MODULE_NOT_FOUND) {
03590                 int found = 0;
03591                 for (; old_i < import_count; ++old_i) {
03592                     found += read_import_replacements(token, &import_list[old_i]);
03593                 }
03594                 if (!found)
03595                     print_module_not_found(token);
03596             }
03597         }
03598         type = get_token(fp, token, MAXTOKEN);
03599     }
03600 
03601     /*
03602      * Save the import information
03603      *   in the global module table
03604      */
03605     for (mp = module_head; mp; mp = mp->next)
03606         if (mp->modid == current_module) {
03607             if (import_count == 0)
03608                 return;
03609             if (mp->imports && (mp->imports != root_imports)) {
03610                 /*
03611                  * this can happen if all modules are in one source file. 
03612                  */
03613                 for (i = 0; i < mp->no_imports; ++i) {
03614                     DEBUGMSGTL(("parse-mibs",
03615                                 "#### freeing Module %d '%s' %d\n",
03616                                 mp->modid, mp->imports[i].label,
03617                                 mp->imports[i].modid));
03618                     free((char *) mp->imports[i].label);
03619                 }
03620                 free((char *) mp->imports);
03621             }
03622             mp->imports = (struct module_import *)
03623                 calloc(import_count, sizeof(struct module_import));
03624             if (mp->imports == NULL)
03625                 return;
03626             for (i = 0; i < import_count; ++i) {
03627                 mp->imports[i].label = import_list[i].label;
03628                 mp->imports[i].modid = import_list[i].modid;
03629                 DEBUGMSGTL(("parse-mibs",
03630                             "#### adding Module %d '%s' %d\n", mp->modid,
03631                             mp->imports[i].label, mp->imports[i].modid));
03632             }
03633             mp->no_imports = import_count;
03634             return;
03635         }
03636 
03637     /*
03638      * Shouldn't get this far
03639      */
03640     print_module_not_found(module_name(current_module, modbuf));
03641     return;
03642 }
03643 
03644 
03645 
03646 /*
03647  * MIB module handling routines
03648  */
03649 
03650 static void
03651 dump_module_list(void)
03652 {
03653     struct module  *mp = module_head;
03654 
03655     DEBUGMSGTL(("parse-mibs", "Module list:\n"));
03656     while (mp) {
03657         DEBUGMSGTL(("parse-mibs", "  %s %d %s %d\n", mp->name, mp->modid,
03658                     mp->file, mp->no_imports));
03659         mp = mp->next;
03660     }
03661 }
03662 
03663 int
03664 which_module(const char *name)
03665 {
03666     struct module  *mp;
03667 
03668     for (mp = module_head; mp; mp = mp->next)
03669         if (!label_compare(mp->name, name))
03670             return (mp->modid);
03671 
03672     DEBUGMSGTL(("parse-mibs", "Module %s not found\n", name));
03673     return (-1);
03674 }
03675 
03676 /*
03677  * module_name - copy module name to user buffer, return ptr to same.
03678  */
03679 char           *
03680 module_name(int modid, char *cp)
03681 {
03682     struct module  *mp;
03683 
03684     for (mp = module_head; mp; mp = mp->next)
03685         if (mp->modid == modid) {
03686             strcpy(cp, mp->name);
03687             return (cp);
03688         }
03689 
03690     if (modid != -1) DEBUGMSGTL(("parse-mibs", "Module %d not found\n", modid));
03691     sprintf(cp, "#%d", modid);
03692     return (cp);
03693 }
03694 
03695 /*
03696  *  Backwards compatability
03697  *  Read newer modules that replace the one specified:-
03698  *      either all of them (read_module_replacements),
03699  *      or those relating to a specified identifier (read_import_replacements)
03700  *      plus an interface to add new replacement requirements
03701  */
03702 void
03703 add_module_replacement(const char *old_module,
03704                        const char *new_module_name,
03705                        const char *tag, int len)
03706 {
03707     struct module_compatability *mcp;
03708 
03709     mcp = (struct module_compatability *)
03710         calloc(1, sizeof(struct module_compatability));
03711     if (mcp == NULL)
03712         return;
03713 
03714     mcp->old_module = strdup(old_module);
03715     mcp->new_module = strdup(new_module_name);
03716     if (tag)
03717         mcp->tag = strdup(tag);
03718     mcp->tag_len = len;
03719 
03720     mcp->next = module_map_head;
03721     module_map_head = mcp;
03722 }
03723 
03724 static int
03725 read_module_replacements(const char *name)
03726 {
03727     struct module_compatability *mcp;
03728 
03729     for (mcp = module_map_head; mcp; mcp = mcp->next) {
03730         if (!label_compare(mcp->old_module, name)) {
03731             if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
03732                                    NETSNMP_DS_LIB_MIB_WARNINGS)) {
03733                 snmp_log(LOG_WARNING,
03734                          "Loading replacement module %s for %s (%s)\n",
03735                          mcp->new_module, name, File);
03736             }
03737             (void) netsnmp_read_module(mcp->new_module);
03738             return 1;
03739         }
03740     }
03741     return 0;
03742 }
03743 
03744 static int
03745 read_import_replacements(const char *old_module_name,
03746                          struct module_import *identifier)
03747 {
03748     struct module_compatability *mcp;
03749 
03750     /*
03751      * Look for matches first
03752      */
03753     for (mcp = module_map_head; mcp; mcp = mcp->next) {
03754         if (!label_compare(mcp->old_module, old_module_name)) {
03755 
03756             if (                /* exact match */
03757                    (mcp->tag_len == 0 &&
03758                     (mcp->tag == NULL ||
03759                      !label_compare(mcp->tag, identifier->label))) ||
03760                    /*
03761                     * prefix match 
03762                     */
03763                    (mcp->tag_len != 0 &&
03764                     !strncmp(mcp->tag, identifier->label, mcp->tag_len))
03765                 ) {
03766 
03767                 if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
03768                                        NETSNMP_DS_LIB_MIB_WARNINGS)) {
03769                     snmp_log(LOG_WARNING,
03770                              "Importing %s from replacement module %s instead of %s (%s)\n",
03771                              identifier->label, mcp->new_module,
03772                              old_module_name, File);
03773                 }
03774                 (void) netsnmp_read_module(mcp->new_module);
03775                 identifier->modid = which_module(mcp->new_module);
03776                 return 1;         /* finished! */
03777             }
03778         }
03779     }
03780 
03781     /*
03782      * If no exact match, load everything relevant
03783      */
03784     return read_module_replacements(old_module_name);
03785 }
03786 
03787 
03788 /*
03789  *  Read in the named module
03790  *      Returns the root of the whole tree
03791  *      (by analogy with 'read_mib')
03792  */
03793 static int
03794 read_module_internal(const char *name)
03795 {
03796     struct module  *mp;
03797     FILE           *fp;
03798     struct node    *np;
03799 
03800     netsnmp_init_mib_internals();
03801 
03802     for (mp = module_head; mp; mp = mp->next)
03803         if (!label_compare(mp->name, name)) {
03804             const char     *oldFile = File;
03805             int             oldLine = mibLine;
03806             int             oldModule = current_module;
03807 
03808             if (mp->no_imports != -1) {
03809                 DEBUGMSGTL(("parse-mibs", "Module %s already loaded\n",
03810                             name));
03811                 return MODULE_ALREADY_LOADED;
03812             }
03813             if ((fp = fopen(mp->file, "r")) == NULL) {
03814                 snmp_log_perror(mp->file);
03815                 return MODULE_LOAD_FAILED;
03816             }
03817             mp->no_imports = 0; /* Note that we've read the file */
03818             File = mp->file;
03819             mibLine = 1;
03820             current_module = mp->modid;
03821             /*
03822              * Parse the file
03823              */
03824             np = parse(fp, NULL);
03825             fclose(fp);
03826             File = oldFile;
03827             mibLine = oldLine;
03828             current_module = oldModule;
03829             return MODULE_LOADED_OK;
03830         }
03831 
03832     return MODULE_NOT_FOUND;
03833 }
03834 
03835 void
03836 adopt_orphans(void)
03837 {
03838     struct node    *np, *onp;
03839     struct tree    *tp;
03840     int             i, adopted = 1;
03841 
03842     if (!orphan_nodes)
03843         return;
03844     init_node_hash(orphan_nodes);
03845     orphan_nodes = NULL;
03846 
03847     while (adopted) {
03848         adopted = 0;
03849         for (i = 0; i < NHASHSIZE; i++)
03850             if (nbuckets[i]) {
03851                 for (np = nbuckets[i]; np != NULL; np = np->next) {
03852                     tp = find_tree_node(np->parent, -1);
03853                     if (tp) {
03854                         do_subtree(tp, &np);
03855                         adopted = 1;
03856                         /*
03857                          * if do_subtree adopted the entire bucket, stop
03858                          */
03859                         if(NULL == nbuckets[i])
03860                             break;
03861 
03862                         /*
03863                          * do_subtree may modify nbuckets, and if np
03864                          * was adopted, np->next probably isn't an orphan
03865                          * anymore. if np is still in the bucket (do_subtree
03866                          * didn't adopt it) keep on plugging. otherwise
03867                          * start over, at the top of the bucket.
03868                          */
03869                         for(onp = nbuckets[i]; onp; onp = onp->next)
03870                             if(onp == np)
03871                                 break;
03872                         if(NULL == onp) { /* not in the list */
03873                             np = nbuckets[i]; /* start over */
03874                         }
03875                     }
03876                 }
03877             }
03878     }
03879 
03880     /*
03881      * Report on outstanding orphans
03882      *    and link them back into the orphan list
03883      */
03884     for (i = 0; i < NHASHSIZE; i++)
03885         if (nbuckets[i]) {
03886             if (orphan_nodes)
03887                 onp = np->next = nbuckets[i];
03888             else
03889                 onp = orphan_nodes = nbuckets[i];
03890             nbuckets[i] = NULL;
03891             while (onp) {
03892                 char            modbuf[256];
03893                 snmp_log(LOG_WARNING,
03894                          "Cannot adopt OID in %s: %s ::= { %s %ld }\n",
03895                          module_name(onp->modid, modbuf),
03896                          (onp->label ? onp->label : "<no label>"),
03897                          (onp->parent ? onp->parent : "<no parent>"),
03898                          onp->subid);
03899 
03900                 np = onp;
03901                 onp = onp->next;
03902             }
03903         }
03904 }
03905 
03906 #ifndef NETSNMP_CLEAN_NAMESPACE
03907 struct tree    *
03908 read_module(const char *name)
03909 {
03910     return netsnmp_read_module(name);
03911 }
03912 #endif
03913 
03914 struct tree    *
03915 netsnmp_read_module(const char *name)
03916 {
03917     if (read_module_internal(name) == MODULE_NOT_FOUND)
03918         if (!read_module_replacements(name))
03919             print_module_not_found(name);
03920     return tree_head;
03921 }
03922 
03923 /*
03924  * Prototype definition 
03925  */
03926 void            unload_module_by_ID(int modID, struct tree *tree_top);
03927 
03928 void
03929 unload_module_by_ID(int modID, struct tree *tree_top)
03930 {
03931     struct tree    *tp, *next;
03932     int             i;
03933 
03934     for (tp = tree_top; tp; tp = next) {
03935         /*
03936          * Essentially, this is equivalent to the code fragment:
03937          *      if (tp->modID == modID)
03938          *        tp->number_modules--;
03939          * but handles one tree node being part of several modules,
03940          * and possible multiple copies of the same module ID.
03941          */
03942         int             nmod = tp->number_modules;
03943         if (nmod > 0) {         /* in some module */
03944             /*
03945              * Remove all copies of this module ID
03946              */
03947             int             cnt = 0, *pi1, *pi2 = tp->module_list;
03948             for (i = 0, pi1 = pi2; i < nmod; i++, pi2++) {
03949                 if (*pi2 == modID)
03950                     continue;
03951                 cnt++;
03952                 *pi1++ = *pi2;
03953             }
03954             if (nmod != cnt) {  /* in this module */
03955                 /*
03956                  * if ( (nmod - cnt) > 1)
03957                  * printf("Dup modid %d,  %d times, '%s'\n", tp->modid, (nmod-cnt), tp->label); fflush(stdout); ?* XXDEBUG 
03958                  */
03959                 tp->number_modules = cnt;
03960                 switch (cnt) {
03961                 case 0:
03962                     tp->module_list[0] = -1;    /* Mark unused, and FALL THROUGH */
03963 
03964                 case 1:        /* save the remaining module */
03965                     if (&(tp->modid) != tp->module_list) {
03966                         tp->modid = tp->module_list[0];
03967                         free(tp->module_list);
03968                         tp->module_list = &(tp->modid);
03969                     }
03970                     break;
03971 
03972                 default:
03973                     break;
03974                 }
03975             }                   /* if tree node is in this module */
03976         }
03977         /*
03978          * if tree node is in some module 
03979          */
03980         next = tp->next_peer;
03981 
03982 
03983         /*
03984          *  OK - that's dealt with *this* node.
03985          *    Now let's look at the children.
03986          *    (Isn't recursion wonderful!)
03987          */
03988         if (tp->child_list)
03989             unload_module_by_ID(modID, tp->child_list);
03990 
03991 
03992         if (tp->number_modules == 0) {
03993             /*
03994              * This node isn't needed any more (except perhaps
03995              * for the sake of the children) 
03996              */
03997             if (tp->child_list == NULL) {
03998                 unlink_tree(tp);
03999                 free_tree(tp);
04000             } else {
04001                 free_partial_tree(tp, TRUE);
04002             }
04003         }
04004     }
04005 }
04006 
04007 #ifndef NETSNMP_CLEAN_NAMESPACE
04008 int
04009 unload_module(const char *name)
04010 {
04011     return netsnmp_unload_module(name);
04012 }
04013 #endif
04014 
04015 int
04016 netsnmp_unload_module(const char *name)
04017 {
04018     struct module  *mp;
04019     int             modID = -1;
04020 
04021     for (mp = module_head; mp; mp = mp->next)
04022         if (!label_compare(mp->name, name)) {
04023             modID = mp->modid;
04024             break;
04025         }
04026 
04027     if (modID == -1) {
04028         DEBUGMSGTL(("unload-mib", "Module %s not found to unload\n",
04029                     name));
04030         return MODULE_NOT_FOUND;
04031     }
04032     unload_module_by_ID(modID, tree_head);
04033     mp->no_imports = -1;        /* mark as unloaded */
04034     return MODULE_LOADED_OK;    /* Well, you know what I mean! */
04035 }
04036 
04037 /*
04038  * Clear module map, tree nodes, textual convention table.
04039  */
04040 void
04041 unload_all_mibs()
04042 {
04043     struct module  *mp;
04044     struct module_compatability *mcp;
04045     struct tc      *ptc;
04046     int             i;
04047 
04048     for (mcp = module_map_head; mcp; mcp = module_map_head) {
04049         if (mcp == module_map)
04050             break;
04051         module_map_head = mcp->next;
04052         if (mcp->tag) free((char *) mcp->tag);
04053         free((char *) mcp->old_module);
04054         free((char *) mcp->new_module);
04055         free(mcp);
04056     }
04057 
04058     for (mp = module_head; mp; mp = module_head) {
04059         struct module_import *mi = mp->imports;
04060         if (mi) {
04061             for (i = 0; i < mp->no_imports; ++i) {
04062                 SNMP_FREE((mi + i)->label);
04063             }
04064             mp->no_imports = 0;
04065             if (mi == root_imports)
04066                 memset(mi, 0, sizeof(*mi));
04067             else
04068                 free(mi);
04069         }
04070 
04071         unload_module_by_ID(mp->modid, tree_head);
04072         module_head = mp->next;
04073         free(mp->name);
04074         free(mp->file);
04075         free(mp);
04076     }
04077     unload_module_by_ID(-1, tree_head);
04078     /*
04079      * tree nodes are cleared 
04080      */
04081 
04082     for (i = 0, ptc = tclist; i < MAXTC; i++, ptc++) {
04083         if (ptc->type == 0)
04084             continue;
04085         free_enums(&ptc->enums);
04086         free_ranges(&ptc->ranges);
04087         free(ptc->descriptor);
04088         if (ptc->hint)
04089             free(ptc->hint);
04090     }
04091     memset(tclist, 0, MAXTC * sizeof(struct tc));
04092 
04093     memset(buckets, 0, sizeof(buckets));
04094     memset(nbuckets, 0, sizeof(nbuckets));
04095     memset(tbuckets, 0, sizeof(tbuckets));
04096 
04097     for (i = 0; i < sizeof(root_imports) / sizeof(root_imports[0]); i++) {
04098         SNMP_FREE(root_imports[i].label);
04099     }
04100 
04101     max_module = 0;
04102     current_module = 0;
04103     module_map_head = NULL;
04104     SNMP_FREE(last_err_module);
04105 }
04106 
04107 static void
04108 new_module(const char *name, const char *file)
04109 {
04110     struct module  *mp;
04111 
04112     for (mp = module_head; mp; mp = mp->next)
04113         if (!label_compare(mp->name, name)) {
04114             DEBUGMSGTL(("parse-mibs", "  Module %s already noted\n", name));
04115             /*
04116              * Not the same file 
04117              */
04118             if (label_compare(mp->file, file)) {
04119                 DEBUGMSGTL(("parse-mibs", "    %s is now in %s\n",
04120                             name, file));
04121                 if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
04122                                        NETSNMP_DS_LIB_MIB_WARNINGS)) {
04123                     snmp_log(LOG_WARNING,
04124                              "Warning: Module %s was in %s now is %s\n",
04125                              name, mp->file, file);
04126                 }
04127 
04128                 /*
04129                  * Use the new one in preference 
04130                  */
04131                 free(mp->file);
04132                 mp->file = strdup(file);
04133             }
04134             return;
04135         }
04136 
04137     /*
04138      * Add this module to the list 
04139      */
04140     DEBUGMSGTL(("parse-mibs", "  Module %d %s is in %s\n", max_module,
04141                 name, file));
04142     mp = (struct module *) calloc(1, sizeof(struct module));
04143     if (mp == NULL)
04144         return;
04145     mp->name = strdup(name);
04146     mp->file = strdup(file);
04147     mp->imports = NULL;
04148     mp->no_imports = -1;        /* Not yet loaded */
04149     mp->modid = max_module;
04150     ++max_module;
04151 
04152     mp->next = module_head;     /* Or add to the *end* of the list? */
04153     module_head = mp;
04154 }
04155 
04156 
04157 static void
04158 scan_objlist(struct node *root, struct objgroup *list, const char *error)
04159 {
04160     int             oLine = mibLine;
04161 
04162     while (list) {
04163         struct objgroup *gp = list;
04164         struct node    *np;
04165         list = list->next;
04166         np = root;
04167         while (np)
04168             if (label_compare(np->label, gp->name))
04169                 np = np->next;
04170             else
04171                 break;
04172         if (!np) {
04173             mibLine = gp->line;
04174             print_error(error, gp->name, QUOTESTRING);
04175         }
04176         free(gp->name);
04177         free(gp);
04178     }
04179     mibLine = oLine;
04180 }
04181 
04182 /*
04183  * Parses a mib file and returns a linked list of nodes found in the file.
04184  * Returns NULL on error.
04185  */
04186 static struct node *
04187 parse(FILE * fp, struct node *root)
04188 {
04189     char            token[MAXTOKEN];
04190     char            name[MAXTOKEN];
04191     int             type = LABEL;
04192     int             lasttype = LABEL;
04193 
04194 #define BETWEEN_MIBS          1
04195 #define IN_MIB                2
04196     int             state = BETWEEN_MIBS;
04197     struct node    *np, *nnp;
04198     struct objgroup *oldgroups = NULL, *oldobjects = NULL, *oldnotifs =
04199         NULL;
04200 
04201     DEBUGMSGTL(("parse-file", "Parsing file:  %s...\n", File));
04202 
04203     if (last_err_module)
04204         free(last_err_module);
04205     last_err_module = 0;
04206 
04207     np = root;
04208     if (np != NULL) {
04209         /*
04210          * now find end of chain 
04211          */
04212         while (np->next)
04213             np = np->next;
04214     }
04215 
04216     while (type != ENDOFFILE) {
04217         if (lasttype == CONTINUE)
04218             lasttype = type;
04219         else
04220             type = lasttype = get_token(fp, token, MAXTOKEN);
04221 
04222         switch (type) {
04223         case END:
04224             if (state != IN_MIB) {
04225                 print_error("Error, END before start of MIB", NULL, type);
04226                 return NULL;
04227             } else {
04228                 struct module  *mp;
04229 #ifdef TEST
04230                 printf("\nNodes for Module %s:\n", name);
04231                 print_nodes(stdout, root);
04232 #endif
04233                 scan_objlist(root, objgroups, "Undefined OBJECT-GROUP");
04234                 scan_objlist(root, objects, "Undefined OBJECT");
04235                 scan_objlist(root, notifs, "Undefined NOTIFICATION");
04236                 objgroups = oldgroups;
04237                 objects = oldobjects;
04238                 notifs = oldnotifs;
04239                 for (mp = module_head; mp; mp = mp->next)
04240                     if (mp->modid == current_module)
04241                         break;
04242                 do_linkup(mp, root);
04243                 np = root = NULL;
04244             }
04245             state = BETWEEN_MIBS;
04246 #ifdef TEST
04247             if (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
04248                                    NETSNMP_DS_LIB_MIB_WARNINGS)) {
04249                 xmalloc_stats(stderr);
04250             }
04251 #endif
04252             continue;
04253         case IMPORTS:
04254             parse_imports(fp);
04255             continue;
04256         case EXPORTS:
04257             while (type != SEMI && type != ENDOFFILE)
04258                 type = get_token(fp, token, MAXTOKEN);
04259             continue;
04260         case LABEL:
04261         case INTEGER:
04262         case INTEGER32:
04263         case UINTEGER32:
04264         case UNSIGNED32:
04265         case COUNTER:
04266         case COUNTER64:
04267         case GAUGE:
04268         case IPADDR:
04269         case NETADDR:
04270         case NSAPADDRESS:
04271         case OBJSYNTAX:
04272         case APPSYNTAX:
04273         case SIMPLESYNTAX:
04274         case OBJNAME:
04275         case NOTIFNAME:
04276         case KW_OPAQUE:
04277         case TIMETICKS:
04278             break;
04279         case ENDOFFILE:
04280             continue;
04281         default:
04282             strcpy(name, token);
04283             type = get_token(fp, token, MAXTOKEN);
04284             nnp = NULL;
04285             if (type == MACRO) {
04286                 nnp = parse_macro(fp, name);
04287                 if (nnp == NULL) {
04288                     print_error("Bad parse of MACRO", NULL, type);
04289                     /*
04290                      * return NULL;
04291                      */
04292                 }
04293                 free_node(nnp); /* IGNORE */
04294                 nnp = NULL;
04295             } else
04296                 print_error(name, "is a reserved word", lasttype);
04297             continue;           /* see if we can parse the rest of the file */
04298         }
04299         strcpy(name, token);
04300         type = get_token(fp, token, MAXTOKEN);
04301         nnp = NULL;
04302 
04303         /*
04304          * Handle obsolete method to assign an object identifier to a
04305          * module
04306          */
04307         if (lasttype == LABEL && type == LEFTBRACKET) {
04308             while (type != RIGHTBRACKET && type != ENDOFFILE)
04309                 type = get_token(fp, token, MAXTOKEN);
04310             if (type == ENDOFFILE) {
04311                 print_error("Expected \"}\"", token, type);
04312                 return NULL;
04313             }
04314             type = get_token(fp, token, MAXTOKEN);
04315         }
04316 
04317         switch (type) {
04318         case DEFINITIONS:
04319             if (state != BETWEEN_MIBS) {
04320                 print_error("Error, nested MIBS", NULL, type);
04321                 return NULL;
04322             }
04323             state = IN_MIB;
04324             current_module = which_module(name);
04325             oldgroups = objgroups;
04326             objgroups = NULL;
04327             oldobjects = objects;
04328             objects = NULL;
04329             oldnotifs = notifs;
04330             notifs = NULL;
04331             if (current_module == -1) {
04332                 new_module(name, File);
04333                 current_module = which_module(name);
04334             }
04335             DEBUGMSGTL(("parse-mibs", "Parsing MIB: %d %s\n",
04336                         current_module, name));
04337             while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE)
04338                 if (type == BEGIN)
04339                     break;
04340             break;
04341         case OBJTYPE:
04342             nnp = parse_objecttype(fp, name);
04343             if (nnp == NULL) {
04344                 print_error("Bad parse of OBJECT-TYPE", NULL, type);
04345                 return NULL;
04346             }
04347             break;
04348         case OBJGROUP:
04349             nnp = parse_objectgroup(fp, name, OBJECTS, &objects);
04350             if (nnp == NULL) {
04351                 print_error("Bad parse of OBJECT-GROUP", NULL, type);
04352                 return NULL;
04353             }
04354             break;
04355         case NOTIFGROUP:
04356             nnp = parse_objectgroup(fp, name, NOTIFICATIONS, &notifs);
04357             if (nnp == NULL) {
04358                 print_error("Bad parse of NOTIFICATION-GROUP", NULL, type);
04359                 return NULL;
04360             }
04361             break;
04362         case TRAPTYPE:
04363             nnp = parse_trapDefinition(fp, name);
04364             if (nnp == NULL) {
04365                 print_error("Bad parse of TRAP-TYPE", NULL, type);
04366                 return NULL;
04367             }
04368             break;
04369         case NOTIFTYPE:
04370             nnp = parse_notificationDefinition(fp, name);
04371             if (nnp == NULL) {
04372                 print_error("Bad parse of NOTIFICATION-TYPE", NULL, type);
04373                 return NULL;
04374             }
04375             break;
04376         case COMPLIANCE:
04377             nnp = parse_compliance(fp, name);
04378             if (nnp == NULL) {
04379                 print_error("Bad parse of MODULE-COMPLIANCE", NULL, type);
04380                 return NULL;
04381             }
04382             break;
04383         case AGENTCAP:
04384             nnp = parse_capabilities(fp, name);
04385             if (nnp == NULL) {
04386                 print_error("Bad parse of AGENT-CAPABILITIES", NULL, type);
04387                 return NULL;
04388             }
04389             break;
04390         case MACRO:
04391             nnp = parse_macro(fp, name);
04392             if (nnp == NULL) {
04393                 print_error("Bad parse of MACRO", NULL, type);
04394                 /*
04395                  * return NULL;
04396                  */
04397             }
04398             free_node(nnp);     /* IGNORE */
04399             nnp = NULL;
04400             break;
04401         case MODULEIDENTITY:
04402             nnp = parse_moduleIdentity(fp, name);
04403             if (nnp == NULL) {
04404                 print_error("Bad parse of MODULE-IDENTITY", NULL, type);
04405                 return NULL;
04406             }
04407             break;
04408         case OBJIDENTITY:
04409             nnp = parse_objectgroup(fp, name, OBJECTS, &objects);
04410             if (nnp == NULL) {
04411                 print_error("Bad parse of OBJECT-IDENTITY", NULL, type);
04412                 return NULL;
04413             }
04414             break;
04415         case OBJECT:
04416             type = get_token(fp, token, MAXTOKEN);
04417             if (type != IDENTIFIER) {
04418                 print_error("Expected IDENTIFIER", token, type);
04419                 return NULL;
04420             }
04421             type = get_token(fp, token, MAXTOKEN);
04422             if (type != EQUALS) {
04423                 print_error("Expected \"::=\"", token, type);
04424                 return NULL;
04425             }
04426             nnp = parse_objectid(fp, name);
04427             if (nnp == NULL) {
04428                 print_error("Bad parse of OBJECT IDENTIFIER", NULL, type);
04429                 return NULL;
04430             }
04431             break;
04432         case EQUALS:
04433             nnp = parse_asntype(fp, name, &type, token);
04434             lasttype = CONTINUE;
04435             break;
04436         case ENDOFFILE:
04437             break;
04438         default:
04439             print_error("Bad operator", token, type);
04440             return NULL;
04441         }
04442         if (nnp) {
04443             if (np)
04444                 np->next = nnp;
04445             else
04446                 np = root = nnp;
04447             while (np->next)
04448                 np = np->next;
04449             if (np->type == TYPE_OTHER)
04450                 np->type = type;
04451         }
04452     }
04453     DEBUGMSGTL(("parse-file", "End of file (%s)\n", File));
04454     return root;
04455 }
04456 
04457 /*
04458  * return zero if character is not a label character. 
04459  */
04460 static int
04461 is_labelchar(int ich)
04462 {
04463     if ((isalnum(ich)) || (ich == '-'))
04464         return 1;
04465     if (ich == '_' && netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
04466                                              NETSNMP_DS_LIB_MIB_PARSE_LABEL)) {
04467         return 1;
04468     }
04469 
04470     return 0;
04471 }
04472 
04473 /*
04474  * Parses a token from the file.  The type of the token parsed is returned,
04475  * and the text is placed in the string pointed to by token.
04476  * Warning: this method may recurse.
04477  */
04478 static int
04479 get_token(FILE * fp, char *token, int maxtlen)
04480 {
04481     register int    ch, ch_next;
04482     register char  *cp = token;
04483     register int    hash = 0;
04484     register struct tok *tp;
04485     int             too_long = 0;
04486 
04487     /*
04488      * skip all white space 
04489      */
04490     do {
04491         ch = getc(fp);
04492         if (ch == '\n')
04493             mibLine++;
04494     }
04495     while (isspace(ch) && ch != EOF);
04496     *cp++ = ch;
04497     *cp = '\0';
04498     switch (ch) {
04499     case EOF:
04500         return ENDOFFILE;
04501     case '"':
04502         return parseQuoteString(fp, token, maxtlen);
04503     case '\'':                 /* binary or hex constant */
04504         while ((ch = getc(fp)) != EOF && ch != '\''
04505                && cp - token < maxtlen - 2)
04506             *cp++ = ch;
04507         if (ch == '\'') {
04508             unsigned long   val = 0;
04509             *cp++ = '\'';
04510             *cp++ = ch = getc(fp);
04511             *cp = 0;
04512             cp = token + 1;
04513             switch (ch) {
04514             case EOF:
04515                 return ENDOFFILE;
04516             case 'b':
04517             case 'B':
04518                 while ((ch = *cp++) != '\'')
04519                     if (ch != '0' && ch != '1')
04520                         return LABEL;
04521                     else
04522                         val = val * 2 + ch - '0';
04523                 break;
04524             case 'h':
04525             case 'H':
04526                 while ((ch = *cp++) != '\'')
04527                     if ('0' <= ch && ch <= '9')
04528                         val = val * 16 + ch - '0';
04529                     else if ('a' <= ch && ch <= 'f')
04530                         val = val * 16 + ch - 'a' + 10;
04531                     else if ('A' <= ch && ch <= 'F')
04532                         val = val * 16 + ch - 'A' + 10;
04533                     else
04534                         return LABEL;
04535                 break;
04536             default:
04537                 return LABEL;
04538             }
04539             sprintf(token, "%ld", val);
04540             return NUMBER;
04541         } else
04542             return LABEL;
04543     case '(':
04544         return LEFTPAREN;
04545     case ')':
04546         return RIGHTPAREN;
04547     case '{':
04548         return LEFTBRACKET;
04549     case '}':
04550         return RIGHTBRACKET;
04551     case '[':
04552         return LEFTSQBRACK;
04553     case ']':
04554         return RIGHTSQBRACK;
04555     case ';':
04556         return SEMI;
04557     case ',':
04558         return COMMA;
04559     case '|':
04560         return BAR;
04561     case '.':
04562         ch_next = getc(fp);
04563         if (ch_next == '.')
04564             return RANGE;
04565         ungetc(ch_next, fp);
04566         return LABEL;
04567     case ':':
04568         ch_next = getc(fp);
04569         if (ch_next != ':') {
04570             ungetc(ch_next, fp);
04571             return LABEL;
04572         }
04573         ch_next = getc(fp);
04574         if (ch_next != '=') {
04575             ungetc(ch_next, fp);
04576             return LABEL;
04577         }
04578         return EQUALS;
04579     case '-':
04580         ch_next = getc(fp);
04581         if (ch_next == '-') {
04582             if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
04583                                        NETSNMP_DS_LIB_MIB_COMMENT_TERM)) {
04584                 /*
04585                  * Treat the rest of this line as a comment. 
04586                  */
04587                 while ((ch_next != EOF) && (ch_next != '\n'))
04588                     ch_next = getc(fp);
04589             } else {
04590                 /*
04591                  * Treat the rest of the line or until another '--' as a comment 
04592                  */
04593                 /*
04594                  * (this is the "technically" correct way to parse comments) 
04595                  */
04596                 ch = ' ';
04597                 ch_next = getc(fp);
04598                 while (ch_next != EOF && ch_next != '\n' &&
04599                        (ch != '-' || ch_next != '-')) {
04600                     ch = ch_next;
04601                     ch_next = getc(fp);
04602                 }
04603             }
04604             if (ch_next == EOF)
04605                 return ENDOFFILE;
04606             if (ch_next == '\n')
04607                 mibLine++;
04608             return get_token(fp, token, maxtlen);
04609         }
04610         ungetc(ch_next, fp);
04611     default:
04612         /*
04613          * Accumulate characters until end of token is found.  Then attempt to
04614          * match this token as a reserved word.  If a match is found, return the
04615          * type.  Else it is a label.
04616          */
04617         if (!is_labelchar(ch))
04618             return LABEL;
04619         hash += tolower(ch);
04620       more:
04621         while (is_labelchar(ch_next = getc(fp))) {
04622             hash += tolower(ch_next);
04623             if (cp - token < maxtlen - 1)
04624                 *cp++ = ch_next;
04625             else
04626                 too_long = 1;
04627         }
04628         ungetc(ch_next, fp);
04629         *cp = '\0';
04630 
04631         if (too_long)
04632             print_error("Warning: token too long", token, CONTINUE);
04633         for (tp = buckets[BUCKET(hash)]; tp; tp = tp->next) {
04634             if ((tp->hash == hash) && (!label_compare(tp->name, token)))
04635                 break;
04636         }
04637         if (tp) {
04638             if (tp->token != CONTINUE)
04639                 return (tp->token);
04640             while (isspace((ch_next = getc(fp))))
04641                 if (ch_next == '\n')
04642                     mibLine++;
04643             if (ch_next == EOF)
04644                 return ENDOFFILE;
04645             if (isalnum(ch_next)) {
04646                 *cp++ = ch_next;
04647                 hash += tolower(ch_next);
04648                 goto more;
04649             }
04650         }
04651         if (token[0] == '-' || isdigit(token[0])) {
04652             for (cp = token + 1; *cp; cp++)
04653                 if (!isdigit(*cp))
04654                     return LABEL;
04655             return NUMBER;
04656         }
04657         return LABEL;
04658     }
04659 }
04660 
04661 int
04662 snmp_get_token(FILE * fp, char *token, int maxtlen)
04663 {
04664     return get_token(fp, token, maxtlen);
04665 }
04666 
04667 int
04668 add_mibfile(const char* tmpstr, const char* d_name, FILE *ip )
04669 {
04670     FILE           *fp;
04671     char            token[MAXTOKEN], token2[MAXTOKEN];
04672 
04673     /*
04674      * which module is this 
04675      */
04676     if ((fp = fopen(tmpstr, "r")) == NULL) {
04677         snmp_log_perror(tmpstr);
04678         return 1;
04679     }
04680     DEBUGMSGTL(("parse-mibs", "Checking file: %s...\n",
04681                 tmpstr));
04682     mibLine = 1;
04683     File = tmpstr;
04684     get_token(fp, token, MAXTOKEN);
04685     /*
04686      * simple test for this being a MIB 
04687      */
04688     if (get_token(fp, token2, MAXTOKEN) == DEFINITIONS) {
04689         new_module(token, tmpstr);
04690         if (ip)
04691             fprintf(ip, "%s %s\n", token, d_name);
04692         fclose(fp);
04693         return 0;
04694     } else {
04695         fclose(fp);
04696         return 1;
04697     }
04698 }
04699 
04700 /* For Win32 platforms, the directory does not maintain a last modification
04701  * date that we can compare with the modification date of the .index file.
04702  * Therefore there is no way to know whether any .index file is valid.
04703  * This is the reason for the #if !(defined(WIN32) || defined(cygwin))
04704  * in the add_mibdir function
04705  */
04706 int
04707 add_mibdir(const char *dirname)
04708 {
04709     FILE           *ip;
04710     DIR            *dir, *dir2;
04711     const char     *oldFile = File;
04712     struct dirent  *file;
04713     char            tmpstr[300];
04714     int             count = 0;
04715     int             fname_len = 0;
04716 #if !(defined(WIN32) || defined(cygwin))
04717     char            token[MAXTOKEN];
04718     char space;
04719     char newline;
04720     struct stat     dir_stat, idx_stat;
04721     char            tmpstr1[300];
04722     int empty = 1;
04723 #endif
04724 
04725     DEBUGMSGTL(("parse-mibs", "Scanning directory %s\n", dirname));
04726 #if !(defined(WIN32) || defined(cygwin))
04727     snprintf(token, sizeof(token), "%s/%s", dirname, ".index");
04728     token[ sizeof(token)-1 ] = 0;
04729     if (stat(token, &idx_stat) == 0 && stat(dirname, &dir_stat) == 0) {
04730         if (dir_stat.st_mtime < idx_stat.st_mtime) {
04731             DEBUGMSGTL(("parse-mibs", "The index is good\n"));
04732             if ((ip = fopen(token, "r")) != NULL) {
04733                 while (fscanf(ip, "%127s%c%299s%c", token, &space, tmpstr,
04734                     &newline) == 4) {
04735 
04736                     empty = 0;
04737                     /*
04738                      * If an overflow of the token or tmpstr buffers has been
04739                      * found log a message and break out of the while loop,
04740                      * thus the rest of the file tokens will be ignored.
04741                      */
04742                     if (space != ' ' || newline != '\n') {
04743                         snmp_log(LOG_ERR,
04744                             "add_mibdir: strings scanned in from %s/%s " \
04745                             "are too large.  count = %d\n ", dirname,
04746                             ".index", count);
04747                             break;
04748                     }
04749                    
04750                     snprintf(tmpstr1, sizeof(tmpstr1), "%s/%s", dirname, tmpstr);
04751                     tmpstr1[ sizeof(tmpstr1)-1 ] = 0;
04752                     new_module(token, tmpstr1);
04753                     count++;
04754                 }
04755                 fclose(ip);
04756                 if ( !empty ) {
04757                     return count;
04758                 }
04759                 DEBUGMSGTL(("parse-mibs", "Empty MIB index\n"));
04760             } else
04761                 DEBUGMSGTL(("parse-mibs", "Can't read index\n"));
04762         } else
04763             DEBUGMSGTL(("parse-mibs", "Index outdated\n"));
04764     } else
04765         DEBUGMSGTL(("parse-mibs", "No index\n"));
04766 #endif
04767 
04768     if ((dir = opendir(dirname))) {
04769         snprintf(tmpstr, sizeof(tmpstr), "%s/.index", dirname);
04770         tmpstr[ sizeof(tmpstr)-1 ] = 0;
04771         ip = fopen(tmpstr, "w");
04772         while ((file = readdir(dir))) {
04773             /*
04774              * Only parse file names that don't begin with a '.' 
04775              * Also skip files ending in '~', or starting/ending
04776              * with '#' which are typically editor backup files.
04777              */
04778             if (file->d_name != NULL) {
04779               fname_len = strlen( file->d_name );
04780               if (fname_len > 0 && file->d_name[0] != '.' 
04781                                 && file->d_name[0] != '#'
04782                                 && file->d_name[fname_len-1] != '#'
04783                                 && file->d_name[fname_len-1] != '~') {
04784                 snprintf(tmpstr, sizeof(tmpstr), "%s/%s", dirname, file->d_name);
04785                 tmpstr[ sizeof(tmpstr)-1 ] = 0;
04786                 if ((dir2 = opendir(tmpstr))) {
04787                     /*
04788                      * file is a directory, don't read it 
04789                      */
04790                     closedir(dir2);
04791                 } else {
04792                     if ( add_mibfile( tmpstr, file->d_name, ip ))
04793                         count++;
04794                 }
04795               }
04796             }
04797         }
04798         File = oldFile;
04799         closedir(dir);
04800         if (ip)
04801             fclose(ip);
04802         return (count);
04803     }
04804     else
04805         DEBUGMSGTL(("parse-mibs","cannot open MIB directory %s\n", dirname));
04806 
04807     return (-1);
04808 }
04809 
04810 
04811 /*
04812  * Returns the root of the whole tree
04813  *   (for backwards compatability)
04814  */
04815 struct tree    *
04816 read_mib(const char *filename)
04817 {
04818     FILE           *fp;
04819     char            token[MAXTOKEN];
04820 
04821     fp = fopen(filename, "r");
04822     if (fp == NULL) {
04823         snmp_log_perror(filename);
04824         return NULL;
04825     }
04826     mibLine = 1;
04827     File = filename;
04828     DEBUGMSGTL(("parse-mibs", "Parsing file: %s...\n", filename));
04829     get_token(fp, token, MAXTOKEN);
04830     fclose(fp);
04831     new_module(token, filename);
04832     (void) netsnmp_read_module(token);
04833 
04834     return tree_head;
04835 }
04836 
04837 
04838 struct tree    *
04839 read_all_mibs()
04840 {
04841     struct module  *mp;
04842 
04843     for (mp = module_head; mp; mp = mp->next)
04844         if (mp->no_imports == -1)
04845             netsnmp_read_module(mp->name);
04846     adopt_orphans();
04847 
04848     return tree_head;
04849 }
04850 
04851 
04852 #ifdef TEST
04853 main(int argc, char *argv[])
04854 {
04855     int             i;
04856     struct tree    *tp;
04857     netsnmp_ds_set_int(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_MIB_WARNINGS, 2);
04858 
04859     netsnmp_init_mib();
04860 
04861     if (argc == 1)
04862         (void) read_all_mibs();
04863     else
04864         for (i = 1; i < argc; i++)
04865             read_mib(argv[i]);
04866 
04867     for (tp = tree_head; tp; tp = tp->next_peer)
04868         print_subtree(stdout, tp, 0);
04869     free_tree(tree_head);
04870 
04871     return 0;
04872 }
04873 #endif                          /* TEST */
04874 
04875 static int
04876 parseQuoteString(FILE * fp, char *token, int maxtlen)
04877 {
04878     register int    ch;
04879     int             count = 0;
04880     int             too_long = 0;
04881     char           *token_start = token;
04882 
04883     for (ch = getc(fp); ch != EOF; ch = getc(fp)) {
04884         if (ch == '\r')
04885             continue;
04886         if (ch == '\n') {
04887             mibLine++;
04888         } else if (ch == '"') {
04889             *token = '\0';
04890             if (too_long && netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID, 
04891                                            NETSNMP_DS_LIB_MIB_WARNINGS) > 1) {
04892                 /*
04893                  * show short form for brevity sake 
04894                  */
04895                 char            ch_save = *(token_start + 50);
04896                 *(token_start + 50) = '\0';
04897                 print_error("Warning: string too long",
04898                             token_start, QUOTESTRING);
04899                 *(token_start + 50) = ch_save;
04900             }
04901             return QUOTESTRING;
04902         }
04903         /*
04904          * maximum description length check.  If greater, keep parsing
04905          * but truncate the string 
04906          */
04907         if (++count < maxtlen)
04908             *token++ = ch;
04909         else
04910             too_long = 1;
04911     }
04912 
04913     return 0;
04914 }
04915 
04916 /*
04917  * struct index_list *
04918  * getIndexes(FILE *fp):
04919  *   This routine parses a string like  { blah blah blah } and returns a
04920  *   list of the strings enclosed within it.
04921  *
04922  */
04923 static struct index_list *
04924 getIndexes(FILE * fp, struct index_list **retp)
04925 {
04926     int             type;
04927     char            token[MAXTOKEN];
04928     char            nextIsImplied = 0;
04929 
04930     struct index_list *mylist = NULL;
04931     struct index_list **mypp = &mylist;
04932 
04933     free_indexes(retp);
04934 
04935     type = get_token(fp, token, MAXTOKEN);
04936 
04937     if (type != LEFTBRACKET) {
04938         return NULL;
04939     }
04940 
04941     type = get_token(fp, token, MAXTOKEN);
04942     while (type != RIGHTBRACKET && type != ENDOFFILE) {
04943         if ((type == LABEL) || (type & SYNTAX_MASK)) {
04944             *mypp =
04945                 (struct index_list *) calloc(1, sizeof(struct index_list));
04946             if (*mypp) {
04947                 (*mypp)->ilabel = strdup(token);
04948                 (*mypp)->isimplied = nextIsImplied;
04949                 mypp = &(*mypp)->next;
04950                 nextIsImplied = 0;
04951             }
04952         } else if (type == IMPLIED) {
04953             nextIsImplied = 1;
04954         }
04955         type = get_token(fp, token, MAXTOKEN);
04956     }
04957 
04958     *retp = mylist;
04959     return mylist;
04960 }
04961 
04962 static struct varbind_list *
04963 getVarbinds(FILE * fp, struct varbind_list **retp)
04964 {
04965     int             type;
04966     char            token[MAXTOKEN];
04967 
04968     struct varbind_list *mylist = NULL;
04969     struct varbind_list **mypp = &mylist;
04970 
04971     free_varbinds(retp);
04972 
04973     type = get_token(fp, token, MAXTOKEN);
04974 
04975     if (type != LEFTBRACKET) {
04976         return NULL;
04977     }
04978 
04979     type = get_token(fp, token, MAXTOKEN);
04980     while (type != RIGHTBRACKET && type != ENDOFFILE) {
04981         if ((type == LABEL) || (type & SYNTAX_MASK)) {
04982             *mypp =
04983                 (struct varbind_list *) calloc(1,
04984                                                sizeof(struct
04985                                                       varbind_list));
04986             if (*mypp) {
04987                 (*mypp)->vblabel = strdup(token);
04988                 mypp = &(*mypp)->next;
04989             }
04990         }
04991         type = get_token(fp, token, MAXTOKEN);
04992     }
04993 
04994     *retp = mylist;
04995     return mylist;
04996 }
04997 
04998 static void
04999 free_indexes(struct index_list **spp)
05000 {
05001     if (spp && *spp) {
05002         struct index_list *pp, *npp;
05003 
05004         pp = *spp;
05005         *spp = NULL;
05006 
05007         while (pp) {
05008             npp = pp->next;
05009             if (pp->ilabel)
05010                 free(pp->ilabel);
05011             free(pp);
05012             pp = npp;
05013         }
05014     }
05015 }
05016 
05017 static void
05018 free_varbinds(struct varbind_list **spp)
05019 {
05020     if (spp && *spp) {
05021         struct varbind_list *pp, *npp;
05022 
05023         pp = *spp;
05024         *spp = NULL;
05025 
05026         while (pp) {
05027             npp = pp->next;
05028             if (pp->vblabel)
05029                 free(pp->vblabel);
05030             free(pp);
05031             pp = npp;
05032         }
05033     }
05034 }
05035 
05036 static void
05037 free_ranges(struct range_list **spp)
05038 {
05039     if (spp && *spp) {
05040         struct range_list *pp, *npp;
05041 
05042         pp = *spp;
05043         *spp = NULL;
05044 
05045         while (pp) {
05046             npp = pp->next;
05047             free(pp);
05048             pp = npp;
05049         }
05050     }
05051 }
05052 
05053 static void
05054 free_enums(struct enum_list **spp)
05055 {
05056     if (spp && *spp) {
05057         struct enum_list *pp, *npp;
05058 
05059         pp = *spp;
05060         *spp = NULL;
05061 
05062         while (pp) {
05063             npp = pp->next;
05064             if (pp->label)
05065                 free(pp->label);
05066             free(pp);
05067             pp = npp;
05068         }
05069     }
05070 }
05071 
05072 static struct enum_list *
05073 copy_enums(struct enum_list *sp)
05074 {
05075     struct enum_list *xp = NULL, **spp = &xp;
05076 
05077     while (sp) {
05078         *spp = (struct enum_list *) calloc(1, sizeof(struct enum_list));
05079         if (!*spp)
05080             break;
05081         (*spp)->label = strdup(sp->label);
05082         (*spp)->value = sp->value;
05083         spp = &(*spp)->next;
05084         sp = sp->next;
05085     }
05086     return (xp);
05087 }
05088 
05089 static struct range_list *
05090 copy_ranges(struct range_list *sp)
05091 {
05092     struct range_list *xp = NULL, **spp = &xp;
05093 
05094     while (sp) {
05095         *spp = (struct range_list *) calloc(1, sizeof(struct range_list));
05096         if (!*spp)
05097             break;
05098         (*spp)->low = sp->low;
05099         (*spp)->high = sp->high;
05100         spp = &(*spp)->next;
05101         sp = sp->next;
05102     }
05103     return (xp);
05104 }
05105 
05106 /*
05107  * This routine parses a string like  { blah blah blah } and returns OBJID if
05108  * it is well formed, and NULL if not.
05109  */
05110 static int
05111 tossObjectIdentifier(FILE * fp)
05112 {
05113     int             type;
05114     char            token[MAXTOKEN];
05115     int             bracketcount = 1;
05116 
05117     type = get_token(fp, token, MAXTOKEN);
05118 
05119     if (type != LEFTBRACKET)
05120         return 0;
05121     while ((type != RIGHTBRACKET || bracketcount > 0) && type != ENDOFFILE) {
05122         type = get_token(fp, token, MAXTOKEN);
05123         if (type == LEFTBRACKET)
05124             bracketcount++;
05125         else if (type == RIGHTBRACKET)
05126             bracketcount--;
05127     }
05128 
05129     if (type == RIGHTBRACKET)
05130         return OBJID;
05131     else
05132         return 0;
05133 }
05134 
05135 /* Find node in any MIB module
05136    Used by Perl modules         */
05137 struct tree    *
05138 find_node(const char *name, struct tree *subtree)
05139 {                               /* Unused */
05140     return (find_tree_node(name, -1));
05141 }
05142 
05143 /* Find node in specific MIB module
05144    Used by Perl modules         */
05145 struct tree    *
05146 find_node2(const char *name, const char *module)
05147 {                               
05148   int modid = -1;
05149   if (module) {
05150     modid = which_module(module);
05151   }
05152   if (modid == -1)
05153   {
05154     return (NULL);
05155   }
05156   return (find_tree_node(name, modid));
05157 }
05158 
05159 struct module  *
05160 find_module(int mid)
05161 {
05162     struct module  *mp;
05163 
05164     for (mp = module_head; mp != NULL; mp = mp->next) {
05165         if (mp->modid == mid)
05166             break;
05167     }
05168     if (mp != 0)
05169         return mp;
05170     return NULL;
05171 }
05172 
05173 
05174 static char     leave_indent[256];
05175 static int      leave_was_simple;
05176 
05177 static void
05178 print_mib_leaves(FILE * f, struct tree *tp, int width)
05179 {
05180     struct tree    *ntp;
05181     char           *ip = leave_indent + strlen(leave_indent) - 1;
05182     char            last_ipch = *ip;
05183 
05184     *ip = '+';
05185     if (tp->type == TYPE_OTHER || tp->type > TYPE_SIMPLE_LAST) {
05186         fprintf(f, "%s--%s(%ld)\n", leave_indent, tp->label, tp->subid);
05187         if (tp->indexes) {
05188             struct index_list *xp = tp->indexes;
05189             int             first = 1, cpos = 0, len, cmax =
05190                 width - strlen(leave_indent) - 12;
05191             *ip = last_ipch;
05192             fprintf(f, "%s  |  Index: ", leave_indent);
05193             while (xp) {
05194                 if (first)
05195                     first = 0;
05196                 else
05197                     fprintf(f, ", ");
05198                 cpos += (len = strlen(xp->ilabel) + 2);
05199                 if (cpos > cmax) {
05200                     fprintf(f, "\n");
05201                     fprintf(f, "%s  |         ", leave_indent);
05202                     cpos = len;
05203                 }
05204                 fprintf(f, "%s", xp->ilabel);
05205                 xp = xp->next;
05206             }
05207             fprintf(f, "\n");
05208             *ip = '+';
05209         }
05210     } else {
05211         const char     *acc, *typ;
05212         int             size = 0;
05213         switch (tp->access) {
05214         case MIB_ACCESS_NOACCESS:
05215             acc = "----";
05216             break;
05217         case MIB_ACCESS_READONLY:
05218             acc = "-R--";
05219             break;
05220         case MIB_ACCESS_WRITEONLY:
05221             acc = "--W-";
05222             break;
05223         case MIB_ACCESS_READWRITE:
05224             acc = "-RW-";
05225             break;
05226         case MIB_ACCESS_NOTIFY:
05227             acc = "---N";
05228             break;
05229         case MIB_ACCESS_CREATE:
05230             acc = "CR--";
05231             break;
05232         default:
05233             acc = "    ";
05234             break;
05235         }
05236         switch (tp->type) {
05237         case TYPE_OBJID:
05238             typ = "ObjID    ";
05239             break;
05240         case TYPE_OCTETSTR:
05241             typ = "String   ";
05242             size = 1;
05243             break;
05244         case TYPE_INTEGER:
05245             if (tp->enums)
05246                 typ = "EnumVal  ";
05247             else
05248                 typ = "INTEGER  ";
05249             break;
05250         case TYPE_NETADDR:
05251             typ = "NetAddr  ";
05252             break;
05253         case TYPE_IPADDR:
05254             typ = "IpAddr   ";
05255             break;
05256         case TYPE_COUNTER:
05257             typ = "Counter  ";
05258             break;
05259         case TYPE_GAUGE:
05260             typ = "Gauge    ";
05261             break;
05262         case TYPE_TIMETICKS:
05263             typ = "TimeTicks";
05264             break;
05265         case TYPE_OPAQUE:
05266             typ = "Opaque   ";
05267             size = 1;
05268             break;
05269         case TYPE_NULL:
05270             typ = "Null     ";
05271             break;
05272         case TYPE_COUNTER64:
05273             typ = "Counter64";
05274             break;
05275         case TYPE_BITSTRING:
05276             typ = "BitString";
05277             break;
05278         case TYPE_NSAPADDRESS:
05279             typ = "NsapAddr ";
05280             break;
05281         case TYPE_UNSIGNED32:
05282             typ = "Unsigned ";
05283             break;
05284         case TYPE_UINTEGER:
05285             typ = "UInteger ";
05286             break;
05287         case TYPE_INTEGER32:
05288             typ = "Integer32";
05289             break;
05290         default:
05291             typ = "         ";
05292             break;
05293         }
05294         fprintf(f, "%s-- %s %s %s(%ld)\n", leave_indent, acc, typ,
05295                 tp->label, tp->subid);
05296         *ip = last_ipch;
05297         if (tp->tc_index >= 0)
05298             fprintf(f, "%s        Textual Convention: %s\n", leave_indent,
05299                     tclist[tp->tc_index].descriptor);
05300         if (tp->enums) {
05301             struct enum_list *ep = tp->enums;
05302             int             cpos = 0, cmax =
05303                 width - strlen(leave_indent) - 16;
05304             fprintf(f, "%s        Values: ", leave_indent);
05305             while (ep) {
05306                 char            buf[80];
05307                 int             bufw;
05308                 if (ep != tp->enums)
05309                     fprintf(f, ", ");
05310                 snprintf(buf, sizeof(buf), "%s(%d)", ep->label, ep->value);
05311                 buf[ sizeof(buf)-1 ] = 0;
05312                 cpos += (bufw = strlen(buf) + 2);
05313                 if (cpos >= cmax) {
05314                     fprintf(f, "\n%s                ", leave_indent);
05315                     cpos = bufw;
05316                 }
05317                 fprintf(f, "%s", buf);
05318                 ep = ep->next;
05319             }
05320             fprintf(f, "\n");
05321         }
05322         if (tp->ranges) {
05323             struct range_list *rp = tp->ranges;
05324             if (size)
05325                 fprintf(f, "%s        Size: ", leave_indent);
05326             else
05327                 fprintf(f, "%s        Range: ", leave_indent);
05328             while (rp) {
05329                 if (rp != tp->ranges)
05330                     fprintf(f, " | ");
05331                 if (rp->low == rp->high)
05332                     fprintf(f, "%d", rp->low);
05333                 else
05334                     fprintf(f, "%d..%d", rp->low, rp->high);
05335                 rp = rp->next;
05336             }
05337             fprintf(f, "\n");
05338         }
05339     }
05340     *ip = last_ipch;
05341     strcat(leave_indent, "  |");
05342     leave_was_simple = tp->type != TYPE_OTHER;
05343 
05344     {
05345         int             i, j, count = 0;
05346         struct leave {
05347             oid             id;
05348             struct tree    *tp;
05349         }              *leaves, *lp;
05350 
05351         for (ntp = tp->child_list; ntp; ntp = ntp->next_peer)
05352             count++;
05353         if (count) {
05354             leaves = (struct leave *) calloc(count, sizeof(struct leave));
05355             if (!leaves)
05356                 return;
05357             for (ntp = tp->child_list, count = 0; ntp;
05358                  ntp = ntp->next_peer) {
05359                 for (i = 0, lp = leaves; i < count; i++, lp++)
05360                     if (lp->id >= ntp->subid)
05361                         break;
05362                 for (j = count; j > i; j--)
05363                     leaves[j] = leaves[j - 1];
05364                 lp->id = ntp->subid;
05365                 lp->tp = ntp;
05366                 count++;
05367             }
05368             for (i = 1, lp = leaves; i <= count; i++, lp++) {
05369                 if (!leave_was_simple || lp->tp->type == 0)
05370                     fprintf(f, "%s\n", leave_indent);
05371                 if (i == count)
05372                     ip[3] = ' ';
05373                 print_mib_leaves(f, lp->tp, width);
05374             }
05375             free(leaves);
05376             leave_was_simple = 0;
05377         }
05378     }
05379     ip[1] = 0;
05380 }
05381 
05382 void
05383 print_mib_tree(FILE * f, struct tree *tp, int width)
05384 {
05385     leave_indent[0] = ' ';
05386     leave_indent[1] = 0;
05387     leave_was_simple = 1;
05388     print_mib_leaves(f, tp, width);
05389 }
05390 
05391 
05392 /*
05393  * Merge the parsed object identifier with the existing node.
05394  * If there is a problem with the identifier, release the existing node.
05395  */
05396 static struct node *
05397 merge_parse_objectid(struct node *np, FILE * fp, char *name)
05398 {
05399     struct node    *nnp;
05400     /*
05401      * printf("merge defval --> %s\n",np->defaultValue); 
05402      */
05403     nnp = parse_objectid(fp, name);
05404     if (nnp) {
05405 
05406         /*
05407          * apply last OID sub-identifier data to the information 
05408          */
05409         /*
05410          * already collected for this node. 
05411          */
05412         struct node    *headp, *nextp;
05413         int             ncount = 0;
05414         nextp = headp = nnp;
05415         while (nnp->next) {
05416             nextp = nnp;
05417             ncount++;
05418             nnp = nnp->next;
05419         }
05420 
05421         np->label = nnp->label;
05422         np->subid = nnp->subid;
05423         np->modid = nnp->modid;
05424         np->parent = nnp->parent;
05425         if (nnp->filename != NULL) {
05426           free(nnp->filename);
05427         }
05428         free(nnp);
05429 
05430         if (ncount) {
05431             nextp->next = np;
05432             np = headp;
05433         }
05434     } else {
05435         free_node(np);
05436         np = NULL;
05437     }
05438 
05439     return np;
05440 }
05441 
05442 /*
05443  * transfer data to tree from node
05444  *
05445  * move pointers for alloc'd data from np to tp.
05446  * this prevents them from being freed when np is released.
05447  * parent member is not moved.
05448  *
05449  * CAUTION: nodes may be repeats of existing tree nodes.
05450  * This can happen especially when resolving IMPORT clauses.
05451  *
05452  */
05453 static void
05454 tree_from_node(struct tree *tp, struct node *np)
05455 {
05456     free_partial_tree(tp, FALSE);
05457 
05458     tp->label = np->label;
05459     np->label = NULL;
05460     tp->enums = np->enums;
05461     np->enums = NULL;
05462     tp->ranges = np->ranges;
05463     np->ranges = NULL;
05464     tp->indexes = np->indexes;
05465     np->indexes = NULL;
05466     tp->augments = np->augments;
05467     np->augments = NULL;
05468     tp->varbinds = np->varbinds;
05469     np->varbinds = NULL;
05470     tp->hint = np->hint;
05471     np->hint = NULL;
05472     tp->units = np->units;
05473     np->units = NULL;
05474     tp->description = np->description;
05475     np->description = NULL;
05476     tp->reference = np->reference;
05477     np->reference = NULL;
05478     tp->defaultValue = np->defaultValue;
05479     np->defaultValue = NULL;
05480     tp->subid = np->subid;
05481     tp->tc_index = np->tc_index;
05482     tp->type = translation_table[np->type];
05483     tp->access = np->access;
05484     tp->status = np->status;
05485 
05486     set_function(tp);
05487 }
05488 
05489 #endif /* NETSNMP_DISABLE_MIB_LOADING */