/**********************************************************************
	Copyright 1988, 1989, 1991, 1992 by Carnegie Mellon University

                      All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, 
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in 
supporting documentation, and that the name of CMU not be
used in advertising or publicity pertaining to distribution of the
software without specific, written prior permission.  

CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
SOFTWARE.
******************************************************************/
#include <config.h>

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <netinet/in.h>
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif

#ifdef linux
#include <stdlib.h>
#endif

#ifdef STDC_HEADERS
#include <string.h>
#endif

#ifdef NEED_MEMORY_H
#include <memory.h>
#endif

#ifndef HAVE_MEMCPY
#define memcpy(d, s, n) bcopy ((s), (d), (n))
#define memmove(d, s, n) bcopy ((s), (d), (n))
#endif

#include "parse.h"
#include "mib.h"
#include "g_snmp.h"

#ifndef MIBTXTDIR
#define MIBTXTDIR "/etc"
#endif

/* fwd: */
static void         sprint_by_type        ();
static void         set_functions         ();
static int          lc_cmp                ();
static void         sprint_objid          ();
static struct tree  *get_symbol           ();
static char         *uptimeString             (guint32 timeticks,
					       char    *buf,
                                               int     buflen);
static void         sprint_asciistring        (char    *buf,
                                               int     buflen,
					       guchar  *cp, 
					       int     len);
/*
 * Local functions
 */
static char *
uptimeString(guint32 timeticks, char *buf, int buflen)
{
  int	seconds, minutes, hours, days;
  
  timeticks /= 100;
  days = timeticks / (60 * 60 * 24);
  timeticks %= (60 * 60 * 24);
  
  hours = timeticks / (60 * 60);
  timeticks %= (60 * 60);
  
  minutes = timeticks / 60;
  seconds = timeticks % 60;
  
  if (days == 0)
    {
      g_snprintf(buf, buflen, "%d:%02d:%02d", hours, minutes, seconds);
    } else if (days == 1) 
      {
	g_snprintf(buf, buflen, "%d day, %d:%02d:%02d", days, hours, 
		minutes, seconds);
      } 
  else 
    {
      g_snprintf(buf, buflen, "%d days, %d:%02d:%02d", days, hours, 
	       minutes, seconds);
    }
  return buf;
}

static void
sprint_hexstring(char *buf, int buflen, u_char *cp, int len)
{

  for(; len >= 16; len -= 16)
    {
      g_snprintf(buf, buflen, "%02X %02X %02X %02X %02X %02X %02X %02X ", 
	      cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
      buflen -= strlen(buf);
      buf += strlen(buf);
      cp += 8;
      g_snprintf(buf, buflen, "%02X %02X %02X %02X %02X %02X %02X %02X\n", 
	      cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]);
      buflen -= strlen(buf);
      buf += strlen(buf);
      cp += 8;
    }
  for(; len > 0; len--)
    {
      g_snprintf(buf, buflen, "%02X ", *cp++);
      buflen -= strlen(buf);
      buf += strlen(buf);
    }
  *buf = '\0';
}

static void
sprint_asciistring(char *buf, int buflen, u_char *cp, int len)
{
  int	x;
  
  for(x = 0; (x < len) && buflen; x++)
    {
      if (isprint(*cp))
	{
	  *buf++ = *cp++;
          buflen --;
	} 
      else 
	{
	  *buf++ = '.';
          buflen --;
	  cp++;
	}
    }
  *buf = '\0';
}

/*
  0
  < 4
  hex

  0 ""
  < 4 hex Hex: oo oo oo
  < 4     "fgh" Hex: oo oo oo
  > 4 hex Hex: oo oo oo oo oo oo oo oo
  > 4     "this is a test"

  */
static void
sprint_octet_string(char *buf, int buflen, SNMP_OBJECT *var, 
                    struct enum_list *enums)
{
  int hex, x;
  u_char *cp;

