/***********************************************************************
Copyright ?2007 LGC Wireless, Inc. All rights reserved
File Name: 		snmp_mib.h
Description:	header file of snmp mib parser
Version:		v9.0.0
Author:			Roy Jiang
Create Date:	2007-6-9

History:
2007-6-9	v9.0.0	Create

BEWARE:	Portions of this file are subject to the copyrights of 
		open-source project Net-SNMP. See the Net-SNMP's COPYING file 
		for more details
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <ctype.h>
#include <errno.h>
#include "snmp_mib.h"

//Macro definition----------------------------------------------------------------
#ifndef TRUE
#define TRUE 1
#endif

#ifndef FALSE
#define FALSE 0
#endif

#define SYNTAX_MASK     0x80
/*
 * types of tokens
 * Tokens wiht the SYNTAX_MASK bit set are syntax tokens 
 */
#define CONTINUE    -1
#define ENDOFFILE   0
#define LABEL       1
#define SUBTREE     2
#define SYNTAX      3
#define OBJID       (4 | SYNTAX_MASK)
#define OCTETSTR    (5 | SYNTAX_MASK)
#define INTEGER     (6 | SYNTAX_MASK)
#define NETADDR     (7 | SYNTAX_MASK)
#define IPADDR      (8 | SYNTAX_MASK)
#define COUNTER     (9 | SYNTAX_MASK)
#define GAUGE       (10 | SYNTAX_MASK)
#define TIMETICKS   (11 | SYNTAX_MASK)
#define KW_OPAQUE   (12 | SYNTAX_MASK)
#define NUL         (13 | SYNTAX_MASK)
#define SEQUENCE    14
#define OF          15          /* SEQUENCE OF */
#define OBJTYPE     16
#define ACCESS      17
#define READONLY    18
#define READWRITE   19
#define WRITEONLY   20
#ifdef NOACCESS
#undef NOACCESS                 /* agent 'NOACCESS' token */
#endif
#define NOACCESS    21
#define STATUS      22
#define MANDATORY   23
#define KW_OPTIONAL    24
#define OBSOLETE    25
/*
 * #define RECOMMENDED 26 
 */
#define PUNCT       27
#define EQUALS      28
#define NUMBER      29
#define LEFTBRACKET 30
#define RIGHTBRACKET 31
#define LEFTPAREN   32
#define RIGHTPAREN  33
#define COMMA       34
#define DESCRIPTION 35
#define QUOTESTRING 36
#define INDEX       37
#define DEFVAL      38
#define DEPRECATED  39
#define SIZE        40
#define BITSTRING   (41 | SYNTAX_MASK)
#define NSAPADDRESS (42 | SYNTAX_MASK)
#define COUNTER64   (43 | SYNTAX_MASK)
#define OBJGROUP    44
#define NOTIFTYPE   45
#define AUGMENTS    46
#define COMPLIANCE  47
#define READCREATE  48
#define UNITS       49
#define REFERENCE   50
#define NUM_ENTRIES 51
#define MODULEIDENTITY 52
#define LASTUPDATED 53
#define ORGANIZATION 54
#define CONTACTINFO 55
#define UINTEGER32 (56 | SYNTAX_MASK)
#define CURRENT     57
#define DEFINITIONS 58
#define END         59
#define SEMI        60
#define TRAPTYPE    61
#define ENTERPRISE  62
/*
 * #define DISPLAYSTR (63 | SYNTAX_MASK) 
 */
#define BEGIN       64
#define IMPORTS     65
#define EXPORTS     66
#define ACCNOTIFY   67
#define BAR         68
#define RANGE       69
#define CONVENTION  70
#define DISPLAYHINT 71
#define FROM        72
#define AGENTCAP    73
#define MACRO       74
#define IMPLIED     75
#define SUPPORTS    76
#define INCLUDES    77
#define VARIATION   78
#define REVISION    79
#define NOTIMPL	    80
#define OBJECTS	    81
#define NOTIFICATIONS	82
#define MODULE	    83
#define MINACCESS   84
#define PRODREL	    85
#define WRSYNTAX    86
#define CREATEREQ   87
#define NOTIFGROUP  88
#define MANDATORYGROUPS	89
#define GROUP	    90
#define OBJECT	    91
#define IDENTIFIER  92
#define CHOICE	    93
#define LEFTSQBRACK	95
#define RIGHTSQBRACK	96
#define IMPLICIT    97
#define APPSYNTAX	(98 | SYNTAX_MASK)
#define OBJSYNTAX	(99 | SYNTAX_MASK)
#define SIMPLESYNTAX	(100 | SYNTAX_MASK)
#define OBJNAME		(101 | SYNTAX_MASK)
#define NOTIFNAME	(102 | SYNTAX_MASK)
#define VARIABLES	103
#define UNSIGNED32	(104 | SYNTAX_MASK)
#define INTEGER32	(105 | SYNTAX_MASK)
#define OBJIDENTITY	106

#define MODULE_NOT_FOUND		0
#define MODULE_LOADED_OK		1
#define MODULE_ALREADY_LOADED	2
#define MODULE_LOAD_FAILED		MODULE_NOT_FOUND
//for hash table
#define HASHSIZE	32
#define BUCKET(x)	(x & (HASHSIZE-1))

#define NHASHSIZE	128
#define NBUCKET(x)	(x & (NHASHSIZE-1))

#define	NUMBER_OF_ROOT_NODES	1

#define	label_compare	strcmp
#define SNMP_FREE(s)    do { if (s) { free((void *)s); s=NULL; } } while(0)
/*
 * Beware of reaching SYNTAX_MASK (0x80) 
 */
//end of macro definition---------------------------------------------------------

//public var definition-----------------------------------------------------------
int             mibLine = 0;
const char     *File = "(none)";

/*
 * A linked list of nodes.
 */
struct node {
    struct node    *next;
    char           *label;     		/* This node's (unique) textual name */
    unsigned int	subid;  		/* This node's integer subidentifier */
    int             modid;  		/* The module containing this node */
    char           *parent; 		/* The parent's textual name */
    int             tc_index;       /* index into tclist (-1 if NA) */
    int             type;   		/* The type of object this represents */
    int             access;
    int             status;
    struct enum_list *enums;        /* (optional) list of enumerated integers */
    struct range_list *ranges;
    struct index_list *indexes;
    char           *augments;
    struct varbind_list *varbinds;
    char           *hint;
    char           *units;
    char           *description;    /* description (a quoted string) */
    char           *reference; 		/* references (a quoted string) */
    char           *defaultValue;
	char           *filename;
    int             lineno;
};

struct tree    *tree_head = NULL;
struct node    *orphan_nodes = NULL;

#define MAXTC   4096
struct tc {                     /* textual conventions */
    int             type;
    int             modid;
    char           *descriptor;
    char           *hint;
    struct enum_list *enums;
    struct range_list *ranges;
    char           *description;
} tclist[MAXTC];

struct tok {
    const char     *name;       /* token name */
    int             len;        /* length not counting nul */
    int             token;      /* value */
    int             hash;       /* hash of name */
    struct tok     *next;       /* pointer to next in hash table */
};
static struct tok tokens[] = {
    {"obsolete", sizeof("obsolete") - 1, OBSOLETE}
    ,
    {"Opaque", sizeof("Opaque") - 1, KW_OPAQUE}
    ,
    {"optional", sizeof("optional") - 1, KW_OPTIONAL}
    ,
    {"LAST-UPDATED", sizeof("LAST-UPDATED") - 1, LASTUPDATED}
    ,
    {"ORGANIZATION", sizeof("ORGANIZATION") - 1, ORGANIZATION}
    ,
    {"CONTACT-INFO", sizeof("CONTACT-INFO") - 1, CONTACTINFO}
    ,
    {"MODULE-IDENTITY", sizeof("MODULE-IDENTITY") - 1, MODULEIDENTITY}
    ,
    {"MODULE-COMPLIANCE", sizeof("MODULE-COMPLIANCE") - 1, COMPLIANCE}
    ,
    {"DEFINITIONS", sizeof("DEFINITIONS") - 1, DEFINITIONS}
    ,
    {"END", sizeof("END") - 1, END}
    ,
    {"AUGMENTS", sizeof("AUGMENTS") - 1, AUGMENTS}
    ,
    {"not-accessible", sizeof("not-accessible") - 1, NOACCESS}
    ,
    {"write-only", sizeof("write-only") - 1, WRITEONLY}
    ,
    {"NsapAddress", sizeof("NsapAddress") - 1, NSAPADDRESS}
    ,
    {"UNITS", sizeof("Units") - 1, UNITS}
    ,
    {"REFERENCE", sizeof("REFERENCE") - 1, REFERENCE}
    ,
    {"NUM-ENTRIES", sizeof("NUM-ENTRIES") - 1, NUM_ENTRIES}
    ,
    {"BITSTRING", sizeof("BITSTRING") - 1, BITSTRING}
    ,
    {"BIT", sizeof("BIT") - 1, CONTINUE}
    ,
    {"BITS", sizeof("BITS") - 1, BITSTRING}
    ,
    {"Counter64", sizeof("Counter64") - 1, COUNTER64}
    ,
    {"TimeTicks", sizeof("TimeTicks") - 1, TIMETICKS}
    ,
    {"NOTIFICATION-TYPE", sizeof("NOTIFICATION-TYPE") - 1, NOTIFTYPE}
    ,
    {"OBJECT-GROUP", sizeof("OBJECT-GROUP") - 1, OBJGROUP}
    ,
    {"OBJECT-IDENTITY", sizeof("OBJECT-IDENTITY") - 1, OBJIDENTITY}
    ,
    {"IDENTIFIER", sizeof("IDENTIFIER") - 1, IDENTIFIER}
    ,
    {"OBJECT", sizeof("OBJECT") - 1, OBJECT}
    ,
    {"NetworkAddress", sizeof("NetworkAddress") - 1, NETADDR}
    ,
    {"Gauge", sizeof("Gauge") - 1, GAUGE}
    ,
    {"Gauge32", sizeof("Gauge32") - 1, GAUGE}
    ,
    {"Unsigned32", sizeof("Unsigned32") - 1, UNSIGNED32}
    ,
    {"read-write", sizeof("read-write") - 1, READWRITE}
    ,
    {"read-create", sizeof("read-create") - 1, READCREATE}
    ,
    {"OCTETSTRING", sizeof("OCTETSTRING") - 1, OCTETSTR}
    ,
    {"OCTET", sizeof("OCTET") - 1, CONTINUE}
    ,
    {"OF", sizeof("OF") - 1, OF}
    ,
    {"SEQUENCE", sizeof("SEQUENCE") - 1, SEQUENCE}
    ,
    {"NULL", sizeof("NULL") - 1, NUL}
    ,
    {"IpAddress", sizeof("IpAddress") - 1, IPADDR}
    ,
    {"UInteger32", sizeof("UInteger32") - 1, UINTEGER32}
    ,
    {"INTEGER", sizeof("INTEGER") - 1, INTEGER}
    ,
    {"Integer32", sizeof("Integer32") - 1, INTEGER32}
    ,
    {"Counter", sizeof("Counter") - 1, COUNTER}
    ,
    {"Counter32", sizeof("Counter32") - 1, COUNTER}
    ,
    {"read-only", sizeof("read-only") - 1, READONLY}
    ,
    {"DESCRIPTION", sizeof("DESCRIPTION") - 1, DESCRIPTION}
    ,
    {"INDEX", sizeof("INDEX") - 1, INDEX}
    ,
    {"DEFVAL", sizeof("DEFVAL") - 1, DEFVAL}
    ,
    {"deprecated", sizeof("deprecated") - 1, DEPRECATED}
    ,
    {"SIZE", sizeof("SIZE") - 1, SIZE}
    ,
    {"MAX-ACCESS", sizeof("MAX-ACCESS") - 1, ACCESS}
    ,
    {"ACCESS", sizeof("ACCESS") - 1, ACCESS}
    ,
    {"mandatory", sizeof("mandatory") - 1, MANDATORY}
    ,
    {"current", sizeof("current") - 1, CURRENT}
    ,
    {"STATUS", sizeof("STATUS") - 1, STATUS}
    ,
    {"SYNTAX", sizeof("SYNTAX") - 1, SYNTAX}
    ,
    {"OBJECT-TYPE", sizeof("OBJECT-TYPE") - 1, OBJTYPE}
    ,
    {"TRAP-TYPE", sizeof("TRAP-TYPE") - 1, TRAPTYPE}
    ,
    {"ENTERPRISE", sizeof("ENTERPRISE") - 1, ENTERPRISE}
    ,
    {"BEGIN", sizeof("BEGIN") - 1, BEGIN}
    ,
    {"IMPORTS", sizeof("IMPORTS") - 1, IMPORTS}
    ,
    {"EXPORTS", sizeof("EXPORTS") - 1, EXPORTS}
    ,
    {"accessible-for-notify", sizeof("accessible-for-notify") - 1, ACCNOTIFY}
    ,
    {"TEXTUAL-CONVENTION", sizeof("TEXTUAL-CONVENTION") - 1, CONVENTION}
    ,
    {"NOTIFICATION-GROUP", sizeof("NOTIFICATION-GROUP") - 1, NOTIFGROUP}
    ,
    {"DISPLAY-HINT", sizeof("DISPLAY-HINT") - 1, DISPLAYHINT}
    ,
    {"FROM", sizeof("FROM") - 1, FROM}
    ,
    {"AGENT-CAPABILITIES", sizeof("AGENT-CAPABILITIES") - 1, AGENTCAP}
    ,
    {"MACRO", sizeof("MACRO") - 1, MACRO}
    ,
    {"IMPLIED", sizeof("IMPLIED") - 1, IMPLIED}
    ,
    {"SUPPORTS", sizeof("SUPPORTS") - 1, SUPPORTS}
    ,
    {"INCLUDES", sizeof("INCLUDES") - 1, INCLUDES}
    ,
    {"VARIATION", sizeof("VARIATION") - 1, VARIATION}
    ,
    {"REVISION", sizeof("REVISION") - 1, REVISION}
    ,
    {"not-implemented", sizeof("not-implemented") - 1, NOTIMPL}
    ,
    {"OBJECTS", sizeof("OBJECTS") - 1, OBJECTS}
    ,
    {"NOTIFICATIONS", sizeof("NOTIFICATIONS") - 1, NOTIFICATIONS}
    ,
    {"MODULE", sizeof("MODULE") - 1, MODULE}
    ,
    {"MIN-ACCESS", sizeof("MIN-ACCESS") - 1, MINACCESS}
    ,
    {"PRODUCT-RELEASE", sizeof("PRODUCT-RELEASE") - 1, PRODREL}
    ,
    {"WRITE-SYNTAX", sizeof("WRITE-SYNTAX") - 1, WRSYNTAX}
    ,
    {"CREATION-REQUIRES", sizeof("CREATION-REQUIRES") - 1, CREATEREQ}
    ,
    {"MANDATORY-GROUPS", sizeof("MANDATORY-GROUPS") - 1, MANDATORYGROUPS}
    ,
    {"GROUP", sizeof("GROUP") - 1, GROUP}
    ,
    {"CHOICE", sizeof("CHOICE") - 1, CHOICE}
    ,
    {"IMPLICIT", sizeof("IMPLICIT") - 1, IMPLICIT}
    ,
    {"ObjectSyntax", sizeof("ObjectSyntax") - 1, OBJSYNTAX}
    ,
    {"SimpleSyntax", sizeof("SimpleSyntax") - 1, SIMPLESYNTAX}
    ,
    {"ApplicationSyntax", sizeof("ApplicationSyntax") - 1, APPSYNTAX}
    ,
    {"ObjectName", sizeof("ObjectName") - 1, OBJNAME}
    ,
    {"NotificationName", sizeof("NotificationName") - 1, NOTIFNAME}
    ,
    {"VARIABLES", sizeof("VARIABLES") - 1, VARIABLES}
    ,
    {NULL}
};

