/*\
 *	DISTRIBUTION: HNMS v2.0
 *	FILE: hnmslib/presentation.c
 *
 *	HNMP presentation layer.
 *
 *	Jude George
 *	NAS Facility, NASA Ames Research Center
 *
 *	Copyright (c) 1994 Jude George
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation; either version 1, or (at your option)
 *	any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program; if not, write to the Free Software
 *	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
\*/

#include "stdhnms.h"

/*\
 *  Create an HNMP Message of the requested type.  The version,
 *  community, and from-module-id fields will be filled in for
 *  all messages.  Return the Message if successful, NULL otherwise.
\*/
Message Message_create(type)
    int			type;
{
    char		*hnms_community;
    int			my_module_id;
    int			my_module_type;
    Message		msg;

    /*
     * Create the message header.
     */
    hnms_community = PARAM_get_str(oid_hnmsModuleHnmsCommunity);
    my_module_id = PARAM_get_int(oid_hnmsModuleId);
    my_module_type = PARAM_get_int(oid_hnmsModuleType);
    msg = HNMS_alloc(1, sizeof *msg);
    msg->version = int_HNMP_version_version__2;
    msg->community = str2qb(hnms_community, strlen(hnms_community), 0);
    msg->from__module__type = my_module_type;
    msg->from__module__id = my_module_id;
    msg->msg__timestamp = HNMS_alloc(1, sizeof *msg->msg__timestamp);

    /*
     * Create the message data section.
     */
    msg->data = HNMS_alloc(1, sizeof *msg->data);
    msg->data->offset = type;
    switch (type) {
    case type_HNMP_PDU_ack:
	msg->data->un.ack = HNMS_alloc(1, sizeof *msg->data->un.ack);
	break;

    case type_HNMP_PDU_redirect:
	msg->data->un.redirect = NULL;
	break;

    case type_HNMP_PDU_hello:
    case type_HNMP_PDU_welcome:
    case type_HNMP_PDU_goodbye:
    case type_HNMP_PDU_get__key:
    case type_HNMP_PDU_send__key:
	msg->data->un.hello = HNMS_alloc(1, sizeof *msg->data->un.hello);
	break;

    case type_HNMP_PDU_announce__object:
    case type_HNMP_PDU_subscribe__relations:
    case type_HNMP_PDU_send__relations:
    case type_HNMP_PDU_subscribe__data:
    case type_HNMP_PDU_send__data:
    case type_HNMP_PDU_set__data:
    case type_HNMP_PDU_get__data:
    case type_HNMP_PDU_get__next__data:
    case type_HNMP_PDU_get__walk__data:
	msg->data->un.announce__object =
	    HNMS_alloc(1, sizeof *msg->data->un.announce__object);
	break;

    case type_HNMP_PDU_activate__object:
    case type_HNMP_PDU_deactivate__object:
    case type_HNMP_PDU_delete__object:
    case type_HNMP_PDU_unsubscribe__relations:
    case type_HNMP_PDU_unsubscribe__data:
	msg->data->un.activate__object =
	    HNMS_alloc(1, sizeof *msg->data->un.activate__object);
	break;

    default:
	hnms_errno = HNMS_err_bad_msg_type;
	return NULL;
    }
    return msg;
}

