/*\
 *	DISTRIBUTION: HNMS v2.0
 *	FILE: hnmslib/mib.c
 *
 *	Code to maintain dynamic OID/ODE entries.
 *
 *	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 <stdio.h>
#include <string.h>
#include <sys/param.h>

#include "stdhnms.h"

#define MIBTABLEFILE	"mibtable"
#define MAX_OID_ELEM	80

/*\
 *  Must be a power of two.
\*/
#define MIB_CHUNKSIZE	128

MIBENTRY			mib = NULL;
int				num_mib_entries = 0;

/*\
 *  Add a MIB variable to the internal list of oids.
 *  For speed, doesn't check if the variable exists already.
\*/
MIBENTRY MIB_add(oid, ode, type_str)
    const OID		oid;
    const char		*ode;
    const char		*type_str;
{
    MIBENTRY		entry;
    int			type;

    entry = HNMS_alloc(1, sizeof *entry);
    entry->oid = oid_cpy(oid);
    strcpy(entry->ode, ode);
    if (!strcmp(type_str, "INTEGER"))
	type = MIB_integer;
    else if (!strcmp(type_str, "Enum"))
	type = MIB_integer;
    else if (!strcmp(type_str, "TimeTicks"))
	type = MIB_timeticks;
    else if (!strcmp(type_str, "Counter"))
	type = MIB_counter;
    else if (!strcmp(type_str, "Gauge"))
	type = MIB_gauge;
    else if (!strcmp(type_str, "IpAddress"))
	type = MIB_ipaddr;
    else if (!strcmp(type_str, "OCTET"))
	type = MIB_octetstring;
    else if (!strcmp(type_str, "DisplayString"))
	type = MIB_displaystring;
    else if (!strcmp(type_str, "reg"))
	type = MIB_regpoint;
    else if (!strcmp(type_str, "SEQUENCE"))
	type = MIB_sequence;
    else
	type = MIB_other;
    entry->type = type;
    entry->next = mib;
    mib = entry;
    num_mib_entries++;
    return (entry);
}

/*\
 *  Delete a MIB variable from the internal list of oids.
\*/
void MIB_delete(me)
    MIBENTRY		me;
{
    MIBENTRY		entry;

    if (!me || !mib) return;

    if (me == mib) {
	mib = mib->next;
	free(me);
	num_mib_entries--;
	return;
	}
    for (entry = mib; entry->next; entry = entry->next)
	if (me == entry->next) {
		entry->next = me->next;
		num_mib_entries--;
		free(me);
		}
}

/*\
 *  Walk through the MIB table.
\*/
MIBENTRY MIB_walk(start)
    int		start;
{
    static MIBENTRY	entry=NULL;
    MIBENTRY		t;

    if (start) entry = mib;

    t = entry;

    if (!t) return(t);

    entry = entry->next;

    return(t);
}

/*\
 *  Read in a table of MIB variables from a file.  The file should be in
 *  the format produced by Synoptics' smic MIB compiler with the -N flag.
 *  Returns 0 if successful, -1 otherwise.
\*/
int MIB_read_file(filename)
    const char	*filename;
{
    char	mibpath[MAXPATHLEN];
    FILE	*infile;
    static char	fline[STRBUFLEN];
    static char	oid_str[STRBUFLEN], ode[STRBUFLEN], type_str[STRBUFLEN];
    OID		oid;
    char	*cp;
    int		rval;
    
    infile = fopen(filename, "r");
    if (!infile)
	return -1;
    
    for (;;) {
	bzero(fline, STRBUFLEN);
	cp = fgets(fline, STRBUFLEN - 1, infile);
	if (!cp)
	    break;
	if (cp[0] != '1')
	    continue;
	cp = strchr(fline, ':');
	if (!cp)
	    continue;
	bzero(oid_str, STRBUFLEN);
	bzero(ode, STRBUFLEN);
	bzero(type_str, STRBUFLEN);
	rval = sscanf(fline, "%s%s%s", oid_str, ode, type_str);
	if (rval != 3)
	    return -1;
	ode[strlen(ode) - 1] = 0;
	oid = fixed_str2oid(oid_str);
	if (!oid)
	    continue;
	MIB_add(oid, ode, type_str);
    }
    return 0;
}

/*\
 *  Given an OID, return the MIB entry of the longest OID that matches.
 *  Split off the instance identifier and return it as well.  The
 *  caller must pass in addresses for me to put 1) the address of the
 *  matching MIBENTRY and 2) the address of the instance identifier.
\*/
int MIB_getbyoid(oid, mibentry, inst, len)
    OID			oid;
    MIBENTRY		*mibentry;
    unsigned int	**inst;
    int			*len;
{
    MIBENTRY		entry, best_entry;
    int			best_nelem;

    best_nelem = 0;
    best_entry = 0;
    for (entry = mib; entry; entry = entry->next) {
	/*
	 * If the number of elements in the MIB entry is greater
	 * than the number of elements in the given OID, this can't
	 * be a match.
	 */
	if (entry->oid->oid_nelem > oid->oid_nelem)
	    continue;
	/*
	 * If the first n elements match, where n is the length of
	 * the MIB entry's OID, this may be a match.
	 */
	if (bcmp(entry->oid->oid_elements, oid->oid_elements,
		 entry->oid->oid_nelem * sizeof(int)))
	    continue;
	/*
	 * Take the longest MIB entry that matches.
	 */
	if (entry->oid->oid_nelem > best_nelem) {
	    best_entry = entry;
	    best_nelem = entry->oid->oid_nelem;
	}
    }
    
    if (!best_entry)
	return 0;
    
    *mibentry = best_entry;
    *inst = &(oid->oid_elements[best_entry->oid->oid_nelem]);
    *len = oid->oid_nelem - best_entry->oid->oid_nelem;
    return 1;
}

