#include <stdio.h>
#include <setjmp.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <varargs.h>
#include <signal.h>
#include "snmp.h"
#define DEF_TABLE 0

static   struct TSAPaddr  snmp_ta;
static   char   *myname = "mstat";
static   int   ontty;
static   int   addronly = 0;
static   char   *community = "public";
int   debug = 0;
int   watch = 0;
static   char   *defs = "/usr/etc/mrouted.defs";
static   int   helpwidth;
static   int   sd;
static   PS ps;
static   SFP   istat;
static   int   armed;
static   jmp_buf  intrenv;
static   int   interrupted;
static   char *prefix = 0;
static   int   table = DEF_TABLE;
u_long   destaddr = 0;

struct dispatch {
    char   *ds_name;    /* command name */
    struct type_SNMP_VarBindList *(*ds_fnx)();    /* dispatch */
};
struct type_SNMP_VarBindList *f_get(), *f_get_next();
static struct dispatch dispatches[] = {
   "get", f_get, 
	"next", f_get_next, 
	NULL
};

static char *errors[] = {
		"noError", "tooBig", "noSuchName", "badValue", "readOnly", "genErr"
};

/* Enumerated types */
static char *ifStatus[] = { "up", "down", "disabled" };
static char *ifType[] = { "tunnel", "srcrt", "querier" };
static char *mrouteProtocol[] = { "other", "local", "netmgmt",
 "dvmrp", "mospf", "pim", "cbt" };
static char *pimMode[] = { "dense", "sparse" };
static char *hopType[] = { "leaf", "branch" };

static struct ivar {
		char   *iv_object;
		char  **iv_values;
		int	    iv_nvalue;
} ivars[] = {
		"dvmrpVInterfaceType", ifType, sizeof ifType / sizeof ifType[0],
		"dvmrpVInterfaceState", ifStatus, sizeof ifStatus / sizeof ifStatus[0],
		"dvmrpRouteNextHopType", hopType, sizeof hopType / sizeof hopType[0],
		"ipMRouteProtocol", mrouteProtocol, sizeof mrouteProtocol / sizeof mrouteProtocol[0],
		"pimGroupMode", pimMode, sizeof pimMode / sizeof pimMode[0],
		"pimInterfaceMode", pimMode, sizeof pimMode / sizeof pimMode[0],
		"pimNeighborMode", pimMode, sizeof pimMode / sizeof pimMode[0],
		NULL
};

struct row {
   char *var_name;
   int   width;
};

/* List of variables for each type of report */
char *header_vars[] = {
   "sysName", 0
};
char *cache_vars[] = { "ipMRouteInIfIndex", "ipMRouteUpTime", 
   "ipMRouteExpiryTime", "ipMRoutePkts", "ipMRouteOctets", 
   "ipMRouteDifferentInIfIndexes", "ipMRouteProtocol", 0
};
char *pim_if_vars[] = { 
   "pimInterfaceAddress", "pimInterfaceNetMask", "pimInterfaceMode", 
   "pimInterfaceDR", "ipMRouteInterfaceTtl", "pimInterfaceQueryInterval", 0
};
char *pim_neighbor_vars[] = { 
   "pimNeighborIfIndex", "pimNeighborUpTime", "pimNeighborExpiryTime", 
   "pimNeighborMode", 0
};
char *vif_vars1[] = { "dvmrpVInterfaceLocalAddress", 
   "dvmrpVInterfaceRemoteAddress", "dvmrpVInterfaceRemoteSubnetMask", 
   "dvmrpVInterfaceMetric", "ipMRouteInterfaceTtl", "dvmrpVInterfaceType", 
   "dvmrpVInterfaceState", 0
};
char *vif_vars2[] = { 
   "dvmrpVInterfaceRemoteAddress", "dvmrpVInterfaceRemoteSubnetMask", 
   "dvmrpVInterfaceInPkts", "dvmrpVInterfaceOutPkts",
   "dvmrpVInterfaceInOctets", "dvmrpVInterfaceOutOctets",
   "dvmrpVInterfaceRateLimit", "dvmrpVInterfaceType", "dvmrpVInterfaceState", 0
};
char *dvmrp_neighbor_vars[] = { 
   "dvmrpNeighborUpTime", "dvmrpNeighborExpiryTime", 
   "dvmrpNeighborGenerationId", "dvmrpNeighborVersion", 0
};
char *dvmrp_route_vars[] = { 
   "dvmrpRouteUpstreamNeighbor", "dvmrpRouteInVifIndex", 
   "dvmrpRouteMetric", "dvmrpRouteExpiryTime", 0
};
char *dvmrp_routenext_vars[] = { 
   "dvmrpRouteNextHopType", 0
};
char *pim_group_vars[] = { 
   "pimGroupMode", "pimGroupRPcount", "pimGroupRPreach",  0
};
char *boundary_vars[] = { 
   "dvmrpBoundaryVifIndex", 0
};