/*\
 *  Set a field in an HNMP message.
 *  Pass in the value as an (int *) or a (char *).
\*/
int Message_set(msg, field, value)
    Message		msg;
    const int		field;
    const caddr_t	value;
{
    switch (msg->data->offset) {
    case type_HNMP_PDU_ack:
	break;

    case type_HNMP_PDU_redirect:
	msg->data->un.redirect = int2qb(*(int *)value);
	break;

    case type_HNMP_PDU_hello:
    case type_HNMP_PDU_welcome:
    case type_HNMP_PDU_goodbye:
    case type_HNMP_PDU_get__key:
    case type_HNMP_PDU_send__key:
	break;

    case type_HNMP_PDU_announce__object:
    case type_HNMP_PDU_subscribe__relations:
    case type_HNMP_PDU_send__relations:
    case type_HNMP_PDU_subscribe__data:
    case type_HNMP_PDU_send__data:
    case type_HNMP_PDU_set__data:
    case type_HNMP_PDU_get__data:
    case type_HNMP_PDU_get__next__data:
    case type_HNMP_PDU_get__walk__data:
	switch (field) {
	case HNMP_object_id:
	    msg->data->un.announce__object->object__id = *(int *)value;
	    break;
	case HNMP_variables:
	    msg->data->un.announce__object->variables =
		VarBindList_copylist((VarBindList)value);
	    break;
	default:
	    hnms_errno = HNMS_err_bad_msg_field;
	    return -1;
	}
	break;

    case type_HNMP_PDU_activate__object:
    case type_HNMP_PDU_deactivate__object:
    case type_HNMP_PDU_delete__object:
    case type_HNMP_PDU_unsubscribe__relations:
    case type_HNMP_PDU_unsubscribe__data:
	if (field == HNMP_object_id)
	    msg->data->un.activate__object->parm = *(int *)value;
	break;

    default:
	hnms_errno = HNMS_err_bad_msg_type;
	return NULL;
    }
    return 0;
}

/*\
 *  Get a field from an HNMP message.
 *  Returns the value as an (int *) or a (char *).
\*/
caddr_t Message_get(msg, field)
    const Message	msg;
    const int		field;
{
    int			ip_address;
    char		*cp;
    int			len;
    static char		community[STRBUFLEN];

    switch (field) {
    case HNMP_msg_type:
	return (caddr_t)&msg->data->offset;
    case HNMP_version:
	return (caddr_t)&msg->version;
    case HNMP_community:
	bzero(community, STRBUFLEN);
	cp = qb2str(msg->community, &len);
	if (cp)
	    bcopy(cp, community, len);
	free(cp);
	return community;
    case HNMP_from_module_type:
	return (caddr_t)&msg->from__module__type;
    case HNMP_from_module_id:
	return (caddr_t)&msg->from__module__id;
    case HNMP_to_module_id:
	return (caddr_t)&msg->to__module__id;
    case HNMP_msg_id:
	return (caddr_t)&msg->msg__id;
    case HNMP_msg_timestamp:
	return (caddr_t)&msg->msg__timestamp;
    default:;
    }

    switch (msg->data->offset) {
    case type_HNMP_PDU_ack:
	    return (caddr_t)&msg->data->un.ack->parm;

    case type_HNMP_PDU_redirect:
	    ip_address = qb2int(msg->data->un.redirect);
	    return (caddr_t)&ip_address;

    case type_HNMP_PDU_announce__object:
    case type_HNMP_PDU_send__relations:
    case type_HNMP_PDU_subscribe__relations:
    case type_HNMP_PDU_subscribe__data:
    case type_HNMP_PDU_send__data:
    case type_HNMP_PDU_set__data:
    case type_HNMP_PDU_get__data:
    case type_HNMP_PDU_get__next__data:
    case type_HNMP_PDU_get__walk__data:
	switch (field) {
	case HNMP_object_id:
	    return (caddr_t)&msg->data->un.announce__object->object__id;
	case HNMP_variables:
	    return (caddr_t)msg->data->un.announce__object->variables;
	default:
	    hnms_errno = HNMS_err_bad_msg_field;
	    return NULL;
	}

    case type_HNMP_PDU_activate__object:
    case type_HNMP_PDU_deactivate__object:
    case type_HNMP_PDU_delete__object:
    case type_HNMP_PDU_unsubscribe__relations:
    case type_HNMP_PDU_unsubscribe__data:
	    return (caddr_t)&msg->data->un.activate__object->parm;

    default:
	hnms_errno = HNMS_err_bad_msg_type;
	return NULL;
    }
}

void Message_free(msg)
    Message	msg;
{
    free_HNMP_Message(msg);
}

/*\
 *  For debugging.  Print a Message.
\*/
void Message_print(msg)
    const Message	msg;
{
    HNMS_debug(DEBUG_HNMP,
	     "msg-type %d, msg-id %d\n", msg->data->offset, msg->msg__id);
}