/*\
 *  Given an ode, return the MIB entry of the longest ode that matches.
 *  Split off the instance identifier and return it as well.  The
 *  caller must pass in addresses for me to put 1) the address of the
 *  matching MIBENTRY and 2) the address of the instance identifier.
\*/
int MIB_getbyode(ode, mibentry, inst, len)
    char		*ode;
    MIBENTRY		*mibentry;
    char		**inst;
    int			*len;
{
    MIBENTRY		entry, best_entry;
    int			entry_len, best_len;

    best_len = 0;
    best_entry = 0;
    for (entry = mib; entry; entry = entry->next) {
	/*
	 * If the given ode matches this MIB entry's ode up to the
	 * length of the MIB entry's ode, this may be a match.
	 */
	entry_len = strlen(entry->ode);
	if (strncmp(ode, entry->ode, entry_len))
	    continue;
	/*
	 * Take the longest MIB entry that matches.
	 */
	if (entry_len > best_len) {
	    best_entry = entry;
	    best_len = entry_len;
	}
    }

    if (!best_entry)
	return 0;

    *mibentry = best_entry;
    *inst = &(ode[best_len + 1]);
    *len = strlen(ode) - best_len - 1;
    return 1;
}

/*\
 *  Given an OID, return its type.
\*/
int MIB_get_type(oid)
    const OID	oid;
{
    MIBENTRY	entry = NULL;
    MIBENTRY	best_entry = NULL;
    int		best_nelem = 0;
    
    for (entry = mib; entry; entry = entry->next) {
	/*
	 * If the number of elements in the MIB entry is greater
	 * than the number of elements in the given OID, this can't
	 * be a match.
	 */
	if (entry->oid->oid_nelem > oid->oid_nelem)
	    continue;
	/*
	 * If the first n elements match, where n is the length of
	 * the MIB entry's OID, this may be a match.
	 */
	if (bcmp(entry->oid->oid_elements, oid->oid_elements,
		 entry->oid->oid_nelem * sizeof(int)))
	    continue;
	/*
	 * Take the longest MIB entry that matches.
	 */
	if (entry->oid->oid_nelem > best_nelem) {
	    best_entry = entry;
	    best_nelem = entry->oid->oid_nelem;
	}
    }

    if (!best_entry)
	return 0;
    return best_entry->type;
}

/*\
 *  Given an OID, return the appropriate object descriptor as a static
 *  NULL-terminated string.  If it cannot be found, return an ASCII
 *  version of the OID elements.  If type is non-NULL, the type of the
 *  variable is also returned.
\*/
char *MIB_oid2ode(oid, type)
    const OID		oid;
    int			*type;
{
    MIBENTRY		entry;
    unsigned int	*inst;
    int			len;
    char		*cp;
    int			i;
    static char		buf[STRBUFLEN], buf2[STRBUFLEN];

    bzero(buf, STRBUFLEN);
    if (!MIB_getbyoid(oid, &entry, &inst, &len)) {
	cp = sprintoid(oid);
	strcpy(buf, cp);
	free(cp);
	return buf;
    }

    if (type)
	*type = entry->type;

    strcpy(buf, entry->ode);

    for (i = 0; i < len; i++) {
	sprintf(buf2, ".%d", inst[i]);
	strcat(buf, buf2);
    }
    return buf;
}

/*\
 *  Given an object descriptor, return the appropriate OID as a static
 *  OID.  If it cannot be found, return NULL.  If type is non-NULL,
 *  the type of the variable is also returned.
\*/
OID MIB_ode2oid(ode, type)
    const char		*ode;
    int			*type;
{
    MIBENTRY		entry;
    char		*inst;
    int			len;
    char		*cp;
    OID			oid;
    static char		buf[STRBUFLEN];

    oid = fixed_str2oid(ode);
    if (oid)
	return oid;

    if (!MIB_getbyode(ode, &entry, &inst, &len))
	return NULL;

    if (type)
	*type = entry->type;

    cp = sprintoid(entry->oid);
    bzero(buf, STRBUFLEN);
    if (len > 0)
	sprintf(buf, "%s.%s", cp, inst);
    else
	strcpy(buf, cp);
    free(cp);

    oid = fixed_str2oid(buf);

    return oid;
}

/*\
 *  Print an OID.
\*/
void MIB_print_oid(oid)
    OID		oid;
{
    char	*cp;

    cp = sprintoid(oid);
    puts(cp);
    free(cp);
}

/*\
 *  For debugging.  Print all known MIB variables.
\*/
void MIB_print()
{
    MIBENTRY	entry;
    char	*cp;
    
    for (entry = mib; entry; entry = entry->next) {
	cp = sprintoid(entry->oid);
	printf("%s %s %d\n", cp, entry->ode, entry->type);
	free(cp);
    }
}
