/*
 * smi.c --
 *
 *      Interface Implementation of libsmi.
 *
 * Copyright (c) 1999 Frank Strauss, Technical University of Braunschweig.
 *
 * See the file "COPYING" for information on usage and redistribution
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
 *
 * @(#) $Id: smi.c 1798 2013-06-06 09:14:13Z schoenw $
 */

#include <config.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <limits.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_PWD_H
#include <pwd.h>
#endif

#include "common.h"
#include "smi.h"
#include "smi-data.h"
#include "yang-data.h"
#include "snprintf.h"

#ifdef BACKEND_SMI
#include "scanner-smi.h"
#include "parser-smi.h"
#endif

#ifdef BACKEND_SMING
#include "scanner-sming.h"
#include "parser-sming.h"
#endif

#ifdef HAVE_DMALLOC_H
#include <dmalloc.h>
#endif



#ifndef MIN
#define MIN(a, b)       ((a) < (b) ? (a) : (b))
#define MAX(a, b)       ((a) < (b) ? (b) : (a))
#endif



const char *smi_library_version = SMI_LIBRARY_VERSION;
const char *smi_version_string = SMI_VERSION_STRING;

Handle *smiHandle = NULL;



/*
 * Internal functions.
 */

static void getModulenameAndName(const char *arg1, const char *arg2,
				 char **module, char **name)
{
    char	    *p;
    int		    l;

    if ((!arg1) && (!arg2)) {
	*module = NULL;
	*name = NULL;
    } else if (!arg2) {
	if (isupper((int)arg1[0])) {
	    if ((p = strstr(arg1, "::"))) {
		/* SMIng style module/label separator */
		*name = smiStrdup(&p[2]);
		l = strcspn(arg1, "::");
		*module = smiStrndup(arg1, l);
	    } else if ((p = strchr(arg1, '!'))) {
		/* old scotty style module/label separator */
		*name = smiStrdup(&p[1]);
		l = strcspn(arg1, "!");
		*module = smiStrndup(arg1, l);
	    } else if ((p = strchr(arg1, '.'))) {
		/* SMIv1/v2 style module/label separator */
		*name = smiStrdup(&p[1]);
		l = strcspn(arg1, ".");
		*module = smiStrndup(arg1, l);
	    } else {
		*name = smiStrdup(arg1);
		*module = smiStrdup("");
	    }
	} else {
	    *name = smiStrdup(arg1);
	    *module = smiStrdup("");
	}
    } else if (!arg1) {
	if (isupper((int)arg2[0])) {
	    if ((p = strstr(arg2, "::"))) {
		/* SMIng style module/label separator */
		*name = smiStrdup(&p[2]);
		l = strcspn(arg2, "::");
		*module = smiStrndup(arg2, l);
	    } else if ((p = strchr(arg2, '!'))) {
		/* old scotty style module/label separator */
		*name = smiStrdup(&p[1]);
		l = strcspn(arg2, "!");
		*module = smiStrndup(arg2, l);
	    } else if ((p = strchr(arg2, '.'))) {
		/* SMIv1/v2 style module/label separator */
		*name = smiStrdup(&p[1]);
		l = strcspn(arg2, ".");
		*module = smiStrndup(arg2, l);
	    } else {
		*name = smiStrdup(arg2);
		*module = smiStrdup("");
	    }
	} else {
	    *name = smiStrdup(arg2);
	    *module = smiStrdup("");
	}
    } else {
	*module = smiStrdup(arg1);
	*name = smiStrdup(arg2);
    }
}



static Node *getNode(unsigned int oidlen, SmiSubid oid[])
{
    Node *nodePtr, *parentPtr;
    unsigned int i;

    for(nodePtr = smiHandle->rootNodePtr, i=0; i < oidlen; i++) {
	parentPtr = nodePtr;
	nodePtr = findNodeByParentAndSubid(parentPtr, oid[i]);
	if (!nodePtr) {
	    return parentPtr;
	}
    }
    
    return nodePtr;
}



static Object *getNextChildObject(Node *startNodePtr, Module *modulePtr,
				  SmiNodekind nodekind)
{
    Node   *nodePtr;
    Object *objectPtr = NULL;

    if (!startNodePtr || !modulePtr)
	return NULL;

    for (nodePtr = startNodePtr; nodePtr; nodePtr = nodePtr->nextPtr) {
	for (objectPtr = nodePtr->firstObjectPtr; objectPtr;
	     objectPtr = objectPtr->nextSameNodePtr) {
	    if (((!modulePtr) || (objectPtr->modulePtr == modulePtr)) &&
		((nodekind == SMI_NODEKIND_ANY) ||
		 (nodekind & objectPtr->export.nodekind))) {
		break;
	    }
	}
	if (objectPtr) break;
	objectPtr = getNextChildObject(nodePtr->firstChildPtr,
				       modulePtr, nodekind);
	if (objectPtr) break;
    }

    return objectPtr;
}


/*
 * Stringification functions. We are using verbose switch statements
 * since this allows the compiler to check completeness and we like
 * to handle invalid values gracefully.
 */

char *smiLanguageAsString(SmiLanguage language)
{
    char *s = "<LANGUAGE-UNDEFINED>";

    switch (language) {
    case SMI_LANGUAGE_UNKNOWN:
	s = "<unknown>";
	break;
    case SMI_LANGUAGE_SMIV1:
	s = "SMIv1";
	break;
    case SMI_LANGUAGE_SMIV2:
	s = "SMIv2";
	break;
    case SMI_LANGUAGE_SMING:
	s = "SMIng";
	break;
    case SMI_LANGUAGE_SPPI:
	s = "SPPI";
	break;
    case SMI_LANGUAGE_YANG:
	s = "YANG";
	break;
    }
    return s;
}

char *smiBasetypeAsString(SmiBasetype basetype)
{
    char *s = "<BASETYPE-UNDEFINED>";

    switch (basetype) {
    case SMI_BASETYPE_UNKNOWN:
	s = "<unknown>";
 	break;
    case SMI_BASETYPE_OCTETSTRING:
	s = "OctetString";
	break;
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	s = "ObjectIdentifier";
	break;
    case SMI_BASETYPE_UNSIGNED32:
        s = "Unsigned32";
	break;
    case SMI_BASETYPE_INTEGER32:
	s = "Integer32";
	break;
    case SMI_BASETYPE_UNSIGNED64:
        s = "Unsigned64";
	break;
    case SMI_BASETYPE_INTEGER64:
	s = "Integer64";
	break;
    case SMI_BASETYPE_FLOAT32:
	s = "Float32";
	break;
    case SMI_BASETYPE_FLOAT64:
	s = "Float64";
	break;
    case SMI_BASETYPE_FLOAT128:
	s = "Float128";
	break;
    case SMI_BASETYPE_ENUM:
	s = "Enumeration";
	break;
    case SMI_BASETYPE_BITS:
	s = "Bits";
	break;
    case SMI_BASETYPE_POINTER:
	s = "Pointer";
	break;
    }
    return s;
}

char *smiStatusAsString(SmiStatus status)
{
    char *s = "<STATUS-UNDEFINED>";
    
    switch (status) {
    case SMI_STATUS_UNKNOWN:
	s = "<unknown>";
	break;
    case SMI_STATUS_CURRENT:
	s = "current";
	break;
    case SMI_STATUS_DEPRECATED:
	s = "deprecated";
	break;
    case SMI_STATUS_OBSOLETE:
	s = "obsolete";
	break;
    case SMI_STATUS_MANDATORY:
	s = "mandatory";
	break;
    case SMI_STATUS_OPTIONAL:
	s = "optional";
	break;
    }
    return s;
}

char *smiAccessAsString(SmiAccess access)
{
    char *s = "<ACCESS-UNDEFINED>";

    switch (access) {
    case SMI_ACCESS_UNKNOWN:
	s = "<unknown>";
	break;
    case SMI_ACCESS_NOT_IMPLEMENTED:
	s = "not-implemented";
	break;
    case SMI_ACCESS_NOT_ACCESSIBLE:
	s = "not-accessible";
	break;
    case SMI_ACCESS_NOTIFY:
	s = "accessible-for-notify";
	break;
    case SMI_ACCESS_READ_ONLY:
	s = "read-only";
	break;
    case SMI_ACCESS_READ_WRITE:
	s = "read-write";
	break;
    case SMI_ACCESS_INSTALL:
	s = "install";
	break;
    case SMI_ACCESS_INSTALL_NOTIFY:
	s = "notify";
	break;
    case SMI_ACCESS_REPORT_ONLY:
	s = "report-only";
	break;
    case SMI_ACCESS_EVENT_ONLY:
	s = "event-only";
	break;
    }
    return s;
}

char *smiNodekindAsString(SmiNodekind nodekind)
{
    char *s = "<NODEKIND-UNDEFINED>";

    switch (nodekind) {
    case SMI_NODEKIND_UNKNOWN:
	s = "<unknown>";
	break;
    case SMI_NODEKIND_NODE:
	s = "node";
	break;
    case SMI_NODEKIND_SCALAR:
	s = "scalar";
	break;
    case SMI_NODEKIND_TABLE:
        s = "table";
	break;
    case SMI_NODEKIND_ROW:
	s = "row";
	break;
    case SMI_NODEKIND_COLUMN:
	s = "column";
	break;
    case SMI_NODEKIND_NOTIFICATION:
	s = "notification";
	break;
    case SMI_NODEKIND_GROUP:
	s = "group";
	break;
    case SMI_NODEKIND_COMPLIANCE:
	s = "compliance";
	break;
    case SMI_NODEKIND_CAPABILITIES:
	s = "capabilities";
	break;
    }
    return s;
}

char *smiDeclAsString(SmiDecl decl)
{
    char *s = "<DECL-UNDEFINED>";

    switch (decl) {
    case SMI_DECL_UNKNOWN:
	s = "<unknown>";
	break;
    case SMI_DECL_IMPLICIT_TYPE:
	s = "<implicit>";
	break;
    case SMI_DECL_TYPEASSIGNMENT:
	s = "<type-assignment>";
	break;
    case SMI_DECL_IMPL_SEQUENCEOF:
	s = "<implicit-sequence-of>";
	break;
    case SMI_DECL_VALUEASSIGNMENT:
	s = "<value-assignment>";
	break;
    case SMI_DECL_OBJECTTYPE:
        s = "OBJECT-TYPE";
	break;
    case SMI_DECL_OBJECTIDENTITY:
	s = "OBJECT-IDENTITY";
	break;
    case SMI_DECL_MODULEIDENTITY:
	s = "MODULE-IDENTITY";
	break;
    case SMI_DECL_NOTIFICATIONTYPE:
	s = "NOTIFICATION-TYPE";
	break;
    case SMI_DECL_TRAPTYPE:
	s = "TRAP-TYPE";
	break;
    case SMI_DECL_OBJECTGROUP:
	s = "OBJECT-GROUP";
	break;
    case SMI_DECL_NOTIFICATIONGROUP:
	s = "NOTIFICATION-GROUP";
	break;
    case SMI_DECL_MODULECOMPLIANCE:
	s = "MODULE-COMPLIANCE";
	break;
    case SMI_DECL_AGENTCAPABILITIES:
	s = "AGENT-CAPABILITIES";
	break;
    case SMI_DECL_TEXTUALCONVENTION:
	s = "TEXTUAL-CONVENTION";
	break;
    case SMI_DECL_MACRO:
	s = "MACRO";
	break;
    case SMI_DECL_COMPL_GROUP:
	s = "GROUP";
	break;
    case SMI_DECL_COMPL_OBJECT:
	s = "OBJECT";
	break;
    case SMI_DECL_MODULE:
	s = "module";
	break;
    case SMI_DECL_EXTENSION:
	s = "extension";
	break;
    case SMI_DECL_TYPEDEF:
	s = "typedef";
	break;
    case SMI_DECL_NODE:
	s = "node";
	break;
    case SMI_DECL_SCALAR:
	s = "scalar";
	break;
    case SMI_DECL_TABLE:
	s = "table";
	break;
    case SMI_DECL_ROW:
	s = "row";
	break;
    case SMI_DECL_COLUMN:
	s = "column";
	break;
    case SMI_DECL_NOTIFICATION:
	s = "notification";
	break;
    case SMI_DECL_GROUP:
	s = "group";
	break;
    case SMI_DECL_COMPLIANCE:
        s = "compliance";
	break;
    case SMI_DECL_IDENTITY:
        s = "identity";
	break;
    case SMI_DECL_CLASS:
	s = "class";
	break;
    case SMI_DECL_ATTRIBUTE:
	s = "attribute";
	break;
    case SMI_DECL_EVENT:
	s = "event";
	break;
    case SMI_DECL_IMPL_OBJECT:
	s = "<implicit object>";
	break;
    }
    return s;
}