/*
 * This is one element of an object identifier with either an integer
 * subidentifier, or a textual string label, or both.
 * The subid is -1 if not present, and label is NULL if not present.
 */
struct subid_s {
    int             subid;
    int             modid;
    char           *label;
};

struct objgroup {
    char           *name;
    int             line;
    struct objgroup *next;
}*objgroups = NULL, *objects = NULL, *notifs = NULL;

static struct tok *buckets[HASHSIZE];
static struct node *nbuckets[NHASHSIZE];
static struct tree *tbuckets[NHASHSIZE];

struct module *module_head = NULL;

static int      current_module = 0;
static int      max_module = 0;
static int      anonymous = 0;
static char    *last_err_module = 0;    /* no repeats on "Cannot find module..." */

static struct module_import root_imports[NUMBER_OF_ROOT_NODES];

//end of public var definition----------------------------------------------------

//forward function declearation---------------------------------------------------
struct tree *read_mib(const char *filename);
struct tree *read_module(const char *name);
static int read_module_internal(const char *name);
int get_token(FILE * fp, char *token, int maxtlen);
void new_module(const char *name, const char *file);
static int is_labelchar(int ich);
static void print_error(const char *str, const char *token, int type);
static int name_hash(const char *name);
void init_mib_internals(void);
static void build_translation_table();
static void init_tree_roots();
static struct node *parse(FILE * fp, struct node *root);
void print_nodes(FILE * fp, struct node *root);
static void scan_objlist(struct node *root, struct objgroup *list, const char *error);
static void do_linkup(struct module *mp, struct node *np);
static void init_node_hash(struct node *nodes);
static int get_tc_index(const char *descriptor, int modid);
static void do_subtree(struct tree *root, struct node **nodes);
static void tree_from_node(struct tree *tp, struct node *np);
static void free_node(struct node *np);
void free_tree(struct tree *Tree);
static void free_indexes(struct index_list **spp);
static void free_varbinds(struct varbind_list **spp);
static void free_enums(struct enum_list **spp);
static void free_ranges(struct range_list **spp);
static void unlink_tbucket(struct tree *tp);
static void unlink_tree(struct tree *tp);
static void merge_anon_children(struct tree *tp1, struct tree *tp2);
static void free_partial_tree(struct tree *tp, int keep_label);
int which_module(const char *name);
static struct node *parse_macro(FILE * fp, char *name);
static struct node *alloc_node(int modid);
static struct node *parse_objecttype(FILE * fp, char *name);
static int get_tc(const char *descriptor, int modid, int *tc_index,
       struct enum_list **ep, struct range_list **rp, char **hint);
static struct index_list *getIndexes(FILE * fp, struct index_list **retp);
static int tossObjectIdentifier(FILE * fp);
static struct node *merge_parse_objectid(struct node *np, FILE * fp, char *name);
static struct node *parse_objectid(FILE * fp, char *name);
static int getoid(FILE * fp, struct subid_s *id, int length);
static struct node *parse_objectgroup(FILE * fp, char *name, int what, struct objgroup **ol);
static struct varbind_list *getVarbinds(FILE * fp, struct varbind_list **retp);
static struct node *parse_trapDefinition(FILE * fp, char *name);
static struct node *parse_notificationDefinition(FILE * fp, char *name);
static struct node *parse_compliance(FILE * fp, char *name);
static int compliance_lookup(const char *name, int modid);
char *module_name(int modid, char *cp);
struct tree *find_tree_node(const char *name, int modid);
static int eat_syntax(FILE * fp, char *token, int maxtoken);
static struct node *parse_capabilities(FILE * fp, char *name);
static struct node *parse_moduleIdentity(FILE * fp, char *name);
static struct node *parse_asntype(FILE * fp, char *name, int *ntype, char *ntoken);
static void parse_imports(FILE * fp);
static void check_utc(const char *utc);
static void print_module_not_found(const char *cp);
void print_subtree(FILE * f, struct tree *tree, int count);
static int parseQuoteString(FILE * fp, char *token, int maxtlen);
//end of forward function declearation--------------------------------------------

//TOP LAYER FUNCTION DEFINITIONS--------------------------------------------------------
//Returns the root of the whole tree
struct tree *read_mib(const char *filename)
{
    FILE           *fp;
    char            token[MAXTOKEN];

    fp = fopen(filename, "r");
    if (fp == NULL) {
//        snmp_log_perror(filename);
        return NULL;
    }
    mibLine = 1;
    File = filename;
    get_token(fp, token, MAXTOKEN);
    fclose(fp);
    new_module(token, filename);
	(void) read_module(token);
    printf("Parsing MIB file: %s", filename);
    printf(".........................................OK\n");

    return tree_head;
}

struct tree *read_module(const char *name)
{
    if (read_module_internal(name) == MODULE_NOT_FOUND)
			print_error("Cannot find module", name, CONTINUE);
    return tree_head;
}

struct tree *get_tree(int *objid, int objidlen, struct tree *subtree)
{
    struct tree    *return_tree = NULL;

    for (; subtree; subtree = subtree->next_peer) {
        if (*objid == subtree->subid)
            goto found;
    }

    return NULL;

  found:
    while (subtree->next_peer && subtree->next_peer->subid == *objid)
		subtree = subtree->next_peer;
		
    if (objidlen > 1)
        return_tree =
            get_tree(objid + 1, objidlen - 1, subtree->child_list);
            
    if (return_tree != NULL)
        return return_tree;
    else
        return subtree;
}
//END OF TOP LAYER FUNCTION DEFINITIONS-------------------------------------------------

//support function definition-----------------------------------------------------
/*
 * Parses a token from the file.  The type of the token parsed is returned,
 * and the text is placed in the string pointed to by token.
 * Warning: this method may recurse.
 */
int get_token(FILE * fp, char *token, int maxtlen)
{
    register int    ch, ch_next;
    register char  *cp = token;
    register int    hash = 0;
    register struct tok *tp;
    int             too_long = 0;

    /*
     * skip all white space 
     */
    do {
        ch = getc(fp);
        if (ch == '\n')
            mibLine++;
    }
    while (isspace(ch) && ch != EOF);
    *cp++ = ch;
    *cp = '\0';
    switch (ch) {
    case EOF:
        return ENDOFFILE;
    case '"':
        return parseQuoteString(fp, token, maxtlen);
    case '\'':                 /* binary or hex constant */
        while ((ch = getc(fp)) != EOF && ch != '\''
               && cp - token < maxtlen - 2)
            *cp++ = ch;
        if (ch == '\'') {
            unsigned int   val = 0;
            *cp++ = '\'';
            *cp++ = ch = getc(fp);
            *cp = 0;
            cp = token + 1;
            switch (ch) {
            case EOF:
                return ENDOFFILE;
            case 'b':
            case 'B':
                while ((ch = *cp++) != '\'')
                    if (ch != '0' && ch != '1')
                        return LABEL;
                    else
                        val = val * 2 + ch - '0';
                break;
            case 'h':
            case 'H':
                while ((ch = *cp++) != '\'')
                    if ('0' <= ch && ch <= '9')
                        val = val * 16 + ch - '0';
                    else if ('a' <= ch && ch <= 'f')
                        val = val * 16 + ch - 'a' + 10;
                    else if ('A' <= ch && ch <= 'F')
                        val = val * 16 + ch - 'A' + 10;
                    else
                        return LABEL;
                break;
            default:
                return LABEL;
            }
            sprintf(token, "%ld", val);
            return NUMBER;
        } else
            return LABEL;
    case '(':
        return LEFTPAREN;
    case ')':
        return RIGHTPAREN;
    case '{':
        return LEFTBRACKET;
    case '}':
        return RIGHTBRACKET;
    case '[':
        return LEFTSQBRACK;
    case ']':
        return RIGHTSQBRACK;
    case ';':
        return SEMI;
    case ',':
        return COMMA;
    case '|':
        return BAR;
    case '.':
        ch_next = getc(fp);
        if (ch_next == '.')
            return RANGE;
        ungetc(ch_next, fp);
        return LABEL;
    case ':':
        ch_next = getc(fp);
        if (ch_next != ':') {
            ungetc(ch_next, fp);
            return LABEL;
        }
        ch_next = getc(fp);
        if (ch_next != '=') {
            ungetc(ch_next, fp);
            return LABEL;
        }
        return EQUALS;
    case '-':
        ch_next = getc(fp);
        if (ch_next == '-') {
        	#if 0
			if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, 
				NETSNMP_DS_LIB_MIB_COMMENT_TERM)) {
                /*
                 * Treat the rest of this line as a comment. 
                 */
                while ((ch_next != EOF) && (ch_next != '\n'))
                    ch_next = getc(fp);
            } else 
            #endif
            {
            
                /*
                 * Treat the rest of the line or until another '--' as a comment 
                 */
                /*
                 * (this is the "technically" correct way to parse comments) 
                 */
                ch = ' ';
                ch_next = getc(fp);
                while (ch_next != EOF && ch_next != '\n' &&
                       (ch != '-' || ch_next != '-')) {
                    ch = ch_next;
                    ch_next = getc(fp);
                }
            }
            if (ch_next == EOF)
                return ENDOFFILE;
            if (ch_next == '\n')
                mibLine++;
            return get_token(fp, token, maxtlen);
        }
        ungetc(ch_next, fp);
    default:
        /*
         * Accumulate characters until end of token is found.  Then attempt to
         * match this token as a reserved word.  If a match is found, return the
         * type.  Else it is a label.
         */
        if (!is_labelchar(ch))
            return LABEL;
        hash += tolower(ch);
      more:
        while (is_labelchar(ch_next = getc(fp))) {
            hash += tolower(ch_next);
            if (cp - token < maxtlen - 1)
                *cp++ = ch_next;
            else
                too_long = 1;
        }
        ungetc(ch_next, fp);
        *cp = '\0';

        if (too_long)
            print_error("Warning: token too long", token, CONTINUE);
        for (tp = buckets[BUCKET(hash)]; tp; tp = tp->next) {
            if ((tp->hash == hash) && (!label_compare(tp->name, token)))
                break;
        }
        if (tp) {
            if (tp->token != CONTINUE)
                return (tp->token);
            while (isspace((ch_next = getc(fp))))
                if (ch_next == '\n')
                    mibLine++;
            if (ch_next == EOF)
                return ENDOFFILE;
            if (isalnum(ch_next)) {
                *cp++ = ch_next;
                hash += tolower(ch_next);
                goto more;
            }
        }
        if (token[0] == '-' || isdigit(token[0])) {
            for (cp = token + 1; *cp; cp++)
                if (!isdigit(*cp))
                    return LABEL;
            return NUMBER;
        }
        return LABEL;
    }
}

void new_module(const char *name, const char *file)
{
    struct module  *mp;

    for (mp = module_head; mp; mp = mp->next)
        if (!label_compare(mp->name, name))
			return;

    /*
     * Add this module to the list 
     */
    mp = (struct module *) calloc(1, sizeof(struct module));
    if (mp == NULL)
        return;
    mp->name = strdup(name);
    mp->file = strdup(file);
    mp->imports = NULL;
    mp->no_imports = -1;        /* Not yet loaded */
    mp->modid = max_module;
    ++max_module;

    mp->next = module_head;     /* Or add to the *end* of the list? */
    module_head = mp;
}

/*
 *  Read in the named module
 *      Returns the root of the whole tree
 *      (by analogy with 'read_mib')
 */
static int read_module_internal(const char *name)
{
	struct module  *mp;
	FILE           *fp;
	struct node    *np;

	init_mib_internals();

	for (mp = module_head; mp; mp = mp->next){
		if (!label_compare(mp->name, name)) {
			const char     *oldFile = File;
			int             oldLine = mibLine;
			int             oldModule = current_module;

			if (mp->no_imports != -1) {
			    return MODULE_ALREADY_LOADED;
			}
			if ((fp = fopen(mp->file, "r")) == NULL) {
			    printf("ERROR: can not open file: %s", mp->file);
			    return MODULE_LOAD_FAILED;
			}
			mp->no_imports = 0; /* Note that we've read the file */
			File = mp->file;
			mibLine = 1;
			current_module = mp->modid;
			/*
			 * Parse the file
			 */
			// printf("Parsing MIB file: %s\n", mp->file);
			np = parse(fp, NULL);
			fclose(fp);
			File = oldFile;
			mibLine = oldLine;
			current_module = oldModule;
			return MODULE_LOADED_OK;
		}
	}
	
    return MODULE_NOT_FOUND;
}

void init_mib_internals(void)
{
    register struct tok *tp;
    register int    b;
//    int             max_modc;

    if (tree_head)
        return;

    /*
     * Set up hash list of pre-defined tokens
     */
    memset(buckets, 0, sizeof(buckets));
    for (tp = tokens; tp->name; tp++) {
        tp->hash = name_hash(tp->name);
        b = BUCKET(tp->hash);
        if (buckets[b])
            tp->next = buckets[b];      /* BUG ??? */
        buckets[b] = tp;
    }

    /*
     * Initialise other internal structures
     */
    memset(nbuckets, 0, sizeof(nbuckets));
    memset(tbuckets, 0, sizeof(tbuckets));
    memset(tclist, 0, MAXTC * sizeof(struct tc));
    build_translation_table();
    init_tree_roots();          /* Set up initial roots */
    /*
     * Relies on 'add_mibdir' having set up the modules 
     */
}