  if (var->type != SNMP_OCTETSTR)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be OCTET STRING): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  hex = 0;
  for(cp = var->syntax.c, x = 0; x < var->syntax_len; x++, cp++)
    {
      if (!(isprint(*cp) || isspace(*cp)))
        hex = 1;
    }
  if (var->syntax_len == 0)
    {
      if (buflen >=2)
	strcpy(buf, "\"\"");
      return;
    }
  if (!hex)
    {
      if (buflen)
        {
          *buf++ = '"';
          buflen --;
        }
      sprint_asciistring(buf, buflen, var->syntax.c, var->syntax_len);
      buflen -= strlen(buf);
      buf += strlen(buf);
      if (buflen)
        {
          *buf++ = '"';
          buflen --;
        }
      *buf = '\0';
    }
  if (hex || var->syntax_len <= 4)
    {
      sprint_hexstring(buf, buflen, var->syntax.c, var->syntax_len);
    }
}

static void
sprint_opaque(char *buf, int buflen, SNMP_OBJECT *var, 
                    struct enum_list *enums)
{

  if (var->type != SNMP_OPAQUE)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be Opaque): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  sprint_hexstring(buf, buflen, var->syntax.c, var->syntax_len);
}

static void
sprint_object_identifier(char *buf, int buflen, SNMP_OBJECT *var, 
                         struct enum_list *enums)
{
  if (var->type != SNMP_OBJECTID)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be OBJECT IDENTIFIER): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  sprint_objid(buf, buflen, (gulong *)var->syntax.ul, 
               var->syntax_len / sizeof(gulong));
}

static void
sprint_timeticks(char *buf, int buflen, SNMP_OBJECT *var, 
                 struct enum_list *enums)
{
  char timebuf[32];

  if (var->type != SNMP_TIMETICKS)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be Timeticks): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  g_snprintf(buf, buflen, "Timeticks: (%ld) %s", var->syntax.ul[0], 
	   uptimeString(var->syntax.ul[0], timebuf, sizeof(timebuf)));
}

static void
sprint_integer(char *buf, int buflen, SNMP_OBJECT *var, 
               struct enum_list *enums)
{
  char    *enum_string = NULL;

  if (var->type != SNMP_INTEGER)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be INTEGER): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  for (; enums; enums = enums->next)
    if (enums->value == var->syntax.l[0])
      {
        enum_string = enums->label;
        break;
      }
  if (enum_string == NULL)
    g_snprintf(buf, buflen, "%ld", var->syntax.l[0]);
  else
    g_snprintf(buf, buflen, "%s(%ld)", enum_string, var->syntax.l[0]);
}

static void
sprint_uinteger(char *buf, int buflen, SNMP_OBJECT *var, 
                struct enum_list *enums)
{
  char    *enum_string = NULL;

  if (var->type != SNMP_UINTEGER)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be UInteger32): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  for (; enums; enums = enums->next)
    if (enums->value == var->syntax.ul[0])
      {
        enum_string = enums->label;
        break;
      }
  if (enum_string == NULL)
    g_snprintf(buf, buflen, "%ld", var->syntax.ul[0]);
  else
    g_snprintf(buf, buflen, "%s(%ld)", enum_string, var->syntax.ul[0]);
}

static void
sprint_gauge(char *buf, int buflen, SNMP_OBJECT *var, 
             struct enum_list *enums)
{
  if (var->type != SNMP_GAUGE)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be Gauge): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  sprintf(buf, "Gauge: %lu", var->syntax.ul[0]);
}

static void
sprint_counter(char *buf, int buflen, SNMP_OBJECT *var, 
               struct enum_list *enums)
{
  if (var->type != SNMP_COUNTER)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be Counter): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  sprintf(buf, "%lu", var->syntax.ul[0]);
}

static void
sprint_networkaddress(char *buf, int buflen, SNMP_OBJECT *var, 
                      struct enum_list *enums)
{
  int x, len;
  u_char *cp;

  sprintf(buf, "Network Address: ");
  buf += strlen(buf);
  cp = var->syntax.c;    
  len = var->syntax_len;
  for(x = 0; x < len; x++)
    {
      sprintf(buf, "%02X", *cp++);
      buf += strlen(buf);
      if (x < (len - 1))
        *buf++ = ':';
    }
}

static void
sprint_ipaddress(char *buf, int buflen, SNMP_OBJECT *var, 
                 struct enum_list *enums)
{
  u_char *ip;

  if (var->type != SNMP_IPADDR)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be Ipaddress): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
    ip = var->syntax.c;
    sprintf(buf, "IpAddress: %d.%d.%d.%d",ip[0], ip[1], ip[2], ip[3]);
}