char *smiValueAsString(SmiValue *smiValue,
		       SmiType *smiType, SmiLanguage smiLanguage)
{
    static char    s[1024];
    char           ss[9];
    int		   n;
    unsigned int   i;
    size_t	   len;
    SmiNamedNumber *nn;
    SmiNode        *nodePtr;
    
    s[0] = 0;
    
    switch (smiValue->basetype) {
    case SMI_BASETYPE_UNSIGNED32:
	snprintf(s, sizeof(s), "%lu", smiValue->value.unsigned32);
	break;
    case SMI_BASETYPE_INTEGER32:
	snprintf(s, sizeof(s), "%ld", smiValue->value.integer32);
	break;
    case SMI_BASETYPE_UNSIGNED64:
	snprintf(s, sizeof(s), UINT64_FORMAT, smiValue->value.unsigned64);
	break;
    case SMI_BASETYPE_INTEGER64:
	snprintf(s, sizeof(s), INT64_FORMAT, smiValue->value.integer64);
	break;
    case SMI_BASETYPE_FLOAT32:
    case SMI_BASETYPE_FLOAT64:
    case SMI_BASETYPE_FLOAT128:
	break;
    case SMI_BASETYPE_ENUM:
	for (nn = smiGetFirstNamedNumber(smiType); nn;
	     nn = smiGetNextNamedNumber(nn)) {
	    if (nn->value.value.unsigned32 == smiValue->value.unsigned32)
		break;
	}
	if (nn) {
	    snprintf(s, sizeof(s), "%s", nn->name);
	} else {
	    snprintf(s, sizeof(s), "%ld", smiValue->value.integer32);
	}
	break;
    case SMI_BASETYPE_OCTETSTRING:
	switch (smiLanguage) {
	case SMI_LANGUAGE_SMIV1:
	case SMI_LANGUAGE_SMIV2:
	case SMI_LANGUAGE_SPPI:
	    for (i = 0; i < smiValue->len; i++) {
		if (!isprint((int)smiValue->value.ptr[i])) break;
	    }
	    if (i == smiValue->len) {
		snprintf(s, sizeof(s), "\"%s\"", smiValue->value.ptr);
	    } else {
		snprintf(s, sizeof(s), "'%*s'H", 2 * smiValue->len, "");
		for (i=0; i < smiValue->len; i++) {
		    sprintf(ss, "%02x", smiValue->value.ptr[i]);
		    strncpy(&s[1+2*i], ss, 2);
		}
	    }
	    break;
	case SMI_LANGUAGE_SMING:
	    for (i = 0; i < smiValue->len; i++) {
		if (!isprint((int)smiValue->value.ptr[i])) break;
	    }
	    if (i == smiValue->len) {
		snprintf(s, sizeof(s), "\"%s\"", smiValue->value.ptr);
	    } else {
		snprintf(s, sizeof(s), "0x%*s", 2 * smiValue->len, "");
		for (i=0; i < smiValue->len; i++) {
		    sprintf(ss, "%02x", smiValue->value.ptr[i]);
		    strncpy(&s[2+2*i], ss, 2);
		}
	    }
	    break;
	case SMI_LANGUAGE_YANG:
	    /* xxx we need to handle UTF8 and what do we do about
	       octet strings that contain true binary data? */
	    break;
	}
	break;
    case SMI_BASETYPE_BITS:
	/* xxx - return NULL if we run out of space? */
	switch (smiLanguage) {
	case SMI_LANGUAGE_SMIV1:
	    sprintf(s, "'%*s'H", 2 * smiValue->len, "");
            for (i = 0; i < smiValue->len; i++) {
		sprintf(ss, "%02x", smiValue->value.ptr[i]);
		strncpy(&s[1+2*i], ss, 2);
            }
	    break;
	case SMI_LANGUAGE_SMIV2:
	case SMI_LANGUAGE_SPPI:
	    strcpy(s, "{");
	    for (i = 0, n = 0, len = 1; i < smiValue->len * 8; i++, len = strlen(s)) {
		if (smiValue->value.ptr[i/8] & (1 << (7-(i%8)))) {
		    if (n) {
			snprintf(s + len, sizeof(s)-len-3, "%s", ", ");
			len = strlen(s);
		    }
		    n++;
		    for (nn = smiGetFirstNamedNumber(smiType); nn;
			 nn = smiGetNextNamedNumber(nn)) {
			if (nn->value.value.unsigned32 == i)
			    break;
		    }
		    if (nn) {
			snprintf(s + len, sizeof(s)-len-1, "%s", nn->name);
		    }
		}
	    }
	    snprintf(s + len, sizeof(s)-len-2, "}");
	    break;
	case SMI_LANGUAGE_SMING:
	    strcpy(s, "(");
	    for (i = 0, n = 0, len = 1; i < smiValue->len * 8; i++, len = strlen(s)) {
		if (smiValue->value.ptr[i/8] & (1 << i%8)) {
		    if (n) {
			snprintf(s + len, sizeof(s)-len-3, "%s", ", ");
			len = strlen(s);
		    }
		    n++;
		    for (nn = smiGetFirstNamedNumber(smiType); nn;
			 nn = smiGetNextNamedNumber(nn)) {
			if (nn->value.value.unsigned32 == i)
			    break;
		    }
		    if (nn) {
			snprintf(s + len, sizeof(s)-len-1, "%s", nn->name);
		    } else {
			snprintf(s + len, sizeof(s)-len-1, "%d", i);
		    }
		}
	    }
	    snprintf(s + len, sizeof(s)-len-2, ")");
	    break;
	default:
	    s[0] = 0;
	    for (i = 0, n = 0, len = 0; i < smiValue->len * 8; i++, len = strlen(s)) {
		if (smiValue->value.ptr[i/8] & (1 << i%8)) {
		    if (n) {
			snprintf(s + len, sizeof(s)-len-2, "%s", " ");
			len = strlen(s);
		    }
		    n++;
		    for (nn = smiGetFirstNamedNumber(smiType); nn;
			 nn = smiGetNextNamedNumber(nn)) {
			if (nn->value.value.unsigned32 == i)
			    break;
		    }
		    if (nn) {
			snprintf(s + len, sizeof(s)-len-1, "%s", nn->name);
		    } else {
			snprintf(s + len, sizeof(s)-len-1, "%d", i);
		    }
		}
	    }
	    break;
	}
    case SMI_BASETYPE_UNKNOWN:
	break;
    case SMI_BASETYPE_POINTER:
	break;
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	/* xxx - return NULL if we run out of space? */
	nodePtr = smiGetNodeByOID(smiValue->len, smiValue->value.oid);
	if (nodePtr) {
	    snprintf(s, sizeof(s), "%s", nodePtr->name);
	} else {
	    switch (smiLanguage) {
	    case SMI_LANGUAGE_SMIV1:
	    case SMI_LANGUAGE_SMIV2:
	    case SMI_LANGUAGE_SPPI:
		strcpy(s, "{");
		for (i = 0, len = 1; i < smiValue->len; i++, len = strlen(s)) {
		    if (i && len < sizeof(s)-2) {
			strcat(s, " ");
			len++;
		    }
		    snprintf(s + len, sizeof(s)-len-2,
			     "%u", smiValue->value.oid[i]);
		}
		snprintf(s + len, sizeof(s)-len-2, "}");
		break;
	    default:
		strcpy(s, "");
		for (i = 0, len = 0; i < smiValue->len; i++, len = strlen(s)) {
		    if (i && len < sizeof(s)-2) {
			strcat(s, ".");
			len++;
		    }
		    snprintf(s + len, sizeof(s)-len-2,
			     "%u", smiValue->value.oid[i]);
		}
		break;
	    }
	}
	break;
    }

    return s;
}


/*
 * Interface Functions.
 */

int smiInit(const char *tag)
{
    char *p, *pp, *tag2;
#ifdef HAVE_PWD_H
    struct passwd *pw;
#endif

    smiHandle = findHandleByName(tag);
    if (smiHandle) {
        return 0;
    }
    smiHandle = addHandle(tag);
    
    smiDepth = 0;

    smiHandle->errorLevel = DEFAULT_ERRORLEVEL;
    smiHandle->errorHandler = smiErrorHandler;
#if !defined(_MSC_VER)
    smiHandle->cache = NULL;
    smiHandle->cacheProg = NULL;
#endif
    
    if (smiInitData()) {
        return -1;
    }

    /*
     * Setup the SMI MIB module search path:
     *  1. set to builtin DEFAULT_SMIPATH
     *  2. read global config file if present (append/prepend/replace)
     *  3. read user config file if present (append/prepend/replace)
     *  4. evaluate SMIPATH env-var if set (append/prepend/replace)
     */

    /* 1. set to builtin DEFAULT_SMIPATH */
    smiHandle->path = smiStrdup(DEFAULT_SMIPATH);

    tag2 = smiStrdup(tag);
    if (tag2) tag2 = strtok(tag2, ":");
    if (tag2) {
	/* 2. read global config file if present (append/prepend/replace) */
	smiReadConfig(DEFAULT_GLOBALCONFIG, tag2);
#ifdef HAVE_PWD_H
	pw = getpwuid(getuid());
	if (pw && pw->pw_dir) {
	    /* 3. read user config file if present (append/prepend/replace) */
	    smiAsprintf(&p, "%s%c%s",
			pw->pw_dir, DIR_SEPARATOR, DEFAULT_USERCONFIG);
	    smiReadConfig(p, tag2);
	    smiFree(p);
	}
#endif
    }
    smiFree(tag2);

    /* 4. evaluate SMIPATH env-var if set (append/prepend/replace) */
    p = getenv("SMIPATH");
    if (p) {
	if (p[0] == PATH_SEPARATOR) {
	    smiAsprintf(&pp, "%s%s", smiHandle->path, p);
	    smiFree(smiHandle->path);
	    smiHandle->path = pp;
	} else if (p[strlen(p)-1] == PATH_SEPARATOR) {
	    smiAsprintf(&pp, "%s%s", p, smiHandle->path);
	    smiFree(smiHandle->path);
	    smiHandle->path = pp;
	} else {
	    smiHandle->path = smiStrdup(p);
	}
    }
    
    if (!smiHandle->path) {
	return -1;
    }

    return 0;
}



void smiExit()
{
    if (!smiHandle)
	return;

    smiFreeData();
#ifdef BACKEND_YANG
    yangFreeData();
#endif

    smiFree(smiHandle->path);
#if !defined(_MSC_VER)
    smiFree(smiHandle->cache);
    smiFree(smiHandle->cacheProg);
#endif
    
    removeHandle(smiHandle);
    
    smiHandle = NULL;
    return;
}

char *smiGetPath()
{
    if (smiHandle->path) {
	return smiStrdup(smiHandle->path);
    } else {
	return NULL;
    }
}