/*\
 *  Get the value of a variable in a VarBindList.  If you want
 *  the value converted to a char * or int *, it will take extra cpu.
 *
 *  Pass in an address to get the value as a char * or int *,
 *	or NULL if you want to get the value back as a PE.
 *  Pass in an address to get the length of the value,
 *	if you are getting the value back as a char * or int *.
 *  Pass in an address to get the type of the converted value,
 *	if you are getting the value back as a char * or int *.
 *  Pass in an address to get the interval field.
 *  Pass in an address to get the timestamp field.
 *
 *  On success: returns the PE of the value in the VarBindList.
 *		This will always be non-NULL on success.
 *  On failure: returns NULL.
\*/
const PE VarBindList_get(vbl, oid, value, len, type, interval, ts)
    const VarBindList	vbl;
    const OID		oid;
    caddr_t		value;
    unsigned int	*len;
    unsigned int	*type;
    unsigned int	*interval;
    unsigned int	*ts;
{
    VarBindList		elem;
    int			s;
    char		*cp;

    if (!oid)
	return NULL;

    for (elem = vbl; elem; elem = elem->next)
	if (!oid_cmp(elem->VarBind->name, oid))
	    break;
    if (!elem) {
	hnms_errno = HNMS_err_var_not_found;
	return NULL;
    }

    if (value)
	switch (elem->VarBind->value->pe_class) {
	case PE_CLASS_UNIV:
	    switch (elem->VarBind->value->pe_id) {
	    case PE_PRIM_INT:
		if (value)
		    *(int *)value = prim2num(elem->VarBind->value);
		if (len)
		    *len = sizeof(int);
		if (type)
		    *type = MIB_integer;
		break;
	    case PE_PRIM_OCTS:
		if (value) {
		    cp = (char *)prim2str(elem->VarBind->value, &s);
		    bcopy(cp, value, s);
		    free(cp);
		}
		if (len)
		   *len = s;
		if (type)
		    *type = MIB_octetstring;
		break;
	    case PE_PRIM_OID:
		if (len)
		    *len = 0;
		if (type)
		    *type = MIB_oid;
		break;
	    case PE_PRIM_NULL:
		if (len)
		    *len = 0;
		if (type)
		    *type = MIB_null;
		break;
	    case PE_UNIV_EOC:
		if (len)
		    *len = 0;
		if (type)
		    *type = MIB_other;
		break;
	    default:;
	    }
	    break;
	    
	case PE_CLASS_APPL:
	    switch (elem->VarBind->value->pe_id) {
	    case 0: /* IpAddress */
		if (value)
		    *(int *)value = ipaddr_pe2int(elem->VarBind->value);
		if (len)
		    *len = sizeof(int);
		if (type)
		    *type = MIB_ipaddr;
		break;
	    case 1: /* Counter */
		if (value)
		    *(int *)value = (int)prim2num(elem->VarBind->value);
		if (len)
		    *len = sizeof(int);
		if (type)
		    *type = MIB_counter;
		break;
	    case 2: /* Gauge */
		if (value)
		    *(int *)value = (int)prim2num(elem->VarBind->value);
		if (len)
		    *len = sizeof(int);
		if (type)
		    *type = MIB_gauge;
		break;
	    case 3: /* TimeTicks */
		if (value)
		    *(int *)value = (int)prim2num(elem->VarBind->value);
		if (len)
		    *len = sizeof(int);
		if (type)
		    *type = MIB_timeticks;
		break;
	    case 4: /* Opaque */
		if (len)
		    *len = 0;
		if (type)
		    *type = MIB_other;
		break;
	    default:;
	    }
	    break;
	    
	default:;
	}

    if (interval)
	*interval = elem->VarBind->interval;
    if (ts)
	*ts = elem->VarBind->timestamp->parm;

    return elem->VarBind->value;
}