static void init_tree_roots()
{
    struct tree    *tp, *lasttp;
    int             base_modid;
    int             hash;

    base_modid = which_module("SNMPv2-SMI");

    /*
     * build root node 
     */
    tp = (struct tree *) calloc(1, sizeof(struct tree));
    if (tp == NULL)
        return;
    tp->label = strdup("joint-iso-ccitt");
    tp->modid = base_modid;
    tp->number_modules = 1;
    tp->module_list = &(tp->modid);
    tp->subid = 2;
    tp->oid[tp->oid_len++] = tp->subid;
    tp->tc_index = -1;
//    set_function(tp);           /* from mib.c */
    hash = NBUCKET(name_hash(tp->label));
    tp->next = tbuckets[hash];
    tbuckets[hash] = tp;
    lasttp = tp;
    root_imports[0].label = strdup(tp->label);
    root_imports[0].modid = base_modid;

    /*
     * build root node 
     */
    tp = (struct tree *) calloc(1, sizeof(struct tree));
    if (tp == NULL)
        return;
    tp->next_peer = lasttp;
    tp->label = strdup("ccitt");
    tp->modid = base_modid;
    tp->number_modules = 1;
    tp->module_list = &(tp->modid);
    tp->subid = 0;
    tp->oid[tp->oid_len++] = tp->subid;
    tp->tc_index = -1;
//    set_function(tp);           /* from mib.c */
    hash = NBUCKET(name_hash(tp->label));
    tp->next = tbuckets[hash];
    tbuckets[hash] = tp;
    lasttp = tp;
    root_imports[1].label = strdup(tp->label);
    root_imports[1].modid = base_modid;

    /*
     * build root node 
     */
    tp = (struct tree *) calloc(1, sizeof(struct tree));
    if (tp == NULL)
        return;
    tp->next_peer = lasttp;
    tp->label = strdup("iso");
    tp->modid = base_modid;
    tp->number_modules = 1;
    tp->module_list = &(tp->modid);
    tp->subid = 1;
    tp->oid[tp->oid_len++] = tp->subid;
    tp->tc_index = -1;
//    set_function(tp);           /* from mib.c */
    hash = NBUCKET(name_hash(tp->label));
    tp->next = tbuckets[hash];
    tbuckets[hash] = tp;
    lasttp = tp;
    root_imports[2].label = strdup(tp->label);
    root_imports[2].modid = base_modid;

    tree_head = tp;
}

/*
 * Parses a mib file and returns a linked list of nodes found in the file.
 * Returns NULL on error.
 */
#define BETWEEN_MIBS          1
#define IN_MIB                2
static struct node *parse(FILE * fp, struct node *root)
{
    char            token[MAXTOKEN];
    char            name[MAXTOKEN];
    int             type = LABEL;
    int             lasttype = LABEL;
    int             state = BETWEEN_MIBS;
    struct node    *np, *nnp;
    struct objgroup *oldgroups = NULL, *oldobjects = NULL, *oldnotifs =
        NULL;

//    if (last_err_module)
//       free(last_err_module);
//    last_err_module = 0;

    np = root;
    if (np != NULL) {
        /*
         * now find end of chain 
         */
        while (np->next)
            np = np->next;
    }

    while (type != ENDOFFILE) {
        if (lasttype == CONTINUE)
            lasttype = type;
        else
            type = lasttype = get_token(fp, token, MAXTOKEN);

        switch (type) {
        case END:
            if (state != IN_MIB) {
                print_error("Error, END before start of MIB", NULL, type);
                return NULL;
            } else {
                struct module  *mp;
#ifdef _TEST_
                printf("\nNodes for Module %s:\n", name);
                print_nodes(stdout, root);
#endif
                scan_objlist(root, objgroups, "Undefined OBJECT-GROUP");
                scan_objlist(root, objects, "Undefined OBJECT");
                scan_objlist(root, notifs, "Undefined NOTIFICATION");
                objgroups = oldgroups;
                objects = oldobjects;
                notifs = oldnotifs;
                for (mp = module_head; mp; mp = mp->next)
                    if (mp->modid == current_module)
                        break;
                do_linkup(mp, root);
                np = root = NULL;
            }
            state = BETWEEN_MIBS;
            continue;
        case IMPORTS:
            parse_imports(fp);
            continue;
        case EXPORTS:
            while (type != SEMI && type != ENDOFFILE)
                type = get_token(fp, token, MAXTOKEN);
            continue;
        case LABEL:
        case INTEGER:
        case INTEGER32:
        case UINTEGER32:
        case UNSIGNED32:
        case COUNTER:
        case COUNTER64:
        case GAUGE:
        case IPADDR:
        case NETADDR:
        case NSAPADDRESS:
        case OBJSYNTAX:
        case APPSYNTAX:
        case SIMPLESYNTAX:
        case OBJNAME:
        case NOTIFNAME:
        case KW_OPAQUE:
        case TIMETICKS:
            break;
        case ENDOFFILE:
            continue;
        default:
            strcpy(name, token);
            type = get_token(fp, token, MAXTOKEN);
            nnp = NULL;
            if (type == MACRO) {
                nnp = parse_macro(fp, name);
                if (nnp == NULL) {
                    print_error("Bad parse of MACRO", NULL, type);
                    /*
                     * return NULL;
                     */
                }
                free_node(nnp); /* IGNORE */
                nnp = NULL;
            } else
                print_error(name, "is a reserved word", lasttype);
            continue;           /* see if we can parse the rest of the file */
        }
        strcpy(name, token);
        type = get_token(fp, token, MAXTOKEN);
        nnp = NULL;

        /*
         * Handle obsolete method to assign an object identifier to a
         * module
         */
        if (lasttype == LABEL && type == LEFTBRACKET) {
            while (type != RIGHTBRACKET && type != ENDOFFILE)
                type = get_token(fp, token, MAXTOKEN);
            if (type == ENDOFFILE) {
                print_error("Expected \"}\"", token, type);
                return NULL;
            }
            type = get_token(fp, token, MAXTOKEN);
        }

        switch (type) {
        case DEFINITIONS:
            if (state != BETWEEN_MIBS) {
                print_error("Error, nested MIBS", NULL, type);
                return NULL;
            }
            state = IN_MIB;
            current_module = which_module(name);
            oldgroups = objgroups;
            objgroups = NULL;
            oldobjects = objects;
            objects = NULL;
            oldnotifs = notifs;
            notifs = NULL;
            if (current_module == -1) {
                new_module(name, File);
                current_module = which_module(name);
            }

            while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE)
                if (type == BEGIN)
                    break;
            break;
        case OBJTYPE:
            nnp = parse_objecttype(fp, name);
            if (nnp == NULL) {
                print_error("Bad parse of OBJECT-TYPE", NULL, type);
                return NULL;
            }
            break;
        case OBJGROUP:
            nnp = parse_objectgroup(fp, name, OBJECTS, &objects);
            if (nnp == NULL) {
                print_error("Bad parse of OBJECT-GROUP", NULL, type);
                return NULL;
            }
            break;
        case NOTIFGROUP:
            nnp = parse_objectgroup(fp, name, NOTIFICATIONS, &notifs);
            if (nnp == NULL) {
                print_error("Bad parse of NOTIFICATION-GROUP", NULL, type);
                return NULL;
            }
            break;
        case TRAPTYPE:
            nnp = parse_trapDefinition(fp, name);
            if (nnp == NULL) {
                print_error("Bad parse of TRAP-TYPE", NULL, type);
                return NULL;
            }
            break;
        case NOTIFTYPE:
            nnp = parse_notificationDefinition(fp, name);
            if (nnp == NULL) {
                print_error("Bad parse of NOTIFICATION-TYPE", NULL, type);
                return NULL;
            }
            break;
        case COMPLIANCE:
            nnp = parse_compliance(fp, name);
            if (nnp == NULL) {
                print_error("Bad parse of MODULE-COMPLIANCE", NULL, type);
                return NULL;
            }
            break;
        case AGENTCAP:
            nnp = parse_capabilities(fp, name);
            if (nnp == NULL) {
                print_error("Bad parse of AGENT-CAPABILITIES", NULL, type);
                return NULL;
            }
            break;
        case MACRO:
            nnp = parse_macro(fp, name);
            if (nnp == NULL) {
                print_error("Bad parse of MACRO", NULL, type);
                /*
                 * return NULL;
                 */
            }
            free_node(nnp);     /* IGNORE */
            nnp = NULL;
            break;
        case MODULEIDENTITY:
            nnp = parse_moduleIdentity(fp, name);
            if (nnp == NULL) {
                print_error("Bad parse of MODULE-IDENTITY", NULL, type);
                return NULL;
            }
            break;
        case OBJIDENTITY:
            nnp = parse_objectgroup(fp, name, OBJECTS, &objects);
            if (nnp == NULL) {
                print_error("Bad parse of OBJECT-IDENTITY", NULL, type);
                return NULL;
            }
            break;
        case OBJECT:
            type = get_token(fp, token, MAXTOKEN);
            if (type != IDENTIFIER) {
                print_error("Expected IDENTIFIER", token, type);
                return NULL;
            }
            type = get_token(fp, token, MAXTOKEN);
            if (type != EQUALS) {
                print_error("Expected \"::=\"", token, type);
                return NULL;
            }
            nnp = parse_objectid(fp, name);
            if (nnp == NULL) {
                print_error("Bad parse of OBJECT IDENTIFIER", NULL, type);
                return NULL;
            }
            break;
        case EQUALS:
            nnp = parse_asntype(fp, name, &type, token);
            lasttype = CONTINUE;
            break;
        case ENDOFFILE:
            break;
        default:
            print_error("Bad operator", token, type);
            return NULL;
        }
        if (nnp) {
            if (np)
                np->next = nnp;
            else
                np = root = nnp;
            while (np->next)
                np = np->next;
            if (np->type == TYPE_OTHER)
                np->type = type;
        }
    }
    return root;
}

static void scan_objlist(struct node *root, struct objgroup *list, const char *error)
{
    int             oLine = mibLine;

    while (list) {
        struct objgroup *gp = list;
        struct node    *np;
        list = list->next;
        np = root;
        while (np)
            if (label_compare(np->label, gp->name))
                np = np->next;
            else
                break;
        if (!np) {
            mibLine = gp->line;
            print_error(error, gp->name, QUOTESTRING);
        }
        free(gp->name);
        free(gp);
    }
    mibLine = oLine;
}

static void do_linkup(struct module *mp, struct node *np)
{
    struct module_import *mip;
    struct node    *onp, *oldp, *newp;
    struct tree    *tp;
    int             i, more;
    /*
     * All modules implicitly import
     *   the roots of the tree
     */
    if (mp->no_imports == 0) {
        mp->no_imports = NUMBER_OF_ROOT_NODES;
        mp->imports = root_imports;
    }

    /*
     * Build the tree
     */
    init_node_hash(np);
    for (i = 0, mip = mp->imports; i < mp->no_imports; ++i, ++mip) {
        char modbuf[256];
        if (get_tc_index(mip->label, mip->modid) != -1)
            continue;
        tp = find_tree_node(mip->label, mip->modid);
        if (!tp) {
			printf("Did not find '%s' in module %s (%s)\n",
				mip->label, module_name(mip->modid, modbuf),File);
            continue;
        }
        do_subtree(tp, &np);
    }

    /*
     * If any nodes left over,
     *   check that they're not the result of a "fully qualified"
     *   name, and then add them to the list of orphans
     */

    if (!np)
        return;
    for (tp = tree_head; tp; tp = tp->next_peer)
        do_subtree(tp, &np);
    if (!np)
        return;

    /*
     * quietly move all internal references to the orphan list 
     */
    oldp = orphan_nodes;
    do {
        for (i = 0; i < NHASHSIZE; i++)
            for (onp = nbuckets[i]; onp; onp = onp->next) {
                struct node    *op = NULL;
                int             hash = NBUCKET(name_hash(onp->label));
                np = nbuckets[hash];
                while (np) {
                    if (label_compare(onp->label, np->parent)) {
                        op = np;
                        np = np->next;
                    } else {
                        if (op)
                            op->next = np->next;
                        else
                            nbuckets[hash] = np->next;
                        np->next = orphan_nodes;
                        orphan_nodes = np;
                        op = NULL;
                        np = nbuckets[hash];
                    }
                }
            }
        newp = orphan_nodes;
        more = 0;
        for (onp = orphan_nodes; onp != oldp; onp = onp->next) {
            struct node    *op = NULL;
            int             hash = NBUCKET(name_hash(onp->label));
            np = nbuckets[hash];
            while (np) {
                if (label_compare(onp->label, np->parent)) {
                    op = np;
                    np = np->next;
                } else {
                    if (op)
                        op->next = np->next;
                    else
                        nbuckets[hash] = np->next;
                    np->next = orphan_nodes;
                    orphan_nodes = np;
                    op = NULL;
                    np = nbuckets[hash];
                    more = 1;
                }
            }
        }
        oldp = newp;
    } while (more);

    /*
     * complain about left over nodes 
     */
    for (np = orphan_nodes; np && np->next; np = np->next);     /* find the end of the orphan list */
    for (i = 0; i < NHASHSIZE; i++)
        if (nbuckets[i]) {
			if (orphan_nodes)
			    onp = np->next = nbuckets[i];
			else
			    onp = orphan_nodes = nbuckets[i];
			nbuckets[i] = NULL;
			while (onp) {
				printf("Unlinked OID in %s: %s ::= { %s %d }\n",
						(mp->name ? mp->name : "<no module>"),
						(onp->label ? onp->label : "<no label>"),
						(onp->parent ? onp->parent : "<no parent>"),
						onp->subid);
				printf("Undefined identifier: %s near line %d of %s\n",
						(onp->parent ? onp->parent : "<no parent>"), 
						onp->lineno, onp->filename);
				np = onp;
				onp = onp->next;
			}
        }
    return;
}

/*
 * Find all the children of root in the list of nodes.  Link them into the
 * tree and out of the nodes list.
 */