static void
sprint_null(char *buf, int buflen, SNMP_OBJECT *var, 
            struct enum_list *enums)
{
  if (var->type != SNMP_NULL)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be NULL): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  g_snprintf(buf, buflen, "NULL");
}

static void
sprint_bitstring(char *buf, int buflen, SNMP_OBJECT *var, 
                 struct enum_list *enums)
{
  int len, bit;
  u_char *cp;
  char *enum_string;

  if (var->type != SNMP_BITSTR)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be BIT STRING): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  sprintf(buf, "BIT_STRING: ");
  buf += strlen(buf);
  sprint_hexstring(buf, buflen, var->syntax.c, var->syntax_len);
  buf += strlen(buf);

  cp = var->syntax.c + 1;
  for(len = 0; len < var->syntax_len - 1; len++)
    {
      for(bit = 0; bit < 8; bit++)
        {
          if (*cp & (0x80 >> bit))
            {
	      enum_string = NULL;
	      for (; enums; enums = enums->next)
              if (enums->value == (len * 8) + bit)
                {
	          enum_string = enums->label;
                  break;
	        }
              if (enum_string == NULL)
	        sprintf(buf, "%d ", (len * 8) + bit);
              else
	        sprintf(buf, "%s(%d) ", enum_string, (len * 8) + bit);
	      buf += strlen(buf);
	    }
	}
      cp ++;	    
    }
}

static void
sprint_nsapaddress(char *buf, int buflen, SNMP_OBJECT *var, 
                   struct enum_list *enums)
{
  if (var->type != SNMP_NSAP)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be NsapAddress): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  sprintf(buf, "NsapAddress: ");
  buf += strlen(buf);
  sprint_hexstring(buf, buflen, var->syntax.c, var->syntax_len);
}

static void
sprint_counter64(char *buf, int buflen, SNMP_OBJECT *var, 
                 struct enum_list *enums)
{
  if (var->type != SNMP_COUNTER64)
    {
      g_snprintf(buf, buflen, "Wrong Type (should be Counter64): ");
      buflen -= strlen(buf);
      buf += strlen(buf);
      sprint_by_type(buf, buflen, var, (struct enum_list *)NULL);
      return;
    }
  sprintf(buf, "Counter64: ");
  buf += strlen(buf);
    
  sprint_hexstring(buf, buflen, &(var->syntax.uc[0]), 4);
  buf += strlen(buf);
  sprint_hexstring(buf, buflen, &(var->syntax.uc[4]), 4);
}


static void
sprint_unknowntype(char *buf, int buflen, SNMP_OBJECT *var, 
                   struct enum_list *enums)
{
  sprint_by_type(buf, buflen, var, NULL);
}

static void
sprint_badtype(char *buf, int buflen, SNMP_OBJECT *var, 
               struct enum_list *enums)
{
  g_snprintf(buf, buflen, "Variable has bad type");
}

static void
sprint_by_type(char *buf, int buflen, SNMP_OBJECT *var,
               struct enum_list *enums)
{
  switch (var->type)
    {
      case SNMP_INTEGER:
        sprint_integer(buf, buflen, var, enums);
        break;
      case SNMP_OCTETSTR:
        sprint_octet_string(buf, buflen, var, enums);
        break;
      case SNMP_OPAQUE:
        sprint_opaque(buf, buflen, var, enums);
        break;
      case SNMP_OBJECTID:
        sprint_object_identifier(buf, buflen, var, enums);
        break;
      case SNMP_TIMETICKS:
        sprint_timeticks(buf, buflen, var, enums);
        break;
      case SNMP_GAUGE:
        sprint_gauge(buf, buflen, var, enums);
        break;
      case SNMP_COUNTER:
        sprint_counter(buf, buflen, var, enums);
        break;
      case SNMP_IPADDR:
        sprint_ipaddress(buf, buflen, var, enums);
        break;
      case SNMP_NULL:
        sprint_null(buf, buflen, var, enums);
        break;
#if 0
      case SNMP_UINTEGER:
        sprint_uinteger(buf, buflen, var, enums);
        break;
#endif
      default:
        sprint_badtype(buf, buflen, var, enums);
        break;
    }
}

struct tree *get_symbol();