/*\
 *  Get the variable in a VarBindList that would sequentially follow
 *  the given variable, in MIB order.  If the name is specified as NULL,
 *  get the first variable, in MIB order.  Otherwise similar to
 *  VarBindList_get().
\*/
const PE VarBindList_getnext(vbl, oid, return_oid, value, len, type,
			     interval, ts)
    const VarBindList	vbl;
    const OID		oid;
    OID			*return_oid;
    caddr_t		value;
    unsigned int	*len;
    unsigned int	*type;
    unsigned int	*interval;
    unsigned int	*ts;
{
    VarBindList		elem, lowest_elem;
    int			s;
    char		*cp;
    OID			lowest_oid;
    OIDentifier		oid_tmp;
    unsigned int	oide_tmp[1];

    if (!vbl)
	return NULL;

    oide_tmp[0] = 65535;
    oid_tmp.oid_nelem = 1;
    oid_tmp.oid_elements = oide_tmp;
    lowest_oid = &oid_tmp;

    if (oid) {
	for (elem = vbl; elem; elem = elem->next)
	    if (oid_cmp_tree(elem->VarBind->name, oid) > 0)
		if (oid_cmp_tree(elem->VarBind->name, lowest_oid) < 0) {
		    lowest_oid = elem->VarBind->name;
		    lowest_elem = elem;
		}
    }
    else {
	for (elem = vbl; elem; elem = elem->next)
	    if (oid_cmp_tree(elem->VarBind->name, lowest_oid) < 0) {
		lowest_oid = elem->VarBind->name;
		lowest_elem = elem;
	    }
    }

    if (lowest_oid == &oid_tmp)
	return NULL;
    
    if (return_oid)
	*return_oid = lowest_elem->VarBind->name;

    switch (lowest_elem->VarBind->value->pe_class) {
    case PE_CLASS_UNIV:
	switch (lowest_elem->VarBind->value->pe_id) {
	case PE_PRIM_INT:
	    if (value)
		*(int *)value = prim2num(lowest_elem->VarBind->value);
	    if (len)
		*len = sizeof(int);
	    if (type)
		*type = MIB_integer;
	    break;
	case PE_PRIM_OCTS:
	    if (value) {
		cp = (char *)prim2str(lowest_elem->VarBind->value, &s);
		bcopy(cp, value, s);
		free(cp);
	    }
	    if (len)
		*len = s;
	    if (type)
		*type = MIB_octetstring;
	    break;
	case PE_PRIM_OID:
	    if (len)
		*len = 0;
	    if (type)
		*type = MIB_oid;
	    break;
	case PE_PRIM_NULL:
	    if (len)
		*len = 0;
	    if (type)
		*type = MIB_null;
	    break;
	case PE_UNIV_EOC:
	    if (len)
		*len = 0;
	    if (type)
		*type = MIB_other;
	    break;
	default:;
	}
	break;
	
    case PE_CLASS_APPL:
	switch (lowest_elem->VarBind->value->pe_id) {
	case 0: /* IpAddress */
	    if (value)
		*(int *)value = ipaddr_pe2int(lowest_elem->VarBind->value);
	    if (len)
		*len = sizeof(int);
	    if (type)
		*type = MIB_ipaddr;
	    break;
	case 1: /* Counter */
	    if (value)
		*(int *)value = (int)prim2num(lowest_elem->VarBind->value);
	    if (len)
		*len = sizeof(int);
	    if (type)
		*type = MIB_counter;
	    break;
	case 2: /* Gauge */
	    if (value)
		*(int *)value = (int)prim2num(lowest_elem->VarBind->value);
	    if (len)
		*len = sizeof(int);
	    if (type)
		*type = MIB_gauge;
	    break;
	case 3: /* TimeTicks */
	    if (value)
		*(int *)value = (int)prim2num(lowest_elem->VarBind->value);
	    if (len)
		*len = sizeof(int);
	    if (type)
		*type = MIB_timeticks;
	    break;
	case 4: /* Opaque */
	    if (len)
		*len = 0;
	    if (type)
		*type = MIB_other;
	    break;
	default:;
	}
	break;
	
    default:;
    }
    
    if (interval)
	*interval = lowest_elem->VarBind->interval;
    if (ts)
	*ts = lowest_elem->VarBind->timestamp->parm;

    return lowest_elem->VarBind->value;
}