int smiSetPath(const char *s)
{
    char *s2;

    if (!smiHandle) smiInit(NULL);

    if (!s) {
	smiFree(smiHandle->path);
	smiHandle->path = NULL;
	return 0;
    }
    
    s2 = smiStrdup(s);
    if (s2) {
	smiFree(smiHandle->path);
	smiHandle->path = s2;
	return 0;
    } else {
	return -1;
    }
    
}



void smiSetSeverity(char *pattern, int severity)
{
    smiSetErrorSeverity(pattern, severity);
}



int smiReadConfig(const char *filename, const char *tag)
{
    FILE *file;
    char buf[201];
    char *cmd, *arg, *s;
    
    file = fopen(filename, "r");
    if (file) {
	while (!feof(file)) {
	    if (!fgets(buf, 200, file)) continue;
	    if ((!strlen(buf)) || (buf[0] == '#')) continue;
	    cmd = strtok(buf, " \t\n\r");
	    if (!cmd) continue;
	    if (cmd[0] == '#') continue;
	    if (cmd[strlen(cmd)-1] == ':') {
		if (!tag) continue;
		cmd[strlen(cmd)-1] = 0;
		if (strcmp(cmd, tag)) continue;
		cmd = strtok(NULL, " \t\n\r");
	    }
	    arg = strtok(NULL, " \t\n\r");
	    if (!strcmp(cmd, "load")) {
		smiLoadModule(arg);
	    } else if (!strcmp(cmd, "path")) {
		if (arg) {
		    if (arg[0] == PATH_SEPARATOR) {
			smiAsprintf(&s, "%s%s", smiHandle->path, arg);
			smiFree(smiHandle->path);
			smiHandle->path = s;
		    } else if (arg[strlen(arg)-1] == PATH_SEPARATOR) {
			smiAsprintf(&s, "%s%s", arg, smiHandle->path);
			smiFree(smiHandle->path);
			smiHandle->path = s;
		    } else {
			smiHandle->path = smiStrdup(arg);
		    }
		}
	    } else if (!strcmp(cmd, "cache")) {
#if !defined(_MSC_VER)
		smiFree(smiHandle->cache);
		smiFree(smiHandle->cacheProg);
#endif
		if (arg && strcmp(arg, "off")) {
#if !defined(_MSC_VER)
		    smiHandle->cache = smiStrdup(arg);
		    arg = strtok(NULL, "\n\r");
		    smiHandle->cacheProg = smiStrdup(arg);
#else
		    smiPrintError(NULL, ERR_CACHE_CONFIG_NOT_SUPPORTED,
				  filename);
#endif
		}
	    } else if (!strcmp(cmd, "level")) {
		smiSetErrorLevel(atoi(arg));
	    } else if (!strcmp(cmd, "hide")) {
		smiSetSeverity(arg, 9);
	    } else {
		smiPrintError(NULL, ERR_UNKNOWN_CONFIG_CMD, cmd, filename);
	    }
	}
	fclose(file);
	return 0;
    }
    return -1;
}



int smiIsLoaded(const char *module)
{
    if (!module)
	return 0;
    
    return isInView(module);
}

extern _YangNode *loadYangModule(const char *modulename, const char *revision, Parser *parserPtr);

char *smiLoadModule(const char *module)
{
    Module *modulePtr;
    SmiLanguage lang;
    
    if (!smiHandle) smiInit(NULL);
    
    lang = smiGuessModuleLanguage(module);

#ifdef BACKEND_YANG
    if (lang == SMI_LANGUAGE_YANG) {
        _YangNode *yangModulePtr = loadYangModule(module, NULL, NULL);
        if (yangModulePtr) {
            return yangModulePtr->export.value;
        } else {
            return NULL;
        }
    }
#endif

    if (smiIsPath(module)) {
	
	modulePtr = loadModule(module, NULL);
	
	if (modulePtr) {
	    if (!isInView(modulePtr->export.name)) {
		addView(modulePtr->export.name);
	    }
	    return modulePtr->export.name;
	} else {
	    return NULL;
	}
    } else {	
	if ((modulePtr = findModuleByName(module))) {
	    /* already loaded. */
	    if (!isInView(module)) {
		addView(module);
	    }
	    return modulePtr->export.name;
	} else {
	    if ((modulePtr = loadModule(module, NULL))) {
		if (!isInView(module)) {
		    addView(module);
		}
		return modulePtr->export.name;
	    } else {
		return NULL;
	    }
	}
    }
}

void smiSetErrorLevel(int level)
{
    if (!smiHandle) smiInit(NULL);
    
    smiHandle->errorLevel = level;
}



void smiSetFlags(int userflags)
{
    if (!smiHandle) smiInit(NULL);
    
    smiHandle->flags = (smiHandle->flags & ~SMI_FLAG_MASK) | userflags;
}



int smiGetFlags()
{
    if (!smiHandle) smiInit(NULL);
    
    return smiHandle->flags & SMI_FLAG_MASK;
}



SmiModule *smiGetModule(const char *module)
{
    Module	      *modulePtr;
    
    if (!module) {
	return NULL;
    }

    modulePtr = findModuleByName(module);
    
    if (!modulePtr) {
	modulePtr = loadModule(module, NULL);
    }
    
    return &modulePtr->export;
}



SmiModule *smiGetFirstModule()
{
    Module	      *modulePtr;

    for (modulePtr = smiHandle->firstModulePtr;
	 modulePtr && modulePtr->export.name &&
	     (strlen(modulePtr->export.name) == 0);
	 modulePtr = modulePtr->nextPtr);

    return &modulePtr->export;
}



SmiModule *smiGetNextModule(SmiModule *smiModulePtr)
{
    Module	      *modulePtr;
    
    if (!smiModulePtr) {
	return NULL;
    }

    
    for (modulePtr = ((Module *)smiModulePtr)->nextPtr;
	 modulePtr && modulePtr->export.name &&
	     (strlen(modulePtr->export.name) == 0);
	 modulePtr = modulePtr->nextPtr);

    return &modulePtr->export;
}



SmiImport *smiGetFirstImport(SmiModule *smiModulePtr)
{
    if (!smiModulePtr) {
	return NULL;
    }
    
    return &((Module *)smiModulePtr)->firstImportPtr->export;
}



SmiImport *smiGetNextImport(SmiImport *smiImportPtr)
{
    if (!smiImportPtr) {
	return NULL;
    }

    return &((Import *)smiImportPtr)->nextPtr->export;
}



int smiIsImported(SmiModule *smiModulePtr,
		  SmiModule *importedModulePtr,
		  char *importedName)
{
    Import	   *importPtr;
    Module	   *modulePtr;
    char	   *importedModule;
    
    if ((!smiModulePtr) || (!importedName)) {
	return 0;
    }

    modulePtr = (Module *)smiModulePtr;
    
    if (importedModulePtr) {
	importedModule = importedModulePtr->name;
    } else {
	importedModule = NULL;
    }
	
    for (importPtr = modulePtr->firstImportPtr; importPtr;
	 importPtr = importPtr->nextPtr) {
	if ((!strcmp(importedName, importPtr->export.name)) &&
	    ((!importedModule) ||
	     (!strcmp(importedModule, importPtr->export.module)))) {
	    return 1;
	}
    }

    return 0;
}



SmiRevision *smiGetFirstRevision(SmiModule *smiModulePtr)
{
    if (!smiModulePtr) {
	return NULL;
    }
    
    return &((Module *)smiModulePtr)->firstRevisionPtr->export;
}



SmiRevision *smiGetNextRevision(SmiRevision *smiRevisionPtr)
{
    if (!smiRevisionPtr) {
	return NULL;
    }

    return &((Revision *)smiRevisionPtr)->nextPtr->export;
}



int smiGetRevisionLine(SmiRevision *smiRevisionPtr)
{
    return ((Revision *)smiRevisionPtr)->line;
}



SmiType *smiGetType(SmiModule *smiModulePtr, char *type)
{
    Type	    *typePtr = NULL;
    Module	    *modulePtr = NULL;
    char	    *module2, *type2;

    if (!type) {
	return NULL;
    }

    modulePtr = (Module *)smiModulePtr;

    getModulenameAndName(smiModulePtr ? smiModulePtr->name : NULL, type,
			 &module2, &type2);
    
    if (!modulePtr && module2 && strlen(module2)) {
	if (!(modulePtr = findModuleByName(module2))) {
	    modulePtr = loadModule(module2, NULL);
	}
    }

    if (modulePtr) {
	typePtr = findTypeByModuleAndName(modulePtr, type2);
    } else {
	typePtr = findTypeByName(type2);
    }
    
    smiFree(module2);
    smiFree(type2);

    if (!typePtr ||
	typePtr->export.basetype == SMI_BASETYPE_UNKNOWN) {
	return NULL;
    }
    
    return &typePtr->export;
}



SmiType *smiGetFirstType(SmiModule *smiModulePtr)
{
    Type *typePtr;
    
    if (!smiModulePtr) {
	return NULL;
    }
    
    for (typePtr = ((Module *)smiModulePtr)->firstTypePtr; typePtr;
	 typePtr = typePtr->nextPtr) {
	/* loop until we found a `real' type */
	if (typePtr->export.name &&
	    typePtr->export.basetype != SMI_BASETYPE_UNKNOWN) {
	    break;
	}
    }
    
    return &typePtr->export;
}



SmiType *smiGetNextType(SmiType *smiTypePtr)
{
    Type *typePtr;

    if (!smiTypePtr) {
	return NULL;
    }

    for (typePtr = ((Type *)smiTypePtr)->nextPtr; typePtr;
	 typePtr = typePtr->nextPtr) {
	/* loop until we found a `real' type */
	if (typePtr->export.name &&
	    typePtr->export.basetype != SMI_BASETYPE_UNKNOWN) {
	    break;
	}
    }
    
    return &typePtr->export;
}


SmiType *smiGetParentType(SmiType *smiTypePtr)
{
    Type *typePtr;

    if (!smiTypePtr) {
	return NULL;
    }

    typePtr = ((Type *)smiTypePtr)->parentPtr;
    
    if (!typePtr ||
	typePtr->export.basetype == SMI_BASETYPE_UNKNOWN) {
	return NULL;
    }
    
    return &typePtr->export;
}



SmiModule *smiGetTypeModule(SmiType *smiTypePtr)
{
    return &((Type *)smiTypePtr)->modulePtr->export;
}

int smiGetTypeLine(SmiType *smiTypePtr)
{
    return ((Type *)smiTypePtr)->line;
}



SmiNamedNumber *smiGetFirstNamedNumber(SmiType *smiTypePtr)
{
    Type    *typePtr;

    typePtr = (Type *)smiTypePtr;
    
    if ((!typePtr) || (!typePtr->listPtr) ||
	((typePtr->export.basetype != SMI_BASETYPE_ENUM) &&
	 (typePtr->export.basetype != SMI_BASETYPE_BITS) && 
	 (typePtr->export.basetype != SMI_BASETYPE_POINTER))) {
	return NULL;
    }
    
    return &((NamedNumber *)typePtr->listPtr->ptr)->export;
}



SmiNamedNumber *smiGetNextNamedNumber(SmiNamedNumber *smiNamedNumberPtr)
{
    Type  *typePtr;
    List  *listPtr;
    
    if (!smiNamedNumberPtr) {
	return NULL;
    }
    
    typePtr = ((NamedNumber *)smiNamedNumberPtr)->typePtr;

    
    if ((!typePtr) || (!typePtr->listPtr) ||
	((typePtr->export.basetype != SMI_BASETYPE_ENUM) &&
	 (typePtr->export.basetype != SMI_BASETYPE_BITS))) {
	return NULL;
    }

    for (listPtr = typePtr->listPtr; listPtr; listPtr = listPtr->nextPtr) {
	if (((NamedNumber *)(listPtr->ptr))->export.name ==
	                                               smiNamedNumberPtr->name)
	    break;
    }

    if ((!listPtr) || (!listPtr->nextPtr)) {
	return NULL;
    }
	
    return &((NamedNumber *)listPtr->nextPtr->ptr)->export;
}