/* Specification of each type of table */
struct table {
   char   opt;
   char **vars;
   char *header;
   char *body;
} tables[] = {

   'C', cache_vars, "\
IP Multicast Route Table for $0\n\
Mcast-group     Origin-Subnet     InIf UpTime ExpTm  Pkts   Bytes RpfFl Proto\n\
", "$A(-12):-15 $S(-8,-4):-18 $0:2 $1:7 $2:5 $3:5 $4:7 $5:5 $6",

   'G', pim_group_vars, "\
PIM Group Table for $0\n\
Group-Address   Mode      RPs Tmr\n\
", "$A(-4):-15 $0:-9 $1:3 $2:3",

   'I', pim_if_vars, "\
PIM Interface Table for $0\n\
If Address            Mode      DRouter         Ttl Int\n\
", "$I(-1):2 $S(0,1):-18 $2:-9 $3:-15 $4:3 $5:3",

   'P', pim_neighbor_vars, "\
PIM Neighbor Table for $0\n\
Neighbor        If UpTime ExpTm Mode\n\
", "$A(-4):-15 $0:2 $1:6 $2:5 $3",

   'b', boundary_vars, "\
Boundary Table for $0\n\
Vif Boundary Address\n\
", "$0:3 $S(-8,-4):-18", 

   'd', dvmrp_neighbor_vars, "\
DVMRP Neighbor Table for $0\n\
Vif Neighbor        UpTime ExpTm     GenId Version\n\
", "$I(-5):2  $A(-4):-15 $0:6 $1:5 $2:9 $3",

   'i', vif_vars1, "\
DVMRP Virtual Interface Table for $0\n\
Vif Local-Address   Remote-Address     Met Thr Type       State\n\
", "$I(-1):2  $0:-15 $S(1,2):-18 $3:3 $4:3 $5:-10 $6",

   'r', dvmrp_route_vars, "\
DVMRP Route Table for $0\n\
Origin-Subnet      UpstreamNeighbor IVif Met Tmr\n\
", "$S(-8,-4):-18 $A(0):-15  $1:4 $2:3 $3:3",

   't', dvmrp_routenext_vars, "\
DVMRP Route Next Hop Table for $0\n\
Origin-Subnet      OVif State\n\
", "$S(-9,-5):-18 $I(-1):4 $0",

   'v', vif_vars2, "\
DVMRP Virtual Interface Statistics Table for $0\n\
Vif Remote-Address      InPkts OutPkts  InBytes OutBytes Rate Type       State\n\
", "$I(-1):2  $S(0,1):-18 $2:7 $3:7 $4:8 $5:8 $6:4 $7:-10 $8",
 
   NULL
};

void
usage()
{
   int i;

   fprintf(stderr, "Usage: %s [-", myname);
   for (i=0; tables[i].opt; i++)
      putc(tables[i].opt, stderr);
   fprintf(stderr, "nw] [-f filename] [-c community] [-p port] [-x prefix] [ host ]\n", myname);
   exit(1);
}

/*
 * Print an enumerated type
 */
static int  
enum_print (x, os)
   integer *x;
   OS os;
{
		int      i = *x;

		if (i <= 0 || i > os -> os_data2)
	  printf ("unknown(%d)", i);
		else
	  printf ("%s(%d)", os -> os_data1[i - 1], i);
}

static char *
get_ipaddr(x, os)
   struct sockaddr_in *x;
   OS os;
{
   static char buff[1024];
   return sprintf (buff, "%s", inet_ntoa (x -> sin_addr));
}

char   *
inet_name(addr)
   u_long  addr;
{
   struct hostent *e;
   struct sockaddr_in tmp;
static char buff[80];
static u_long prevaddr = 0;

   if (addr==prevaddr)
      return buff;
   prevaddr = addr;

   if (!addronly) {
      e = gethostbyaddr(&addr, sizeof(addr), AF_INET);
      if (e) {
         strcpy(buff, e->h_name);
         return buff;
      }
   }

   tmp.sin_addr.s_addr = addr;
   strcpy(buff, get_ipaddr(&tmp, NULL));
   return buff;
}