static void do_subtree(struct tree *root, struct node **nodes)
{
    struct tree    *tp, *anon_tp = NULL;
    struct tree    *xroot = root;
    struct node    *np, **headp;
    struct node    *oldnp = NULL, *child_list = NULL, *childp = NULL;
    int             hash;
    int            *int_p;

    while (xroot->next_peer && xroot->next_peer->subid == root->subid) {
        xroot = xroot->next_peer;
    }

    tp = root;
    headp = &nbuckets[NBUCKET(name_hash(tp->label))];
    /*
     * Search each of the nodes for one whose parent is root, and
     * move each into a separate list.
     */
    for (np = *headp; np; np = np->next) {
        if (!label_compare(tp->label, np->parent)) {
            /*
             * take this node out of the node list 
             */
            if (oldnp == NULL) {
                *headp = np->next;      /* fix root of node list */
            } else {
                oldnp->next = np->next; /* link around this node */
            }
            if (child_list)
                childp->next = np;
            else
                child_list = np;
            childp = np;
        } else {
            oldnp = np;
        }

    }
    if (childp)
        childp->next = NULL;
    /*
     * Take each element in the child list and place it into the tree.
     */
    for (np = child_list; np; np = np->next) {
        struct tree    *otp = NULL;
        struct tree    *xxroot = xroot;
        anon_tp = NULL;
        tp = xroot->child_list;

        if (np->subid == -1) {
            /*
             * name ::= { parent } 
             */
            np->subid = xroot->subid;
            tp = xroot;
            xxroot = xroot->parent;
        }

        while (tp) {
            if (tp->subid == np->subid)
                break;
            else {
                otp = tp;
                tp = tp->next_peer;
            }
        }
        if (tp) {
            if (!label_compare(tp->label, np->label)) {
                /*
                 * Update list of modules 
                 */
                int_p =
                    (int *) malloc((tp->number_modules + 1) * sizeof(int));
                if (int_p == NULL)
                    return;
                memcpy(int_p, tp->module_list,
                       tp->number_modules * sizeof(int));
                int_p[tp->number_modules] = np->modid;
                if (tp->number_modules > 1)
                    free((char *) tp->module_list);
                ++tp->number_modules;
                tp->module_list = int_p;

	             /*
                 * Handle children 
                 */
                do_subtree(tp, nodes);
                continue;
            }
            if (!strncmp(np->label, ANON, ANON_LEN) ||
                !strncmp(tp->label, ANON, ANON_LEN)) {
                anon_tp = tp;   /* Need to merge these two trees later */
            }
        }

        tp = (struct tree *) calloc(1, sizeof(struct tree));
        if (tp == NULL)
            return;
        tp->parent = xxroot;
        tp->modid = np->modid;
        tp->number_modules = 1;
        tp->module_list = &(tp->modid);
        tree_from_node(tp, np);
        tp->next_peer = otp ? otp->next_peer : xxroot->child_list;
        if (otp)
            otp->next_peer = tp;
        else
            xxroot->child_list = tp;
        hash = NBUCKET(name_hash(tp->label));
        tp->next = tbuckets[hash];
        tbuckets[hash] = tp;
        do_subtree(tp, nodes);

        if (anon_tp) {
            if (!strncmp(tp->label, ANON, ANON_LEN)) {
                /*
                 * The new node is anonymous,
                 *  so merge it with the existing one.
                 */
                merge_anon_children(tp, anon_tp);

                /*
                 * unlink and destroy tp 
                 */
                unlink_tree(tp);
                free_tree(tp);
            } else if (!strncmp(anon_tp->label, ANON, ANON_LEN)) {
                struct tree    *ntp;
                /*
                 * The old node was anonymous,
                 *  so merge it with the existing one,
                 *  and fill in the full information.
                 */
                merge_anon_children(anon_tp, tp);

                /*
                 * unlink anon_tp from the hash 
                 */
                unlink_tbucket(anon_tp);

                /*
                 * get rid of old contents of anon_tp 
                 */
                free_partial_tree(anon_tp, FALSE);

                /*
                 * put in the current information 
                 */
                anon_tp->label = tp->label;
                anon_tp->child_list = tp->child_list;
                anon_tp->modid = tp->modid;
                anon_tp->tc_index = tp->tc_index;
                anon_tp->type = tp->type;
                anon_tp->enums = tp->enums;
                anon_tp->indexes = tp->indexes;
                anon_tp->augments = tp->augments;
                anon_tp->varbinds = tp->varbinds;
                anon_tp->ranges = tp->ranges;
                anon_tp->hint = tp->hint;
                anon_tp->units = tp->units;
                anon_tp->description = tp->description;
                anon_tp->reference = tp->reference;
                anon_tp->defaultValue = tp->defaultValue;
                anon_tp->parent = tp->parent;

 //               set_function(anon_tp);

                /*
                 * update parent pointer in moved children 
                 */
                ntp = anon_tp->child_list;
                while (ntp) {
                    ntp->parent = anon_tp;
                    ntp = ntp->next_peer;
                }

                /*
                 * hash in anon_tp in its new place 
                 */
                hash = NBUCKET(name_hash(anon_tp->label));
                anon_tp->next = tbuckets[hash];
                tbuckets[hash] = anon_tp;

                /*
                 * unlink and destroy tp 
                 */
                unlink_tbucket(tp);
                unlink_tree(tp);
                free(tp);
            }
            anon_tp = NULL;
        }
    }
    /*
     * free all nodes that were copied into tree 
     */
    oldnp = NULL;
    for (np = child_list; np; np = np->next) {
        if (oldnp)
            free_node(oldnp);
        oldnp = np;
    }
    if (oldnp)
        free_node(oldnp);
}

static void merge_anon_children(struct tree *tp1, struct tree *tp2)
/*
 * NB: tp1 is the 'anonymous' node 
 */
{
    struct tree    *child1, *child2, *previous;

    for (child1 = tp1->child_list; child1;) {

        for (child2 = tp2->child_list, previous = NULL;
             child2; previous = child2, child2 = child2->next_peer) {

            if (child1->subid == child2->subid) {
                /*
                 * Found 'matching' children,
                 *  so merge them
                 */
                if (!strncmp(child1->label, ANON, ANON_LEN)) {
                    merge_anon_children(child1, child2);

                    child1->child_list = NULL;
                    previous = child1;  /* Finished with 'child1' */
                    child1 = child1->next_peer;
                    free_tree(previous);
                    goto next;
                }

                else if (!strncmp(child2->label, ANON, ANON_LEN)) {
                    merge_anon_children(child2, child1);

                    if (previous)
                        previous->next_peer = child2->next_peer;
                    else
                        tp2->child_list = child2->next_peer;
                    free_tree(child2);

                    previous = child1;  /* Move 'child1' to 'tp2' */
                    child1 = child1->next_peer;
                    previous->next_peer = tp2->child_list;
                    tp2->child_list = previous;
                    for (previous = tp2->child_list;
                         previous; previous = previous->next_peer)
                        previous->parent = tp2;
                    goto next;
                } else if (!label_compare(child1->label, child2->label)) {
                    continue;
                } else {
                    /*
                     * Two copies of the same node.
                     * 'child2' adopts the children of 'child1'
                     */

                    if (child2->child_list) {
                        for (previous = child2->child_list; previous->next_peer; previous = previous->next_peer);       /* Find the end of the list */
                        previous->next_peer = child1->child_list;
                    } else
                        child2->child_list = child1->child_list;
                    for (previous = child1->child_list;
                         previous; previous = previous->next_peer)
                        previous->parent = child2;
                    child1->child_list = NULL;

                    previous = child1;  /* Finished with 'child1' */
                    child1 = child1->next_peer;
                    free_tree(previous);
                    goto next;
                }
            }
        }
        /*
         * If no match, move 'child1' to 'tp2' child_list
         */
        if (child1) {
            previous = child1;
            child1 = child1->next_peer;
            previous->parent = tp2;
            previous->next_peer = tp2->child_list;
            tp2->child_list = previous;
        }
      next:;
    }
}

/*
 * Parses a MACRO definition
 * Expect BEGIN, discard everything to end.
 * Returns 0 on error.
 */
static struct node *parse_macro(FILE * fp, char *name)
{
    register int    type;
    char            token[MAXTOKEN];
    struct node    *np;

    np = alloc_node(current_module);
    if (np == NULL)
        return (NULL);
    type = get_token(fp, token, sizeof(token));
    while (type != EQUALS && type != ENDOFFILE) {
        type = get_token(fp, token, sizeof(token));
    }
    if (type != EQUALS) {
        if (np)
            free_node(np);
        return NULL;
    }
    while (type != BEGIN && type != ENDOFFILE) {
        type = get_token(fp, token, sizeof(token));
    }
    if (type != BEGIN) {
        if (np)
            free_node(np);
        return NULL;
    }
    while (type != END && type != ENDOFFILE) {
        type = get_token(fp, token, sizeof(token));
    }
    if (type != END) {
        if (np)
            free_node(np);
        return NULL;
    }

//	printf("%s MACRO (lines %d..%d parsed and ignored).\n", name,iLine, mibLine);
	
    return np;
}

/*
 * Parses an enumeration list of the form:
 *        { label(value) label(value) ... }
 * The initial { has already been parsed.
 * Returns NULL on error.
 */
static struct enum_list *parse_enumlist(FILE * fp, struct enum_list **retp)
{
    register int    type;
    char            token[MAXTOKEN];
    struct enum_list *ep = NULL, **epp = &ep;

    free_enums(retp);

    while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE) {
        if (type == RIGHTBRACKET)
            break;
        if (type == LABEL) {
            /*
             * this is an enumerated label 
             */
            *epp =
                (struct enum_list *) calloc(1, sizeof(struct enum_list));
            if (*epp == NULL)
                return (NULL);
            /*
             * a reasonable approximation for the length 
             */
            (*epp)->label = strdup(token);
            type = get_token(fp, token, MAXTOKEN);
            if (type != LEFTPAREN) {
                print_error("Expected \"(\"", token, type);
                return NULL;
            }
            type = get_token(fp, token, MAXTOKEN);
            if (type != NUMBER) {
                print_error("Expected integer", token, type);
                return NULL;
            }
            (*epp)->value = strtol(token, NULL, 10);
            type = get_token(fp, token, MAXTOKEN);
            if (type != RIGHTPAREN) {
                print_error("Expected \")\"", token, type);
                return NULL;
            }
            epp = &(*epp)->next;
        }
    }
    if (type == ENDOFFILE) {
        print_error("Expected \"}\"", token, type);
        return NULL;
    }
    *retp = ep;
    return ep;
}

static struct range_list *parse_ranges(FILE * fp, struct range_list **retp)
{
    int             low, high;
    char            nexttoken[MAXTOKEN];
    int             nexttype;
    struct range_list *rp = NULL, **rpp = &rp;
    int             size = 0, taken = 1;

    free_ranges(retp);

    nexttype = get_token(fp, nexttoken, MAXTOKEN);
    if (nexttype == SIZE) {
        size = 1;
        taken = 0;
        nexttype = get_token(fp, nexttoken, MAXTOKEN);
        if (nexttype != LEFTPAREN)
            print_error("Expected \"(\" after SIZE", nexttoken, nexttype);
    }

    do {
        if (!taken)
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
        else
            taken = 0;
        high = low = strtol(nexttoken, NULL, 10);
        nexttype = get_token(fp, nexttoken, MAXTOKEN);
        if (nexttype == RANGE) {
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
            errno = 0;
            high = strtol(nexttoken, NULL, 10);
            if ( errno == ERANGE ) {
				printf("Warning: Upper bound not handled correctly (%s != %d): At line %d in %s\n",
				       nexttoken, high, mibLine, File);
            }
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
        }
        *rpp = (struct range_list *) calloc(1, sizeof(struct range_list));
        if (*rpp == NULL)
            break;
        (*rpp)->low = low;
        (*rpp)->high = high;
        rpp = &(*rpp)->next;

    } while (nexttype == BAR);
    if (size) {
        if (nexttype != RIGHTPAREN)
            print_error("Expected \")\" after SIZE", nexttoken, nexttype);
        nexttype = get_token(fp, nexttoken, nexttype);
    }
    if (nexttype != RIGHTPAREN)
        print_error("Expected \")\"", nexttoken, nexttype);

    *retp = rp;
    return rp;
}

/*
 * Parses a module import clause
 *   loading any modules referenced
 */
#define MAX_IMPORTS	256
static void parse_imports(FILE * fp)
{
    register int    type;
    char            token[MAXTOKEN];
    char            modbuf[256];
    struct module_import import_list[MAX_IMPORTS];
    int             this_module;
    struct module  *mp;

    int             import_count = 0;   /* Total number of imported descriptors */
    int             i = 0, old_i;       /* index of first import from each module */

    type = get_token(fp, token, MAXTOKEN);

    /*
     * Parse the IMPORTS clause
     */
    while (type != SEMI && type != ENDOFFILE) {
        if (type == LABEL) {
            if (import_count == MAX_IMPORTS) {
                print_error("Too many imported symbols", token, type);
                do {
                    type = get_token(fp, token, MAXTOKEN);
                } while (type != SEMI && type != ENDOFFILE);
                return;
            }
            import_list[import_count++].label = strdup(token);
        } else if (type == FROM) {
            type = get_token(fp, token, MAXTOKEN);
            if (import_count == i) {    /* All imports are handled internally */
                type = get_token(fp, token, MAXTOKEN);
                continue;
            }
            this_module = which_module(token);

            for (old_i = i; i < import_count; ++i)
                import_list[i].modid = this_module;

            /*
             * Recursively read any pre-requisite modules
             */
            if (read_module_internal(token) == MODULE_NOT_FOUND) {
				int found = 0;
//				for (; old_i < import_count; ++old_i) {
//					found += read_import_replacements(token, &import_list[old_i]);
//				}
				if (!found)
					print_module_not_found(token);
			}
        }
        type = get_token(fp, token, MAXTOKEN);
    }

    /*
     * Save the import information
     *   in the global module table
     */
    for (mp = module_head; mp; mp = mp->next)
        if (mp->modid == current_module) {
            if (import_count == 0)
                return;
            if (mp->imports && (mp->imports != root_imports)) {
                /*
                 * this can happen if all modules are in one source file. 
                 */
                for (i = 0; i < mp->no_imports; ++i) {
                    free((char *) mp->imports[i].label);
                }
                free((char *) mp->imports);
            }
            mp->imports = (struct module_import *)
                calloc(import_count, sizeof(struct module_import));
            if (mp->imports == NULL)
                return;
            for (i = 0; i < import_count; ++i) {
                mp->imports[i].label = import_list[i].label;
                mp->imports[i].modid = import_list[i].modid;
            }
            mp->no_imports = import_count;
            return;
        }

    /*
     * Shouldn't get this far
     */
    print_module_not_found(module_name(current_module, modbuf));
    return;
}

/*
 * Parses an OBJECT TYPE macro.
 * Returns 0 on error.
 */
static struct node *parse_objecttype(FILE * fp, char *name)
{
    register int    type;
    char            token[MAXTOKEN];
    char            nexttoken[MAXTOKEN];
    char            quoted_string_buffer[MAXQUOTESTR];
    int             nexttype, tctype;
    register struct node *np;