/*\
 *  Set the value of a variable in a VarBindList.  If the variable
 *  does not exist, add it to end of the VarBindList.  If the value
 *  is the same, don't update anything (including the timestamp).
 *
 *  Pass in an address of the value as a PE, char *, or int *.
 *  Pass in the length of the value, or 0 if the value is a PE.
 *  Pass in the type of the value, or 0 if the value is a PE.
 *  Pass in an address to set the interval field.
 *  Pass in an address to set the timestamp field.
 *
 *  On success: returns the PE of the value in the VarBindList.
 *		This will always be non-NULL on success.
 *  On failure: returns NULL.
\*/
const PE VarBindList_set(vbl, oid, value, len, type, interval, ts)
    VarBindList		*vbl;
    const OID		oid;
    const caddr_t	value;
    const unsigned int	len;
    const unsigned int	type;
    const unsigned int	*interval;
    const unsigned int	*ts;
{
    VarBindList		elem, prev;
    unsigned int	current_time;
    PE			pe;

    if (!oid) {
	hnms_errno = HNMS_err_null_funcall;
	return NULL;
    }

    for (elem = *vbl, prev = NULL; elem; prev = elem, elem = elem->next)
	if (!oid_cmp(elem->VarBind->name, oid))
	    break;
    if (!elem) {
	elem = (VarBindList)HNMS_alloc(1, sizeof *elem);
	elem->VarBind = HNMS_alloc(1, sizeof *elem->VarBind);
	elem->VarBind->name = oid_cpy(oid);
	elem->VarBind->timestamp =
	    HNMS_alloc(1, sizeof *elem->VarBind->timestamp);
	elem->next = NULL;
	if (*vbl)
	    prev->next = elem;
	else
	    *vbl = elem;
    }
    if (type == 0)
	pe = pe_cpy((PE)value);
    else
	switch (type) {
	case MIB_integer:
	case MIB_enum:
	case MIB_timeticks:
	case MIB_counter:
	case MIB_gauge:
	    pe = int2prim(*(int *)value);
	    break;
	case MIB_ipaddr:
	    pe = ipaddr_int2pe(*(int *)value);
	    break;
	case MIB_octetstring:
	case MIB_displaystring:
	    pe = oct2prim((char *)value, len);
	    break;
	case MIB_oid:
	case MIB_null:
	    break;
	default:;
	}

    /*
     * Don't update the VarBind if the new value is the same as the old.
     */
    if (elem->VarBind->value) {
	if (!pe_cmp(pe, elem->VarBind->value)) {
	    pe_free(pe);
	    return NULL;
	}
	else
	    pe_free(elem->VarBind->value);
    }
    elem->VarBind->value = pe;

    if (interval)
	elem->VarBind->interval = *interval;
    if (ts)
	elem->VarBind->timestamp->parm = *ts;

    return elem->VarBind->value;
}

/*\
 *  Delete a variable from a VarBindList.
 *  Return 0 if the operation completed successfully, -1 otherwise;
\*/
int VarBindList_unset(vbl, oid)
    VarBindList		*vbl;
    const OID		oid;
{
    VarBindList		elem, prev;

    if (!oid) {
	hnms_errno = HNMS_err_null_funcall;
	return -1;
    }

    for (elem = *vbl, prev = NULL; elem; prev = elem, elem = elem->next)
	if (!oid_cmp(elem->VarBind->name, oid)) {
	    prev->next = elem->next;
	    elem->next = NULL;
	    VarBindList_free(elem);
	}
    return 0;
}

/*\
 *  Call this function with a VarBindList to get the value of the first
 *  variable.  Successively calling it with NULL will iterate through
 *  the variables in that list.  Returns NULL when there are no more
 *  variables.  Other calling conventions are similar to VarBindList_get.
\*/
const PE VarBindList_walk(vbl, value, len, type, interval, ts)
    const VarBindList	vbl;
    caddr_t		value;
    unsigned int	*len;
    unsigned int	*type;
    unsigned int	*interval;
    unsigned int	*ts;
{
    static VarBindList	last_elem = NULL;
    PE			pe;

    if (!vbl) {
	if (!last_elem)
	    return NULL;
	last_elem = last_elem->next;
	if (!last_elem) {
	    return NULL;
	}
	pe = VarBindList_get(last_elem, vbl->VarBind->name, value, len,
			     interval, ts);
    }
    else {
	pe = VarBindList_get(vbl, vbl->VarBind->name, value, len,
			     interval, ts);
	last_elem = vbl;
	return pe;
    }	
}