static void  _advise (ap)
va_list  ap;
{
		char    buffer[BUFSIZ];

		asprintf (buffer, ap);
		(void) fflush (stdout);
		fprintf (stderr, "%s: ", myname);
		(void) fputs (buffer, stderr);
		(void) fputc ('\n', stderr);
		(void) fflush (stderr);
}

void  advise (va_alist)
va_dcl
{
		va_list ap;
		va_start (ap);
		_advise (ap);
		va_end (ap);
}

void  adios (va_alist)
va_dcl
{
		va_list ap;
		va_start (ap);
		_advise (ap);
		va_end (ap);
		_exit (1);
}

static	moresyntax (check)
int	check;
{
		register struct ivar *iv;
		register OT	   ot;
		register OS	   os;

		for (iv = ivars; iv -> iv_object; iv++)
	  if (ot = text2obj (iv -> iv_object)) {
			char   *name;

			if ((os = ot -> ot_syntax) == NULL) {
		  advise (NULLCP, "no syntax defined for object \"%s\"",
			  iv -> iv_object);
		  continue;
			}
			name = os -> os_name;

			(void) add_syntax (iv -> iv_object, os -> os_encode,
						os -> os_decode, os -> os_free, os -> os_parse,
						enum_print);
			if ((os = text2syn (iv -> iv_object)) == NULL)
		  adios (NULLCP, "lost syntax for object \"%s\"",
					iv -> iv_object);
			ot -> ot_syntax = os;
			os -> os_name = name;
			os -> os_data1 = iv -> iv_values;
			os -> os_data2 = iv -> iv_nvalue;
	  }
	  else
			if (check)
		  advise (NULLCP, "no \"%s\" object", iv -> iv_object);
}