SmiNamedNumber *smiGetAttributeFirstNamedNumber(SmiAttribute *smiAttributePtr)
{
    Attribute    *attributePtr;

    attributePtr = (Attribute *)smiAttributePtr;
    
    if ((!attributePtr) || (!attributePtr->listPtr) ||
	((attributePtr->export.basetype != SMI_BASETYPE_ENUM) &&
	 (attributePtr->export.basetype != SMI_BASETYPE_BITS) && 
	 (attributePtr->export.basetype != SMI_BASETYPE_POINTER))) {
	return NULL;
    }
    
    return &((NamedNumber *)attributePtr->listPtr->ptr)->export;
}



SmiNamedNumber *smiGetAttributeNextNamedNumber(SmiNamedNumber *smiNamedNumberPtr)
{
    Attribute  *attributePtr;
    List  *listPtr;
    
    if (!smiNamedNumberPtr) {
	return NULL;
    }
    
    attributePtr = (Attribute*)(((NamedNumber *)smiNamedNumberPtr)->typePtr);

    
    if ((!attributePtr) || (!attributePtr->listPtr) ||
	((attributePtr->export.basetype != SMI_BASETYPE_ENUM) &&
	 (attributePtr->export.basetype != SMI_BASETYPE_BITS))) {
	return NULL;
    }

    for (listPtr = attributePtr->listPtr; listPtr; listPtr = listPtr->nextPtr) {
	if (((NamedNumber *)(listPtr->ptr))->export.name ==
	                                               smiNamedNumberPtr->name)
	    break;
    }

    if ((!listPtr) || (!listPtr->nextPtr)) {
	return NULL;
    }
	
    return &((NamedNumber *)listPtr->nextPtr->ptr)->export;
}

SmiRange *smiGetFirstRange(SmiType *smiTypePtr)
{
    Type    *typePtr;

    typePtr = (Type *)smiTypePtr;
    
    if ((!typePtr) || (!typePtr->listPtr) ||
	(typePtr->export.basetype == SMI_BASETYPE_ENUM) ||
	(typePtr->export.basetype == SMI_BASETYPE_BITS)) {
	return NULL;
    }

    return &((Range *)typePtr->listPtr->ptr)->export;
}



SmiRange *smiGetNextRange(SmiRange *smiRangePtr)
{
    Type  *typePtr;
    List  *listPtr;

    if (!smiRangePtr) {
	return NULL;
    }
    
    typePtr = ((Range *)smiRangePtr)->typePtr;

    if ((!typePtr) || (!typePtr->listPtr) ||
	(typePtr->export.basetype == SMI_BASETYPE_ENUM) ||
	(typePtr->export.basetype == SMI_BASETYPE_BITS)) {
	return NULL;
    }
 
    for (listPtr = typePtr->listPtr; listPtr; listPtr = listPtr->nextPtr) {
	if (!memcmp(&((Range *)listPtr->ptr)->export.minValue,
		    &smiRangePtr->minValue, sizeof(struct SmiValue))
	    && !memcmp(&((Range *)listPtr->ptr)->export.maxValue,
		       &smiRangePtr->maxValue, sizeof(struct SmiValue))
	    && smiRangePtr == &((Range *)listPtr->ptr)->export)
	    break;
    }

    if ((!listPtr) || (!listPtr->nextPtr)) {
	return NULL;
    }
	
    return &((Range *)listPtr->nextPtr->ptr)->export;
}

SmiRange *smiGetAttributeFirstRange(SmiAttribute *smiAttributePtr)
{
    Attribute    *attributePtr;

    attributePtr = (Attribute *)smiAttributePtr;
    
    if ((!attributePtr) || (!attributePtr->listPtr) ||
	(attributePtr->export.basetype == SMI_BASETYPE_ENUM) ||
	(attributePtr->export.basetype == SMI_BASETYPE_BITS)) {
	return NULL;
    }

    return &((Range *)attributePtr->listPtr->ptr)->export;
}



SmiRange *smiGetAttributeNextRange(SmiRange *smiRangePtr)
{
    Attribute  *attributePtr;
    List  *listPtr;

    if (!smiRangePtr) {
	return NULL;
    }
    
    attributePtr = (Attribute*)((Range *)smiRangePtr)->typePtr;

    if ((!attributePtr) || (!attributePtr->listPtr) ||
	(attributePtr->export.basetype == SMI_BASETYPE_ENUM) ||
	(attributePtr->export.basetype == SMI_BASETYPE_BITS)) {
	return NULL;
    }
 
    for (listPtr = attributePtr->listPtr; listPtr; listPtr = listPtr->nextPtr) {
	if (!memcmp(&((Range *)listPtr->ptr)->export.minValue,
		    &smiRangePtr->minValue, sizeof(struct SmiValue))
	    && !memcmp(&((Range *)listPtr->ptr)->export.maxValue,
		       &smiRangePtr->maxValue, sizeof(struct SmiValue))
	    && smiRangePtr == &((Range *)listPtr->ptr)->export)
	    break;
    }

    if ((!listPtr) || (!listPtr->nextPtr)) {
	return NULL;
    }
	
    return &((Range *)listPtr->nextPtr->ptr)->export;
}


SmiIdentity *smiGetFirstIdentity(SmiModule *smiModulePtr)
{
    if (!smiModulePtr) {
	return NULL;
    }
    
    return ((Module *)smiModulePtr)->firstIdentityPtr ?
	&((Module *)smiModulePtr)->firstIdentityPtr->export : NULL;
    
}

SmiIdentity *smiGetNextIdentity(SmiIdentity *smiIdentityPtr)
{
    if (!smiIdentityPtr) {
	return NULL;
    }

    return ((Identity *)smiIdentityPtr)->nextPtr ?
	&((Identity *)smiIdentityPtr)->nextPtr->export : NULL;
}

SmiModule *smiGetIdentityModule(SmiIdentity *smiIdentityPtr)
{
    return &((Identity *)smiIdentityPtr)->modulePtr->export;
}

SmiIdentity *smiGetParentIdentity(SmiIdentity *smiIdentityPtr)
{
    return (SmiIdentity*)(((Identity *)smiIdentityPtr)->parentPtr);
}

SmiIdentity *smiGetIdentity(SmiModule *smiModulePtr, char *identity)
{
    SmiIdentity *ide; 
    
    if (!smiModulePtr) {
	return NULL;
    }
    	
    for (ide = smiGetFirstIdentity(smiModulePtr); 
	 ide; ide = smiGetNextIdentity(ide)) {
	if (!strncmp(ide->name,identity,64)) break;
    }
    return ide;
}

int smiGetIdentityLine(SmiIdentity *smiIdentityPtr)
{
    return ((Identity *)smiIdentityPtr)->line;
}

	
SmiClass *smiGetFirstClass(SmiModule *smiModulePtr)
{
    if (!smiModulePtr) {
	return NULL;
    }
    
    return ((Module *)smiModulePtr)->firstClassPtr ?
	&((Module *)smiModulePtr)->firstClassPtr->export : NULL;
    
}

SmiClass *smiGetNextClass(SmiClass *smiClassPtr)
{
    if (!smiClassPtr) {
	return NULL;
    }
    
    return ((Class *)smiClassPtr)->nextPtr ?
	&((Class *)smiClassPtr)->nextPtr->export : NULL;
}

SmiModule *smiGetClassModule(SmiClass *smiClassPtr)
{
    return &((Class *)smiClassPtr)->modulePtr->export;
}

SmiClass *smiGetParentClass(SmiClass *smiClassPtr)
{
    return (SmiClass*)(((Class *)smiClassPtr)->parentPtr);
}

SmiClass *smiGetClass(SmiModule *smiModulePtr, char *className)
{
    SmiClass *cl;
    
    if (!smiModulePtr) {
	return NULL;
    }
    
    for (cl = smiGetFirstClass(smiModulePtr); 
	 cl; cl = smiGetNextClass(cl)) {
	if (!strncmp(cl->name, className, 64)) break;
    }
    return cl;
}

int smiGetClassLine(SmiClass *smiClassPtr)
{
    return ((Class *)smiClassPtr)->line;
}

SmiAttribute *smiGetFirstAttribute(SmiClass *smiClassPtr)
{
    Attribute *attributePtr;
    
    if (!smiClassPtr) {
	return NULL;
    }
    
    attributePtr = ((Class *)smiClassPtr)->firstAttributePtr;
    return &attributePtr->export;
}

SmiAttribute *smiGetNextAttribute(SmiAttribute *smiTypePtr)
{
    Attribute *attributePtr;

    if (!smiTypePtr) {
	return NULL;
    }

    attributePtr = ((Attribute *)smiTypePtr)->nextPtr;
    return &attributePtr->export;
}

SmiAttribute *smiGetAttribute(SmiClass *smiClassPtr, char *attribute)
{
    Attribute *attributePtr;
    
    if (! smiClassPtr) {
	return NULL;
    }
    
    for (attributePtr = ((Class *)smiClassPtr)->firstAttributePtr; 
	 attributePtr; attributePtr = attributePtr->nextPtr)
    {
	if (!strncmp(attributePtr->export.name, attribute,64)) {
	    return &attributePtr->export;
	}
    }
    
    /*
     * attribute might belong to the parent so check parent if
     * attribute not found
     */
    
    smiClassPtr = smiGetParentClass(smiClassPtr);
    attributePtr = (Attribute*)smiGetAttribute(smiClassPtr , attribute);
  		
    return &attributePtr->export;
}

SmiType *smiGetAttributeParentType(SmiAttribute *smiAttributePtr)
{
    Type *parentTypePtr;
    
    if (! smiAttributePtr) {
	return NULL;
    }
    
    parentTypePtr = ((Attribute*)smiAttributePtr)->parentTypePtr;
    
    return (parentTypePtr) ? &parentTypePtr->export : NULL;
}

SmiClass *smiGetAttributeParentClass( SmiAttribute *smiAttributePtr)
{
    Class *parentClassPtr;
    
    if (! smiAttributePtr) {
	return NULL;
    }
    
    parentClassPtr = ((Attribute*)smiAttributePtr)->parentClassPtr;

    return parentClassPtr ? &parentClassPtr->export : NULL;
}

SmiAttribute *smiGetFirstUniqueAttribute(SmiClass *smiClassPtr)
{
    Class *classPtr;
    
    if (! smiClassPtr) {
	return NULL;
    }
	
    classPtr = (Class*)smiClassPtr;
	
    if (! classPtr->uniqueList) {
	return NULL;
    }
    
    if (classPtr->uniqueList->ptr == classPtr) {
	return NULL; /* scalar class */
    }

    return (SmiAttribute*)(classPtr->uniqueList->ptr);
}

SmiAttribute *smiGetNextUniqueAttribute( SmiAttribute *smiTypePtr)
{
    Class *classPtr;
    List  *listPtr; 
    
    if (! smiTypePtr) {
	return NULL;
    }
	
    classPtr = ((Attribute*)smiTypePtr)->classPtr;
    
    if (classPtr && classPtr->uniqueList) {
	for (listPtr=classPtr->uniqueList;listPtr; listPtr=listPtr->nextPtr) {
	    if (&((Attribute*)(listPtr->ptr))->export ==  smiTypePtr) {	
		if (listPtr->nextPtr) {
		    return &((Attribute*)(listPtr->nextPtr->ptr))->export;
		}
	    }
	}
    }
    return NULL;
}



int smiGetAttributeLine(SmiAttribute *smiAttributePtr)
{
    return ((Attribute *)smiAttributePtr)->line;
}