    type = get_token(fp, token, MAXTOKEN);
    if (type != SYNTAX) {
        print_error("Bad format for OBJECT-TYPE", token, type);
        return NULL;
    }
    np = alloc_node(current_module);
    if (np == NULL)
        return (NULL);
    type = get_token(fp, token, MAXTOKEN);
    if (type == OBJECT) {
        type = get_token(fp, token, MAXTOKEN);
        if (type != IDENTIFIER) {
            print_error("Expected IDENTIFIER", token, type);
            free_node(np);
            return NULL;
        }
        type = OBJID;
    }
    if (type == LABEL) {
        int tmp_index;
        tctype = get_tc(token, current_module, &tmp_index,
                        &np->enums, &np->ranges, &np->hint);
        type = tctype;
        np->tc_index = tmp_index;       /* store TC for later reference */
    }
    np->type = type;
    nexttype = get_token(fp, nexttoken, MAXTOKEN);
    switch (type) {
    case SEQUENCE:
        if (nexttype == OF) {
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
        }
        break;
    case INTEGER:
    case INTEGER32:
    case UINTEGER32:
    case UNSIGNED32:
    case COUNTER:
    case GAUGE:
    case BITSTRING:
    case LABEL:
        if (nexttype == LEFTBRACKET) {
            /*
             * if there is an enumeration list, parse it 
             */
            np->enums = parse_enumlist(fp, &np->enums);
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
        } else if (nexttype == LEFTPAREN) {
            /*
             * if there is a range list, parse it 
             */
            np->ranges = parse_ranges(fp, &np->ranges);
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
        }
        break;
    case OCTETSTR:
    case KW_OPAQUE:
        /*
         * parse any SIZE specification 
         */
        if (nexttype == LEFTPAREN) {
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
            if (nexttype == SIZE) {
                nexttype = get_token(fp, nexttoken, MAXTOKEN);
                if (nexttype == LEFTPAREN) {
                    np->ranges = parse_ranges(fp, &np->ranges);
                    nexttype = get_token(fp, nexttoken, MAXTOKEN);      /* ) */
                    if (nexttype == RIGHTPAREN) {
                        nexttype = get_token(fp, nexttoken, MAXTOKEN);
                        break;
                    }
                }
            }
            print_error("Bad SIZE syntax", token, type);
            free_node(np);
            return NULL;
        }
        break;
    case OBJID:
    case NETADDR:
    case IPADDR:
    case TIMETICKS:
    case NUL:
    case NSAPADDRESS:
    case COUNTER64:
        break;
    default:
        print_error("Bad syntax", token, type);
        free_node(np);
        return NULL;
    }
    if (nexttype == UNITS) {
        type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
        if (type != QUOTESTRING) {
            print_error("Bad UNITS", quoted_string_buffer, type);
            free_node(np);
            return NULL;
        }
        np->units = strdup(quoted_string_buffer);
        nexttype = get_token(fp, nexttoken, MAXTOKEN);
    }
    if (nexttype != ACCESS) {
        print_error("Should be ACCESS", nexttoken, nexttype);
        free_node(np);
        return NULL;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != READONLY && type != READWRITE && type != WRITEONLY
        && type != NOACCESS && type != READCREATE && type != ACCNOTIFY) {
        print_error("Bad ACCESS type", token, type);
        free_node(np);
        return NULL;
    }
    np->access = type;
    type = get_token(fp, token, MAXTOKEN);
    if (type != STATUS) {
        print_error("Should be STATUS", token, type);
        free_node(np);
        return NULL;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != MANDATORY && type != CURRENT && type != KW_OPTIONAL &&
        type != OBSOLETE && type != DEPRECATED) {
        print_error("Bad STATUS", token, type);
        free_node(np);
        return NULL;
    }
    np->status = type;
    /*
     * Optional parts of the OBJECT-TYPE macro
     */
    type = get_token(fp, token, MAXTOKEN);
    while (type != EQUALS && type != ENDOFFILE) {
        switch (type) {
        case DESCRIPTION:
            type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);

            if (type != QUOTESTRING) {
                print_error("Bad DESCRIPTION", quoted_string_buffer, type);
                free_node(np);
                return NULL;
            }
            np->description = strdup(quoted_string_buffer);
            break;

        case REFERENCE:
            type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
            if (type != QUOTESTRING) {
                print_error("Bad REFERENCE", quoted_string_buffer, type);
                free_node(np);
                return NULL;
            }
            np->reference = strdup(quoted_string_buffer);
            break;
        case INDEX:
            if (np->augments) {
                print_error("Cannot have both INDEX and AUGMENTS", token,
                            type);
                free_node(np);
                return NULL;
            }
            np->indexes = getIndexes(fp, &np->indexes);
            if (np->indexes == NULL) {
                print_error("Bad INDEX list", token, type);
                free_node(np);
                return NULL;
            }
            break;
        case AUGMENTS:
            if (np->indexes) {
                print_error("Cannot have both INDEX and AUGMENTS", token,
                            type);
                free_node(np);
                return NULL;
            }
            np->indexes = getIndexes(fp, &np->indexes);
            if (np->indexes == NULL) {
                print_error("Bad AUGMENTS list", token, type);
                free_node(np);
                return NULL;
            }
            np->augments = strdup(np->indexes->ilabel);
            free_indexes(&np->indexes);
            break;
        case DEFVAL:
            /*
             * Mark's defVal section 
             */
            type = get_token(fp, quoted_string_buffer, MAXTOKEN);
            if (type != LEFTBRACKET) {
                print_error("Bad DEFAULTVALUE", quoted_string_buffer,
                            type);
                free_node(np);
                return NULL;
            }

            {
                int level = 1;
                char defbuf[512];

                defbuf[0] = 0;
                while (1) {
                    type = get_token(fp, quoted_string_buffer, MAXTOKEN);
                    if ((type == RIGHTBRACKET && --level == 0)
                        || type == ENDOFFILE)
                        break;
                    else if (type == LEFTBRACKET)
                        level++;
                    if (type == QUOTESTRING) {
                        if (strlen(defbuf)+2 < sizeof(defbuf)) {
                            defbuf[ strlen(defbuf)+2 ] = 0;
                            defbuf[ strlen(defbuf)+1 ] = '"';
                            defbuf[ strlen(defbuf)   ] = '\\';
                        }
                        defbuf[ sizeof(defbuf)-1 ] = 0;
                    }
                    strncat(defbuf, quoted_string_buffer,
                            sizeof(defbuf)-strlen(defbuf));
                    defbuf[ sizeof(defbuf)-1 ] = 0;
                    if (type == QUOTESTRING) {
                        if (strlen(defbuf)+2 < sizeof(defbuf)) {
                            defbuf[ strlen(defbuf)+2 ] = 0;
                            defbuf[ strlen(defbuf)+1 ] = '"';
                            defbuf[ strlen(defbuf)   ] = '\\';
                        }
                        defbuf[ sizeof(defbuf)-1 ] = 0;
                    }
                    if (strlen(defbuf)+1 < sizeof(defbuf)) {
                        defbuf[ strlen(defbuf)+1 ] = 0;
                        defbuf[ strlen(defbuf)   ] = ' ';
                    }
                }

                if (type != RIGHTBRACKET) {
                    print_error("Bad DEFAULTVALUE", quoted_string_buffer,
                                type);
                    free_node(np);
                    return NULL;
                }

                defbuf[strlen(defbuf) - 1] = 0;
                np->defaultValue = strdup(defbuf);
            }

            break;

        case NUM_ENTRIES:
            if (tossObjectIdentifier(fp) != OBJID) {
                print_error("Bad Object Identifier", token, type);
                free_node(np);
                return NULL;
            }
            break;

        default:
            print_error("Bad format of optional clauses", token, type);
            free_node(np);
            return NULL;
		}
        type = get_token(fp, token, MAXTOKEN);
    }
    if (type != EQUALS) {
        print_error("Bad format", token, type);
        free_node(np);
        return NULL;
    }
    return merge_parse_objectid(np, fp, name);
}

/*
 * Parse a sequence of object subidentifiers for the given name.
 * The "label OBJECT IDENTIFIER ::=" portion has already been parsed.
 *
 * The majority of cases take this form :
 * label OBJECT IDENTIFIER ::= { parent 2 }
 * where a parent label and a child subidentifier number are specified.
 *
 * Variations on the theme include cases where a number appears with
 * the parent, or intermediate subidentifiers are specified by label,
 * by number, or both.
 *
 * Here are some representative samples :
 * internet        OBJECT IDENTIFIER ::= { iso org(3) dod(6) 1 }
 * mgmt            OBJECT IDENTIFIER ::= { internet 2 }
 * rptrInfoHealth  OBJECT IDENTIFIER ::= { snmpDot3RptrMgt 0 4 }
 *
 * Here is a very rare form :
 * iso             OBJECT IDENTIFIER ::= { 1 }
 *
 * Returns NULL on error.  When this happens, memory may be leaked.
 */
static struct node *parse_objectid(FILE * fp, char *name)
{
    register int    count;
    register struct subid_s *op, *nop;
    int             length;
    struct subid_s  loid[32];
    struct node    *np, *root = NULL, *oldnp = NULL;
    struct tree    *tp;

    if ((length = getoid(fp, loid, 32)) == 0) {
        print_error("Bad object identifier", NULL, CONTINUE);
        return NULL;
    }

    /*
     * Handle numeric-only object identifiers,
     *  by labelling the first sub-identifier
     */
    op = loid;
    if (!op->label) {
        if (length == 1) {
            print_error("Attempt to define a root oid", name, OBJECT);
            return NULL;
        }
        for (tp = tree_head; tp; tp = tp->next_peer)
            if ((int) tp->subid == op->subid) {
                op->label = strdup(tp->label);
                break;
            }
    }

    /*
     * Handle  "label OBJECT-IDENTIFIER ::= { subid }"
     */
    if (length == 1) {
        op = loid;
        np = alloc_node(op->modid);
        if (np == NULL)
            return (NULL);
        np->subid = op->subid;
        np->label = strdup(name);
        np->parent = op->label;
        return np;
    }

    /*
     * For each parent-child subid pair in the subid array,
     * create a node and link it into the node list.
     */
    for (count = 0, op = loid, nop = loid + 1; count < (length - 1);
         count++, op++, nop++) {
        /*
         * every node must have parent's name and child's name or number 
         */
        /*
         * XX the next statement is always true -- does it matter ?? 
         */
        if (op->label && (nop->label || (nop->subid != -1))) {
            np = alloc_node(nop->modid);
            if (np == NULL)
                return (NULL);
            if (root == NULL)
                root = np;

            np->parent = strdup(op->label);
            if (count == (length - 2)) {
                /*
                 * The name for this node is the label for this entry 
                 */
                np->label = strdup(name);
            } else {
                if (!nop->label) {
                    nop->label = (char *) malloc(20 + ANON_LEN);
                    if (nop->label == NULL)
                        return (NULL);
                    sprintf(nop->label, "%s%d", ANON, anonymous++);
                }
                np->label = strdup(nop->label);
            }
            if (nop->subid != -1)
                np->subid = nop->subid;
            else
                print_error("Warning: This entry is pretty silly",
                            np->label, CONTINUE);

            /*
             * set up next entry 
             */
            if (oldnp)
                oldnp->next = np;
            oldnp = np;
        }                       /* end if(op->label... */
    }

    /*
     * free the loid array 
     */
    for (count = 0, op = loid; count < length; count++, op++) {
        if (op->label)
            free(op->label);
    }

    return root;
}

/*
 * Parses an OBJECT GROUP macro.
 * Returns 0 on error.
 *
 * Also parses object-identity, since they are similar (ignore STATUS).
 *   - WJH 10/96
 */
static struct node *parse_objectgroup(FILE * fp, char *name, int what, struct objgroup **ol)
{
    int             type;
    char            token[MAXTOKEN];
    char            quoted_string_buffer[MAXQUOTESTR];
    struct node    *np;