static	arginit (vec)
char    **vec;
{
		int	    w;
		register char  *ap,
			  *pp;
		register struct dispatch *ds;
		int	    port;
		struct sockaddr_in in_socket;
		register struct sockaddr_in *isock = &in_socket;
		register struct hostent *hp;
		register struct TSAPaddr *ta = &snmp_ta, *tz;
		register struct NSAPaddr *na = ta -> ta_addrs;

		if (myname = strrchr (*vec, '/'))
	  myname++;
		if (myname == NULL || *myname == NULL)
	  myname = *vec;

		isodetailor (myname, 1);

		bzero ((char *) ta, sizeof *ta);
		na -> na_stack = NA_TCP;
		na -> na_community = ts_comm_tcp_default;
		(void) strncpy (na -> na_domain, getlocalhost (),
				sizeof na -> na_domain - 1);
		na -> na_port = htons ((u_short) 161);
		na -> na_tset = NA_TSET_UDP;
		ta -> ta_naddr = 1;

		for (vec++; ap = *vec; vec++) {
	  if (*ap == '-') {
			while (*++ap) {
           for (w=0; tables[w].opt && tables[w].opt!= *ap; w++);
           if (tables[w].opt) {
              table = w;
              continue;
           }
         
		  switch (*ap) {
           case 'x':
					 if ((pp = *++vec) == NULL || *pp == '-')
					adios (NULLCP, "usage: %s -x prefix", myname);
               if (inet_addr(pp) == -1)
				      adios (NULLCP, "%s: unknown host", pp);
               prefix = pp;
			  break;

				case 'p':
			  if ((pp = *++vec) == NULL
						 || *pp == '-'
						 || (port = atoi (pp)) <= 0)
					adios (NULLCP, "usage: %s -p portno", myname);
			  if (na -> na_stack != NA_TCP)
					adios (NULLCP, "-p not allowed with %s",
					  taddr2str (ta));
			  na -> na_port = htons ((u_short) port);
			  break;

				case 'c':
					 if ((pp = *++vec) == NULL || *pp == '-')
					adios (NULLCP, "usage: %s -c community", myname);
			  community = pp;
			  break;

				case 'n':
					 addronly++;
			  break;

				case 'w':
					 watch++;
			  break;

				case 'f':
			  if ((pp = *++vec) == NULL || *pp == '-')
					adios (NULLCP, "usage: %s -f file", myname);
			  defs = pp;
			  break;
			  
				default:
			  usage();
		  }
        }
			continue;
	  }

     if (hp = gethostbystring (ap)) {
        if (na -> na_stack != NA_TCP)
           adios (NULLCP, "specify host at most once...");
        inaddr_copy (hp, isock);
        (void) strncpy (na -> na_domain, inet_ntoa (isock -> sin_addr),
           sizeof na -> na_domain - 1);
        destaddr = isock->sin_addr.s_addr;
	  } else if ((tz = str2taddr (ap)) && tz -> ta_naddr > 0) {
		  *ta = *tz;	/* struct copy */
		  if (na -> na_stack == NA_TCP) {
           if (na -> na_port == 0)
              na -> na_port = htons ((u_short) 161);
           na -> na_tset = NA_TSET_UDP;
		  }
     } else
		  adios (NULLCP, "%s: unknown host", ap);
      }

		helpwidth = 0;
		for (ds = dispatches; ds -> ds_name; ds++)
	  if ((w = strlen (ds -> ds_name)) > helpwidth)
			helpwidth = w;

		if (ta -> ta_naddr == 0)
	  adios (NULLCP, "usage: %s -a string", myname);
		switch (na -> na_stack) {
	  case NA_TCP:
			{
		  struct sockaddr_in lo_socket;
		  register struct sockaddr_in *lsock = &lo_socket;

		  bzero ((char *) lsock, sizeof *lsock);
		  if ((hp = gethostbystring (pp = getlocalhost ())) == NULL)
				adios (NULLCP, "%s: unknown host", pp);
		  lsock -> sin_family = hp -> h_addrtype;
		  inaddr_copy (hp, lsock);
        destaddr = lsock->sin_addr.s_addr;
		  /* If the request is being sent out on the loopback interface,
			  make sure it appears to have originated from the loopback
			  interface, rather than the interface used as the default
			  hostname.  If the interface used as the default hostname
			  is a serial link, responses to requests with this origin
			  may be undeliverable.  (EJP) */
#define	LOOPBACK	((127 << 24) + 1)
		  if (isock -> sin_addr.s_addr == LOOPBACK)
				lsock -> sin_addr.s_addr = LOOPBACK;
#undef	LOOPBACK
		  if ((sd = start_udp_client (lsock, 0, 0, 0)) == NOTOK)
				adios ("failed", "start_udp_client");

		  bzero ((char *) isock, sizeof *isock);
		  if ((hp = gethostbystring (na -> na_domain)) == NULL)
				adios (NULLCP, "%s: unknown host", na -> na_domain);
		  isock -> sin_family = hp -> h_addrtype;
		  isock -> sin_port = na -> na_port;
		  inaddr_copy (hp, isock);

		  if (join_udp_server (sd, isock) == NOTOK)
				adios ("failed", "join_udp_server");

		  if ((ps = ps_alloc (dg_open)) == NULLPS
					 || dg_setup (ps, sd, MAXDGRAM, read_udp_socket,
						 write_udp_socket, check_udp_socket)
					  == NOTOK) {
				if (ps == NULLPS)
			  adios (NULLCP, "ps_alloc: out of memory");
				else
			  adios (NULLCP, "dg_setup: %s",
						ps_error (ps -> ps_errno));
		  }
			}
			break;

	  default:
			adios (NULLCP, "unknown network type 0x%x", na -> na_stack);
			/* NOT REACHED */

		}

#ifdef	SYS5
		(void) srand ((unsigned int) time ((long *) 0));
#else
		(void) srandom ((int) time ((long *) 0));
#endif

		ps_len_strategy = PS_LEN_LONG;

		if (loadobjects (defs) == NOTOK)
	  adios (NULLCP, "loadobjects: %s", PY_pepy);
		if (defs && (ap = strrchr (defs, '/')))
	  ap++;
		else
	  ap = defs ? defs : "";
		moresyntax (strcmp (ap, "objects.defs") == 0);
}