int smiIsClassScalar(SmiClass *smiClassPtr)
{
    Class *classPtr;
    
    if (! smiClassPtr) {
	return 0;
    }
	
    classPtr = (Class*)smiClassPtr;
    
    if (! classPtr->uniqueList) {
	return 0;
    }

    return (classPtr->uniqueList->ptr == classPtr);
}



SmiEvent *smiGetFirstEvent(SmiClass *smiClassPtr)
{
    Event *eventPtr;
    
    if (! smiClassPtr) {
	return NULL;
    }
    
    eventPtr = ((Class *)smiClassPtr)->firstEventPtr;
    return &(eventPtr->export);
}



SmiEvent *smiGetNextEvent(SmiEvent *smiEventPtr)
{
    Event *eventPtr;

    if (! smiEventPtr) {
	return NULL;
    }

    eventPtr = ((Event *)smiEventPtr)->nextPtr;
    return &eventPtr->export;
}



int smiGetEventLine(SmiEvent *smiEventPtr)
{
    return ((Event *)smiEventPtr)->line;
}

	

SmiMacro *smiGetMacro(SmiModule *smiModulePtr, char *macro)
{
    Macro	    *macroPtr = NULL;
    Module	    *modulePtr = NULL;
    char	    *module2, *macro2;
    
    if (!macro) {
	return NULL;
    }

    modulePtr = (Module *)smiModulePtr;

    getModulenameAndName(smiModulePtr ? smiModulePtr->name : NULL, macro,
			 &module2, &macro2);
    
    if (!modulePtr && module2 && strlen(module2)) {
	if (!(modulePtr = findModuleByName(module2))) {
	    modulePtr = loadModule(module2, NULL);
	}
    }

    if (modulePtr) {
	macroPtr = findMacroByModuleAndName(modulePtr, macro2);
    } else {
	macroPtr = findMacroByName(macro2);
    }
    
    smiFree(module2);
    smiFree(macro2);
    return macroPtr ? &macroPtr->export : NULL;
}



SmiMacro *smiGetFirstMacro(SmiModule *smiModulePtr)
{
    if (!smiModulePtr) {
	return NULL;
    }
    
    return ((Module *)smiModulePtr)->firstMacroPtr ?
	&((Module *)smiModulePtr)->firstMacroPtr->export : NULL;
}



SmiMacro *smiGetNextMacro(SmiMacro *smiMacroPtr)
{
    if (!smiMacroPtr) {
	return NULL;
    }

    return ((Macro *)smiMacroPtr)->nextPtr ?
	&((Macro *)smiMacroPtr)->nextPtr->export : NULL;
}


SmiModule *smiGetMacroModule(SmiMacro *smiMacroPtr)
{
    return &((Macro *)smiMacroPtr)->modulePtr->export;
}


int smiGetMacroLine(SmiMacro *smiMacroPtr)
{
    return ((Macro *)smiMacroPtr)->line;
}


SmiNode *smiGetNode(SmiModule *smiModulePtr, const char *node)
{
    Object	    *objectPtr = NULL;
    Module	    *modulePtr = NULL;
    Node            *nodePtr;
    char	    *module2, *node2, *p;
    unsigned int    oidlen;
    SmiSubid	    oid[128];
    
    if (!node) {
	return NULL;
    }

    modulePtr = (Module *)smiModulePtr;

    getModulenameAndName(smiModulePtr ? smiModulePtr->name : NULL, node,
			 &module2, &node2);
    
    if (!modulePtr && module2 && strlen(module2)) {
	if (!(modulePtr = findModuleByName(module2))) {
	    modulePtr = loadModule(module2, NULL);
	}
    }

    if (isdigit((int)node2[0])) {
	for (oidlen = 0, p = strtok(node2, ". ");
	     p && oidlen < sizeof(oid)/sizeof(oid[0]);
	     oidlen++, p = strtok(NULL, ". ")) {
	    oid[oidlen] = strtoul(p, NULL, 0);
	}
	if (p) {
	    /* the numeric OID is too long */
	    return NULL;
	}
	nodePtr = getNode(oidlen, oid);
	if (nodePtr) {
	    if (modulePtr) {
		objectPtr = findObjectByModuleAndNode(modulePtr, nodePtr);
	    } else {
		objectPtr = findObjectByNode(nodePtr);
	    }
	}
    } else {
	p = strtok(node2, ". ");
	if (modulePtr) {
	    objectPtr = findObjectByModuleAndName(modulePtr, p);
	} else {
	    objectPtr = findObjectByName(p);
	}
    }
    
    smiFree(module2);
    smiFree(node2);
    return objectPtr ? &objectPtr->export : NULL;
}



SmiNode *smiGetNodeByOID(unsigned int oidlen, SmiSubid oid[])
{
    Node            *nodePtr;
    Object	    *objectPtr;
    
    if (!oidlen) {
	return NULL;
    }

    nodePtr = getNode(oidlen, oid);

    if (!nodePtr) {
	return NULL;
    }
    
    objectPtr = findObjectByNode(nodePtr);

    return objectPtr ? &objectPtr->export : NULL;
}



SmiNode *smiGetFirstNode(SmiModule *smiModulePtr, SmiNodekind nodekind)
{
    Module *modulePtr;
    Node   *nodePtr = NULL;
    Object *objectPtr;

    if (!smiModulePtr) {
	return NULL;
    }
    
    modulePtr = (Module *)smiModulePtr;

    if (modulePtr && modulePtr->prefixNodePtr) {
	/* start at the common oid prefix of this module */
	nodePtr = modulePtr->prefixNodePtr;
    } else {
	nodePtr = smiHandle->rootNodePtr->firstChildPtr;
    }

    do {
	objectPtr = getNextChildObject(nodePtr, modulePtr, nodekind);
	
	if (objectPtr)
	    return &objectPtr->export;
	
	if (nodePtr->firstChildPtr) {
	    nodePtr = nodePtr->firstChildPtr;
	} else if (nodePtr->nextPtr) {
	    nodePtr = nodePtr->nextPtr;
	} else {
	    for (nodePtr = nodePtr->parentPtr;
		 nodePtr && (nodePtr->parentPtr) && (!nodePtr->nextPtr);
		 nodePtr = nodePtr->parentPtr);
	    if (nodePtr) nodePtr = nodePtr->nextPtr;
	}
    } while (nodePtr);

    return NULL;
}



SmiNode *smiGetNextNode(SmiNode *smiNodePtr, SmiNodekind nodekind)
{
    Module	      *modulePtr;
    Object	      *objectPtr;
    Node	      *nodePtr;
    int               i;
    
    if (!smiNodePtr) {
	return NULL;
    }

    objectPtr = (Object *)smiNodePtr;
    nodePtr = objectPtr->nodePtr;
    modulePtr = objectPtr->modulePtr;

    if (!modulePtr) {
	return NULL;
    }

    if (!nodePtr) {
	return NULL;
    }

    do {
	if (nodePtr->firstChildPtr) {
	    nodePtr = nodePtr->firstChildPtr;
	} else if (nodePtr->nextPtr) {
	    nodePtr = nodePtr->nextPtr;
	} else {
	    for (nodePtr = nodePtr->parentPtr;
		 (nodePtr->parentPtr) && (!nodePtr->nextPtr);
		 nodePtr = nodePtr->parentPtr);
	    nodePtr = nodePtr->nextPtr;
	    /* did we move outside the common oid prefix of this module? */
	    for (i = 0; i < modulePtr->prefixNodePtr->oidlen; i++)
		if ((!nodePtr) || (!nodePtr->oid) ||
		    (nodePtr->oid[i] != modulePtr->prefixNodePtr->oid[i]))
		    return NULL;
	}

	objectPtr = getNextChildObject(nodePtr, modulePtr, nodekind);

	if (objectPtr)
	    return &objectPtr->export;
	
    } while (nodePtr);

    return NULL;
}



SmiNode *smiGetParentNode(SmiNode *smiNodePtr)
{
    Module	      *modulePtr;
    Object	      *objectPtr;
    Import	      *importPtr;
    Node	      *nodePtr;
    
    if (!smiNodePtr) {
	return NULL;
    }

    objectPtr = (Object *)smiNodePtr;
    nodePtr = objectPtr->nodePtr;
    modulePtr = objectPtr->modulePtr;

    if (!nodePtr) {
	return NULL;
    }

    if (nodePtr == smiHandle->rootNodePtr) {
	return NULL;
    }

    nodePtr = nodePtr->parentPtr;
    if (! nodePtr) {
        return NULL;
    }

    /*
     * First, try to find a definition in the same module.
     */
    objectPtr = NULL;
    if (modulePtr) {
	objectPtr = findObjectByModuleAndNode(modulePtr, nodePtr);
    }

    /*
     * If found, check if it's imported. In case, get the original definition.
     */
    if (objectPtr) {
	importPtr = findImportByName(objectPtr->export.name,
				     objectPtr->modulePtr);
	if (importPtr) {
	    objectPtr = findObjectByModulenameAndNode(importPtr->export.module,
						      nodePtr);
	} else {
	    objectPtr = NULL;
	}
    }
    
    /*
     * If not yet found, try to find any definition.
     */
    if (!objectPtr) {
	objectPtr = findObjectByNode(nodePtr);

	if ((!objectPtr) && (nodePtr->parentPtr)) {
	    /* an implicitly created node, e.g. gaga.0 in an object
	     * definition with oid == gaga.0.1.
	     */
	    objectPtr = addObject(SMI_UNKNOWN_LABEL,
				  nodePtr->parentPtr, nodePtr->subid,
				  0, NULL);
	    objectPtr->nodePtr = nodePtr;
	    objectPtr->modulePtr = modulePtr;
	}
    }

    return objectPtr ? &objectPtr->export : NULL;
}



SmiNode *smiGetRelatedNode(SmiNode *smiNodePtr)
{
    if (!smiNodePtr) {
	return NULL;
    }

    return &((Object *)smiNodePtr)->relatedPtr->export;
}



SmiNode *smiGetFirstChildNode(SmiNode *smiNodePtr)
{
    Module	      *modulePtr;
    Object	      *objectPtr;
    Node	      *nodePtr;

    if (!smiNodePtr) {
	return NULL;
    }

    objectPtr = (Object *)smiNodePtr;
    nodePtr = objectPtr->nodePtr;
    modulePtr = objectPtr->modulePtr;

    if (!nodePtr) {
	return NULL;
    }

    nodePtr = nodePtr->firstChildPtr;

    if (!nodePtr) {
	return NULL;
    }

    objectPtr = findObjectByModuleAndNode(modulePtr, nodePtr);
    if (!objectPtr) objectPtr = findObjectByNode(nodePtr);

    return objectPtr ? &objectPtr->export : NULL;
}



SmiNode *smiGetNextChildNode(SmiNode *smiNodePtr)
{
    Module	      *modulePtr;
    Object	      *objectPtr;
    Node	      *nodePtr;
    
    if (!smiNodePtr) {
	return NULL;
    }

    objectPtr = (Object *)smiNodePtr;
    nodePtr = objectPtr->nodePtr;
    modulePtr = objectPtr->modulePtr;

    if (!nodePtr) {
	return NULL;
    }

    nodePtr = nodePtr->nextPtr;

    if (!nodePtr) {
	return NULL;
    }

    objectPtr = findObjectByModuleAndNode(modulePtr, nodePtr);
    if (!objectPtr) objectPtr = findObjectByNode(nodePtr);
    
    return objectPtr ? &objectPtr->export : NULL;
}



SmiNode *smiGetModuleIdentityNode(SmiModule *smiModulePtr)
{
    if (!smiModulePtr) {
	return NULL;
    }

    return &((Module *)smiModulePtr)->objectPtr->export;
}



