/*
 * check.c --
 *
 *      This module contains semantics checks that are shared between
 *	the SMI parser backends.
 *
 * Copyright (c) 2000 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-check.c 1783 2013-01-08 18:17:14Z schoenw $
 */

#include <config.h>

#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_WIN_H
#include "win.h"
#endif

#include "error.h"
#include "util.h"
#include "smi-data.h"
#include "smi-check.h"
#include "smi.h"

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



/*
 *----------------------------------------------------------------------
 *
 * compareValues --
 *
 *	Compare two SmiValues a and b.
 *
 * Results:
 *      <= -2   if a is less than b-1
 *      -1      if a is b-1
 *      0       if equal
 *      1       if a is b+1
 *      >= 2    if a is greater than b+1
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
compareValues(SmiValue *a, SmiValue *b) {
    if ((a->basetype == SMI_BASETYPE_UNSIGNED32) &&
	(b->basetype == SMI_BASETYPE_UNSIGNED32)) {
	if (a->value.unsigned32 == b->value.unsigned32) {
	    return 0;
	} else {
	    if (a->value.unsigned32 > b->value.unsigned32) {
		if (a->value.unsigned32 == b->value.unsigned32 + 1) {
		    return 1;
		} else {
		    return 2;
		}
	    } else if (a->value.unsigned32 < b->value.unsigned32) {
		if (a->value.unsigned32 + 1 == b->value.unsigned32) {
		    return -1;
		} else {
		    return -2;
		}
	    }
	}
    }
    if ((a->basetype == SMI_BASETYPE_INTEGER32) &&
	(b->basetype == SMI_BASETYPE_INTEGER32)) {
	if (a->value.integer32 == b->value.integer32) {
	    return 0;
	} else {
	    if (a->value.integer32 > b->value.integer32) {
		if (a->value.integer32 == b->value.integer32 + 1) {
		    return 1;
		} else {
		    return 2;
		}
	    } else if (a->value.integer32 < b->value.integer32) {
		if (a->value.integer32 + 1 == b->value.integer32) {
		    return -1;
		} else {
		    return -2;
		}
	    }
	}
    }
    if ((a->basetype == SMI_BASETYPE_UNSIGNED32) &&
	(b->basetype == SMI_BASETYPE_INTEGER32)) {
	if ((b->value.integer32 < -1) ||
	    ((a->value.unsigned32 > 1) &&
	     (a->value.unsigned32-1 > 2147483647))) {
	    return 2;
	}
	return a->value.unsigned32 - b->value.integer32;
    }
    if ((a->basetype == SMI_BASETYPE_INTEGER32) &&
	(b->basetype == SMI_BASETYPE_UNSIGNED32)) {
	if ((a->value.integer32 < -1) ||
	    ((b->value.unsigned32 > 1) &&
	     (b->value.unsigned32-1 > 2147483647))) {
	    return -2;
	}
	return b->value.unsigned32 - a->value.integer32;
    }
    return 0;
}



/*
 *----------------------------------------------------------------------
 *
 * redefinition --
 *
 *	Print out error messages about a (case) redefinition.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
redefinition(Parser *parser, int line1, char *name1, Module *module,
	     int line2, char *name2)
{
    char *tmp = parser->path;
    int equal = (strcmp(name1, name2) == 0);

    if (!strcmp(name1, "IpAddress") ||
	!strcmp(name1, "TimeTicks") ||
	!strcmp(name1, "Opaque") ||
	!strcmp(name1, "Integer32") ||
	!strcmp(name1, "Unsigned32") ||
	!strcmp(name1, "Counter32") ||
	!strcmp(name1, "Gauge32") ||
	!strcmp(name1, "Counter64") ||
	!strcmp(name1, "Integer64") ||
	!strcmp(name1, "Unsigned64")) {
	if (line1) {
	    smiPrintErrorAtLine(parser, ERR_BASETYPE_REDEFINITION, line1, name1);
	} else {
	    smiPrintError(parser, ERR_BASETYPE_REDEFINITION, name1);
	}
    } else {
	if (! module) {
	    if (equal) {
		if (line1) {
		    smiPrintErrorAtLine(parser, ERR_REDEFINITION, line1, name1);
		} else {
		    smiPrintError(parser, ERR_REDEFINITION, name1);
		}
	    } else {
		if (line1) {
		    smiPrintErrorAtLine(parser, ERR_CASE_REDEFINITION,
					line1, name1, name2);
		} else {
		    smiPrintError(parser, ERR_CASE_REDEFINITION, name1, name2);
		}
	    }
	} else {
	    if (equal) {
		if (line1) {
		    smiPrintErrorAtLine(parser, ERR_EXT_REDEFINITION, line1,
					module->export.name, name1);
		} else {
		    smiPrintError(parser, ERR_EXT_REDEFINITION,
				  module->export.name, name1);
		}
	    } else {
		if (line1) {
		    smiPrintErrorAtLine(parser, ERR_EXT_CASE_REDEFINITION, line1,
					name1, module->export.name, name2);
		} else {
		    smiPrintError(parser, ERR_EXT_CASE_REDEFINITION,
				  name1, module->export.name, name2);
		}
	    }
	    parser->path = module->export.path;
	}
	smiPrintErrorAtLine(parser, ERR_PREVIOUS_DEFINITION, line2, name2);
	if (module) {
	    parser->path = tmp;
	}
    }
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckObjectName --
 *
 *      Check whether a given object name already exists
 *	in a given module.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckObjectName(Parser *parser, Module *module, char *name)
{
    Object	*objectPtr;
    Type        *typePtr;
    Module	*modPtr;

    int errRedef = smiGetErrorSeverity(ERR_REDEFINITION);
    int errExtRedef = smiGetErrorSeverity(ERR_EXT_REDEFINITION);
    int errCaseRedef = smiGetErrorSeverity(ERR_CASE_REDEFINITION);
    int errExtCaseRedef = smiGetErrorSeverity(ERR_EXT_CASE_REDEFINITION);

    if (! (parser->flags & SMI_FLAG_ERRORS)
	|| (errRedef > smiHandle->errorLevel
	    && errExtRedef > smiHandle->errorLevel
	    && errCaseRedef > smiHandle->errorLevel
	    && errExtCaseRedef > smiHandle->errorLevel)) {
	return;
    }

    /*
     * This would really benefit from having a hash table...
     */

    for (modPtr = smiHandle->firstModulePtr;
	 modPtr; modPtr = modPtr->nextPtr) {

	/*
	 * Skip all external modules if we are not interested in
	 * generating warning on extern redefinitions.
	 */

	if (errExtRedef > smiHandle->errorLevel
	    && errExtCaseRedef > smiHandle->errorLevel
	    && modPtr != module) {
	    continue;
	}

        for (objectPtr = modPtr->firstObjectPtr;
	     objectPtr; objectPtr = objectPtr->nextPtr) {
	    if (! (objectPtr->flags & FLAG_INCOMPLETE)
		&& ! strcasecmp(name, objectPtr->export.name)) {
		redefinition(parser, 0, name,
			     modPtr == module ? NULL : objectPtr->modulePtr,
			     objectPtr->line, objectPtr->export.name);
	    }
	}
	for (typePtr = modPtr->firstTypePtr;
	     typePtr; typePtr = typePtr->nextPtr) {
	    /* TODO: must ignore SEQUENCE types here ... */
	    if (! (typePtr->flags & FLAG_INCOMPLETE)
		&& typePtr->export.name
		&& !strcasecmp(name, typePtr->export.name)) {
		redefinition(parser, 0, name,
			     modPtr == module ? NULL : typePtr->modulePtr,
			     typePtr->line, typePtr->export.name);
	    }
	}
    }
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckTypeName --
 *
 *      Check whether a given type name already exists
 *	in a given module.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckTypeName(Parser *parser, Module *module, char *name, int line)
{
    Object	*objectPtr;
    Type        *typePtr;
    Module	*modPtr;

    int errRedef = smiGetErrorSeverity(ERR_REDEFINITION);
    int errExtRedef = smiGetErrorSeverity(ERR_EXT_REDEFINITION);
    int errCaseRedef = smiGetErrorSeverity(ERR_CASE_REDEFINITION);
    int errExtCaseRedef = smiGetErrorSeverity(ERR_EXT_CASE_REDEFINITION);

    if (! (parser->flags & SMI_FLAG_ERRORS)
	|| (errRedef > smiHandle->errorLevel
	    && errExtRedef > smiHandle->errorLevel
	    && errCaseRedef > smiHandle->errorLevel
	    && errExtCaseRedef > smiHandle->errorLevel)) {
	return;
    }

    /*
     * This would really benefit from having a hash table...
     */

    for (modPtr = smiHandle->firstModulePtr;
	 modPtr; modPtr = modPtr->nextPtr) {

	/*
	 * Skip all external modules if we are not interested in
	 * generating warning on extern redefinitions.
	 */

	if (errExtRedef > smiHandle->errorLevel
	    && errExtCaseRedef > smiHandle->errorLevel
	    && modPtr != module) {
	    continue;
	}

	for (typePtr = modPtr->firstTypePtr;
	     typePtr; typePtr = typePtr->nextPtr) {
	    /* TODO: must ignore SEQUENCE types here ... */
	    if (! (typePtr->flags & FLAG_INCOMPLETE)
		&& typePtr->export.name
		&& !strcasecmp(name, typePtr->export.name)) {
		redefinition(parser, line, name,
			     modPtr == module ? NULL : typePtr->modulePtr,
			     typePtr->line, typePtr->export.name);
	    }
	}

        for (objectPtr = modPtr->firstObjectPtr;
	     objectPtr; objectPtr = objectPtr->nextPtr) {
	    if (! (objectPtr->flags & FLAG_INCOMPLETE)
		&& ! strcasecmp(name, objectPtr->export.name)) {
		redefinition(parser, line, name,
			     modPtr == module ? NULL : objectPtr->modulePtr,
			     objectPtr->line, objectPtr->export.name);
	    }
	}
    }
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckFormat --
 *
 *      Check whether a format specification is valid.
 *
 * Results:
 *      Returns 1 of the format is acceptable and 0 otherwise.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

int
smiCheckFormat(Parser *parser, SmiBasetype basetype, char *format, int line)
{
    int n, repeat, error = 1;
    char *p = format;

    switch (basetype) {
    case SMI_BASETYPE_INTEGER32:
    case SMI_BASETYPE_INTEGER64:
    case SMI_BASETYPE_UNSIGNED32:
    case SMI_BASETYPE_UNSIGNED64:
	if (*p == 'x' || *p == 'o' || *p == 'b') {
	    p++;
	    error = (*p != 0);
	} else if (*p == 'd') {
	    p++;
	    if (! *p) {
		error = 0;
		break;
	    }
	    if (*p != '-') {
		error = 0;
		break;
	    }
	    for (n = 0, p++; *p && isdigit((int) *p); p++, n++) ;
	    error = (*p != 0 || n <= 0);
	}
	break;
    case SMI_BASETYPE_OCTETSTRING:
	while (*p) {
	    if ((repeat = (*p == '*'))) p++;                /* part 1 */
	    
	    for (n = 0; *p && isdigit((int) *p); p++, n++) ;/* part 2 */
	    if (! *p || n == 0) {
		break;
	    }
	    
	    if (*p != 'x' && *p != 'd' && *p != 'o'         /* part 3 */
		&& *p != 'a' && *p != 't') {
		break;
	    }
	    p++;
	    
	    if (*p                                          /* part 4 */
		&& ! isdigit((int) *p) && *p != '*') p++;
	    
	    if (repeat && *p                                /* part 5 */
		&& ! isdigit((int) *p) && *p != '*') p++;
	}
	error = *p;
	break;
    default:
	break;
    }

    if (error) {
	if (line) {
	    smiPrintErrorAtLine(parser, ERR_INVALID_FORMAT, line, format);
	} else {
	    smiPrintError(parser, ERR_INVALID_FORMAT, format);
	}
    }

    return ! error;
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckNamedNumberRedefinition --
 *
 *      Check whether named numbers redefine names or numbers.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckNamedNumberRedefinition(Parser *parser, Type *type)
{
    List *list1Ptr, *list2Ptr;
    NamedNumber *nn1Ptr, *nn2Ptr;

    if (! type || (type->export.basetype != SMI_BASETYPE_ENUM
		      && type->export.basetype != SMI_BASETYPE_BITS)) {
	return;
    }
	    
    for (list1Ptr = type->listPtr;
	 list1Ptr; list1Ptr = list1Ptr->nextPtr) {
	
	nn1Ptr = (NamedNumber *)(list1Ptr->ptr);

	for (list2Ptr = list1Ptr->nextPtr;
	     list2Ptr; list2Ptr = list2Ptr->nextPtr) {
	    
	    nn2Ptr = (NamedNumber *)(list2Ptr->ptr);

	    if (type->export.basetype == SMI_BASETYPE_ENUM) {
		if (!strcmp(nn1Ptr->export.name, nn2Ptr->export.name)) {
		    smiPrintErrorAtLine(parser, ERR_ENUM_NAME_REDEFINITION,
					type->line,
					nn1Ptr->export.name);
		}
		if (nn1Ptr->export.value.value.integer32
		    == nn2Ptr->export.value.value.integer32) {
		    smiPrintErrorAtLine(parser, ERR_ENUM_NUMBER_REDEFINITION,
					type->line,
					nn1Ptr->export.value.value.integer32);
		}
	    }
	    if (type->export.basetype == SMI_BASETYPE_BITS) {
		if (!strcmp(nn1Ptr->export.name, nn2Ptr->export.name)) {
		    smiPrintErrorAtLine(parser, ERR_BITS_NAME_REDEFINITION,
					type->line,
					nn1Ptr->export.name);
		}
		if (nn1Ptr->export.value.value.unsigned32
		    == nn2Ptr->export.value.value.unsigned32) {
		    smiPrintErrorAtLine(parser, ERR_BITS_NUMBER_REDEFINITION,
					type->line,
					nn1Ptr->export.value.value.unsigned32);
		}
	    }
	}
    }   
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckNamedNumberSubtyping --
 *
 *      Check whether named numbers in a derived type are compatible
 *	with the named numbers in the parent type.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckNamedNumberSubtyping(Parser *parser, Type *type)
{
    List *list1Ptr, *list2Ptr;
    NamedNumber *nn1Ptr, *nn2Ptr;
	
    if (! type || ! type->parentPtr || ! type->parentPtr->parentPtr
	|| (type->export.basetype != SMI_BASETYPE_ENUM
	    && type->export.basetype != SMI_BASETYPE_BITS)) {
	return;
    }

    for (list1Ptr = type->listPtr;
	 list1Ptr; list1Ptr = list1Ptr->nextPtr) {
	
	nn1Ptr = (NamedNumber *)(list1Ptr->ptr);
	
	for (list2Ptr = type->parentPtr->listPtr;
	     list2Ptr; list2Ptr = list2Ptr->nextPtr) {
	    
	    nn2Ptr = (NamedNumber *)(list2Ptr->ptr);
	    
	    if (type->export.basetype == SMI_BASETYPE_ENUM) {
		if (! strcmp(nn1Ptr->export.name, nn2Ptr->export.name)
		    && nn1Ptr->export.value.value.integer32
		    == nn2Ptr->export.value.value.integer32) {
		    break;
		}
	    }
	    
	    if (type->export.basetype == SMI_BASETYPE_BITS) {
		if (! strcmp(nn1Ptr->export.name, nn2Ptr->export.name)
		    && nn1Ptr->export.value.value.unsigned32
		    == nn2Ptr->export.value.value.unsigned32) {
		    break;
		}
	    }
	}
	    
	if (! list2Ptr) {
	    if (type->export.basetype == SMI_BASETYPE_ENUM) {
		if (type->parentPtr->export.name) {
		    smiPrintErrorAtLine(parser, ERR_ENUM_SUBTYPE_OF,
					type->line,
					nn1Ptr->export.name,
					nn1Ptr->export.value.value.integer32,
					type->parentPtr->export.name);
		} else {
		    smiPrintErrorAtLine(parser, ERR_ENUM_SUBTYPE,
					type->line,
					nn1Ptr->export.name,
					nn1Ptr->export.value.value.integer32);
		}
	    }
	    if (type->export.basetype == SMI_BASETYPE_BITS) {
		if (type->parentPtr->export.name) {
		    smiPrintErrorAtLine(parser, ERR_BITS_SUBTYPE_OF,
					type->line,
					nn1Ptr->export.name,
					type->parentPtr->export.name);
		} else {
		    smiPrintErrorAtLine(parser, ERR_BITS_SUBTYPE,
					type->line,
					nn1Ptr->export.name);
		}
	    }
	}
    }
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckNamedNumbersOrder --
 *
 *      Check and normalize the order of named numbers in a bits
 *	or enumeration type.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckNamedNumbersOrder(Parser *parser, Type *type)
{
    List *listPtr, *lastPtr, *nextPtr, *ptr;
    NamedNumber *nnPtr;
    int shutup = 0;
    
    if (! type || ! type->parentPtr
	|| (type->export.basetype != SMI_BASETYPE_ENUM
	    && type->export.basetype != SMI_BASETYPE_BITS)) {
	return;
    }

    /* Check whether the first bit has been given a name. */

    if (type->export.basetype == SMI_BASETYPE_BITS) {
	for (listPtr = type->listPtr; listPtr; listPtr = listPtr->nextPtr) {
	    nnPtr = (NamedNumber *)(listPtr->ptr);
	    if (nnPtr->export.value.value.unsigned32 == 0) break;
	}
	if (! listPtr) {
	    smiPrintErrorAtLine(parser, ERR_BITS_ZERO_NOT_NAMED, type->line);
	}
    }

    lastPtr = NULL;
    for (listPtr = type->listPtr; listPtr; listPtr = nextPtr) {

	nextPtr = listPtr->nextPtr;

	nnPtr = (NamedNumber *)(listPtr->ptr);

	if (lastPtr) {
	    
	    if ((type->export.basetype == SMI_BASETYPE_ENUM) &&
		(((NamedNumber *)(listPtr->ptr))->export.value.value.integer32 <=
		 ((NamedNumber *)(lastPtr->ptr))->export.value.value.integer32)) {
		if (!shutup) {
		    smiPrintErrorAtLine(parser, ERR_NAMED_NUMBERS_NOT_ASCENDING,
					type->line,
					type->export.name);
		    shutup = 1;
		}
		/* remove listPtr from the list */
		lastPtr->nextPtr = listPtr->nextPtr;
		/* re-insert listPtr at the right position */
		if (((NamedNumber *)(type->listPtr->ptr))->export.value.value.integer32 > ((NamedNumber *)(listPtr->ptr))->export.value.value.integer32) {
		    listPtr->nextPtr = type->listPtr;
		    type->listPtr = listPtr;
		} else {
		    for (ptr = type->listPtr; ptr; ptr = ptr->nextPtr) {
			if ((!ptr->nextPtr) ||
			    (((NamedNumber *)(ptr->nextPtr->ptr))->export.value.value.integer32 >= ((NamedNumber *)(listPtr->ptr))->export.value.value.integer32)) {
			    listPtr->nextPtr = ptr->nextPtr;
			    ptr->nextPtr = listPtr;
			    break;
			}
		    }
		}
		/* set lastPtr to the last processed item */
		for (lastPtr = listPtr; lastPtr->nextPtr != nextPtr;
		     lastPtr = lastPtr->nextPtr);
		continue;
	    }
	    if ((type->export.basetype == SMI_BASETYPE_BITS) &&
		(((NamedNumber *)(listPtr->ptr))->export.value.value.unsigned32 <=
		 ((NamedNumber *)(lastPtr->ptr))->export.value.value.unsigned32)) {
		if (!shutup) {
		    smiPrintErrorAtLine(parser, ERR_NAMED_NUMBERS_NOT_ASCENDING,
					type->line,
					type->export.name);
		    shutup = 1;
		}
		/* remove listPtr from the list */
		lastPtr->nextPtr = listPtr->nextPtr;
		/* re-insert listPtr at the right position */
		if (((NamedNumber *)(type->listPtr->ptr))->export.value.value.unsigned32 > ((NamedNumber *)(listPtr->ptr))->export.value.value.unsigned32) {
		    listPtr->nextPtr = type->listPtr;
		    type->listPtr = listPtr;
		} else {
		    for (ptr = type->listPtr; ptr; ptr = ptr->nextPtr) {
			if ((!ptr->nextPtr) ||
			    (((NamedNumber *)(ptr->nextPtr->ptr))->export.value.value.unsigned32 > ((NamedNumber *)(listPtr->ptr))->export.value.value.unsigned32)) {
			    listPtr->nextPtr = ptr->nextPtr;
			    ptr->nextPtr = listPtr;
			    break;
			}
		    }
		}
		/* set lastPtr to the last processed item */
		for (lastPtr = listPtr; lastPtr->nextPtr != nextPtr;
		     lastPtr = lastPtr->nextPtr);
		continue;
	    }
	}
	lastPtr = listPtr;
    }
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckIndex --
 *
 *      Check whether an index conforms to the SMI restrictions.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckIndex(Parser *parser, Object *object)
{
    List *listPtr, *list2Ptr;
    Object *indexPtr;
    Type *typePtr, *rTypePtr;
    Range *rangePtr;
    NamedNumber *nnPtr;
    Node *nodePtr;
    int minSize, maxSize, len = 0;
    int aux = 0, cols = 0, acc = 0;

    for (nodePtr = object->nodePtr->firstChildPtr, cols = 0;
	 nodePtr; nodePtr = nodePtr->nextPtr) {
	cols++;
    }

    for (listPtr = object->listPtr; listPtr; listPtr = listPtr->nextPtr) {
	
	indexPtr = (Object *) listPtr->ptr;
	typePtr = indexPtr->typePtr;

	/* checkObjects() already handles unknown objects */
	if (indexPtr->export.nodekind != SMI_NODEKIND_COLUMN &&
	    ((indexPtr->flags & FLAG_INCOMPLETE) == 0)) {
	    smiPrintErrorAtLine(parser, ERR_INDEX_NOT_COLUMN,
				indexPtr->line,
				indexPtr->export.name,
				object->export.name);
	}

	if (!typePtr)
	    continue;
	
	switch (typePtr->export.basetype) {
	case SMI_BASETYPE_INTEGER32:
	    for (rTypePtr = typePtr; rTypePtr && ! rTypePtr->listPtr;
 		 rTypePtr = rTypePtr->parentPtr) {
	    }
	    if (! rTypePtr) {
		if (object->modulePtr != indexPtr->modulePtr) {
		    smiPrintErrorAtLine(parser, ERR_INDEX_NO_RANGE_MOD,
					object->line,
					indexPtr->modulePtr->export.name,
					indexPtr->export.name,
					object->export.name);

		} else {
		    smiPrintErrorAtLine(parser, ERR_INDEX_NO_RANGE,
					indexPtr->line,
					indexPtr->export.name,
					object->export.name);
		}
	    } else {
		for (list2Ptr = rTypePtr->listPtr;
		     list2Ptr; list2Ptr = list2Ptr->nextPtr) {
		    rangePtr = (Range *) list2Ptr->ptr;
		    if (rangePtr->export.maxValue.value.integer32 < 0) {
			smiPrintErrorAtLine(parser, ERR_INDEX_RANGE_NEGATIVE,
					    indexPtr->line,
					    indexPtr->export.name,
					    object->export.name);
			break;
		    }
		}
	    }
	    len++;
	    break;
	case SMI_BASETYPE_OCTETSTRING:
	    /* TODO: We need to check ranges of parent types as well
	       if this type does not have a range restriction. */
	    for (rTypePtr = typePtr; rTypePtr && ! rTypePtr->listPtr;
		 rTypePtr = rTypePtr->parentPtr) {
	    }
	    minSize = 65535;
	    maxSize = -1;
	    if (! rTypePtr) {
		if (object->modulePtr != indexPtr->modulePtr) {
		    smiPrintErrorAtLine(parser, ERR_INDEX_STRING_NO_SIZE_MOD,
					object->line,
					indexPtr->modulePtr->export.name,
					indexPtr->export.name,
					object->export.name);
		} else {
		    smiPrintErrorAtLine(parser, ERR_INDEX_STRING_NO_SIZE,
					indexPtr->line,
					indexPtr->export.name,
					object->export.name);
		}
		minSize = 0;
		maxSize = 65535;
	    } else {
	        for (list2Ptr = rTypePtr->listPtr;
		     list2Ptr; list2Ptr = list2Ptr->nextPtr) {
		    rangePtr = (Range *) list2Ptr->ptr;
		    if (rangePtr->export.minValue.value.integer32 < minSize) {
			minSize = rangePtr->export.minValue.value.integer32;
		    }
		    if (rangePtr->export.maxValue.value.integer32 > maxSize) {
			maxSize = rangePtr->export.maxValue.value.integer32;
		    }
		}
		if (minSize == 65535) {
		    minSize = 0;
		}
		if (maxSize < 0) {
		    maxSize = 65535;
		}
	    }
	    len += maxSize;
	    if (minSize != maxSize) {
		if (! (object->export.implied && (! listPtr->nextPtr))) {
		    len++;
		}
	    }
	    break;
	case SMI_BASETYPE_OBJECTIDENTIFIER:
	    if (object->modulePtr != indexPtr->modulePtr) {
		smiPrintErrorAtLine(parser, ERR_INDEX_OID_NO_SIZE_MOD,
				    object->line,
				    indexPtr->modulePtr->export.name,
				    indexPtr->export.name,
				    object->export.name);
	    } else {
		smiPrintErrorAtLine(parser, ERR_INDEX_OID_NO_SIZE,
				    indexPtr->line,
				    indexPtr->export.name,
				    object->export.name);
	    }
	    len += 128;
	    if (!indexPtr->export.implied) {
		len++;
	    }
	    break;
	case SMI_BASETYPE_UNSIGNED32:
	    len++;
	    break;
	case SMI_BASETYPE_INTEGER64:
	case SMI_BASETYPE_UNSIGNED64:
	case SMI_BASETYPE_FLOAT32:
	case SMI_BASETYPE_FLOAT64:
	case SMI_BASETYPE_FLOAT128:
	case SMI_BASETYPE_UNKNOWN:
	    smiPrintErrorAtLine(parser, ERR_INDEX_BASETYPE, object->line,
				typePtr->export.name ? typePtr->export.name
				                     : "[unknown]",
				indexPtr->export.name, object->export.name);
	    break;
	case SMI_BASETYPE_BITS:
	    /* TODO: BITS are somehow treated as octet strings - but
	       what is the max len? */
	    break;
	case SMI_BASETYPE_ENUM:
	    for (list2Ptr = typePtr->listPtr;
		 list2Ptr; list2Ptr = list2Ptr->nextPtr) {
		
		nnPtr = (NamedNumber *)(list2Ptr->ptr);

		if (nnPtr->export.value.value.integer32 < 0) {
			smiPrintErrorAtLine(parser, ERR_INDEX_ENUM_NEGATIVE,
					    indexPtr->line,
					    indexPtr->export.name,
					    object->export.name);
			break;
		}
	    }
	    len++;
	    break;
	}

	if (indexPtr->export.value.basetype != SMI_BASETYPE_UNKNOWN) {
	    smiPrintErrorAtLine(parser, ERR_INDEX_DEFVAL,
				indexPtr->line,
				indexPtr->export.name,
				object->export.name);
	}

    	for (nodePtr = object->nodePtr->firstChildPtr;
	     nodePtr; nodePtr = nodePtr->nextPtr) {
	    if (indexPtr == nodePtr->lastObjectPtr) {
		aux++;
		break;
	    }
	}
    }

    if (object->export.oidlen + 1 + len > 128) {
	smiPrintErrorAtLine(parser, ERR_INDEX_TOO_LARGE, object->line,
			    object->export.name,
			    (object->export.oidlen + 1 + len) - 128);
    }

    /* RFC 2578 section 7.7: Auxiliary objects must be not-accessible except
       in some interesting corner cases. */

    for (listPtr = object->listPtr; listPtr; listPtr = listPtr->nextPtr) {
	
	indexPtr = (Object *) listPtr->ptr;
	typePtr = indexPtr->typePtr;

	if (aux < cols) {
	    if ((parser->modulePtr->export.language == SMI_LANGUAGE_SMIV2)
		&& (indexPtr->nodePtr->parentPtr == object->nodePtr)) {
		if (indexPtr->export.access != SMI_ACCESS_NOT_ACCESSIBLE) {
		    smiPrintErrorAtLine(parser, ERR_INDEX_ACCESSIBLE,
					object->line,
					indexPtr->export.name, object->export.name);
		}
	    }
	}
	
	for (nodePtr = object->nodePtr->firstChildPtr, acc = 0;
	     nodePtr; nodePtr = nodePtr->nextPtr) {
	    if (indexPtr == nodePtr->lastObjectPtr
		&& indexPtr->export.access != SMI_ACCESS_NOT_ACCESSIBLE) {
		acc++;
	    }
	}
    }

    if ((parser->modulePtr->export.language == SMI_LANGUAGE_SMIV2)
	&& aux == cols && acc != 1) {
	smiPrintErrorAtLine(parser, ERR_INDEX_NON_ACCESSIBLE,
			    object->line, object->export.name);
    }
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckAugment --
 *
 *      Check whether a table augmentation conforms to the SMI
 *	restrictions.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckAugment(Parser *parser, Object *object)
{
    if (! object->relatedPtr) {
	return;
    }

    if (object->relatedPtr->export.nodekind != SMI_NODEKIND_ROW) {
	smiPrintErrorAtLine(parser, ERR_AUGMENT_NO_ROW, object->line,
			    object->export.name,
			    object->relatedPtr->export.name);
	return;
    }
    
    if (object->relatedPtr->export.indexkind == SMI_INDEX_INDEX)
        return;
    
    if (object->export.indexkind == SMI_INDEX_AUGMENT)
    {
	smiPrintErrorAtLine(parser, ERR_AUGMENT_NESTED, object->line,
			    object->export.name,
			    object->relatedPtr->export.name);
	return;
    }
    
    if (object->relatedPtr->export.indexkind != SMI_INDEX_SPARSE) {
	smiPrintErrorAtLine(parser, ERR_EXTENDS_WRONG_ROW_TYPE, object->line,
			    object->export.name,
			    object->relatedPtr->export.name);
	return;
    }
    
    /*
     * TODO: Check the size of the instance identifier and the OID
     * for this entry node.
     */
}


/*
 *----------------------------------------------------------------------
 *
 * smiCheckRowMembers --
 *
 *      Check whether the members of a row are columnar objects
 *	and that their types match the row's type.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckRowMembers(Parser *parser, Object *row)
{
    Object *object;
    Node *nodePtr;
    
    for (nodePtr = row->nodePtr->firstChildPtr;
	 nodePtr; nodePtr = nodePtr->nextPtr) {
	object = nodePtr->firstObjectPtr;
	if (object->export.nodekind != SMI_NODEKIND_COLUMN) {
	    smiPrintErrorAtLine(parser, ERR_ROW_CHILD_TYPE, object->line,
				object->export.name, row->export.name);
	}
    }

    /* TODO - check the row type consistency */
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckTypeRanges --
 *
 *      Check whether all ranges of a given type are valid.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckTypeRanges(Parser *parser, Type *type)
{
    List *p, *nextPtr, *pp, *nextPP;
    
    for (p = type->listPtr; p; p = nextPtr) {

	nextPtr = p->nextPtr;
	
	((Range *)p->ptr)->typePtr = type;

	if (type->export.basetype == SMI_BASETYPE_INTEGER32) {
	    if ((((Range *)p->ptr)->export.minValue.basetype ==
		 SMI_BASETYPE_UNSIGNED32) &&
		(((Range *)p->ptr)->export.minValue.value.unsigned32 >
		    2147483647)) {
		smiPrintError(parser, ERR_RANGE_OUT_OF_BASETYPE);
	    }
	    if ((((Range *)p->ptr)->export.maxValue.basetype ==
		 SMI_BASETYPE_UNSIGNED32) &&
		(((Range *)p->ptr)->export.maxValue.value.unsigned32 >
		    2147483647)) {
		smiPrintError(parser, ERR_RANGE_OUT_OF_BASETYPE);
	    }
	    ((Range *)p->ptr)->export.minValue.basetype =
		SMI_BASETYPE_INTEGER32;
	    ((Range *)p->ptr)->export.maxValue.basetype =
		SMI_BASETYPE_INTEGER32;
	}

	if (type->export.basetype == SMI_BASETYPE_UNSIGNED32) {
	    if ((((Range *)p->ptr)->export.minValue.basetype ==
		 SMI_BASETYPE_INTEGER32) &&
		(((Range *)p->ptr)->export.minValue.value.integer32 < 0)) {
		smiPrintError(parser, ERR_RANGE_OUT_OF_BASETYPE);
	    }
	    if ((((Range *)p->ptr)->export.maxValue.basetype ==
		 SMI_BASETYPE_INTEGER32) &&
		(((Range *)p->ptr)->export.maxValue.value.integer32 < 0)) {
		smiPrintError(parser, ERR_RANGE_OUT_OF_BASETYPE);
	    }
	    ((Range *)p->ptr)->export.minValue.basetype =
		SMI_BASETYPE_UNSIGNED32;
	    ((Range *)p->ptr)->export.maxValue.basetype =
		SMI_BASETYPE_UNSIGNED32;
	}

	if (type->export.basetype == SMI_BASETYPE_OCTETSTRING) {
	    if ((((Range *)p->ptr)->export.minValue.basetype ==
		 SMI_BASETYPE_INTEGER32) &&
		(((Range *)p->ptr)->export.minValue.value.integer32 < 0)) {
		smiPrintError(parser, ERR_RANGE_OUT_OF_BASETYPE);
	    }
	    if ((((Range *)p->ptr)->export.maxValue.basetype ==
		 SMI_BASETYPE_INTEGER32) &&
		(((Range *)p->ptr)->export.maxValue.value.integer32 < 0)) {
		smiPrintError(parser, ERR_RANGE_OUT_OF_BASETYPE);
	    }
	    if ((((Range *)p->ptr)->export.minValue.basetype ==
		 SMI_BASETYPE_UNSIGNED32) &&
		(((Range *)p->ptr)->export.minValue.value.unsigned32 > 65535)) {
		smiPrintError(parser, ERR_RANGE_OUT_OF_BASETYPE);
	    }
	    if ((((Range *)p->ptr)->export.maxValue.basetype ==
		 SMI_BASETYPE_UNSIGNED32) &&
		(((Range *)p->ptr)->export.maxValue.value.unsigned32 > 65535)) {
		smiPrintError(parser, ERR_RANGE_OUT_OF_BASETYPE);
	    }
	    ((Range *)p->ptr)->export.minValue.basetype =
		SMI_BASETYPE_UNSIGNED32;
	    ((Range *)p->ptr)->export.maxValue.basetype =
		SMI_BASETYPE_UNSIGNED32;
	}

	if (compareValues(&((Range *)p->ptr)->export.minValue,
			  &((Range *)p->ptr)->export.maxValue) > 0) {
	    SmiValue v;
	    v = ((Range *)p->ptr)->export.minValue;
	    ((Range *)p->ptr)->export.minValue = ((Range *)p->ptr)->export.maxValue;
	    ((Range *)p->ptr)->export.maxValue = v;
	    smiPrintError(parser, ERR_EXCHANGED_RANGE_LIMITS);
	}

	/* sort */
	p->nextPtr = NULL;
	if (p != type->listPtr) {
	    if (compareValues(&((Range *)p->ptr)->export.minValue,
	      	       &((Range *)type->listPtr->ptr)->export.minValue) <= 0) {
		if (compareValues(&((Range *)p->ptr)->export.maxValue,
	      	       &((Range *)type->listPtr->ptr)->export.minValue) >= 0) {
		    smiPrintError(parser, ERR_RANGE_OVERLAP);
		}
		smiPrintError(parser, ERR_RANGES_NOT_ASCENDING);
		p->nextPtr = type->listPtr;
		type->listPtr = p;
	    } else {
		for (pp = type->listPtr; pp; pp = nextPP) {
		    nextPP = pp->nextPtr;
		    if ((!nextPP) ||
			(compareValues(&((Range *)p->ptr)->export.minValue,
			     &((Range *)nextPP->ptr)->export.minValue) <= 0)) {
			if (((nextPP) &&
			     (compareValues(&((Range *)p->ptr)->export.maxValue,
					    &((Range *)nextPP->ptr)->export.minValue) >= 0)) ||
			    (compareValues(&((Range *)p->ptr)->export.minValue,
					   &((Range *)pp->ptr)->export.maxValue) <= 0)) {
			    smiPrintError(parser, ERR_RANGE_OVERLAP);
			}
			p->nextPtr = pp->nextPtr;
			pp->nextPtr = p;
			if (p->nextPtr) {
			    smiPrintError(parser, ERR_RANGES_NOT_ASCENDING);
			    pp->nextPtr = NULL;
			} 
			break;
		    }
		}
	    }
	}
    }

    /* range normalization */
    for (p = type->listPtr, pp = p; p; p = nextPtr) {
	nextPtr = p->nextPtr;
	if (nextPtr &&
	    compareValues(&((Range *)p->ptr)->export.maxValue,
			  &((Range *)nextPtr->ptr)->export.minValue) == -1) {
	    ((Range *)nextPtr->ptr)->export.minValue =
		((Range *)p->ptr)->export.minValue;
	    if (p == type->listPtr) {
		type->listPtr = nextPtr;
		pp = nextPtr;
	    } else {
		pp->nextPtr = nextPtr;
	    }
	    smiFree(p);
	} else {
	    pp = p;
	}
    }

}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckTypeFormat --
 *
 *      Check whether we know a format specification for integer types.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckTypeFormat(Parser *parser, Type *type)
{
    Type *t;
    
    if (! type || !type->export.name) {
	return;
    }

    if (type->export.basetype != SMI_BASETYPE_INTEGER32
	&& type->export.basetype != SMI_BASETYPE_INTEGER64
	&& type->export.basetype != SMI_BASETYPE_UNSIGNED32
	&& type->export.basetype != SMI_BASETYPE_UNSIGNED64
	&& type->export.basetype != SMI_BASETYPE_OCTETSTRING) {
	return;
    }

    for (t = type; t; t = t->parentPtr) {
	if (t->export.format) {
	    break;
	}
    }

    if (! t) {
	smiPrintErrorAtLine(parser, ERR_TYPE_WITHOUT_FORMAT, type->line,
			    type->export.name);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckValueType --
 *
 *      Check whether a given value matches a given type.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckValueType(Parser *parser, SmiValue *value, Type *type, int line)
{
    List *p, *nextPtr;
    
    if (value && (value->basetype != SMI_BASETYPE_UNKNOWN) && type) {

	/*
	 * If defval type and object type don't match, check whether
	 * the defval value is in the allowed range of the object's basetype.
	 */
	if ((type->export.basetype == SMI_BASETYPE_INTEGER32) ||
	    (type->export.basetype == SMI_BASETYPE_ENUM)) {
	    if (((value->basetype == SMI_BASETYPE_INTEGER64) &&
		 ((value->value.integer64 < (SmiInteger64)(-2147483647 - 1)) ||
		  (value->value.integer64 > (SmiInteger64)2147483647))) ||
		((value->basetype == SMI_BASETYPE_UNSIGNED32) &&
		 ((value->value.unsigned32 > 2147483647))) ||
		((value->basetype == SMI_BASETYPE_UNSIGNED64) &&
		 ((value->value.unsigned32 > 2147483647)))) {
		smiPrintErrorAtLine(parser, ERR_DEFVAL_OUT_OF_BASETYPE, line);
	    }
	}
	if (type->export.basetype == SMI_BASETYPE_UNSIGNED32) {
	    if (((value->basetype == SMI_BASETYPE_INTEGER64) &&
		 ((value->value.integer64 < 0) ||
		  (value->value.integer64 > (SmiInteger64)4294967295UL))) ||
		((value->basetype == SMI_BASETYPE_INTEGER32) &&
		 ((value->value.integer32 < 0))) ||
		((value->basetype == SMI_BASETYPE_UNSIGNED64) &&
		 ((value->value.unsigned32 > (SmiUnsigned32)4294967295UL)))) {
		smiPrintErrorAtLine(parser, ERR_DEFVAL_OUT_OF_BASETYPE, line);
	    }
	}

	/*
	 * "cast" the defval to the object's basetype.
	 */
	value->basetype = type->export.basetype;

	/*
	 * check whether the defval matches the object's range restriction.
	 */
	if ((value->basetype == SMI_BASETYPE_UNSIGNED32) ||
	    (value->basetype == SMI_BASETYPE_UNSIGNED64) ||
	    (value->basetype == SMI_BASETYPE_INTEGER32) ||
	    (value->basetype == SMI_BASETYPE_INTEGER64)) {
	    for (p = type->listPtr; p; p = nextPtr) {
		nextPtr = p->nextPtr;
		if ((compareValues(&((Range *)p->ptr)->export.minValue,
				   value) <= 0) &&
		    (compareValues(&((Range *)p->ptr)->export.maxValue,
				   value) >= 0)) {
		    break;
		}
	    }
	    if ((p == NULL) && type->listPtr) {
		smiPrintErrorAtLine(parser, ERR_DEFVAL_OUT_OF_RANGE, line);
	    }
	}

	/*
	 * check whether the defval matches the object's enumeration.
	 */
	if (value->basetype == SMI_BASETYPE_ENUM) {
	    for (p = type->listPtr; p; p = nextPtr) {
		nextPtr = p->nextPtr;

		if (((NamedNumber *)(p->ptr))->export.value.value.integer32 ==
		    value->value.integer32) {
			break;
		}
	    }
	    if (p == NULL) {
		smiPrintErrorAtLine(parser, ERR_DEFVAL_OUT_OF_ENUM, line);
	    }
	}
    }
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckDefault --
 *
 *      Check whether the default value (if present) matches the
 *	underlying type and restrictions.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckDefault(Parser *parser, Object *object)
{
    smiCheckValueType(parser, &object->export.value, object->typePtr,
		      object->line);
}



/*
 *----------------------------------------------------------------------
 *
 * smiCheckTypeUsage --
 *
 *      Check whether the types of all objects are used appropriately.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

static void
checkInetAddressType(Parser *parserPtr, Module *modulePtr, Object *objectPtr)
{
    Module *inetModulePtr = NULL;		/* RFC 3291 */
    Type *inetAddressTypePtr = NULL;		/* RFC 3291 */
    Type *inetAddressPtr = NULL;		/* RFC 3291 */
    Node *nodePtr;
    List *listPtr = NULL;
    int i;

    const char *protected[] = {
	"InetAddressIPv4", "InetAddressIPv6",
	"InetAddressIPv4z", "InetAddressIPv6z",
	"InetAddressDNS", NULL
    };

    inetModulePtr = findModuleByName("INET-ADDRESS-MIB");
    if (! inetModulePtr) {
	return;
    }

    inetAddressTypePtr = findTypeByModuleAndName(inetModulePtr,
						 "InetAddressType");
    inetAddressPtr = findTypeByModuleAndName(inetModulePtr,
					     "InetAddress");
    
    if (!inetAddressTypePtr || !inetAddressPtr) {
	return;
    }
    
    /* check InetAddressType/InetAddress pair */
    if (smiTypeDerivedFrom(objectPtr->typePtr, inetAddressPtr)) {
	Object *indexObject = NULL;
	Object *entryObject = objectPtr->nodePtr->parentPtr->lastObjectPtr;
	if (entryObject) {
	    switch (entryObject->export.indexkind) {
	    case SMI_INDEX_INDEX:
		indexObject = entryObject;
		break;
	    case SMI_INDEX_AUGMENT:
		indexObject = entryObject->relatedPtr;
		break;
	    default:
		/* xxx need to handle other index constructions */
		indexObject = NULL;
		break;
	    }
	}
	if (indexObject) {
	    for (listPtr = indexObject->listPtr; listPtr;
		 listPtr = listPtr->nextPtr) {
		Object *iObject = (Object *) listPtr->ptr;
		if (iObject
		    && iObject->typePtr == inetAddressTypePtr) {
		    break;
		}
	    }
	    
	}
	if (! indexObject || ! listPtr) {
	    for (nodePtr =
		     objectPtr->nodePtr->parentPtr->firstChildPtr;
		 nodePtr &&
		     nodePtr->subid < objectPtr->nodePtr->subid &&
		     nodePtr->lastObjectPtr->typePtr !=
		     inetAddressTypePtr;
		 nodePtr = nodePtr->nextPtr);
	    if (!nodePtr ||
		nodePtr->subid >= objectPtr->nodePtr->subid) {
		smiPrintErrorAtLine(parserPtr,
				    ERR_INETADDRESS_WITHOUT_TYPE,
				    objectPtr->line);
	    }
	}
    }
    
    /* check InetAddressType subtyping */
    if (objectPtr->typePtr->parentPtr == inetAddressTypePtr) {
	smiPrintErrorAtLine(parserPtr,
			    ERR_INETADDRESSTYPE_SUBTYPED,
			    objectPtr->line);
    }

    /* check for TCs that should not be used directly */
    for (i = 0; protected[i]; i++) {
	if (objectPtr->typePtr == findTypeByModuleAndName(inetModulePtr,
							  protected[i])) {
	    smiPrintErrorAtLine(parserPtr,
				ERR_INETADDRESS_SPECIFIC,
				objectPtr->line,
				objectPtr->typePtr->export.name);
	    break;
	}
    }
}


static void
checkTransportAddressType(Parser *parserPtr, Module *modulePtr, Object *objectPtr)
{
    Module *transportModulePtr = NULL;		/* RFC 3419 */
    Type *transportAddressTypePtr = NULL;	/* RFC 3419 */
    Type *transportAddressPtr = NULL;		/* RFC 3419 */
    Type *transportDomainPtr = NULL;		/* RFC 3419 */
    Node *nodePtr;
    List *listPtr = NULL;
    int i;

    const char *protected[] = {
	"TransportAddressIPv4", "TransportAddressIPv6",
	"TransportAddressIPv4z", "TransportAddressIPv6z",
	"TransportAddressDNS", "TransportAddressLocal",
	NULL
    };

    transportModulePtr = findModuleByName("TRANSPORT-ADDRESS-MIB");
    if (! transportModulePtr) {
	return;
    }

    transportAddressTypePtr = findTypeByModuleAndName(transportModulePtr,
						 "TransportAddressType");
    transportAddressPtr = findTypeByModuleAndName(transportModulePtr,
					     "TransportAddress");
    transportDomainPtr = findTypeByModuleAndName(transportModulePtr,
						 "TransportDomain");

    if (!transportAddressTypePtr || !transportAddressPtr || !transportDomainPtr) {
	return;
    }
    
    /* check TransportAddressType/TransportAddress pair */
    if (smiTypeDerivedFrom(objectPtr->typePtr, transportAddressPtr)) {
	Object *indexObject = NULL;
	Object *entryObject = objectPtr->nodePtr->parentPtr->lastObjectPtr;
	if (entryObject) {
	    switch (entryObject->export.indexkind) {
	    case SMI_INDEX_INDEX:
		indexObject = entryObject;
		break;
	    case SMI_INDEX_AUGMENT:
		indexObject = entryObject->relatedPtr;
		break;
	    default:
		/* xxx need to handle other index constructions */
		indexObject = NULL;
		break;
	    }
	}
	if (indexObject) {
	    for (listPtr = indexObject->listPtr; listPtr;
		 listPtr = listPtr->nextPtr) {
		Object *iObject = (Object *) listPtr->ptr;
		if (iObject
		    && (iObject->typePtr == transportAddressTypePtr
			|| iObject->typePtr == transportDomainPtr)) {
		    break;
		}
	    }
	}
	if (! indexObject || ! listPtr) {
	    for (nodePtr =
		     objectPtr->nodePtr->parentPtr->firstChildPtr;
		 nodePtr &&
		     nodePtr->subid < objectPtr->nodePtr->subid &&
		     nodePtr->lastObjectPtr->typePtr !=
		     transportAddressTypePtr &&
		     nodePtr->lastObjectPtr->typePtr !=
		     transportDomainPtr;
		 nodePtr = nodePtr->nextPtr);
	    if (!nodePtr ||
		nodePtr->subid >= objectPtr->nodePtr->subid) {
		smiPrintErrorAtLine(parserPtr,
				    ERR_TRANSPORTADDRESS_WITHOUT_TYPE,
				    objectPtr->line);
	    }
	}
    }
    
    /* check TransportAddressType subtyping */
    if (objectPtr->typePtr->parentPtr == transportAddressTypePtr) {
	smiPrintErrorAtLine(parserPtr,
			    ERR_TRANSPORTADDRESSTYPE_SUBTYPED,
			    objectPtr->line);
    }

    /* check for TCs that should not be used directly */
    for (i = 0; protected[i]; i++) {
	if (objectPtr->typePtr == findTypeByModuleAndName(transportModulePtr,
							  protected[i])) {
	    smiPrintErrorAtLine(parserPtr,
				ERR_TRANSPORTADDRESS_SPECIFIC,
				objectPtr->line,
				objectPtr->typePtr->export.name);
	    break;
	}
    }
}


void
smiCheckTypeUsage(Parser *parserPtr, Module *modulePtr)
{
    Object *objectPtr;
    Module *tcModulePtr = NULL;
    Type *rowStatusPtr = NULL;
    Type *storageTypePtr = NULL;
    Type *taddressPtr = NULL;
    Type *tdomainPtr = NULL;
    NamedNumber *nnPtr;
    Node *nodePtr;
    
    tcModulePtr = findModuleByName("SNMPv2-TC");
    if (tcModulePtr) {
	rowStatusPtr = findTypeByModuleAndName(tcModulePtr, "RowStatus");
	storageTypePtr = findTypeByModuleAndName(tcModulePtr, "StorageType");
	taddressPtr = findTypeByModuleAndName(tcModulePtr, "TAddress");
	tdomainPtr = findTypeByModuleAndName(tcModulePtr, "TDomain");
    }
    
    for (objectPtr = modulePtr->firstObjectPtr;
	 objectPtr; objectPtr = objectPtr->nextPtr) {

	if (objectPtr->typePtr) {

	    if (tcModulePtr) {

		/* check RowStatus DEFVAL */
		if (objectPtr->typePtr == rowStatusPtr) {
		    if ((objectPtr->export.value.value.integer32 >= 4) &&
			(objectPtr->export.value.value.integer32 <= 6)) {
			nnPtr = findTypeNamedNumber(rowStatusPtr,
				     objectPtr->export.value.value.integer32);
			smiPrintErrorAtLine(parserPtr,
					    ERR_ILLEGAL_ROWSTATUS_DEFAULT,
					    objectPtr->line,
					    nnPtr->export.name);
		    }
		}

		/* check RowStatus read-create status */
		if (objectPtr->typePtr == rowStatusPtr) {
		    Object *entryObject
			= objectPtr->nodePtr->parentPtr->lastObjectPtr;
		    if (objectPtr->export.access != SMI_ACCESS_READ_WRITE
			|| !entryObject->export.create) {
			smiPrintErrorAtLine(parserPtr,
					    ERR_ILLEGAL_ROWSTATUS_ACCESS,
					    objectPtr->line);
		    }
		}
		
		/* check StorageType DEFVAL */
		if (objectPtr->typePtr == storageTypePtr) {
		    if ((objectPtr->export.value.value.integer32 >= 4) &&
			(objectPtr->export.value.value.integer32 <= 5)) {
			nnPtr = findTypeNamedNumber(storageTypePtr,
				     objectPtr->export.value.value.integer32);
			smiPrintErrorAtLine(parserPtr,
					    ERR_ILLEGAL_STORAGETYPE_DEFAULT,
					    objectPtr->line,
					    nnPtr->export.name);
		    }
		}

		/* check TDomain/TAddress pair */
		if (smiTypeDerivedFrom(objectPtr->typePtr, taddressPtr)) {
		    for (nodePtr =
			     objectPtr->nodePtr->parentPtr->firstChildPtr;
			 nodePtr &&
			     nodePtr->lastObjectPtr->typePtr != tdomainPtr;
			 nodePtr = nodePtr->nextPtr);
		    if (!nodePtr ||
			nodePtr->lastObjectPtr->typePtr != tdomainPtr) {
			smiPrintErrorAtLine(parserPtr,
					    ERR_TADDRESS_WITHOUT_TDOMAIN,
					    objectPtr->line);
		    }
		}
		
	    }
	    checkInetAddressType(parserPtr, modulePtr, objectPtr);
	    checkTransportAddressType(parserPtr, modulePtr, objectPtr);
	}
    }
}


static char *status[] = { "Unknown", "current", "deprecated",
			  "mandatory", "optional", "obsolete" };

static int
memberOfGroup(Object *object, Object *group)
{
    List *listPtr;

    for (listPtr = group->listPtr; listPtr; listPtr = listPtr->nextPtr) {
	if (listPtr->ptr == object) {
	    return 1;
	}
    }
    return 0;
}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckComplianceStatus --
 *
 *      Make sure that all groups and objects in a compliance statement
 *      are at least as current as the compliance itself.
 *      XXX I'm not sure I traversed the whole compliance statement,
 *          this at least covers the common case
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckComplianceStatus(Parser *parser, Object *compliance)
{
    List *listPtr, *groupListPtr;
    Object *memberPtr;
    Object *group;

    for (listPtr = compliance->listPtr;
	 listPtr; listPtr = listPtr->nextPtr) {
	
	memberPtr = (Object *) listPtr->ptr;
	if (!memberPtr)
	    continue;
	addObjectFlags(memberPtr, FLAG_INCOMPLIANCE);
	if (memberPtr->export.status > compliance->export.status) {
	    smiPrintErrorAtLine(parser, ERR_COMPLIANCE_GROUP_STATUS,
				compliance->line,
				status[compliance->export.status],
				compliance->export.name,
				status[memberPtr->export.status],
				memberPtr->export.name);
	}
    }
    for (listPtr = compliance->optionlistPtr;
	 listPtr; listPtr = listPtr->nextPtr) {
	
	memberPtr = ((Option *) listPtr->ptr)->objectPtr;
	addObjectFlags(memberPtr, FLAG_INCOMPLIANCE);
	if (memberPtr->export.status > compliance->export.status) {
	    smiPrintErrorAtLine(parser, ERR_COMPLIANCE_GROUP_STATUS,
				((Option *) listPtr->ptr)->line,
				status[compliance->export.status],
				compliance->export.name,
				status[memberPtr->export.status],
				memberPtr->export.name);
	}

	for (groupListPtr = compliance->listPtr;
	     groupListPtr; groupListPtr = groupListPtr->nextPtr) {
	    
	    group = (Object *) groupListPtr->ptr;
	    if (strcmp(group->export.name, memberPtr->export.name) == 0) {
		smiPrintErrorAtLine(parser, ERR_COMPLIANCE_GROUP_INVALID,
				    ((Option *) listPtr->ptr)->line,
				    group->export.name,
				    compliance->export.name);
	    }
	}
    }
    for (listPtr = compliance->refinementlistPtr;
	 listPtr; listPtr = listPtr->nextPtr) {
	
	memberPtr = ((Refinement *) listPtr->ptr)->objectPtr;

	for (groupListPtr = compliance->listPtr;
	     groupListPtr; groupListPtr = groupListPtr->nextPtr) {

	    group = (Object *) groupListPtr->ptr;
	    if (group && memberOfGroup(memberPtr, group)) {
		break;
	    }
	}

	if (! groupListPtr) {
	    for (groupListPtr = compliance->optionlistPtr;
		 groupListPtr; groupListPtr = groupListPtr->nextPtr) {

		group = ((Option *) groupListPtr->ptr)->objectPtr;
		if (group && memberOfGroup(memberPtr, group)) {
		    break;
		}
	    }
	}

	if (! groupListPtr) {
	    smiPrintErrorAtLine(parser, ERR_REFINEMENT_NOT_LISTED,
				((Refinement *) listPtr->ptr)->line,
				memberPtr->export.name);
	}
		
	addObjectFlags(memberPtr, FLAG_INCOMPLIANCE);
	if (memberPtr->export.status > compliance->export.status) {
	    smiPrintErrorAtLine(parser, ERR_COMPLIANCE_OBJECT_STATUS,
				((Refinement *) listPtr->ptr)->line,
				status[compliance->export.status],
				compliance->export.name,
				status[memberPtr->export.status],
				memberPtr->export.name);
	}
    }
}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckGroupMembers --
 *
 *      Check whether only scalar and column nodes and notifications
 *	are contained in a conformance group.
 *
 *      Also ensure that group members are at least as current
 *      as the group itself.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckGroupMembers(Parser *parser, Object *group)
{
    List *listPtr;
    Object *memberPtr;
    int scalarsOrColumns = 0;
    int notifications = 0;
    
    for (listPtr = group->listPtr;
	 listPtr; listPtr = listPtr->nextPtr) {
	
	memberPtr = (Object *) listPtr->ptr;

	if (((memberPtr->export.nodekind == SMI_NODEKIND_COLUMN
	      || memberPtr->export.nodekind == SMI_NODEKIND_SCALAR)
	     && memberPtr->export.access != SMI_ACCESS_NOT_ACCESSIBLE)
	    || memberPtr->export.nodekind == SMI_NODEKIND_NOTIFICATION) {
	    if (memberPtr->export.nodekind == SMI_NODEKIND_NOTIFICATION) {
		notifications++;
		if (group->export.decl == SMI_DECL_OBJECTGROUP) {
		    smiPrintErrorAtLine(parser,
					ERR_NOTIFICATION_IN_OBJECT_GROUP,
					group->line,
					group->export.name,
					memberPtr->export.name);
		}
	    } else {
		scalarsOrColumns++;
		if (group->export.decl == SMI_DECL_NOTIFICATIONGROUP) {
		    smiPrintErrorAtLine(parser,
					ERR_OBJECT_IN_NOTIFICATION_GROUP,
					group->line,
					group->export.name,
					memberPtr->export.name);
		}
	    }
	    addObjectFlags(memberPtr, FLAG_INGROUP);
	} else if (!(memberPtr->flags & FLAG_INCOMPLETE)) {
	    /* unknown OIDs are already flagged */
	    smiPrintErrorAtLine(parser, ERR_INVALID_GROUP_MEMBER,
				group->line,
				memberPtr->export.name,
				group->export.name);
	}
	if (memberPtr->export.status > group->export.status) {
	    smiPrintErrorAtLine(parser, ERR_GROUP_OBJECT_STATUS,
				group->line,
				status[group->export.status],
				group->export.name,
				status[memberPtr->export.status],
				memberPtr->export.name);
	}
    }

    if (scalarsOrColumns && notifications) {
	smiPrintErrorAtLine(parser, ERR_MIXED_GROUP_MEMBERS,
			    group->line,
			    group->export.name);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckGroupMembership --
 *
 *      Check whether scalar and column nodes and notifications are
 *	contained in at least one conformance group.
 *
 *	This function assumes that smiCheckGroupMembers() has been
 *	called on all group objects and smiCheckComplianceStatus()
 *      has been called on all compliance objects before.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckGroupMembership(Parser *parser, Object *objectPtr)
{
    int found;
    
    if (((objectPtr->export.nodekind == SMI_NODEKIND_COLUMN
	  || objectPtr->export.nodekind == SMI_NODEKIND_SCALAR)
	 && objectPtr->export.access != SMI_ACCESS_NOT_ACCESSIBLE)
	|| objectPtr->export.nodekind == SMI_NODEKIND_NOTIFICATION) {

	found = (objectPtr->flags & FLAG_INGROUP);
	    
	if (! found) {
	    if (objectPtr->export.nodekind == SMI_NODEKIND_NOTIFICATION) {
		smiPrintErrorAtLine(parser, ERR_NOTIFICATION_NOT_IN_GROUP,
				    objectPtr->line,
				    objectPtr->export.name);
	    } else {
		smiPrintErrorAtLine(parser, ERR_NODE_NOT_IN_GROUP,
				    objectPtr->line,
				    objectPtr->export.name);
	    }
	}
    }
    if (objectPtr->export.nodekind == SMI_NODEKIND_GROUP) {

	found = (objectPtr->flags & FLAG_INCOMPLIANCE);

	if (!found && objectPtr->export.status != SMI_STATUS_OBSOLETE) {
	    smiPrintErrorAtLine(parser, ERR_GROUP_UNREF,
				objectPtr->line,
				status[objectPtr->export.status],
				objectPtr->export.name);
	}
    }

}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckObjectReuse --
 *
 *      Check whether a newly defined Object represents a duplicate
 *      or a reused OID.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      Allocates a new Object and adjusts the objectPtr parameter.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckObjectReuse(Parser *parser, char *name, Object **objectPtr)
{
    if ((((*objectPtr)->flags & FLAG_INCOMPLETE) == 0) &&
	strcmp(name, (*objectPtr)->export.name)) {
	if ((*objectPtr)->export.decl >= SMI_DECL_OBJECTTYPE) {
	    smiPrintError(parser, ERR_OID_REGISTERED, name,
			  (*objectPtr)->export.name);
	} else {
	    smiPrintError(parser, ERR_OID_REUSE, name,
			  (*objectPtr)->export.name);
	}
	smiPrintErrorAtLine(parser, ERR_PREVIOUS_DEFINITION,
			    (*objectPtr)->line, (*objectPtr)->export.name);
	*objectPtr = duplicateObject(*objectPtr, 0, parser);
    }
    
    if ((*objectPtr)->modulePtr != parser->modulePtr) {
	*objectPtr = duplicateObject(*objectPtr, 0, parser);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckNotificationOid --
 *
 *      Check whether SMIv2 notifications are reversible and whether
 *	the last sub-identifer fits into a signed 32-bit integer.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckNotificationOid(Parser *parser, Module *module, Object *object)
{
    static const char *name[] = {
	"SNMPv2-MIB", "coldStart",
	"SNMPv2-MIB", "warmStart",
	"IF-MIB", "linkDown",
	"IF-MIB", "linkUp",
	"SNMPv2-MIB", "authenticationFailure",
	/* egpNeighborLoss is not really defined in any SMI module */
	NULL, NULL };

    int i;

    if (parser->modulePtr->export.language == SMI_LANGUAGE_SMIV2) {
	for (i = 0; name[i]; i+= 2) {
	    if (strcmp(name[i], module->export.name) == 0
		&& strcmp(name[i+1], object->export.name) == 0) {
		break;
	    }
	}
	if (! name[i]) {
	    Node *parent = object->nodePtr->parentPtr;
	    if (parent && parent->subid != 0) {
		smiPrintErrorAtLine(parser, ERR_NOTIFICATION_NOT_REVERSIBLE,
				    object->line, object->export.name);
	    }
	}
    }

    if (object->nodePtr->subid > 2147483647) {
	smiPrintErrorAtLine(parser, ERR_NOTIFICATION_ID_TOO_LARGE,
			    object->line, object->export.name);
    }
}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckNotificationMembers --
 *
 *      Check whether a newly defined notification contains only members
 *	of a single logical object.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckNotificationMembers(Parser *parser, Object *object)
{
    List *listPtr;
    Object *memberPtr;
#if 0
    Node *parent = NULL;
#endif
    Node *node = NULL;

    for (listPtr = object->listPtr;
	 listPtr; listPtr = listPtr->nextPtr) {
	
	memberPtr = (Object *) listPtr->ptr;

	if (memberPtr->export.nodekind == SMI_NODEKIND_SCALAR) {
	    if (memberPtr->nodePtr && memberPtr->nodePtr->parentPtr) {
		node = memberPtr->nodePtr->parentPtr;
	    }
	} else if (memberPtr->export.nodekind == SMI_NODEKIND_COLUMN) {
	    if (memberPtr->nodePtr && memberPtr->nodePtr->parentPtr
		&& memberPtr->nodePtr->parentPtr->parentPtr) {
		node = memberPtr->nodePtr->parentPtr->parentPtr;
	    }
	} else {
	    smiPrintErrorAtLine(parser, ERR_NOTIFICATION_OBJECT_TYPE,
				object->line, memberPtr->export.name,
				object->export.name);
	}

	if (memberPtr->export.access == SMI_ACCESS_NOT_ACCESSIBLE) {
	    smiPrintErrorAtLine(parser, ERR_NOTIFICATION_OBJECT_ACCESS,
				object->line, memberPtr->export.name,
				object->export.name);
	}

	/* xxx check for duplicates */
#if 0
	if (node) {
	    if (! parent) {
		parent = node;
	    } else {
		if (parent != node) {
		    /* xxx do not report multiple times xxx */
		    smiPrintErrorAtLine(parser, ERR_NOTIFICATION_OBJECT_MIX,
					object->line, object->export.name);
		}
	    }
	}
#endif
    }
}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckUniqueness --
 *
 *      Check whether all entries for an UNIQUENESS clause are in fact
 *      columns.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void smiCheckUniqueness(Parser *parser, Object *object)
{
    List *p;

    for (p = object->uniquenessPtr; p; p = p->nextPtr) {
        Object *uniq = (Object *)p->ptr;
        int found = 0;
        List *pp;

        if (uniq && object->typePtr) {
            for (pp = object->typePtr->listPtr; pp; pp = pp->nextPtr)
                if (pp->ptr &&
                    !strcmp(uniq->export.name, ((Object *)pp->ptr)->export.name)) {
                    found = 1;
                    break;
                }
            if (!found) {
                if (((object->export.indexkind == SMI_INDEX_AUGMENT) ||
                    (object->export.indexkind == SMI_INDEX_SPARSE)) &&
                    (object->relatedPtr && object->relatedPtr->typePtr)) {
                    for (pp = object->relatedPtr->typePtr->listPtr; pp;
                         pp = pp->nextPtr)
                        if (pp->ptr &&
                            !strcmp(uniq->export.name, ((Object *)pp->ptr)->export.name)) {
                            found = 1;
                            break;
                        }
                }
            }
            if (!found)
                smiPrintErrorAtLine(parser, ERR_NOT_A_COLUMN,
                                    object->line, uniq->export.name);
        }
    }
}

/*
 *----------------------------------------------------------------------
 *
 * smiCheckModuleIdentityRegistration --
 *
 *      Check whether the module identity is registered in a well
 *	known (IANA) controlled location. In particular, warn if
 *	the OID is below iso(1).org(3).dod(6).mgmt(1) and not
 *	below well known registration locations such as mib-2,
 *	transmission, or snmpModules.
 *
 * Results:
 *      None.
 *
 * Side effects:
 *      None.
 *
 *----------------------------------------------------------------------
 */

void
smiCheckModuleIdentityRegistration(Parser *parser, Object *object)
{
    static const SmiSubid mgmt[] = { 1, 3, 6, 1, 2 };
    static const SmiSubid mib2[] = { 1, 3, 6, 1, 2, 1 };
    static const SmiSubid transmission[] = { 1, 3, 6, 1, 2, 1, 10 };
    static const SmiSubid snmpModules[] = { 1, 3, 6, 1, 6, 3 };

    /*
     * If the module OID has less than two sub-identifier, the OID and
     * hence the registration is invalid.
     */

    if (object->export.oidlen < 2) {
	smiPrintErrorAtLine(parser, ERR_MODULE_IDENTITY_REG_ILLEGAL,
			    object->line);
	return;
    }

    /*
     * If the module OID begins with the IETF mgmt prefix, check
     * whether the OID is directly below one of the well known
     * registration points (mib2, transmission, snmpModules).
     */

    if (object->export.oidlen >= sizeof(mgmt)/sizeof(SmiSubid)
	&& memcmp(object->export.oid, mgmt, sizeof(mgmt)) == 0) {

	if (object->export.oidlen == sizeof(mib2)/sizeof(SmiSubid) + 1
	    && memcmp(object->export.oid, mib2, sizeof(mib2)) == 0) {
	    return;
	}
	
	if (object->export.oidlen == sizeof(transmission)/sizeof(SmiSubid) + 1
	    && memcmp(object->export.oid, transmission, sizeof(transmission)) == 0) {
	    return;
	}
	
	if (object->export.oidlen == sizeof(snmpModules)/sizeof(SmiSubid) + 1
	    && memcmp(object->export.oid, snmpModules, sizeof(snmpModules)) == 0) {
	    return;
	}

	smiPrintErrorAtLine(parser, ERR_MODULE_IDENTITY_REG_UNCONTROLLED,
			    object->line);
	return;
    }
}

/*
 *----------------------------------------------------------------------
 *
 * smiyyerror --
 *
 *      Prints an error message from the parser.  In SMIv1 and v2,
 *      a common error is to terminate a comment early, so if the
 *	current line contains a comment (parserPtr->lcline) print
 *	the ERR_COMMENT_TERMINATES.
 *
 *----------------------------------------------------------------------
 */
void smiyyerror(char *msg, Parser *parserPtr)
{
    if (parserPtr->line == parserPtr->lcline &&
	parserPtr->modulePtr &&
	(parserPtr->modulePtr->export.language == SMI_LANGUAGE_SMIV1 ||
	 parserPtr->modulePtr->export.language == SMI_LANGUAGE_SMIV2))
	smiPrintError(parserPtr, ERR_COMMENT_TERMINATES);
    smiPrintError(parserPtr, ERR_OTHER_ERROR, msg);
}