static int  get_ava (v, ava, offset)
register struct type_SNMP_VarBind *v;
char   *ava;
int	offset;
{
		int	    result;
		caddr_t value;
		register char *cp;
		register OI	   oi;
		register OT	   ot;
		register OS	   os;
		OID	    oid;

		if (cp = strchr (ava, '=')) {
	  if (offset != type_SNMP_PDUs_set__request)
			advise (NULLCP, "value unnecessary for get operation");
	  *cp++ = NULL;
		}
		else
	  if (offset == type_SNMP_PDUs_set__request) {
			advise (NULLCP, "need variable=value for set operation");
			return NOTOK;
	  }

		if ((oi = text2inst (ava)) == NULL) {
	  if (cp || (oid = text2oid (ava)) == NULL) {
			advise (NULLCP, "unknown variable \"%s\"", ava);
			return NOTOK;
	  }

	  ot = NULLOT;
		}
		else
	  ot = oi -> oi_type;

		if ((v -> name = oid_cpy (oi ? oi -> oi_name : oid)) == NULLOID)
	  adios (NULLCP, "out of memory");

		if (cp == NULL) {
	  if ((v -> value = pe_alloc (PE_CLASS_UNIV, PE_FORM_PRIM, PE_PRIM_NULL))
				 == NULLPE)
	  adios (NULLCP, "out of memory");
		}
		else {
	  if ((os = ot -> ot_syntax) == NULL) {
			advise (NULLCP, "no syntax defined for object \"%s\"", ava);
			return NOTOK;
	  }

	  if ((*os -> os_parse) (&value, cp) == NOTOK) {
			advise (NULLCP, "invalid value for variable \"%s\": \"%s\"",
				ava, cp);
			return NOTOK;
	  }
	  result = (*os -> os_encode) (value, &v -> value);
	  (*os -> os_free) (value);

	  if (result == NOTOK) {
			advise (NULLCP, "encoding error for variable \"%s\"", ava);
			return NOTOK;
	  }	
		}

		if (oi == NULL)
	  oid_free (oid);

		return OK;
}

static struct type_SNMP_Message *new_message (offset, vec)
int	offset;
char  **vec;
{
		register struct type_SNMP_Message *msg;
		register struct type_SNMP_PDUs *pdu;
		register struct type_SNMP_PDU *parm;
		register struct type_SNMP_VarBindList **vp;

		if ((msg = (struct type_SNMP_Message *) calloc (1, sizeof *msg)) == NULL)
	  adios (NULLCP, "out of memory");

		msg -> version = int_SNMP_version_version__1;

		if ((msg -> community = str2qb (community, strlen (community), 1)) == NULL)
	  adios (NULLCP, "out of memory");

		if ((pdu = (struct type_SNMP_PDUs *) calloc (1, sizeof *pdu)) == NULL)
	  adios (NULLCP, "out of memory");
		msg -> data = pdu;

		pdu -> offset = offset;
		
/* for now, always a PDU... */

		if ((parm = (struct type_SNMP_PDU *) calloc (1, sizeof *parm)) == NULL)
	  adios (NULLCP, "out of memory");
		pdu -> un.get__request = parm;

#ifndef	SYS5
		parm -> request__id = ((int) random ()) & 0x7fffffff;
#else
		parm -> request__id = ((int) rand ()) & 0x7fffffff;
#endif

		vp = &parm -> variable__bindings;
		for (vec++; *vec; vec++) {
	  register struct type_SNMP_VarBindList *bind;
	  register struct type_SNMP_VarBind *v;

	  if ((bind = (struct type_SNMP_VarBindList *) calloc (1, sizeof *bind))
					== NULL)
			adios (NULLCP, "out of memory");
	  *vp = bind, vp = &bind -> next;

	  if ((v = (struct type_SNMP_VarBind *) calloc (1, sizeof *v)) == NULL)
			adios (NULLCP, "out of memory");
	  bind -> VarBind = v;

	  if (get_ava (v, *vec, offset) == NOTOK) {
			free_SNMP_Message (msg);
			return NULL;
	  }
		}
		
		return msg;
}

char   *snmp_error (i)
integer  i;
{
		static char buffer[BUFSIZ];

		if (0 < i && i < sizeof errors / sizeof errors[0])
	  return errors[i];
		(void) sprintf (buffer, "error %d", i);

		return buffer;
}

/* Get an IP address from an OI starting at element n */
int
get_address(oid, addr, n)
   OID oid;
   u_long *addr;
   int n;
{
   int i;
   int ok = 1;

   (*addr) = 0;

   if (oid -> oid_nelem < n+4)
      return 0;

   for (i=n; i<n+4; i++) {
      (*addr) <<= 8;
      if (i >= oid->oid_nelem)
          ok = 0;
      else
         (*addr) |= oid->oid_elements[i];
   }
   return ok;
}

char *
get_name(v)
	  struct type_SNMP_VarBind *v;
{
	  return oid2ode(v->name);
}

static char *
get_timesecs(x, os)
u_long *x;
OS os;
{
   static char buff[1024];
   return sprintf(buff, "%U", *x / 100);
}