SmiModule *smiGetNodeModule(SmiNode *smiNodePtr)
{
    return &((Object *)smiNodePtr)->modulePtr->export;
}



SmiType *smiGetNodeType(SmiNode *smiNodePtr)
{
    Type *typePtr;

    typePtr = ((Object *)smiNodePtr)->typePtr;
    
    if (!typePtr ||
	typePtr->export.basetype == SMI_BASETYPE_UNKNOWN) {
	return NULL;
    }
    
    return &typePtr->export;
}



int smiGetNodeLine(SmiNode *smiNodePtr)
{
    return ((Object *)smiNodePtr)->line;
}



SmiElement *smiGetFirstElement(SmiNode *smiNodePtr)
{
    List	      *listPtr;
    
    if (!smiNodePtr) {
	return NULL;
    }

    listPtr = ((Object *)smiNodePtr)->listPtr;

    return (SmiElement *)listPtr;
}



SmiElement *smiGetNextElement(SmiElement *smiElementPtr)
{
    List	      *listPtr;
    
    if (!smiElementPtr) {
	return NULL;
    }

    listPtr = ((List *)smiElementPtr)->nextPtr;
    
    return (SmiElement *)listPtr;
}



SmiNode *smiGetElementNode(SmiElement *smiElementPtr)
{
    if ((Object *)((List *)smiElementPtr)->ptr)
        return &((Object *)((List *)smiElementPtr)->ptr)->export;
    else
        return NULL;
}



SmiOption *smiGetFirstOption(SmiNode *smiComplianceNodePtr)
{
    Object	      *objectPtr;
    
    if (!smiComplianceNodePtr) {
	return NULL;
    }

    objectPtr = (Object *)smiComplianceNodePtr;
    
    if (!objectPtr->optionlistPtr) {
	return NULL;
    }

    if (objectPtr->export.nodekind != SMI_NODEKIND_COMPLIANCE) {
	return NULL;
    }
						     
    return &((Option *)objectPtr->optionlistPtr->ptr)->export;
}



SmiOption *smiGetNextOption(SmiOption *smiOptionPtr)
{
    List	      *listPtr;
    
    if (!smiOptionPtr) {
	return NULL;
    }
						     
    for (listPtr =
	    ((Option *)smiOptionPtr)->compliancePtr->optionlistPtr;
	 listPtr;
	 listPtr = listPtr->nextPtr) {
	if ((Option *)(listPtr->ptr) == (Option *)smiOptionPtr) {
	    if (listPtr->nextPtr) {
		return &((Option *)listPtr->nextPtr->ptr)->export;
	    } else {
		return NULL;
	    }
	}
    }
    
    return NULL;
}



SmiNode *smiGetOptionNode(SmiOption *smiOptionPtr)
{
    return &((Option *)smiOptionPtr)->objectPtr->export;
}



int smiGetOptionLine(SmiOption *smiOptionPtr)
{
    return ((Option *)smiOptionPtr)->line;
}



SmiRefinement *smiGetFirstRefinement(SmiNode *smiComplianceNodePtr)
{
    Object	      *objectPtr;
    
    if (!smiComplianceNodePtr) {
	return NULL;
    }

    objectPtr = (Object *)smiComplianceNodePtr;
    
    if (!objectPtr->refinementlistPtr) {
	return NULL;
    }

    if (objectPtr->export.nodekind != SMI_NODEKIND_COMPLIANCE) {
	return NULL;
    }
						     
    return &((Refinement *)objectPtr->refinementlistPtr->ptr)->export;
}



SmiRefinement *smiGetNextRefinement(SmiRefinement *smiRefinementPtr)
{
    List	      *listPtr;
    
    if (!smiRefinementPtr) {
	return NULL;
    }
						     
    for (listPtr =
	    ((Refinement *)smiRefinementPtr)->compliancePtr->refinementlistPtr;
	 listPtr;
	 listPtr = listPtr->nextPtr) {
	if ((Refinement *)(listPtr->ptr) == (Refinement *)smiRefinementPtr) {
	    if (listPtr->nextPtr) {
		return &((Refinement *)listPtr->nextPtr->ptr)->export;
	    } else {
		return NULL;
	    }
	}
    }
    
    return NULL;
}



SmiNode *smiGetRefinementNode(SmiRefinement *smiRefinementPtr)
{
    return &((Refinement *)smiRefinementPtr)->objectPtr->export;
}



SmiType *smiGetRefinementType(SmiRefinement *smiRefinementPtr)
{
    Type *typePtr;

    typePtr = ((Refinement *)smiRefinementPtr)->typePtr;
    
    if (!typePtr ||
	typePtr->export.basetype == SMI_BASETYPE_UNKNOWN) {
	return NULL;
    }
    
    return &typePtr->export;
}



SmiType *smiGetRefinementWriteType(SmiRefinement *smiRefinementPtr)
{
    Type *typePtr;

    typePtr = ((Refinement *)smiRefinementPtr)->writetypePtr;
    
    if (!typePtr ||
	typePtr->export.basetype == SMI_BASETYPE_UNKNOWN) {
	return NULL;
    }
    
    return &typePtr->export;
}



int smiGetRefinementLine(SmiRefinement *smiRefinementPtr)
{
    return ((Refinement *)smiRefinementPtr)->line;
}



SmiElement *smiGetFirstUniquenessElement(SmiNode *smiNodePtr)
{
    List	      *listPtr;
    
    if (!smiNodePtr) {
	return NULL;
    }

    listPtr = ((Object *)smiNodePtr)->uniquenessPtr;

    return (SmiElement *)listPtr;
}



char *smiRenderOID(unsigned int oidlen, SmiSubid *oid, int flags)
{
    SmiNode *nodePtr = NULL;
    SmiModule *modulePtr = NULL;
    unsigned int i = 0;
    char *ss, *s = NULL;

    if (!oid) {
	if (flags & SMI_RENDER_UNKNOWN) {
	    smiAsprintf(&s, SMI_UNKNOWN_LABEL);
	} else {
	    s = NULL;
	}
	return s;
    }
    
    if (flags & (SMI_RENDER_NAME | SMI_RENDER_QUALIFIED)) {
	int len;
	for (len = oidlen; len; len--) {
	    nodePtr = smiGetNodeByOID(len, oid);
	    if (! nodePtr || nodePtr->name) break;
	}
	if (nodePtr && nodePtr->name) {
	    i = nodePtr->oidlen;
	    if (flags & SMI_RENDER_QUALIFIED) {
		modulePtr = smiGetNodeModule(nodePtr);
	    }
	    if (modulePtr) {
		smiAsprintf(&s, "%s::%s",
			    modulePtr->name, nodePtr->name);
	    } else {
		smiAsprintf(&s, "%s", nodePtr->name);
	    }
	}
    }

    for (; i < oidlen; i++) {
	ss = s;
	smiAsprintf(&s, "%s%s%u", ss ? ss : "", i ? "." : "", oid[i]);
	smiFree(ss);
    }

    if ((!s) && (flags & SMI_RENDER_UNKNOWN)) {
	smiAsprintf(&s, SMI_UNKNOWN_LABEL);
    }
    
    return s;
}