    np = alloc_node(current_module);
    if (np == NULL)
        return (NULL);
    type = get_token(fp, token, MAXTOKEN);
    if (type == what) {
        type = get_token(fp, token, MAXTOKEN);
        if (type != LEFTBRACKET) {
            print_error("Expected \"{\"", token, type);
            goto skip;
        }
        do {
            struct objgroup *o;
            type = get_token(fp, token, MAXTOKEN);
            if (type != LABEL) {
                print_error("Bad identifier", token, type);
                goto skip;
            }
            o = (struct objgroup *) malloc(sizeof(struct objgroup));
            if (!o) {
                print_error("Resource failure", token, type);
                goto skip;
            }
            o->line = mibLine;
            o->name = strdup(token);
            o->next = *ol;
            *ol = o;
            type = get_token(fp, token, MAXTOKEN);
        } while (type == COMMA);
        if (type != RIGHTBRACKET) {
            print_error("Expected \"}\" after list", token, type);
            goto skip;
        }
        type = get_token(fp, token, type);
    }
    if (type != STATUS) {
        print_error("Expected STATUS", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) {
        print_error("Bad STATUS value", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != DESCRIPTION) {
        print_error("Expected DESCRIPTION", token, type);
        goto skip;
    }
    type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
    if (type != QUOTESTRING) {
        print_error("Bad DESCRIPTION", quoted_string_buffer, type);
        free_node(np);
        return NULL;
    }
    np->description = strdup(quoted_string_buffer);
    type = get_token(fp, token, MAXTOKEN);
    if (type == REFERENCE) {
        type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
        if (type != QUOTESTRING) {
            print_error("Bad REFERENCE", quoted_string_buffer, type);
            free_node(np);
            return NULL;
        }
        np->reference = strdup(quoted_string_buffer);
        type = get_token(fp, token, MAXTOKEN);
    }
    if (type != EQUALS)
        print_error("Expected \"::=\"", token, type);
  skip:
    while (type != EQUALS && type != ENDOFFILE)
        type = get_token(fp, token, MAXTOKEN);

    return merge_parse_objectid(np, fp, name);
}

static int parseQuoteString(FILE * fp, char *token, int maxtlen)
{
    register int    ch;
    int             count = 0;
    int             too_long = 0;
    char           *token_start = token;

    for (ch = getc(fp); ch != EOF; ch = getc(fp)) {
        if (ch == '\r')
            continue;
        if (ch == '\n') {
            mibLine++;
        } else if (ch == '"') {
            *token = '\0';
            if (too_long) 
            {
                /*
                 * show short form for brevity sake 
                 */
                char            ch_save = *(token_start + 50);
                *(token_start + 50) = '\0';
                print_error("Warning: string too long",
                            token_start, QUOTESTRING);
                *(token_start + 50) = ch_save;
            }
            return QUOTESTRING;
        }
        /*
         * maximum description length check.  If greater, keep parsing
         * but truncate the string 
         */
        if (++count < maxtlen)
            *token++ = ch;
        else
            too_long = 1;
    }

    return 0;
}

/*
 * Parses a TRAP-TYPE macro.
 * Returns 0 on error.
 */
static struct node *parse_trapDefinition(FILE * fp, char *name)
{
    register int    type;
    char            token[MAXTOKEN];
    char            quoted_string_buffer[MAXQUOTESTR];
    register struct node *np;

    np = alloc_node(current_module);
    if (np == NULL)
        return (NULL);
    type = get_token(fp, token, MAXTOKEN);
    while (type != EQUALS && type != ENDOFFILE) {
        switch (type) {
        case DESCRIPTION:
            type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
            if (type != QUOTESTRING) {
                print_error("Bad DESCRIPTION", quoted_string_buffer, type);
                free_node(np);
                return NULL;
            }
            np->description = strdup(quoted_string_buffer);
            break;
        case REFERENCE:
            /* I'm not sure REFERENCEs are legal in smiv1 traps??? */
            type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
            if (type != QUOTESTRING) {
                print_error("Bad REFERENCE", quoted_string_buffer, type);
                free_node(np);
                return NULL;
            }
            np->reference = strdup(quoted_string_buffer);
            break;
        case ENTERPRISE:
            type = get_token(fp, token, MAXTOKEN);
            if (type == LEFTBRACKET) {
                type = get_token(fp, token, MAXTOKEN);
                if (type != LABEL) {
                    print_error("Bad Trap Format", token, type);
                    free_node(np);
                    return NULL;
                }
                np->parent = strdup(token);
                /*
                 * Get right bracket 
                 */
                type = get_token(fp, token, MAXTOKEN);
            } else if (type == LABEL)
                np->parent = strdup(token);
            break;
        case VARIABLES:
            np->varbinds = getVarbinds(fp, &np->varbinds);
            if (!np->varbinds) {
                print_error("Bad VARIABLES list", token, type);
                free_node(np);
                return NULL;
            }
            break;
        default:
            /*
             * NOTHING 
             */
            break;
        }
        type = get_token(fp, token, MAXTOKEN);
    }
    type = get_token(fp, token, MAXTOKEN);

    np->label = strdup(name);

    if (type != NUMBER) {
        print_error("Expected a Number", token, type);
        free_node(np);
        return NULL;
    }
    np->subid = strtoul(token, NULL, 10);
    np->next = alloc_node(current_module);
    if (np->next == NULL) {
        free_node(np);
        return (NULL);
    }
    np->next->parent = np->parent;
    np->parent = (char *) malloc(strlen(np->parent) + 2);
    if (np->parent == NULL) {
        free_node(np->next);
        free_node(np);
        return (NULL);
    }
    strcpy(np->parent, np->next->parent);
    strcat(np->parent, "#");
    np->next->label = strdup(np->parent);
    return np;
}

/*
 * Parses a NOTIFICATION-TYPE macro.
 * Returns 0 on error.
 */
static struct node *parse_notificationDefinition(FILE * fp, char *name)
{
    register int    type;
    char            token[MAXTOKEN];
    char            quoted_string_buffer[MAXQUOTESTR];
    register struct node *np;

    np = alloc_node(current_module);
    if (np == NULL)
        return (NULL);
    type = get_token(fp, token, MAXTOKEN);
    while (type != EQUALS && type != ENDOFFILE) {
        switch (type) {
        case DESCRIPTION:
            type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
            if (type != QUOTESTRING) {
                print_error("Bad DESCRIPTION", quoted_string_buffer, type);
                free_node(np);
                return NULL;
            }
            np->description = strdup(quoted_string_buffer);
            break;
        case REFERENCE:
            type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
            if (type != QUOTESTRING) {
                print_error("Bad REFERENCE", quoted_string_buffer, type);
                free_node(np);
                return NULL;
            }
            np->reference = strdup(quoted_string_buffer);
            break;
        case OBJECTS:
            np->varbinds = getVarbinds(fp, &np->varbinds);
            if (!np->varbinds) {
                print_error("Bad OBJECTS list", token, type);
                free_node(np);
                return NULL;
            }
            break;
        default:
            /*
             * NOTHING 
             */
            break;
        }
        type = get_token(fp, token, MAXTOKEN);
    }
    return merge_parse_objectid(np, fp, name);
}

static struct node *parse_compliance(FILE * fp, char *name)
{
    int             type;
    char            token[MAXTOKEN];
    char            quoted_string_buffer[MAXQUOTESTR];
    struct node    *np;

    np = alloc_node(current_module);
    if (np == NULL)
        return (NULL);
    type = get_token(fp, token, MAXTOKEN);
    if (type != STATUS) {
        print_error("Expected STATUS", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != CURRENT && type != DEPRECATED && type != OBSOLETE) {
        print_error("Bad STATUS", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != DESCRIPTION) {
        print_error("Expected DESCRIPTION", token, type);
        goto skip;
    }
    type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
    if (type != QUOTESTRING) {
        print_error("Bad DESCRIPTION", quoted_string_buffer, type);
        goto skip;
    }
	np->description = strdup(quoted_string_buffer);
    type = get_token(fp, token, MAXTOKEN);
    if (type == REFERENCE) {
        type = get_token(fp, quoted_string_buffer, MAXTOKEN);
        if (type != QUOTESTRING) {
            print_error("Bad REFERENCE", quoted_string_buffer, type);
            goto skip;
        }
        np->reference = strdup(quoted_string_buffer);
        type = get_token(fp, token, MAXTOKEN);
    }
    if (type != MODULE) {
        print_error("Expected MODULE", token, type);
        goto skip;
    }
    while (type == MODULE) {
        int             modid = -1;
        char            modname[MAXTOKEN];
        type = get_token(fp, token, MAXTOKEN);
        if (type == LABEL
            && strcmp(token, module_name(current_module, modname))) {
            modid = read_module_internal(token);
            if (modid != MODULE_LOADED_OK
                && modid != MODULE_ALREADY_LOADED) {
                print_error("Unknown module", token, type);
                goto skip;
            }
            modid = which_module(token);
            type = get_token(fp, token, MAXTOKEN);
        }
        if (type == MANDATORYGROUPS) {
            type = get_token(fp, token, MAXTOKEN);
            if (type != LEFTBRACKET) {
                print_error("Expected \"{\"", token, type);
                goto skip;
            }
            do {
                type = get_token(fp, token, MAXTOKEN);
                if (type != LABEL) {
                    print_error("Bad group name", token, type);
                    goto skip;
                }
                if (!compliance_lookup(token, modid))
                    print_error("Unknown group", token, type);
                type = get_token(fp, token, MAXTOKEN);
            } while (type == COMMA);
            if (type != RIGHTBRACKET) {
                print_error("Expected \"}\"", token, type);
                goto skip;
            }
            type = get_token(fp, token, MAXTOKEN);
        }
        while (type == GROUP || type == OBJECT) {
            if (type == GROUP) {
                type = get_token(fp, token, MAXTOKEN);
                if (type != LABEL) {
                    print_error("Bad group name", token, type);
                    goto skip;
                }
                if (!compliance_lookup(token, modid))
                    print_error("Unknown group", token, type);
                type = get_token(fp, token, MAXTOKEN);
            } else {
                type = get_token(fp, token, MAXTOKEN);
                if (type != LABEL) {
                    print_error("Bad object name", token, type);
                    goto skip;
                }
                if (!compliance_lookup(token, modid))
                    print_error("Unknown group", token, type);
                type = get_token(fp, token, MAXTOKEN);
                if (type == SYNTAX)
                    type = eat_syntax(fp, token, MAXTOKEN);
                if (type == WRSYNTAX)
                    type = eat_syntax(fp, token, MAXTOKEN);
                if (type == MINACCESS) {
                    type = get_token(fp, token, MAXTOKEN);
                    if (type != NOACCESS && type != ACCNOTIFY
                        && type != READONLY && type != WRITEONLY
                        && type != READCREATE && type != READWRITE) {
                        print_error("Bad MIN-ACCESS spec", token, type);
                        goto skip;
                    }
                    type = get_token(fp, token, MAXTOKEN);
                }
            }
            if (type != DESCRIPTION) {
                print_error("Expected DESCRIPTION", token, type);
                goto skip;
            }
            type = get_token(fp, token, MAXTOKEN);
            if (type != QUOTESTRING) {
                print_error("Bad DESCRIPTION", token, type);
                goto skip;
            }
            type = get_token(fp, token, MAXTOKEN);
        }
    }
  skip:
    while (type != EQUALS && type != ENDOFFILE)
        type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);

    return merge_parse_objectid(np, fp, name);
}

/*
 * Parses a capabilities macro
 * Returns 0 on error.
 */
static struct node *parse_capabilities(FILE * fp, char *name)
{
    int             type;
    char            token[MAXTOKEN];
    char            quoted_string_buffer[MAXQUOTESTR];
    struct node    *np;

    np = alloc_node(current_module);
    if (np == NULL)
        return (NULL);
    type = get_token(fp, token, MAXTOKEN);
    if (type != PRODREL) {
        print_error("Expected PRODUCT-RELEASE", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != QUOTESTRING) {
        print_error("Expected STRING after PRODUCT-RELEASE", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != STATUS) {
        print_error("Expected STATUS", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != CURRENT && type != OBSOLETE) {
        print_error("STATUS should be current or obsolete", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != DESCRIPTION) {
        print_error("Expected DESCRIPTION", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != QUOTESTRING) {
        print_error("Bad DESCRIPTION", token, type);
        goto skip;
    }
	np->description = strdup(token);
    type = get_token(fp, token, MAXTOKEN);
    if (type == REFERENCE) {
        type = get_token(fp, token, MAXTOKEN);
        if (type != QUOTESTRING) {
            print_error("Bad REFERENCE", token, type);
            goto skip;
        }
        np->reference = strdup(token);
        type = get_token(fp, token, type);
    }
    while (type == SUPPORTS) {
        int             modid;
        struct tree    *tp;

        type = get_token(fp, token, MAXTOKEN);
        if (type != LABEL) {
            print_error("Bad module name", token, type);
            goto skip;
        }
        modid = read_module_internal(token);
        if (modid != MODULE_LOADED_OK && modid != MODULE_ALREADY_LOADED) {
            print_error("Module not found", token, type);
            goto skip;
        }
        modid = which_module(token);
        type = get_token(fp, token, MAXTOKEN);
        if (type != INCLUDES) {
            print_error("Expected INCLUDES", token, type);
            goto skip;
        }
        type = get_token(fp, token, MAXTOKEN);
        if (type != LEFTBRACKET) {
            print_error("Expected \"{\"", token, type);
            goto skip;
        }
        do {
            type = get_token(fp, token, MAXTOKEN);
            if (type != LABEL) {
                print_error("Expected group name", token, type);
                goto skip;
            }
            tp = find_tree_node(token, modid);
            if (!tp)
                print_error("Group not found in module", token, type);
            type = get_token(fp, token, MAXTOKEN);
        } while (type == COMMA);
        if (type != RIGHTBRACKET) {
            print_error("Expected \"}\" after group list", token, type);
            goto skip;
        }
        type = get_token(fp, token, MAXTOKEN);
        while (type == VARIATION) {
            type = get_token(fp, token, MAXTOKEN);
            if (type != LABEL) {
                print_error("Bad object name", token, type);
                goto skip;
            }
            tp = find_tree_node(token, modid);
            if (!tp)
                print_error("Object not found in module", token, type);
            type = get_token(fp, token, MAXTOKEN);
            if (type == SYNTAX) {
                type = eat_syntax(fp, token, MAXTOKEN);
            }
            if (type == WRSYNTAX) {
                type = eat_syntax(fp, token, MAXTOKEN);
            }
            if (type == ACCESS) {
                type = get_token(fp, token, MAXTOKEN);
                if (type != ACCNOTIFY && type != READONLY
                    && type != READWRITE && type != READCREATE
                    && type != WRITEONLY && type != NOTIMPL) {
                    print_error("Bad ACCESS", token, type);
                    goto skip;
                }
                type = get_token(fp, token, MAXTOKEN);
            }
            if (type == CREATEREQ) {
                type = get_token(fp, token, MAXTOKEN);
                if (type != LEFTBRACKET) {
                    print_error("Expected \"{\"", token, type);
                    goto skip;
                }
                do {
                    type = get_token(fp, token, MAXTOKEN);
                    if (type != LABEL) {
                        print_error("Bad object name in list", token,
                                    type);
                        goto skip;
                    }
                    type = get_token(fp, token, MAXTOKEN);
                } while (type == COMMA);
                if (type != RIGHTBRACKET) {
                    print_error("Expected \"}\" after list", token, type);
                    goto skip;
                }
                type = get_token(fp, token, MAXTOKEN);
            }
            if (type == DEFVAL) {
                int             level = 1;
                type = get_token(fp, token, MAXTOKEN);
                if (type != LEFTBRACKET) {
                    print_error("Expected \"{\" after DEFVAL", token,
                                type);
                    goto skip;
                }
                do {
                    type = get_token(fp, token, MAXTOKEN);
                    if (type == LEFTBRACKET)
                        level++;
                    else if (type == RIGHTBRACKET)
                        level--;
                } while (type != RIGHTBRACKET && type != ENDOFFILE
                         && level != 0);
                if (type != RIGHTBRACKET) {
                    print_error("Missing \"}\" after DEFVAL", token, type);
                    goto skip;
                }
                type = get_token(fp, token, MAXTOKEN);
            }
            if (type != DESCRIPTION) {
                print_error("Expected DESCRIPTION", token, type);
                goto skip;
            }
            type = get_token(fp, token, MAXTOKEN);
            if (type != QUOTESTRING) {
                print_error("Bad DESCRIPTION", token, type);
                goto skip;
            }
            type = get_token(fp, token, MAXTOKEN);
        }
    }
    if (type != EQUALS)
        print_error("Expected \"::=\"", token, type);
  skip:
    while (type != EQUALS && type != ENDOFFILE) {
        type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
    }
    return merge_parse_objectid(np, fp, name);
}

static struct node *parse_moduleIdentity(FILE * fp, char *name)
{
    register int    type;
    char            token[MAXTOKEN];
    char            quoted_string_buffer[MAXQUOTESTR];
    register struct node *np;

    np = alloc_node(current_module);
    if (np == NULL)
        return (NULL);
    type = get_token(fp, token, MAXTOKEN);
    if (type != LASTUPDATED) {
        print_error("Expected LAST-UPDATED", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != QUOTESTRING) {
        print_error("Need STRING for LAST-UPDATED", token, type);
        goto skip;
    }
    check_utc(token);
    type = get_token(fp, token, MAXTOKEN);
    if (type != ORGANIZATION) {
        print_error("Expected ORGANIZATION", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != QUOTESTRING) {
        print_error("Bad ORGANIZATION", token, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != CONTACTINFO) {
        print_error("Expected CONTACT-INFO", token, type);
        goto skip;
    }
    type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
    if (type != QUOTESTRING) {
        print_error("Bad CONTACT-INFO", quoted_string_buffer, type);
        goto skip;
    }
    type = get_token(fp, token, MAXTOKEN);
    if (type != DESCRIPTION) {
        print_error("Expected DESCRIPTION", token, type);
        goto skip;
    }
    type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
    if (type != QUOTESTRING) {
        print_error("Bad DESCRIPTION", quoted_string_buffer, type);
        goto skip;
    }
    np->description = strdup(quoted_string_buffer);
    type = get_token(fp, token, MAXTOKEN);
    while (type == REVISION) {
        type = get_token(fp, token, MAXTOKEN);
        if (type != QUOTESTRING) {
            print_error("Bad REVISION", token, type);
            goto skip;
        }
        check_utc(token);
        type = get_token(fp, token, MAXTOKEN);
        if (type != DESCRIPTION) {
            print_error("Expected DESCRIPTION", token, type);
            goto skip;
        }
        type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
        if (type != QUOTESTRING) {
            print_error("Bad DESCRIPTION", quoted_string_buffer, type);
            goto skip;
        }
        type = get_token(fp, token, MAXTOKEN);
    }
    if (type != EQUALS)
        print_error("Expected \"::=\"", token, type);
  skip:
    while (type != EQUALS && type != ENDOFFILE) {
        type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
    }
    return merge_parse_objectid(np, fp, name);
}

/*
 * Parses an asn type.  Structures are ignored by this parser.
 * Returns NULL on error.
 */
static struct node *parse_asntype(FILE * fp, char *name, int *ntype, char *ntoken)
{
    int             type, i;
    char            token[MAXTOKEN];
    char            quoted_string_buffer[MAXQUOTESTR];
    char           *hint = NULL;
    char           *descr = NULL;
    struct tc      *tcp;
    int             level;

    type = get_token(fp, token, MAXTOKEN);
    if (type == SEQUENCE || type == CHOICE) {
        level = 0;
        while ((type = get_token(fp, token, MAXTOKEN)) != ENDOFFILE) {
            if (type == LEFTBRACKET) {
                level++;
            } else if (type == RIGHTBRACKET && --level == 0) {
                *ntype = get_token(fp, ntoken, MAXTOKEN);
                return NULL;
            }
        }
        print_error("Expected \"}\"", token, type);
        return NULL;
    } else if (type == LEFTBRACKET) {
        struct node    *np;
        int             ch_next = '{';
        ungetc(ch_next, fp);
        np = parse_objectid(fp, name);
        if (np != NULL) {
            *ntype = get_token(fp, ntoken, MAXTOKEN);
            return np;
        }
        return NULL;
    } else if (type == LEFTSQBRACK) {
        int             size = 0;
        do {
            type = get_token(fp, token, MAXTOKEN);
        } while (type != ENDOFFILE && type != RIGHTSQBRACK);
        if (type != RIGHTSQBRACK) {
            print_error("Expected \"]\"", token, type);
            return NULL;
        }
        type = get_token(fp, token, MAXTOKEN);
        if (type == IMPLICIT)
            type = get_token(fp, token, MAXTOKEN);
        *ntype = get_token(fp, ntoken, MAXTOKEN);
        if (*ntype == LEFTPAREN) {
            switch (type) {
            case OCTETSTR:
                *ntype = get_token(fp, ntoken, MAXTOKEN);
                if (*ntype != SIZE) {
                    print_error("Expected SIZE", ntoken, *ntype);
                    return NULL;
                }
                size = 1;
                *ntype = get_token(fp, ntoken, MAXTOKEN);
                if (*ntype != LEFTPAREN) {
                    print_error("Expected \"(\" after SIZE", ntoken,
                                *ntype);
                    return NULL;
                }
                /*
                 * fall through 
                 */
            case INTEGER:
                *ntype = get_token(fp, ntoken, MAXTOKEN);
                do {
                    if (*ntype != NUMBER)
                        print_error("Expected NUMBER", ntoken, *ntype);
                    *ntype = get_token(fp, ntoken, MAXTOKEN);
                    if (*ntype == RANGE) {
                        *ntype = get_token(fp, ntoken, MAXTOKEN);
                        if (*ntype != NUMBER)
                            print_error("Expected NUMBER", ntoken, *ntype);
                        *ntype = get_token(fp, ntoken, MAXTOKEN);
                    }
                } while (*ntype == BAR);
                if (*ntype != RIGHTPAREN) {
                    print_error("Expected \")\"", ntoken, *ntype);
                    return NULL;
                }
                *ntype = get_token(fp, ntoken, MAXTOKEN);
                if (size) {
                    if (*ntype != RIGHTPAREN) {
                        print_error("Expected \")\" to terminate SIZE",
                                    ntoken, *ntype);
                        return NULL;
                    }
                    *ntype = get_token(fp, ntoken, MAXTOKEN);
                }
            }
        }
        return NULL;
    } else {
        if (type == CONVENTION) {
            while (type != SYNTAX && type != ENDOFFILE) {
                if (type == DISPLAYHINT) {
                    type = get_token(fp, token, MAXTOKEN);
                    if (type != QUOTESTRING)
                        print_error("DISPLAY-HINT must be string", token,
                                    type);
                    else
                        hint = strdup(token);
                } else if (type == DESCRIPTION) {
                    type = get_token(fp, quoted_string_buffer, MAXQUOTESTR);
                    if (type != QUOTESTRING)
                        print_error("DESCRIPTION must be string", token,
                                    type);
                    else
                        descr = strdup(quoted_string_buffer);
                } else
                    type =
                        get_token(fp, quoted_string_buffer, MAXQUOTESTR);
            }
            type = get_token(fp, token, MAXTOKEN);
            if (type == OBJECT) {
                type = get_token(fp, token, MAXTOKEN);
                if (type != IDENTIFIER) {
                    print_error("Expected IDENTIFIER", token, type);
                    SNMP_FREE(hint);
                    return NULL;
                }
                type = OBJID;
            }
        } else if (type == OBJECT) {
            type = get_token(fp, token, MAXTOKEN);
            if (type != IDENTIFIER) {
                print_error("Expected IDENTIFIER", token, type);
                return NULL;
            }
            type = OBJID;
        }

        if (type == LABEL) {
            type = get_tc(token, current_module, NULL, NULL, NULL, NULL);
        }

        /*
         * textual convention 
         */
        for (i = 0; i < MAXTC; i++) {
            if (tclist[i].type == 0)
                break;
        }

        if (i == MAXTC) {
            print_error("Too many textual conventions", token, type);
            SNMP_FREE(hint);
            return NULL;
        }
        if (!(type & SYNTAX_MASK)) {
            print_error("Textual convention doesn't map to real type",
                        token, type);
            SNMP_FREE(hint);
            return NULL;
        }
        tcp = &tclist[i];
        tcp->modid = current_module;
        tcp->descriptor = strdup(name);
        tcp->hint = hint;
        tcp->description = descr;
        tcp->type = type;
        *ntype = get_token(fp, ntoken, MAXTOKEN);
        if (*ntype == LEFTPAREN) {
            tcp->ranges = parse_ranges(fp, &tcp->ranges);
            *ntype = get_token(fp, ntoken, MAXTOKEN);
        } else if (*ntype == LEFTBRACKET) {
            /*
             * if there is an enumeration list, parse it 
             */
            tcp->enums = parse_enumlist(fp, &tcp->enums);
            *ntype = get_token(fp, ntoken, MAXTOKEN);
        }
        return NULL;
    }
}

/*
 * Parses a compliance macro
 * Returns 0 on error.
 */
static int eat_syntax(FILE * fp, char *token, int maxtoken)
{
    int             type, nexttype;
    struct node    *np = alloc_node(current_module);
    char            nexttoken[MAXTOKEN];

    type = get_token(fp, token, maxtoken);
    nexttype = get_token(fp, nexttoken, MAXTOKEN);
    switch (type) {
    case INTEGER:
    case INTEGER32:
    case UINTEGER32:
    case UNSIGNED32:
    case COUNTER:
    case GAUGE:
    case BITSTRING:
    case LABEL:
        if (nexttype == LEFTBRACKET) {
            /*
             * if there is an enumeration list, parse it 
             */
            np->enums = parse_enumlist(fp, &np->enums);
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
        } else if (nexttype == LEFTPAREN) {
            /*
             * if there is a range list, parse it 
             */
            np->ranges = parse_ranges(fp, &np->ranges);
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
        }
        break;
    case OCTETSTR:
    case KW_OPAQUE:
        /*
         * parse any SIZE specification 
         */
        if (nexttype == LEFTPAREN) {
            nexttype = get_token(fp, nexttoken, MAXTOKEN);
            if (nexttype == SIZE) {
                nexttype = get_token(fp, nexttoken, MAXTOKEN);
                if (nexttype == LEFTPAREN) {
                    np->ranges = parse_ranges(fp, &np->ranges);
                    nexttype = get_token(fp, nexttoken, MAXTOKEN);      /* ) */
                    if (nexttype == RIGHTPAREN) {
                        nexttype = get_token(fp, nexttoken, MAXTOKEN);
                        break;
                    }
                }
            }
            print_error("Bad SIZE syntax", token, type);
            free_node(np);
            return nexttype;
        }
        break;
    case OBJID:
    case NETADDR:
    case IPADDR:
    case TIMETICKS:
    case NUL:
    case NSAPADDRESS:
    case COUNTER64:
        break;
    default:
        print_error("Bad syntax", token, type);
        free_node(np);
        return nexttype;
    }
    free_node(np);
    return nexttype;
}

/*
 * Parses a module identity macro
 * Returns 0 on error.
 */
static void check_utc(const char *utc)
{
    int             len, year, month, day, hour, minute;

    len = strlen(utc);
    if (utc[len - 1] != 'Z' && utc[len - 1] != 'z') {
        print_error("Timestamp should end with Z", utc, QUOTESTRING);
        return;
    }
    if (len == 11) {
        len =
            sscanf(utc, "%2d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
                   &minute);
        year += 1900;
    } else if (len == 13)
        len =
            sscanf(utc, "%4d%2d%2d%2d%2dZ", &year, &month, &day, &hour,
                   &minute);
    else {
        print_error("Bad timestamp format (11 or 13 characters)",
                    utc, QUOTESTRING);
        return;
    }
    if (len != 5) {
        print_error("Bad timestamp format", utc, QUOTESTRING);
        return;
    }
    if (month < 1 || month > 12)
        print_error("Bad month in timestamp", utc, QUOTESTRING);
    if (day < 1 || day > 31)
        print_error("Bad day in timestamp", utc, QUOTESTRING);
    if (hour < 0 || hour > 23)
        print_error("Bad hour in timestamp", utc, QUOTESTRING);
    if (minute < 0 || minute > 59)
        print_error("Bad minute in timestamp", utc, QUOTESTRING);
}
//end of support function definition----------------------------------------------

//tools definition----------------------------------------------------------------
/*
 * return zero if character is not a label character. 
 */
static int is_labelchar(int ich)
{
    if ((isalnum(ich)) || (ich == '-'))
        return 1;

    return 0;
}

static void print_error(const char *str, const char *token, int type)
{
//    erroneousMibs++;
    if (type == ENDOFFILE)
        printf("%s (EOF): At line %d in %s\n", str, mibLine, File);
    else if (token && *token)
        printf("%s (%s): At line %d in %s\n", str, token, mibLine, File);
    else
        printf("%s: At line %d in %s\n", str, mibLine, File);
}

static int name_hash(const char *name)
{
    int             hash = 0;
    const char     *cp;

    if (!name)
        return 0;
    for (cp = name; *cp; cp++)
        hash += tolower(*cp);
    return (hash);
}

static int translation_table[256];
static void build_translation_table()
{
    int             count;

    for (count = 0; count < 256; count++) {
        switch (count) {
        case OBJID:
            translation_table[count] = TYPE_OBJID;
            break;
        case OCTETSTR:
            translation_table[count] = TYPE_OCTETSTR;
            break;
        case INTEGER:
            translation_table[count] = TYPE_INTEGER;
            break;
        case NETADDR:
            translation_table[count] = TYPE_NETADDR;
            break;
        case IPADDR:
            translation_table[count] = TYPE_IPADDR;
            break;
        case COUNTER:
            translation_table[count] = TYPE_COUNTER;
            break;
        case GAUGE:
            translation_table[count] = TYPE_GAUGE;
            break;
        case TIMETICKS:
            translation_table[count] = TYPE_TIMETICKS;
            break;
        case KW_OPAQUE:
            translation_table[count] = TYPE_OPAQUE;
            break;
        case NUL:
            translation_table[count] = TYPE_NULL;
            break;
        case COUNTER64:
            translation_table[count] = TYPE_COUNTER64	;
            break;
        case BITSTRING:
            translation_table[count] = TYPE_BITSTRING;
            break;
        case NSAPADDRESS:
            translation_table[count] = TYPE_NSAPADDRESS;
            break;
        case INTEGER32:
            translation_table[count] = TYPE_INTEGER32;
            break;
        case UINTEGER32:
            translation_table[count] = TYPE_UINTEGER;
            break;
        case UNSIGNED32:
            translation_table[count] = TYPE_UNSIGNED32;
            break;
        case TRAPTYPE:
            translation_table[count] = TYPE_TRAPTYPE;
            break;
        case NOTIFTYPE:
            translation_table[count] = TYPE_NOTIFTYPE;
            break;
        case NOTIFGROUP:
            translation_table[count] = TYPE_NOTIFGROUP;
            break;
        case OBJGROUP:
            translation_table[count] = TYPE_OBJGROUP;
            break;
        case MODULEIDENTITY:
            translation_table[count] = TYPE_MODID;
            break;
        case OBJIDENTITY:
            translation_table[count] = TYPE_OBJIDENTITY;
            break;
        case AGENTCAP:
            translation_table[count] = TYPE_AGENTCAP;
            break;
        case COMPLIANCE:
            translation_table[count] = TYPE_MODCOMP;
            break;
        default:
            translation_table[count] = TYPE_OTHER;
            break;
        }
    }
}

void print_nodes(FILE * fp, struct node *root)
{
    extern void     xmalloc_stats(FILE *);
    struct enum_list *ep;
    struct index_list *ip;
    struct range_list *rp;
    struct varbind_list *vp;
    struct node    *np;

    for (np = root; np; np = np->next) {
        fprintf(fp, "%s ::= { %s %d } (%d)\n", np->label, np->parent,
                np->subid, np->type);
        if (np->tc_index >= 0)
            fprintf(fp, "  TC = %s\n", tclist[np->tc_index].descriptor);
        if (np->enums) {
            fprintf(fp, "  Enums: \n");
            for (ep = np->enums; ep; ep = ep->next) {
                fprintf(fp, "    %s(%d)\n", ep->label, ep->value);
            }
        }
        if (np->ranges) {
            fprintf(fp, "  Ranges: \n");
            for (rp = np->ranges; rp; rp = rp->next) {
                fprintf(fp, "    %d..%d\n", rp->low, rp->high);
            }
        }
        if (np->indexes) {
            fprintf(fp, "  Indexes: \n");
            for (ip = np->indexes; ip; ip = ip->next) {
                fprintf(fp, "    %s\n", ip->ilabel);
            }
        }
        if (np->augments)
            fprintf(fp, "  Augments: %s\n", np->augments);
        if (np->varbinds) {
            fprintf(fp, "  Varbinds: \n");
            for (vp = np->varbinds; vp; vp = vp->next) {
                fprintf(fp, "    %s\n", vp->vblabel);
            }
        }
        if (np->hint)
            fprintf(fp, "  Hint: %s\n", np->hint);
        if (np->units)
            fprintf(fp, "  Units: %s\n", np->units);
        if (np->defaultValue)
            fprintf(fp, "  DefaultValue: %s\n", np->defaultValue);
    }
}

static void init_node_hash(struct node *nodes)
{
    struct node    *np, *nextp;
    int             hash;

    memset(nbuckets, 0, sizeof(nbuckets));
    for (np = nodes; np;) {
        nextp = np->next;
        hash = NBUCKET(name_hash(np->parent));
        np->next = nbuckets[hash];
        nbuckets[hash] = np;
        np = nextp;
    }
}

/*
 * return index into tclist of given TC descriptor
 * return -1 if not found
 */
static int get_tc_index(const char *descriptor, int modid)
{
    int             i;
    struct tc      *tcp;
    struct module  *mp;
    struct module_import *mip;

    /*
     * Check that the descriptor isn't imported
     *  by searching the import list
     */

    for (mp = module_head; mp; mp = mp->next)
        if (mp->modid == modid)
            break;
    if (mp)
        for (i = 0, mip = mp->imports; i < mp->no_imports; ++i, ++mip) {
            if (!label_compare(mip->label, descriptor)) {
                /*
                 * Found it - so amend the module ID 
                 */
                modid = mip->modid;
                break;
            }
        }


    for (i = 0, tcp = tclist; i < MAXTC; i++, tcp++) {
        if (tcp->type == 0)
            break;
        if (!label_compare(descriptor, tcp->descriptor) &&
            ((modid == tcp->modid) || (modid == -1))) {
            return i;
        }
    }
    return -1;
}

static void free_indexes(struct index_list **spp)
{
    if (spp && *spp) {
        struct index_list *pp, *npp;

        pp = *spp;
        *spp = NULL;

        while (pp) {
            npp = pp->next;
            if (pp->ilabel)
                free(pp->ilabel);
            free(pp);
            pp = npp;
        }
    }
}

static void free_varbinds(struct varbind_list **spp)
{
    if (spp && *spp) {
        struct varbind_list *pp, *npp;

        pp = *spp;
        *spp = NULL;

        while (pp) {
            npp = pp->next;
            if (pp->vblabel)
                free(pp->vblabel);
            free(pp);
            pp = npp;
        }
    }
}

static void free_ranges(struct range_list **spp)
{
    if (spp && *spp) {
        struct range_list *pp, *npp;

        pp = *spp;
        *spp = NULL;

        while (pp) {
            npp = pp->next;
            free(pp);
            pp = npp;
        }
    }
}

static void free_enums(struct enum_list **spp)
{
    if (spp && *spp) {
        struct enum_list *pp, *npp;

        pp = *spp;
        *spp = NULL;

        while (pp) {
            npp = pp->next;
            if (pp->label)
                free(pp->label);
            free(pp);
            pp = npp;
        }
    }
}

static void free_partial_tree(struct tree *tp, int keep_label)
{
    if (!tp)
        return;

    /*
     * remove the data from this tree node 
     */
    free_enums(&tp->enums);
    free_ranges(&tp->ranges);
    free_indexes(&tp->indexes);
    free_varbinds(&tp->varbinds);
    if (!keep_label)
        SNMP_FREE(tp->label);
    SNMP_FREE(tp->hint);
    SNMP_FREE(tp->units);
    SNMP_FREE(tp->description);
    SNMP_FREE(tp->reference);
    SNMP_FREE(tp->augments);
    SNMP_FREE(tp->defaultValue);
}

/*
 * transfer data to tree from node
 *
 * move pointers for alloc'd data from np to tp.
 * this prevents them from being freed when np is released.
 * parent member is not moved.
 *
 * CAUTION: nodes may be repeats of existing tree nodes.
 * This can happen especially when resolving IMPORT clauses.
 *
 */
static void tree_from_node(struct tree *tp, struct node *np)
{
    free_partial_tree(tp, FALSE);

    tp->label = np->label;
    np->label = NULL;
    tp->enums = np->enums;
    np->enums = NULL;
    tp->ranges = np->ranges;
    np->ranges = NULL;
    tp->indexes = np->indexes;
    np->indexes = NULL;
    tp->augments = np->augments;
    np->augments = NULL;
    tp->varbinds = np->varbinds;
    np->varbinds = NULL;
    tp->hint = np->hint;
    np->hint = NULL;
    tp->units = np->units;
    np->units = NULL;
    tp->description = np->description;
    np->description = NULL;
    tp->reference = np->reference;
    np->reference = NULL;
    tp->defaultValue = np->defaultValue;
    np->defaultValue = NULL;
    tp->subid = np->subid;
    tp->oid_len = tp->parent->oid_len;
    memcpy(tp->oid, tp->parent->oid, tp->oid_len * sizeof(int));
    tp->oid[tp->oid_len++] = np->subid;
    tp->tc_index = np->tc_index;
    tp->type = translation_table[np->type];
    tp->access = np->access;
    tp->status = np->status;

//    set_function(tp);
}

static void free_node(struct node *np)
{
    if (!np)
        return;

    free_enums(&np->enums);
    free_ranges(&np->ranges);
    free_indexes(&np->indexes);
    free_varbinds(&np->varbinds);
    if (np->label)
        free(np->label);
    if (np->hint)
        free(np->hint);
    if (np->units)
        free(np->units);
    if (np->description)
        free(np->description);
    if (np->reference)
        free(np->reference);
    if (np->defaultValue)
        free(np->defaultValue);
    if (np->parent)
        free(np->parent);
    if (np->augments)
        free(np->augments);
    if (np->filename)
	free(np->filename);
    free((char *) np);
}

/*
 * free a tree node. Note: the node must already have been unlinked
 * from the tree when calling this routine
 */
void free_tree(struct tree *Tree)
{
    if (!Tree)
        return;

    unlink_tbucket(Tree);
    free_partial_tree(Tree, FALSE);
    if (Tree->number_modules > 1)
        free((char *) Tree->module_list);
    free((char *) Tree);
}

static struct enum_list *copy_enums(struct enum_list *sp)
{
    struct enum_list *xp = NULL, **spp = &xp;

    while (sp) {
        *spp = (struct enum_list *) calloc(1, sizeof(struct enum_list));
        if (!*spp)
            break;
        (*spp)->label = strdup(sp->label);
        (*spp)->value = sp->value;
        spp = &(*spp)->next;
        sp = sp->next;
    }
    return (xp);
}

static struct range_list *copy_ranges(struct range_list *sp)
{
    struct range_list *xp = NULL, **spp = &xp;

    while (sp) {
        *spp = (struct range_list *) calloc(1, sizeof(struct range_list));
        if (!*spp)
            break;
        (*spp)->low = sp->low;
        (*spp)->high = sp->high;
        spp = &(*spp)->next;
        sp = sp->next;
    }
    return (xp);
}

static void unlink_tbucket(struct tree *tp)
{
    int             hash = NBUCKET(name_hash(tp->label));
    struct tree    *otp = NULL, *ntp = tbuckets[hash];

    while (ntp && ntp != tp) {
        otp = ntp;
        ntp = ntp->next;
    }
    if (!ntp)
        printf("Can't find %s in tbuckets\n", tp->label);
    else if (otp)
        otp->next = ntp->next;
    else
        tbuckets[hash] = tp->next;
}

static void unlink_tree(struct tree *tp)
{
    struct tree    *otp = NULL, *ntp = tp->parent;

    if (ntp){
        ntp = ntp->child_list;

        while (ntp && ntp != tp) {
            otp = ntp;
            ntp = ntp->next_peer;
        }
        if (!ntp)
            printf("Can't find %s in %s's children\n",
                     tp->label, tp->parent->label);
        else if (otp)
            otp->next_peer = ntp->next_peer;
        else
            tp->parent->child_list = tp->next_peer;
    }

    if (tree_head == tp)
        tree_head = tp->next_peer;
}

int which_module(const char *name)
{
    struct module  *mp;

    for (mp = module_head; mp; mp = mp->next)
        if (!label_compare(mp->name, name))
            return (mp->modid);

    return (-1);
}

static struct node *alloc_node(int modid)
{
    struct node    *np;
    np = (struct node *) calloc(1, sizeof(struct node));
    if (np) {
		np->tc_index = -1;
		np->modid = modid;
		np->filename = strdup(File);
		np->lineno = mibLine;
    }
    return np;
}

static int get_tc(const char *descriptor, int modid, int *tc_index,
       struct enum_list **ep, struct range_list **rp, char **hint)
{
    int             i;
    struct tc      *tcp;

    i = get_tc_index(descriptor, modid);
    if (tc_index)
        *tc_index = i;
    if (i != -1) {
        tcp = &tclist[i];
        if (ep) {
            free_enums(ep);
            *ep = copy_enums(tcp->enums);
        }
        if (rp) {
            free_ranges(rp);
            *rp = copy_ranges(tcp->ranges);
        }
        if (hint) {
            if (*hint)
                free(*hint);
            *hint = (tcp->hint ? strdup(tcp->hint) : NULL);
        }
        return tcp->type;
    }
    return LABEL;
}

/*
 * struct index_list *
 * getIndexes(FILE *fp):
 *   This routine parses a string like  { blah blah blah } and returns a
 *   list of the strings enclosed within it.
 *
 */
static struct index_list *getIndexes(FILE * fp, struct index_list **retp)
{
    int             type;
    char            token[MAXTOKEN];
    char            nextIsImplied = 0;

    struct index_list *mylist = NULL;
    struct index_list **mypp = &mylist;

    free_indexes(retp);

    type = get_token(fp, token, MAXTOKEN);

    if (type != LEFTBRACKET) {
        return NULL;
    }

    type = get_token(fp, token, MAXTOKEN);
    while (type != RIGHTBRACKET && type != ENDOFFILE) {
        if ((type == LABEL) || (type & SYNTAX_MASK)) {
            *mypp =
                (struct index_list *) calloc(1, sizeof(struct index_list));
            if (*mypp) {
                (*mypp)->ilabel = strdup(token);
                (*mypp)->isimplied = nextIsImplied;
                mypp = &(*mypp)->next;
                nextIsImplied = 0;
            }
        } else if (type == IMPLIED) {
            nextIsImplied = 1;
        }
        type = get_token(fp, token, MAXTOKEN);
    }

    *retp = mylist;
    return mylist;
}

/*
 * This routine parses a string like  { blah blah blah } and returns OBJID if
 * it is well formed, and NULL if not.
 */
static int tossObjectIdentifier(FILE * fp)
{
    int             type;
    char            token[MAXTOKEN];
    int             bracketcount = 1;

    type = get_token(fp, token, MAXTOKEN);

    if (type != LEFTBRACKET)
        return 0;
    while ((type != RIGHTBRACKET || bracketcount > 0) && type != ENDOFFILE) {
        type = get_token(fp, token, MAXTOKEN);
        if (type == LEFTBRACKET)
            bracketcount++;
        else if (type == RIGHTBRACKET)
            bracketcount--;
    }

    if (type == RIGHTBRACKET)
        return OBJID;
    else
        return 0;
}

/*
 * Merge the parsed object identifier with the existing node.
 * If there is a problem with the identifier, release the existing node.
 */
static struct node *merge_parse_objectid(struct node *np, FILE * fp, char *name)
{
    struct node    *nnp;
    /*
     * printf("merge defval --> %s\n",np->defaultValue); 
     */
    nnp = parse_objectid(fp, name);
    if (nnp) {

        /*
         * apply last OID sub-identifier data to the information 
         */
        /*
         * already collected for this node. 
         */
        struct node    *headp, *nextp;
        int             ncount = 0;
        nextp = headp = nnp;
        while (nnp->next) {
            nextp = nnp;
            ncount++;
            nnp = nnp->next;
        }

        np->label = nnp->label;
        np->subid = nnp->subid;
        np->modid = nnp->modid;
        np->parent = nnp->parent;
	if (nnp->filename != NULL) {
	  free(nnp->filename);
	}
        free(nnp);

        if (ncount) {
            nextp->next = np;
            np = headp;
        }
    } else {
        free_node(np);
        np = NULL;
    }

    return np;
}

/*
 * Takes a list of the form:
 * { iso org(3) dod(6) 1 }
 * and creates several nodes, one for each parent-child pair.
 * Returns 0 on error.
 */
static int getoid(FILE * fp, struct subid_s *id,   /* an array of subids */
       int length)	/* the length of the array */
{                               
    register int    count;
    int             type;
    char            token[MAXTOKEN];

    if ((type = get_token(fp, token, MAXTOKEN)) != LEFTBRACKET) {
        print_error("Expected \"{\"", token, type);
        return 0;
    }
    type = get_token(fp, token, MAXTOKEN);
    for (count = 0; count < length; count++, id++) {
        id->label = NULL;
        id->modid = current_module;
        id->subid = -1;
        if (type == RIGHTBRACKET){
            return count;
        }
        if (type == LABEL) {
            /*
             * this entry has a label 
             */
            id->label = strdup(token);
            type = get_token(fp, token, MAXTOKEN);
            if (type == LEFTPAREN) {
                type = get_token(fp, token, MAXTOKEN);
                if (type == NUMBER) {
                    id->subid = strtoul(token, NULL, 10);
                    if ((type =
                         get_token(fp, token, MAXTOKEN)) != RIGHTPAREN) {
                        print_error("Expected a closing parenthesis",
                                    token, type);
                        return 0;
                    }
                } else {
                    print_error("Expected a number", token, type);
                    return 0;
                }
            } else {
                continue;
            }
        } else if (type == NUMBER) {
            /*
             * this entry  has just an integer sub-identifier 
             */
            id->subid = strtoul(token, NULL, 10);
        } else {
            print_error("Expected label or number", token, type);
            return 0;
        }
        type = get_token(fp, token, MAXTOKEN);
    }
    print_error("Too long OID", token, type);
    return 0;
}

static struct varbind_list *getVarbinds(FILE * fp, struct varbind_list **retp)
{
    int             type;
    char            token[MAXTOKEN];

    struct varbind_list *mylist = NULL;
    struct varbind_list **mypp = &mylist;

    free_varbinds(retp);

    type = get_token(fp, token, MAXTOKEN);

    if (type != LEFTBRACKET) {
        return NULL;
    }

    type = get_token(fp, token, MAXTOKEN);
    while (type != RIGHTBRACKET && type != ENDOFFILE) {
        if ((type == LABEL) || (type & SYNTAX_MASK)) {
            *mypp =
                (struct varbind_list *) calloc(1,
                                               sizeof(struct
                                                      varbind_list));
            if (*mypp) {
                (*mypp)->vblabel = strdup(token);
                mypp = &(*mypp)->next;
            }
        }
        type = get_token(fp, token, MAXTOKEN);
    }

    *retp = mylist;
    return mylist;
}

static int compliance_lookup(const char *name, int modid)
{
    if (modid == -1) {
        struct objgroup *op =
            (struct objgroup *) malloc(sizeof(struct objgroup));
        if (!op)
            return 0;
        op->next = objgroups;
        op->name = strdup(name);
        op->line = mibLine;
        objgroups = op;
        return 1;
    } else
        return find_tree_node(name, modid) != NULL;
}

/*
 * module_name - copy module name to user buffer, return ptr to same.
 */
char *module_name(int modid, char *cp)
{
    struct module  *mp;

    for (mp = module_head; mp; mp = mp->next)
        if (mp->modid == modid) {
            strcpy(cp, mp->name);
            return (cp);
        }

    sprintf(cp, "#%d", modid);
    return (cp);
}

struct tree *find_tree_node(const char *name, int modid)
{
    struct tree    *tp, *headtp;
    int             count, *int_p;

    if (!name || !*name)
        return (NULL);

    headtp = tbuckets[NBUCKET(name_hash(name))];
    for (tp = headtp; tp; tp = tp->next) {
        if (tp->label && !label_compare(tp->label, name)) {

            if (modid == -1)    /* Any module */
                return (tp);

            for (int_p = tp->module_list, count = 0;
                 count < tp->number_modules; ++count, ++int_p)
                if (*int_p == modid)
                    return (tp);
        }
    }

    return (NULL);
}

static void print_module_not_found(const char *cp)
{
    if (!last_err_module || strcmp(cp, last_err_module))
        print_error("Cannot find module", cp, CONTINUE);
    if (last_err_module)
        free(last_err_module);
    last_err_module = strdup(cp);
}

void print_subtree(FILE * f, struct tree *tree, int count)
{
    struct tree    *tp;
    int             i;
    char            modbuf[256];

    for (i = 0; i < count; i++)
        fprintf(f, "  ");
        
    fprintf(f, "Children of %s(%d):\n", tree->label, tree->subid);
    count++;
    
    for (tp = tree->child_list; tp; tp = tp->next_peer) {
        for (i = 0; i < count; i++)
            fprintf(f, "  ");
            
        fprintf(f, "%s:%s(%d) type=%d",
                module_name(tp->module_list[0], modbuf),
                tp->label, tp->subid, tp->type);
        if (tp->tc_index != -1)
            fprintf(f, " tc=%d", tp->tc_index);
        if (tp->hint)
            fprintf(f, " hint=%s", tp->hint);
        if (tp->units)
            fprintf(f, " units=%s", tp->units);
            
        if (tp->number_modules > 1) {
            fprintf(f, " modules:");
            for (i = 1; i < tp->number_modules; i++)
                fprintf(f, " %s", module_name(tp->module_list[i], modbuf));
        }
        fprintf(f, " OID: %d", tp->oid[0]);
        for(i = 1; i < tp->oid_len; i++)
	        fprintf(f, ".%d", tp->oid[i]);
	        
        fprintf(f, "\n");
    }
    
    for (tp = tree->child_list; tp; tp = tp->next_peer) {
        if (tp->child_list)
            print_subtree(f, tp, count);
    }
}

//end of tools definition---------------------------------------------------------