static char *
get_timeticks(x, os)
u_long *x;
OS os;
{
    u_long  d,
	    h,
	    m,
	    s,
	    ds;
   static char buff[1024];
   char buff2[80];

    ds = *x;
    s = ds / 100, ds = ds % 100;
    m = s / 60, s = s % 60;
    h = m / 60, m = m % 60;
    d = h / 24, h = h % 24;

    buff[0] = '\0';
    if (d > 0) 
	     strcat(buff, sprintf(buff2, "%d days, ", d));
    if (d > 0 || h > 0)
        strcat(buff, sprintf(buff2, "%d hours, ", h));
    if (d > 0 || h > 0 || m > 0)
        strcat(buff, sprintf(buff2, "%d minutes, ", m));
    strcat(buff, sprintf(buff2, "%d", s));
    if (ds > 0)
       strcat(buff, sprintf(buff2, ".%02d", ds));
    strcat(buff, sprintf(buff2, " seconds (%U timeticks)", *x));
    return buff;
}

static char *
get_counter(x, os)
integer *x;
OS os;
{
   static char buff[1024];
   return sprintf(buff, "%U", *x);
}

static char *
get_integer(x, os)
integer *x;
OS os;
{
   static char buff[1024];
   return sprintf (buff, "%d", *x);
}

static char *
get_enum(x, os)
integer *x;
OS os;
{
   static char buff[1024];
      int      i = *x;

      if (i <= 0 || i > os -> os_data2)
     return sprintf (buff, "unknown(%d)", i);
      else
     return sprintf (buff, "%s(%d)", os -> os_data1[i - 1], i);
}

/*
 * For DisplayString objects, return the string to display
 */
static char *
get_string(x, os)
   struct qbuf *x;
   OS os;
{
   register struct qbuf *qb;
   static char buff[1024];
          char buff2[255];

   buff[0] = '\0';
   for (qb = x -> qb_forw; qb != x; qb = qb -> qb_forw) {
      sprintf(buff2, "%*.*s", qb -> qb_len, qb -> qb_len, qb -> qb_data);
      strcat(buff, buff2);
   }
   return buff;
}

/* 
 * Return the value of an object in a string
 */
caddr_t
get_value(v)
	  struct type_SNMP_VarBind *v;
{
	  register OI	oi;
	  register OS	os;
	  caddr_t	 value;

	  if ((oi = name2inst (v -> name)) == NULL) {
			advise (NULLCP, "unknown variable \"%s\"", oid2ode (v -> name));
			vunknown (v -> value);
	  }
	  
	  if ((os = oi -> oi_type -> ot_syntax) == NULL) {
			advise(NULLCP, "unknown syntax for object \"%s\"", 
          oi->oi_type->ot_text);
			vunknown (v -> value);
	  }
	  
	  if ((*os -> os_decode) (&value, v -> value) == NOTOK) {
			advise (NULLCP, "decode error for variable \"%s\"",
				oid2ode (v -> name));
			vunknown (v -> value);
	  }

     if (os->os_print == enum_print)
        return get_enum(value, os);
     if (!strcmp(os->os_name, "DisplayString"))
        return get_string(value, os);
     if (!strcmp(os->os_name, "INTEGER"))
        return get_integer(value, os);
     if (!strcmp(os->os_name, "IpAddress"))
        return get_ipaddr(value, os);
     if (!strcmp(os->os_name, "Counter"))
        return get_counter(value, os);
     if (!strcmp(os->os_name, "TimeTicks"))
        return get_timesecs(value, os); /* return as seconds */
/*
     if (!strcmp(os->os_name, "TimeTicks"))
        return get_timeticks(value, os);
*/
     return "<unknown type>";
}

struct type_SNMP_VarBindList *
process (msg)
struct type_SNMP_Message *msg;
{
		int	    request_id;
		PE	    pe;
		register struct type_SNMP_PDU *parm;
		register struct type_SNMP_VarBindList *vp;
		register struct type_SNMP_VarBind *v;

		if (msg == NULL)
	  return OK;

		request_id = msg -> data -> un.get__request -> request__id;
		if (encode_SNMP_Message (&pe, 1, 0, NULLCP, msg) != NOTOK) {
	  if (watch)
			(void) print_SNMP_Message (pe, 1, NULLIP, NULLVP, NULLCP);

	  if (pe2ps (ps, pe) == NOTOK) {
			advise (NULLCP, "pe2ps: %s", ps_error (ps -> ps_errno));
			goto out;
	  }
		}
		else
	  advise (NULLCP, "encode_SNMP_Message: %s", PY_pepy);

try_again: ;
		if (pe)
	  pe_free (pe);
		pe = NULLPE;
		