char *smiRenderValue(SmiValue *smiValuePtr, SmiType *smiTypePtr, int flags)
{
    unsigned int i, pfx;
    int j, k, n, have_pfx;
    char *last_fmt, *fmt;
    SmiUnsigned64 vv;
    int xlen;
    SmiNamedNumber *nn;
    char *s = NULL, *ss;
    char f[10];
    SmiUnsigned32 v32;
    SmiUnsigned64 v64;
    
    if (!smiValuePtr) {
	if (flags & SMI_RENDER_UNKNOWN) {
	    smiAsprintf(&s, SMI_UNKNOWN_LABEL);
	} else {
	    s = NULL;
	}
	return s;
    }
    
    switch (smiValuePtr->basetype) {
    case SMI_BASETYPE_UNSIGNED32:
	if (!(flags & SMI_RENDER_FORMAT) ||
	    !smiTypePtr || !smiTypePtr->format ||
	    !strlen(smiTypePtr->format) || smiTypePtr->format[0] == 'd') {
	    if (smiTypePtr->format && (strlen(smiTypePtr->format) >= 3) &&
		(smiTypePtr->format[1] == '-')) {
		i = atoi(&smiTypePtr->format[2]);
		if (i < 0) i = 0;
		if (i > 20) i = 20;
		smiAsprintf(&s, "%0*lu.",
			    1 + i,
			    smiValuePtr->value.unsigned32);
		if (s) {
		    for (j = strlen(s) - 1; i > 0; i--, j--) {
			s[j] = s[j-1];
		    }
		    s[j] = '.';
		}
	    } else {
		smiAsprintf(&s, "%lu", smiValuePtr->value.unsigned32);
	    }
	} else if (smiTypePtr->format[0] == 'x') {
	    smiAsprintf(&s, "%lx", smiValuePtr->value.unsigned32);
	} else if (smiTypePtr->format[0] == 'o') {
	    smiAsprintf(&s, "%lo", smiValuePtr->value.unsigned32);
	} else if (smiTypePtr->format[0] == 'b') {
	    for (i = 32 - 1;
		 i > 0 && !(smiValuePtr->value.unsigned32 & (1 << i)); i--);
	    s = smiMalloc(i + 1 + 1);
	    if (s) {
		for (j = 0; i >= 0; i--, j++) {
		    s[j] = smiValuePtr->value.unsigned32 & (1<<i) ? '1' : '0';
		}
		s[j] = 0;
	    }
	}
	break;
    case SMI_BASETYPE_UNSIGNED64:
	if (!(flags & SMI_RENDER_FORMAT) ||
	    !smiTypePtr || !smiTypePtr->format ||
	    !strlen(smiTypePtr->format) || smiTypePtr->format[0] == 'd') {
	    if (smiTypePtr->format && (strlen(smiTypePtr->format) >= 3) &&
		(smiTypePtr->format[1] == '-')) {
		i = atoi(&smiTypePtr->format[2]);
		if (i < 0) i = 0;
		if (i > 20) i = 20;
		sprintf(f, "%%0%s.", UINT64_FORMAT);
		f[2] = '*';
		smiAsprintf(&s, f,
			    1 + i,
			    smiValuePtr->value.unsigned64);
		if (s) {
		    for (j = strlen(s) - 1; i > 0; i--, j--) {
			s[j] = s[j-1];
		    }
		    s[j] = '.';
		}
	    } else {
		smiAsprintf(&s, UINT64_FORMAT, smiValuePtr->value.unsigned64);
	    }
	} else if (smiTypePtr->format[0] == 'x') {
	    strcpy(f, UINT64_FORMAT);
	    f[strlen(f)-1] = 'x';
	    smiAsprintf(&s, f, smiValuePtr->value.unsigned64);
	} else if (smiTypePtr->format[0] == 'o') {
	    strcpy(f, UINT64_FORMAT);
	    f[strlen(f)-1] = 'o';
	    smiAsprintf(&s, f, smiValuePtr->value.unsigned64);
	} else if (smiTypePtr->format[0] == 'b') {
	    for (i = 64 - 1;
		 i > 0 && !(smiValuePtr->value.unsigned64 & (1 << i)); i--);
	    s = smiMalloc(i + 1 + 1);
	    if (s) {
		for (j = 0; i >= 0; i--, j++) {
		    s[j] = smiValuePtr->value.unsigned64 & (1<<i) ? '1' : '0';
		}
		s[j] = 0;
	    }
	}
	break;
    case SMI_BASETYPE_INTEGER32:
	if (!(flags & SMI_RENDER_FORMAT) ||
	    !smiTypePtr || !smiTypePtr->format ||
	    !strlen(smiTypePtr->format) || smiTypePtr->format[0] == 'd') {
	    if (smiTypePtr->format && (strlen(smiTypePtr->format) >= 3) &&
		(smiTypePtr->format[1] == '-')) {
		i = atoi(&smiTypePtr->format[2]);
		if (i < 0) i = 0;
		if (i > 20) i = 20;
		smiAsprintf(&s, "%0*ld.",
			    1 + i + (smiValuePtr->value.integer32 < 0 ? 1 : 0),
			    smiValuePtr->value.integer32);
		if (s) {
		    for (j = strlen(s) - 1; i > 0; i--, j--) {
			s[j] = s[j-1];
		    }
		    s[j] = '.';
		}
	    } else {
		smiAsprintf(&s, "%ld", smiValuePtr->value.integer32);
	    }
	} else if (smiTypePtr->format[0] == 'x') {
	    if (smiValuePtr->value.integer32 >= 0) {
		smiAsprintf(&s, "%lx", smiValuePtr->value.integer32);
	    } else {
		smiAsprintf(&s, "-%lx", - smiValuePtr->value.integer32);
	    }
	} else if (smiTypePtr->format[0] == 'o') {
	    if (smiValuePtr->value.integer32 >= 0) {
		smiAsprintf(&s, "%lo", smiValuePtr->value.integer32);
	    } else {
		smiAsprintf(&s, "-%lo", - smiValuePtr->value.integer32);
	    }
	} else if (smiTypePtr->format[0] == 'b') {
	    if (smiValuePtr->value.integer32 >= 0) {
		v32 = smiValuePtr->value.integer32;
		j = 0;
	    } else {
		v32 = - smiValuePtr->value.integer32;
		j = 1;
	    }
	    for (i = 32 - 1;
		 i > 0 && !(v32 & (1 << i)); i--);
	    s = smiMalloc(i + j + 1 + 1);
	    if (s) {
		s[0] = '-';
		for (; i >= 0; i--, j++) {
		    s[j] = v32 & (1<<i) ? '1' : '0';
		}
		s[j] = 0;
	    }
	}
	break;
    case SMI_BASETYPE_INTEGER64:
	if (!(flags & SMI_RENDER_FORMAT) ||
	    !smiTypePtr || !smiTypePtr->format ||
	    !strlen(smiTypePtr->format) || smiTypePtr->format[0] == 'd') {
	    if (smiTypePtr->format && (strlen(smiTypePtr->format) >= 3) &&
		(smiTypePtr->format[1] == '-')) {
		i = atoi(&smiTypePtr->format[2]);
		if (i < 0) i = 0;
		if (i > 20) i = 20;
		sprintf(f, "%%0%s.", INT64_FORMAT);
		f[2] = '*';
		smiAsprintf(&s, f,
			    1 + i + (smiValuePtr->value.integer64 < 0 ? 1 : 0),
			    smiValuePtr->value.integer64);
		if (s) {
		    for (j = strlen(s) - 1; i > 0; i--, j--) {
			s[j] = s[j-1];
		    }
		    s[j] = '.';
		}
	    } else {
		smiAsprintf(&s, INT64_FORMAT, smiValuePtr->value.integer64);
	    }
	} else if (smiTypePtr->format[0] == 'x') {
	    if (smiValuePtr->value.integer64 >= 0) {
		strcpy(f, UINT64_FORMAT);
		f[strlen(f)-1] = 'x';
		smiAsprintf(&s, f, smiValuePtr->value.integer64);
	    } else {
		sprintf(f, "-%s", UINT64_FORMAT);
		f[strlen(f)-1] = 'x';
		smiAsprintf(&s, f, - smiValuePtr->value.integer64);
	    }
	} else if (smiTypePtr->format[0] == 'o') {
	    if (smiValuePtr->value.integer64 >= 0) {
		strcpy(f, UINT64_FORMAT);
		sprintf(f, "-%s", UINT64_FORMAT);
		f[strlen(f)-1] = 'o';
		smiAsprintf(&s, f, smiValuePtr->value.integer64);
	    } else {
		smiAsprintf(&s, f, - smiValuePtr->value.integer64);
	    }
	} else if (smiTypePtr->format[0] == 'b') {
	    if (smiValuePtr->value.integer64 >= 0) {
		v64 = smiValuePtr->value.integer64;
		j = 0;
	    } else {
		v64 = - smiValuePtr->value.integer64;
		j = 1;
	    }
	    for (i = 64 - 1;
		 i > 0 && !(v64 & (1 << i)); i--);
	    s = smiMalloc(i + j + 1 + 1);
	    if (s) {
		s[0] = '-';
		for (; i >= 0; i--, j++) {
		    s[j] = v64 & (1<<i) ? '1' : '0';
		}
		s[j] = 0;
	    }
	}
	break;
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	s = smiRenderOID(smiValuePtr->len, smiValuePtr->value.oid, flags);
	break;
    case SMI_BASETYPE_OCTETSTRING:
	if (!(flags & SMI_RENDER_FORMAT) ||
	    (!smiTypePtr->format && !smiTypePtr->name) ||
	    (!smiTypePtr->format &&
	     (smiTypePtr->name && strcmp( smiTypePtr->name, "IpAddress")) ) ) {
	    for (i = 0; i < smiValuePtr->len; i++) {
		if (!isprint((int)smiValuePtr->value.ptr[i])) break;
	    }
	    if ((i < smiValuePtr->len) ||
		!(flags & SMI_RENDER_PRINTABLE)) {
		smiAsprintf(&s, "");
		for (i=0; i < smiValuePtr->len; i++) {
		    ss = s;
		    smiAsprintf(&s, "%s%02x", ss, smiValuePtr->value.ptr[i]);
		    smiFree(ss);
		}
	    } else {
		smiAsprintf(&s, "%s", smiValuePtr->value.ptr);
	    }
	} else {
	    i = 0;
	    smiAsprintf(&s, "");
	    /* SNMPv2-SMI:IpAddress does not have a display hint.
	       ==> let's use this one: "1d." if we have an IpAddress here */
	    fmt = (smiTypePtr->name &&
		   strcmp( smiTypePtr->name, "IpAddress" ) ) ?
		smiTypePtr->format : "1d.";
	    while (*fmt && i < smiValuePtr->len) {
		last_fmt = fmt;
		have_pfx = pfx = 0; /* scan prefix: */
		while (*fmt && isdigit((int)*fmt)) {
		    pfx = pfx * 10 + *fmt - '0', have_pfx = 1, fmt++;
		}
		if (! have_pfx) {
		    pfx = 1;
		}
		switch (*fmt) {
		case 't':
		    /* XXX UTF-8 not implemented, fall through to ASCII (a) */
		case 'a':
		    n = (pfx < (smiValuePtr->len - i)) ?
			pfx : smiValuePtr->len - i;
		    for (k = 0; k < n; k++) {
			if (! isascii((int) smiValuePtr->value.ptr[i+k])) {
			    smiFree(s);
			    if (flags & SMI_RENDER_UNKNOWN) {
				smiAsprintf(&s, SMI_UNKNOWN_LABEL);
			    } else {
				s = NULL;
			    }
			    return s;
			}
			ss = s;
			smiAsprintf(&s, "%s%c", ss, smiValuePtr->value.ptr[i+k]);
			smiFree(ss);
		    }
		    i += n;
		    break;
		case 'b':
		case 'd':
		case 'o':
		case 'x':
		    /* XXX: limited to no more than
		       sizeof(SmiUnsigned64) octets */
		    vv = 0;
		    xlen = pfx * 2;
		    while (pfx > 0 && i < smiValuePtr->len) {
			vv = vv * 256 +
			     ((unsigned char)smiValuePtr->value.ptr[i]);
			i++;
			pfx--;
		    }
		    switch (*fmt) {
		    case 'd':
			ss = s;
			sprintf(f, "%%s%s", UINT64_FORMAT);
			smiAsprintf(&s, f, ss, vv);
			smiFree(ss);
			break;
		    case 'o':
			ss = s;
			sprintf(f, "%%s%s", UINT64_FORMAT);
			f[strlen(f)-1] = 'o';
			smiAsprintf(&s, f, ss, vv);
			smiFree(ss);
			break;
		    case 'x':
			ss = s;
			sprintf(f, "%%s%%0%s", UINT64_FORMAT);
			f[4] = '*';
			f[strlen(f)-1] = 'x';
			smiAsprintf(&s, f, ss, xlen, vv);
			smiFree(ss);
			break;
		    case 'b':
			k = pfx * 8 - 1;
			if (k > sizeof(SmiUnsigned64) * 8 - 1)
			    k = sizeof(SmiUnsigned64) * 8 - 1;
			for (j = 0; k >= 0; k--, j++) {
			    ss = s;
			    smiAsprintf(&s, "%s%c",
					ss, vv & (1 << k) ? '1' : '0');
			    smiFree(ss);
			}
			break;
		    }
		    break;
		default:
		    smiFree(s);
		    if (flags & SMI_RENDER_UNKNOWN) {
			smiAsprintf(&s, SMI_UNKNOWN_LABEL);
		    } else {
			s = NULL;
		    }
		    return s;
		}
		fmt++;

		/*
		 * Check for a separator and repeat with last format if
		 * data is still available.
		 */
		if (*fmt && ! isdigit((int) *fmt) && *fmt != '*') {
		    if (i < smiValuePtr->len) {
			ss = s;
			smiAsprintf(&s, "%s%c", ss, fmt[0]);
			smiFree(ss);
		    }
		    fmt++;
		}

		if (! *fmt && (i < smiValuePtr->len)) {
		    fmt = last_fmt;
		}
	    }
	}
	break;
    case SMI_BASETYPE_ENUM:
	if ((flags & SMI_RENDER_NAME) && (smiTypePtr)) {
	    for (nn = smiGetFirstNamedNumber(smiTypePtr); nn;
		 nn = smiGetNextNamedNumber(nn)) {
		if (nn->value.value.integer32 == smiValuePtr->value.integer32)
		    break;
	    }
	    if (nn) {
		if (flags & SMI_RENDER_NUMERIC) {
		    smiAsprintf(&s, "%s(%ld)",
				nn->name, nn->value.value.integer32);
		} else {
		    smiAsprintf(&s, "%s", nn->name);
		}
	    } else {
		smiAsprintf(&s, "%ld", smiValuePtr->value.integer32);
	    }
	} else {
	    smiAsprintf(&s, "%ld", smiValuePtr->value.integer32);
	}
	break;
    case SMI_BASETYPE_BITS:
	smiAsprintf(&s, "");
	for (i = 0, nn = NULL; i < smiValuePtr->len * 8; i++) {
	    if (smiValuePtr->value.ptr[i/8] & (1 << (7-(i%8)))) {
		if ((flags & SMI_RENDER_NAME) && (smiTypePtr)) {
		    for (nn = smiGetFirstNamedNumber(smiTypePtr); nn;
			 nn = smiGetNextNamedNumber(nn)) {
			if (nn->value.value.unsigned32 == i)
			    break;
		    }
		}
		ss = s;
		if ((flags & SMI_RENDER_NAME) &&
		    (flags & SMI_RENDER_NUMERIC) && nn) {
		    smiAsprintf(&s, "%s%s%s(%d)",
				ss, strlen(ss) ? " " : "", nn->name, i);
		} else if (nn) {
		    smiAsprintf(&s, "%s%s%s",
				ss, strlen(ss) ? " " : "", nn->name);
		} else {
		    smiAsprintf(&s, "%s%s%d",
				ss, strlen(ss) ? " " : "", i);
		}
		smiFree(ss);
	    }
	}
	break;
    case SMI_BASETYPE_FLOAT32:
    case SMI_BASETYPE_FLOAT64:
    case SMI_BASETYPE_FLOAT128:
    case SMI_BASETYPE_UNKNOWN:
    default:
	if (flags & SMI_RENDER_UNKNOWN) {
	    smiAsprintf(&s, SMI_UNKNOWN_LABEL);
	} else {
	    s = NULL;
	}
	break;
    }

    return s;
}