/*\
 *  Merge one VarBindList into another.  The new VarBinds, if any,
 *  are created at the end of the second VarBindList.  Return 0 if
 *  the operation completed successfully, -1 otherwise.
\*/
int VarBindList_mergelists(vbl1, vbl2)
    const VarBindList		vbl1;
    VarBindList			*vbl2;
{
    register VarBindList	elem;

    if (!vbl1 || !vbl2) {
	hnms_errno = HNMS_err_null_funcall;
	return -1;
    }

    for (elem = vbl1; elem; elem = elem->next)
	VarBindList_set(vbl2, elem->VarBind->name, elem->VarBind->value,
			   0, 0, 0);
    return 0;
}

/*\
 *  Subtract one VarBindList from another.  The second list is the one
 *  that loses elements.  Return 0 if the operation completed
 *  successfully, -1 otherwise.
\*/
int VarBindList_subtractlists(vbl1, vbl2)
    const VarBindList		vbl1;
    VarBindList			*vbl2;
{
    register VarBindList	elem;

    if (!vbl1 || !vbl2) {
	hnms_errno = HNMS_err_null_funcall;
	return -1;
    }

    for (elem = vbl1; elem; elem = elem->next)
	VarBindList_unset(vbl2, elem->VarBind->name);

    return 0;
}

/*\
 *  Compare two VarBindLists (the OIDs and their intervals, but not values).
 *  The order of the variables is not important.
 *  Return 1 if there is any difference, otherwise return 0.
\*/
int VarBindList_cmplists(vbl1, vbl2)
    VarBindList			vbl1, vbl2;
{
    VarBindList			v1, v2;

    for (v1 = vbl1; v1; v1 = v1->next) {
	for (v2 = vbl2; v2; v2 = v2->next)
	    if (!oid_cmp(v1->VarBind->name, v2->VarBind->name))
		if (v1->VarBind->interval != v2->VarBind->interval)
		    return 1;
	if (!v2)
	    return 1;
    }
}

/*\
 *  Make a copy of a VarBindList.  Return the VarBindList if
 *  successful, NULL otherwise.
\*/
VarBindList VarBindList_copylist(vbl)
    const VarBindList		vbl;
{
    register VarBindList	newlist, elem, newelem, prevelem;
    register VarBind		var, newvar;

    for (prevelem = NULL, elem = vbl; elem;
	 prevelem = newelem, elem = elem->next) {
	var = elem->VarBind;
	newelem = (VarBindList)HNMS_alloc(1, sizeof *newelem);
	newvar = (VarBind)HNMS_alloc(1, sizeof *newvar);
	newvar->name = oid_cpy(var->name);
	newvar->value = pe_cpy(var->value);
	newvar->interval = var->interval;
	newvar->timestamp = HNMS_alloc(1, sizeof *newvar->timestamp);
	newvar->timestamp->parm = var->timestamp->parm;
	newelem->VarBind = newvar;
	newelem->next = NULL;
	if (prevelem)
	    prevelem->next = newelem;
	else
	    newlist = newelem;
    }
    if (!newlist)
	hnms_errno = HNMS_err_var_not_found;

    return newlist;
}

/*\
 *  Make a copy of a VarBindList, with the PEs in the copy set to a
 *  PE of value NULL.  The timestamp fields are set to 0.  However,
 *  the interval fields keep their original values.
 *  Return the VarBindList if successful, NULL otherwise.
\*/
VarBindList VarBindList_copylist_null(vbl)
    const VarBindList		vbl;
{
    register VarBindList	newlist, elem, newelem, prevelem;
    register VarBind		var, newvar;

    for (prevelem = NULL, elem = vbl; elem;
	 prevelem = newelem, elem = elem->next) {
	var = elem->VarBind;
	newelem = (VarBindList)HNMS_alloc(1, sizeof *newelem);
	newvar = (VarBind)HNMS_alloc(1, sizeof *newvar);
	newvar->name = oid_cpy(var->name);
	newvar->value = pe_create_null();
	newvar->interval = var->interval;
	newvar->timestamp = HNMS_alloc(1, sizeof *newvar->timestamp);
	newvar->timestamp->parm = 0;
	newelem->VarBind = newvar;
	newelem->next = NULL;
	if (prevelem)
	    prevelem->next = newelem;
	else
	    newlist = newelem;
    }
    if (!newlist)
	hnms_errno = HNMS_err_var_not_found;

    return newlist;
}