		if ((pe = ps2pe (ps)) == NULLPE) {
	  advise (NULLCP, "ps2pe: %s", ps_error (ps -> ps_errno));
	  goto out;
		}

		if (decode_SNMP_Message (pe, 1, NULLIP, NULLVP, &msg) == NOTOK) {
	  advise (NULLCP, "decode_SNMP_Message: %s", PY_pepy);
	  goto out;
		}

		if (watch)
	  (void) print_SNMP_Message (pe, 1, NULLIP, NULLVP, NULLCP);

		if (msg -> data -> offset != type_SNMP_PDUs_get__response) {
	  advise (NULLCP, "unexpected message type %d",
		  msg -> data -> offset);
	  goto out;
		}

		if ((parm = msg -> data -> un.get__response) -> request__id
			!= request_id) {
	  fprintf (stderr, "request-id mismatch (got %d, wanted %d)\n",
			parm -> request__id, request_id);
	  goto try_again;
		}
	  
		if (parm -> error__status != int_SNMP_error__status_noError) {
	  fprintf (stderr, "%s at position %d\n",
		  snmp_error (parm -> error__status), parm -> error__index);
	  goto out;
		}

	  vp = parm->variable__bindings;

	out: ;
		if (pe)
	  pe_free (pe);

		return vp;
}

struct type_SNMP_VarBindList *
f_get (vec)
char  **vec;
{
   static struct type_SNMP_Message *msg = NULL;

	if (msg)
	  free_SNMP_Message (msg);
	msg = new_message (type_SNMP_PDUs_get__request, vec);
	return process (msg);
}

struct type_SNMP_VarBindList *
f_get_next (vec)
char  **vec;
{
   static struct type_SNMP_Message *msg = NULL;

	if (msg)
	  free_SNMP_Message (msg);
   msg = new_message(type_SNMP_PDUs_get__next__request, vec);
   return process(msg);
}

static struct dispatch *getds (name)
char   *name;
{
    register int    longest,
                    nmatches;
    register char  *p,
                   *q;
    char    buffer[BUFSIZ];
    register struct dispatch   *ds,
                               *fs;

    longest = nmatches = 0;
    for (ds = dispatches; p = ds -> ds_name; ds++) {
	for (q = name; *q == *p++; q++)
	    if (*q == NULL)
		return ds;

	if (*q == NULL)
	    if (q - name > longest) {
		longest = q - name;
		nmatches = 1;
		fs = ds;
	    }
	    else
		if (q - name == longest)
		    nmatches++;
    }

    switch (nmatches) {
	case 0: 
	    advise (NULLCP, "unknown operation \"%s\"", name);
	    return NULL;

	case 1: 
	    return fs;

	default: 
	    for (ds = dispatches, p = buffer; q = ds -> ds_name; ds++)
		if (strncmp (q, name, longest) == 0) {
		    (void) sprintf (p, "%s \"%s\"", p != buffer ? "," : "", q);
		    p += strlen (p);
		}
	    advise (NULLCP, "ambiguous operation, it could be one of:%s",
			buffer);
	    return NULL;
    }
}

struct type_SNMP_VarBindList *
snmploop (vec, error)
char  **vec;
int	error;
{
    register struct dispatch *ds;

    if ((ds = getds (strcmp (*vec, "?") ? *vec : "help")) == NULL)
	    return 0;;
    return ds->ds_fnx(vec);
}

static   SFD intrser (sig)
int   sig;
{
#ifndef  BSDSIGS
    (void) signal (SIGINT, intrser);
#endif

    if (armed)
   longjmp (intrenv, NOTOK);

    interrupted++;
}

struct type_SNMP_VarBindList *
command(buffer)
   char *buffer;
{
   int status, vecp;
   char *vec[NVEC + 1];

   bzero((char *)vec, sizeof(vec));
   if ((vecp = str2vec (buffer, vec)) < 1)
       return 0; /* continue; */

   return snmploop(vec, OK);
}

/* 
 * Given a body specification for a table, expand all the macros in it
 * and display the result
 */