char *smiRenderNode(SmiNode *smiNodePtr, int flags)
{
    char *s;
    SmiModule *modulePtr;
    
    if ((!smiNodePtr) || (smiNodePtr->name == NULL)) {
	if (flags & SMI_RENDER_UNKNOWN) {
	    smiAsprintf(&s, SMI_UNKNOWN_LABEL);
	} else {
	    s = NULL;
	}
    } else {
	modulePtr = smiGetNodeModule(smiNodePtr);
	if ((!(flags & SMI_RENDER_QUALIFIED)) ||
	    (!modulePtr) ||
	    (!strlen(modulePtr->name))) {
	    smiAsprintf(&s, "%s", smiNodePtr->name);
	} else {
	    smiAsprintf(&s, "%s::%s", modulePtr->name, smiNodePtr->name);
	}
    }
    return s;
}



char *smiRenderType(SmiType *smiTypePtr, int flags)
{
    char *s;
    SmiModule *modulePtr;
    
    if ((!smiTypePtr) || (smiTypePtr->name == NULL)) {
        if (smiTypePtr && (flags & SMI_RENDER_TYPE_RECURSIVE)) {
	    return smiRenderType(smiGetParentType(smiTypePtr), flags);
	}
	if (flags & SMI_RENDER_UNKNOWN) {
	    smiAsprintf(&s, SMI_UNKNOWN_LABEL);
	} else {
	    s = NULL;
	}
    } else {
	modulePtr = smiGetTypeModule(smiTypePtr);
	if ((!(flags & SMI_RENDER_QUALIFIED)) ||
	    (!modulePtr) ||
	    (!strlen(modulePtr->name))) {
	    smiAsprintf(&s, "%s", smiTypePtr->name);
	} else {
	    smiAsprintf(&s, "%s::%s", modulePtr->name, smiTypePtr->name);
	}
    }
    return s;
}



unsigned int smiGetMinSize(SmiType *smiType)
{
    SmiRange *smiRange;
    SmiType  *parentType;
    unsigned int min = 65535, size;
    
    switch (smiType->basetype) {
    case SMI_BASETYPE_BITS:
	return 0;
    case SMI_BASETYPE_OCTETSTRING:
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	size = 0;
	break;
    default:
	return 0;
    }

    for (smiRange = smiGetFirstRange(smiType);
	 smiRange ; smiRange = smiGetNextRange(smiRange)) {
	if (smiRange->minValue.value.unsigned32 < min) {
	    min = smiRange->minValue.value.unsigned32;
	}
    }
    if (min < 65535 && min > size) {
	size = min;
    }

    parentType = smiGetParentType(smiType);
    if (parentType) {
	unsigned int psize = smiGetMinSize(parentType);
	if (psize > size) {
	    size = psize;
	}
    }

    return size;
}



unsigned int smiGetMaxSize(SmiType *smiType)
{
    SmiRange *smiRange;
    SmiType  *parentType;
    SmiNamedNumber *nn;
    unsigned int max = 0, size;
    
    switch (smiType->basetype) {
    case SMI_BASETYPE_BITS:
    case SMI_BASETYPE_OCTETSTRING:
	size = 65535;
	break;
    case SMI_BASETYPE_OBJECTIDENTIFIER:
	size = 128;
	break;
    default:
	return 0xffffffff;
    }

    if (smiType->basetype == SMI_BASETYPE_BITS) {
	for (nn = smiGetFirstNamedNumber(smiType);
	     nn;
	     nn = smiGetNextNamedNumber(nn)) {
	    if (nn->value.value.unsigned32 > max) {
		max = nn->value.value.unsigned32;
	    }
	}
	size = (max / 8) + 1;
	return size;
    }

    for (smiRange = smiGetFirstRange(smiType);
	 smiRange ; smiRange = smiGetNextRange(smiRange)) {
	if (smiRange->maxValue.value.unsigned32 > max) {
	    max = smiRange->maxValue.value.unsigned32;
	}
    }
    if (max > 0 && max < size) {
	size = max;
    }

    parentType = smiGetParentType(smiType);
    if (parentType) {
	unsigned int psize = smiGetMaxSize(parentType);
	if (psize < size) {
	    size = psize;
	}
    }

    return size;
}



int smiUnpack(SmiNode *row, SmiSubid *oid, unsigned int oidlen,
	      SmiValue **vals, int *valslen)
{
    SmiNode *indexNode = NULL;
    SmiElement *smiElement;
    SmiNode *iNode;
    SmiType *iType; 
    int i, j, last = 0;
   
    if (!vals || !valslen || !row || !oid) {
	return 0;
    }

    switch (row->indexkind) {
    case SMI_INDEX_INDEX:
    case SMI_INDEX_REORDER:
	indexNode = row;
	break;
    case SMI_INDEX_EXPAND:	/* TODO: we have to do more work here! */
	indexNode = NULL;
	break;
    case SMI_INDEX_AUGMENT:
    case SMI_INDEX_SPARSE:
	indexNode = smiGetRelatedNode(row);
	break;
    case SMI_INDEX_UNKNOWN:
	indexNode = NULL;
	break;
    }

    *valslen = 0;
    for (smiElement = smiGetFirstElement(indexNode);
	 smiElement; smiElement = smiGetNextElement(smiElement)) {
	iNode = smiGetElementNode(smiElement);
	if (iNode) {
	    iType = smiGetNodeType(iNode);
	    if (! iType) break;
	    (*valslen)++;
	}
    }
    if (smiElement) {
	return 0;
    }

    *vals = smiMalloc(*valslen * sizeof(SmiValue));

    for (smiElement = smiGetFirstElement(indexNode), i = 0, j = 0;
	 smiElement; smiElement = smiGetNextElement(smiElement), i++) {
	iNode = smiGetElementNode(smiElement);
	last = (smiGetNextElement(smiElement) == NULL);
	iType = smiGetNodeType(iNode);
	fprintf(stderr, "** %s (%s)\n", iNode->name, iType->name);
	(*vals)[i].basetype = iType->basetype;
	switch (iType->basetype) {
	case SMI_BASETYPE_ENUM:
	case SMI_BASETYPE_INTEGER32:
	    (*vals)[i].value.integer32 = oid[j]; j++;
	    break;
	case SMI_BASETYPE_UNSIGNED32:
	    (*vals)[i].value.unsigned32 = oid[j]; j++;
	    break;
	case SMI_BASETYPE_OCTETSTRING:
	    /* need to know whether implied/fixed length or not */
	    break;
	case SMI_BASETYPE_OBJECTIDENTIFIER:
	    /* need to know whether implied/fixed length or not */
	    break;
	default:
	    return 0;
	}
    }

    return *valslen;
}



int smiAsprintf(char **strp, const char *format, ...)
{
    int rc;
    va_list ap;

    va_start(ap, format);
    rc = vasprintf(strp, format, ap);
    va_end(ap);
    if (! strp) {
	smiPrintError(NULL, ERR_OUT_OF_MEMORY);
    }
    return rc;
}



int smiVasprintf(char **strp, const char *format, va_list ap)
{
    int rc;

    rc = vasprintf(strp, format, ap);
    if (! strp) {
	smiPrintError(NULL, ERR_OUT_OF_MEMORY);
    }
    return rc;
}


int smiGetMinMaxRange(SmiType *smiType, SmiValue *min, SmiValue *max)
{
    SmiBasetype    basetype = SMI_BASETYPE_UNKNOWN;
    SmiRange       *range;

    min->basetype = max->basetype = SMI_BASETYPE_UNKNOWN;
    min->len = max->len = 0;

    range = smiGetFirstRange(smiType);
    if (!range) {
	return 0;
    }

    basetype = range->minValue.basetype;
    min->basetype = max->basetype = basetype;

    switch (basetype) {
    case SMI_BASETYPE_INTEGER32:
	min->value.integer32 = SMI_BASETYPE_INTEGER32_MAX;
	max->value.integer32 = SMI_BASETYPE_INTEGER32_MIN;
	break;
    case SMI_BASETYPE_INTEGER64:
	min->value.integer64 = SMI_BASETYPE_INTEGER64_MAX;
	max->value.integer64 = SMI_BASETYPE_INTEGER64_MIN;
	break;
    case SMI_BASETYPE_UNSIGNED32:
	min->value.unsigned32 = SMI_BASETYPE_UNSIGNED32_MAX;
	max->value.unsigned32 = SMI_BASETYPE_UNSIGNED32_MIN;
	break;
    case SMI_BASETYPE_UNSIGNED64:
	min->value.unsigned64 = SMI_BASETYPE_UNSIGNED64_MAX;
	max->value.unsigned64 = SMI_BASETYPE_UNSIGNED32_MIN;
	break;
    default:
	fprintf(stderr, "smidump: unexpected basetype %d\n", basetype);
	return -1;
    }

    for (range = smiGetFirstRange(smiType);
	 range;
	 range = smiGetNextRange(range)) {
	switch (basetype) {
	case SMI_BASETYPE_INTEGER32:
	    if (range->minValue.value.integer32 < min->value.integer32) {
		min->value.integer32 = range->minValue.value.integer32;
	    }
	    if (range->maxValue.value.integer32 > max->value.integer32) {
		max->value.integer32 = range->maxValue.value.integer32;
	    }
	    break;
	case SMI_BASETYPE_INTEGER64:
	    if (range->minValue.value.integer64 < min->value.integer64) {
		min->value.integer64 = range->minValue.value.integer64;
	    }
	    if (range->maxValue.value.integer64 > max->value.integer64) {
		max->value.integer64 = range->maxValue.value.integer64;
	    }
	    break;
	case SMI_BASETYPE_UNSIGNED32:
	    if (range->minValue.value.unsigned32 < min->value.unsigned32) {
		min->value.unsigned32 = range->minValue.value.unsigned32;
	    }
	    if (range->maxValue.value.unsigned32 > max->value.unsigned32) {
		max->value.unsigned32 = range->maxValue.value.unsigned32;
	    }
	    break;
	case SMI_BASETYPE_UNSIGNED64:
	    if (range->minValue.value.unsigned64 < min->value.unsigned64) {
		min->value.unsigned64 = range->minValue.value.unsigned64;
	    }
	    if (range->maxValue.value.unsigned64 > max->value.unsigned64) {
		max->value.unsigned64 = range->maxValue.value.unsigned64;
	    }
	    break;
	default:
	    fprintf(stderr, "smidump: unexpected basetype %d\n", basetype);
	    return -1;
	}
    }

    return 0;
}


SmiNode *smiGetFirstAlias(SmiNode *smiNodePtr)
{
    Object *objectPtr;
    Node *nodePtr;

    if (! smiNodePtr) {
	return NULL;
    }

    objectPtr = (Object *) smiNodePtr;
    nodePtr = objectPtr->nodePtr;

    if (!nodePtr || !nodePtr->firstChildPtr) {
	return NULL;
    }

    return &(nodePtr->firstObjectPtr->export);
}

SmiNode *smiGetNextAlias(SmiNode *smiNodePtr)
{
    Object *objectPtr;
    Node *nodePtr;

    if (!smiNodePtr) {
	return NULL;
    }

    objectPtr = (Object *) smiNodePtr;
    nodePtr = objectPtr->nodePtr;

    if (!nodePtr) {
	return NULL;
    }

    for (objectPtr = nodePtr->firstObjectPtr;
	 objectPtr && &objectPtr->export != smiNodePtr;
	 objectPtr = objectPtr->nextSameNodePtr) {
    }

    if (!objectPtr || !objectPtr->nextSameNodePtr) {
	return NULL;
    }

    return &objectPtr->nextSameNodePtr->export;
}