gulong RFC1213_MIB[] = { 1, 3, 6, 1, 2, 1 };
unsigned char RFC1213_MIB_text[] = ".iso.org.dod.internet.mgmt.mib-2";
unsigned char EXPERIMENTAL_MIB_text[] = ".iso.org.dod.internet.experimental";
unsigned char PRIVATE_MIB_text[] = ".iso.org.dod.internet.private";
unsigned char PARTY_MIB_text[] = ".iso.org.dod.internet.snmpParties";
unsigned char SECRETS_MIB_text[] = ".iso.org.dod.internet.snmpSecrets";
struct tree *Mib = 0;

static char Standard_Prefix[] = ".1.3.6.1.2.1.";
static char Prefix[256];
static int Suffix;

void
init_mib()
{
  char *file, *getenv(), *prefix;
  
  Mib = 0;
  file = getenv("MIBFILE");
  if (file)
    Mib = read_mib(file);
  if (!Mib)
    Mib = read_mib("mib.txt");
#ifdef MIBFILEPATH
  if (!Mib)
    {
      char tmp [1024];
      sprintf (tmp, "%s/mib.txt", MIBFILEPATH);
      Mib = read_mib(tmp);
    }
#endif
  if (!Mib)
    {
      char tmp [1024];
      sprintf (tmp, "%s/mib.txt", MIBTXTDIR);
      Mib = read_mib(tmp);
    }
  if (!Mib)
    {
      fprintf(stderr, "Couldn't find mib file\n");
      exit(2);
    }
  prefix = getenv("PREFIX");
  if (! prefix) 
    {
      prefix = Standard_Prefix;
    }
  
  /* save prefix: */
  snmp_new_prefix (prefix);
  
  if (getenv("SUFFIX"))
    Suffix = TRUE;
  else
    Suffix = FALSE;
  set_functions(Mib);
}


/* 
 * Phil Wood <cpw@lanl.gov>:
 *
 * [...] I made an addition to mib.c to accomodate some old perl snmp
 * code for a program called vulture that used a global pointer to the
 * prefix to change things.  
 */

char *
snmp_new_prefix (char *prefix) 
{
  char *lastchar;
  int  plen;
  
  if (prefix) 
    {
      lastchar = ".";   
      if (*prefix == '.') 
	{ 
	  prefix++; 
	}
      if ((plen = strlen (prefix))) 
	{
	  lastchar = prefix + plen - 1; 
	}
      strncpy (Prefix, prefix, sizeof (Prefix) - 2);
      Prefix [sizeof (Prefix) - 2] = 0;
      if (*lastchar != '.') 
	{
	  Prefix [plen++] = '.';
	  Prefix [plen] = 0;
	}
      return Prefix;
    }
  return NULL;
}

static void
set_functions(struct tree *subtree)
{
  for(; subtree; subtree = subtree->next_peer)
    {
      switch(subtree->type)
	{
	case TYPE_OBJID:
	  subtree->printer = sprint_object_identifier;
	  break;
	case TYPE_OCTETSTR:
	  subtree->printer = sprint_octet_string;
	  break;
	case TYPE_INTEGER:
	  subtree->printer = sprint_integer;
	  break;
	case TYPE_NETADDR:
	  subtree->printer = sprint_networkaddress;
	  break;
	case TYPE_IPADDR:
	  subtree->printer = sprint_ipaddress;
	  break;
	case TYPE_COUNTER:
	  subtree->printer = sprint_counter;
	  break;
	case TYPE_GAUGE:
	  subtree->printer = sprint_gauge;
	  break;
	case TYPE_TIMETICKS:
	  subtree->printer = sprint_timeticks;
	  break;
	case TYPE_OPAQUE:
	  subtree->printer = sprint_opaque;
	  break;
	case TYPE_NULL:
	  subtree->printer = sprint_null;
	  break;
	case TYPE_BITSTRING:
	  subtree->printer = sprint_bitstring;
	  break;
	case TYPE_NSAPADDRESS:
	  subtree->printer = sprint_nsapaddress;
	  break;
	case TYPE_COUNTER64:
	  subtree->printer = sprint_counter64;
	  break;
	case TYPE_UINTEGER:
	  subtree->printer = sprint_uinteger;
	  break;
	case TYPE_OTHER:
	default:
	  subtree->printer = sprint_unknowntype;
	  break;
	}
      set_functions(subtree->child_list);
    }
}