void
expand(body, vparr, oid)
   char *body;
   struct type_SNMP_VarBindList *vparr[10];
   OID oid;
{
   char *p = body;
   int width, n, var, var2, addr, mask;
   char str[80];
   struct sockaddr_in tmp;
   char buff[255];

   while (p && *p) {
      if (*p == '$') {
         p++;
         if (isdigit(*p)) {
            var = *p - '0';
            strcpy(str, get_value(vparr[var]->VarBind));
            p++;
         } else if (*p=='S') {
            var = atoi(p+2);
            p = strchr(p, ',')+1;
            var2 = atoi(p);
            p = strchr(p, ')')+1;

            if (var>=0)
               addr = inet_addr(get_value(vparr[var ]->VarBind));
            else
               get_address(oid, &addr,  oid->oid_nelem+var);

            if (var2>=0)
               mask = inet_addr(get_value(vparr[var2]->VarBind));
            else
               get_address(oid, &mask,  oid->oid_nelem+var2);

            if (mask)
               inet_fmts(addr, mask, str);
            else 
               strcpy(str, inet_name(addr));
         } else if (*p=='I') { /* retrieve int from oid location (n) */
            n = atoi(p+2);
            p = strchr(p, ')')+1;
            sprintf(str, "%d", oid->oid_elements[ oid->oid_nelem+n ]);
         } else if (*p=='A') { /* retrieve ipaddr from oid loc (n) */
            var = atoi(p+2);
            p = strchr(p, ')')+1;

            if (var>=0)
               addr = inet_addr(get_value(vparr[var ]->VarBind));
            else
               get_address(oid, &addr,  oid->oid_nelem+var);

            strcpy(str, inet_name(addr));
         } else if (*p=='H') { /* destination host */
            strcpy(str, inet_name(destaddr));
            p++;
         }

         width = 0;
         if (*p == ':') {
            p++;
            width = atoi(p);
            p += strspn(p, "-0123456789");
         }
         sprintf(buff, "%*s", width, str);
         if (width)
            buff[abs(width)]='\0';
         printf("%s", buff);
      } else 
         putchar(*p++);
   }
}

main(argc, argv)
   int    argc;
   char **argv;
{
    int grp, src, mask, out=0;
    int	    eof, status, vecp, i;
    char    buffer[BUFSIZ], *vec[NVEC + 1];
    char    initvar[10][256], var[10][256];
    struct type_SNMP_VarBindList *vp, *vpc, *vparr[10];
    struct type_SNMP_VarBind *v;
    struct sockaddr_in tmp;

    arginit (argv);

    status = 0;
    istat = (SFP)signal (SIGINT, intrser);
    
    /* Request variables that can go in header */
    strcpy(buffer, "next");
    for (i=0; header_vars[i]; i++) {
       strcat(buffer, " ");
       strcat(buffer, header_vars[i]);
    }
    vp = command(buffer);
    for (i=0, vpc=vp; vpc; i++, vpc=vpc->next) { 
       vparr[i] = vpc;
       strcpy(var[i], get_name(vpc->VarBind));
    }

    /* Display table header */
    expand(tables[table].header, vparr, vp->VarBind->name);

    /* Initialize column variables */
    for(i=0; tables[table].vars[i]; i++)
       strcpy(initvar[i], tables[table].vars[i]);
    if (prefix) {
       for (i=0; tables[table].vars[i]; i++) {
          strcat(initvar[i], ".");
          strcat(initvar[i], prefix);
       }
    }
    for (i=0; tables[table].vars[i]; i++)
       strcpy(var[i], initvar[i]);

    /* Get and dump the table */
    for (interrupted = 0; !interrupted; ) {
       strcpy(buffer, "next");
       for (i=0; tables[table].vars[i]; i++) {
          strcat(buffer, " ");
          strcat(buffer, var[i]);
       }
       vp = command(buffer);
       if (!vp || interrupted) break;

       if (strncmp(get_name(vp->VarBind), initvar[0], strlen(initvar[0])))
           break;

       for (i=0, vpc=vp; vpc; i++, vpc=vpc->next) {
          vparr[i] = vpc;
          strcpy(var[i], get_name(vpc->VarBind));
       }

       expand(tables[table].body, vparr, vp->VarBind->name);
       printf("\n");
       out++;
    }
    if (!out) {
       strcpy(buffer, "get");
       for (i=0; tables[table].vars[i]; i++) {
          strcat(buffer, " ");
          strcat(buffer, initvar[i]);
       }
       vp = command(buffer);
       if (vp && !interrupted 
        && !strncmp(get_name(vp->VarBind), initvar[0], strlen(initvar[0]))) {
          for (i=0, vpc=vp; vpc; i++, vpc=vpc->next)
             vparr[i] = vpc;
          expand(tables[table].body, vparr, vp->VarBind->name);
          printf("\n");
       }
    }
    (void) signal (SIGINT, istat);

    exit (status);		/* NOTREACHED */
}