/*\
 *  Free a VarBindList.
\*/
void VarBindList_free(vbl)
    VarBindList		vbl;
{
    free_HNMP_VarBindList(vbl);
}

/*\
 *  Iterate a function over each variable in a VarBindList.  The
 *  function should take an OID and a PE as the first two parameters.
 *  If magic exists, it will be passed to the function as the third
 *  parameter.  The function will be expected to know what to do with it.
\*/
void VarBindList_iterate(vbl, func, magic)
    const VarBindList	vbl;
    int			(*func)();
    caddr_t		magic;
{
    VarBindList		v;

    for (v = vbl; v; v = v->next)
	if (magic)
	    (*func)(v->VarBind->name, v->VarBind->value, magic);
	else
	    (*func)(v->VarBind->name, v->VarBind->value);
}

/*\
 *  For debugging.  Print a VarBindList.
\*/
void VarBindList_print(vbl)
    const VarBindList	vbl;
{
    VarBindList		elem;
    char		*str;
    int			n;
    int			len;

    for (elem = vbl; elem; elem = elem->next) {
	/*
	 * Print the variable's name.
	 */
	str = MIB_oid2ode(elem->VarBind->name, NULL);
	if (str)
	    printf(str);
	else
	    printf("bad variable");

	/*
	 * Print the interval and ASN.1 type.
	 */
	printf(" [%d]", elem->VarBind->interval);
	if (elem->VarBind->value->pe_form != PE_FORM_PRIM) {
	    printf("not a primitive type\n");
	    continue;
	}
	switch (elem->VarBind->value->pe_class) {
	case PE_CLASS_UNIV:
	    switch (elem->VarBind->value->pe_id) {
	    case PE_PRIM_INT:
		printf(" integer 0x%x", prim2num(elem->VarBind->value));
		break;
	    case PE_PRIM_OCTS:
		str = prim2str(elem->VarBind->value, &len);
		if (len == strlen(str))
		    printf(" octet string %s (%d)", str, len);
		else {
		    printf(" octet string");
		    for (n = 0; n < len; n++)
			printf( " %02x", (unsigned char)str[n]);
		    printf(" (%d, hex)", len);
		}
		free(str);
		break;
	    case PE_PRIM_OID:
		printf(" oid");
		break;
	    case PE_PRIM_NULL:
		printf(" null");
		break;
	    case PE_UNIV_EOC:
		printf(" end of contents");
		break;
	    default:;
		printf(" unknown universal type %d",
			 elem->VarBind->value->pe_id);
	    }
	    break;

	case PE_CLASS_APPL:
	    switch (elem->VarBind->value->pe_id) {
	    case 0: /* IpAddress */
		printf(" ipaddress %s", ipaddr_pe2str(elem->VarBind->value));
		break;
	    case 1: /* Counter */
		printf(" counter %d", prim2num(elem->VarBind->value));
		break;
	    case 2: /* Gauge */
		printf(" gauge %d", prim2num(elem->VarBind->value));
		break;
	    case 3: /* TimeTicks */
		printf(" timeticks %d", prim2num(elem->VarBind->value));
		break;
	    case 4: /* Opaque */
		printf(" opaque");
		break;
	    default:
		printf(" unknown application-specific type %d",
			 elem->VarBind->value->pe_id);
	    }
	    break;

	default:
	    printf(" unknown class %d", elem->VarBind->value->pe_class);
	}

	/*
	 * Print the timestamp values.
	 */
	printf(" ts %s\n", time_int2str(elem->VarBind->timestamp->parm));
    }
}