static void
sprint_objid(char *buf, int buflen, gulong *objid, int objidlen)
{
  char    tempbuf[2048], *cp;
  struct tree    *subtree = Mib;

  *tempbuf = '.';	/* this is a fully qualified name */
  get_symbol(objid, objidlen, subtree, tempbuf + 1);
  if (Suffix)
    {
      for(cp =tempbuf; *cp; cp++)
        ;
      while(cp >= tempbuf)
        {
          if (isalpha(*cp))
            break;
          cp--;
	}
      while(cp >= tempbuf)
        {
          if (*cp == '.')
            break;
          cp--;
	}
      cp++;
      if (cp < tempbuf)
        cp = tempbuf;

    } 
  else 
    {
      cp = tempbuf;
      if ((strlen(tempbuf) > strlen((char *)RFC1213_MIB_text))
        && !bcmp(tempbuf, (char *)RFC1213_MIB_text,
                 strlen((char *)RFC1213_MIB_text)))
        {
          cp += sizeof(RFC1213_MIB_text);
	}
      if ((strlen(tempbuf) > strlen((char *)EXPERIMENTAL_MIB_text))
        && !bcmp(tempbuf, (char *) EXPERIMENTAL_MIB_text,
                 strlen((char *)EXPERIMENTAL_MIB_text)))
        {
          cp += sizeof(EXPERIMENTAL_MIB_text);
	}
      if ((strlen(tempbuf) > strlen((char *)PRIVATE_MIB_text))
        && !bcmp(tempbuf, (char *) PRIVATE_MIB_text,
                 strlen((char *)PRIVATE_MIB_text)))
        {
          cp += sizeof(PRIVATE_MIB_text);
	}
      if ((strlen(tempbuf) > strlen((char *)PARTY_MIB_text))
        && !bcmp(tempbuf, (char *) PARTY_MIB_text,
                 strlen((char *)PARTY_MIB_text)))
        {
          cp += sizeof(PARTY_MIB_text);
	}
      if ((strlen(tempbuf) > strlen((char *)SECRETS_MIB_text))
        && !bcmp(tempbuf, (char *) SECRETS_MIB_text,
                 strlen((char *)SECRETS_MIB_text)))
        {
          cp += sizeof(SECRETS_MIB_text);
	}
    }
  strcpy(buf, cp);
}

void
print_objid(gulong *objid, int objidlen)
{
  char    buf[256];

  sprint_objid(buf, sizeof(buf), objid, objidlen);
  printf("%s\n", buf);
}


void
sprint_value(char *buf, int buflen, gulong *objid, int objidlen, 
             SNMP_OBJECT *variable)
{
  char    tempbuf[2048];
  struct tree    *subtree = Mib;

  if (variable->type == SNMP_NOSUCHOBJECT)
    g_snprintf(buf, buflen, "No Such Object available on this agent");
  else if (variable->type == SNMP_NOSUCHINSTANCE)
    g_snprintf(buf, buflen, "No Such Instance currently exists");
  else if (variable->type == SNMP_ENDOFMIBVIEW)
    g_snprintf(buf, buflen, "No more variables left in this MIB View");
  else 
    {
      subtree = get_symbol(objid, objidlen, subtree, tempbuf);
      if (subtree->printer)
        (*subtree->printer)(buf, buflen, variable, subtree->enums);
      else 
        {
          sprint_by_type(buf, buflen, variable, subtree->enums);
        }
    }
}

void
print_value(gulong *objid, int objidlen, SNMP_OBJECT *variable)
{
  char    tempbuf[2048];

  sprint_value(tempbuf, sizeof(tempbuf), objid, objidlen, variable);
  printf("%s\n", tempbuf);
}

static struct tree *
get_symbol(gulong *objid, int objidlen, struct tree *subtree, char *buf)
{
    struct tree    *return_tree = NULL;

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

    /* subtree not found */
    while(objidlen--){	/* output rest of name, uninterpreted */
	sprintf(buf, "%lu.", *objid++);
	while(*buf)
	    buf++;
    }
    *(buf - 1) = '\0'; /* remove trailing dot */
    return NULL;

found:
    if (objidlen > 1)
      {
	while(*buf)
	  buf++;
	*buf++ = '.';
	*buf = '\0';
	return_tree = get_symbol(objid + 1, objidlen - 1, subtree->child_list,
				 buf);
      } 
    if (return_tree != NULL)
      return return_tree;
    else
      return subtree;
}


static int
lc_cmp(char *s1, char *s2)
{
  char c1, c2;

  while(*s1 && *s2)
    {
      if (isupper(*s1))
        c1 = tolower(*s1);
      else
        c1 = *s1;
      if (isupper(*s2))
        c2 = tolower(*s2);
      else
        c2 = *s2;
      if (c1 != c2)
        return ((c1 - c2) > 0 ? 1 : -1);
      s1++;
      s2++;
    }

  if (*s1)
    return -1;
  if (*s2)
    return 1;
  return 0;
}

/*
 * Clone of get_symbol that doesn't take a buffer argument
 */
static struct tree *
get_tree(gulong *objid, int objidlen, struct tree *subtree)
{
  struct tree    *return_tree = NULL;

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

found:
  if (objidlen > 1)
    return_tree = get_tree(objid + 1, objidlen - 1, subtree->child_list);
  if (return_tree != NULL)
    return return_tree;
  else
    return subtree;
}


static struct tree *
find_node(char *name, struct tree *subtree)
{
  struct tree *tp, *ret;

  for(tp = subtree; tp; tp = tp->next_peer)
    {
      if (!strcasecmp(name, tp->label))
        return tp;
      ret = find_node(name, tp->child_list);
      if (ret)
        return ret;
    }
  return 0;
}

static int
parse_subtree(struct tree *subtree, char *input, gulong *output, int *out_len)
{
  char buf[128], *to = buf;
  guint32 subid = 0;
  struct tree *tp;

    /*
     * No empty strings.  Can happen if there is a trailing '.' or two '.'s
     * in a row, i.e. "..".
     */
  if ((*input == '\0') ||
     (*input == '.'))
    return (0);

  if (isdigit(*input)) 
    {
	/*
	 * Read the number, then try to find it in the subtree.
	 */
      while (isdigit(*input)) 
        {
          subid *= 10;
          subid += *input++ - '0';
	}
      for (tp = subtree; tp; tp = tp->next_peer) 
        {
          if (tp->subid == subid)
            goto found;
	}
      tp = NULL;
    }
  else 
    {
	/*
	 * Read the name into a buffer.
	 */
      while ((*input != '\0') &&
            (*input != '.'))
        {
          *to++ = *input++;
	}
      *to = '\0';

	/*
	 * Find the name in the subtree;
	 */
      for (tp = subtree; tp; tp = tp->next_peer) 
        {
          if (lc_cmp(tp->label, buf) == 0)  
            {
              subid = tp->subid;
              goto found;
	    }
	}

	/*
	 * If we didn't find the entry, punt...
	 */
      if (tp == NULL) 
        {
          fprintf(stderr, "sub-identifier not found: %s\n", buf);
          return (0);
	}
    }

found:
#if 0
  if(subid > (guint32))
    {
      fprintf(stderr, "sub-identifier too large: %s\n", buf);
      return (0);
    }
#endif

  if ((*out_len)-- <= 0)
    {
      fprintf(stderr, "object identifier too long\n");
      return (0);
    }
  *output++ = subid;

  if (*input != '.')
    return (1);
  if ((*out_len = parse_subtree(tp ? tp->child_list : NULL, 
                                ++input, output, out_len)) == 0)
    return (0);
  return (++*out_len);
}

int 
read_objid(char *input, gulong *output, int *out_len)
{
  struct tree *root = Mib;
  gulong *op = output;
  char buf[512];

  g_warning("Obsolete function read_objid called");
  memset (buf, '\0', sizeof (buf));

  if (*input == '.')
    input++;
  else 
    {
      strcpy(buf, Prefix);
      strcat(buf, input);
      input = buf;
    }

  if (root == NULL)
    {
      fprintf(stderr, "Mib not initialized.  Exiting.\n");
      exit(1);
    }
  if ((*out_len = parse_subtree(root, input, output, out_len)) == 0)
    return (0);
  *out_len += output - op;

  return (1);
}

struct tree *
get_mib_tree (gulong *objid, gint objidlen)
{
  /*  struct tree *root = Mib; */

  return (get_tree (objid, objidlen, Mib));
}

/* EOF */
