/*\
 *	DISTRIBUTION: HNMS v2.0
 *	FILE: hnmslib/aux.c
 *
 *	Miscellaneous auxiliary functions for HNMS.
 *
 *	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 <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <search.h>

#include "stdhnms.h"

/*\
 *  Create a presentation element of the NULL ASN.1 data type.
 *  (This is not the same as NULLPE, which is just (PE)0.)
 *  Return the element.
\*/
PE pe_create_null()
{
    return pe_alloc(PE_CLASS_UNIV, PE_FORM_PRIM, PE_PRIM_NULL);
}

/*\
 *  The ISODE prim2num() can't handle positive integers greater
 *  than 2^31 since they cannot be represented within a 4-byte int.
 *  This is a wrapper that deals with the problem by stuffing it
 *  into an unsigned int, giving us the range 0..2^32-1.  Do not use
 *  this function if you are expecting a negative value.
\*/
unsigned int fixed_prim2num(pe)
    PE			pe;
{
    if (pe->pe_len < 5)
	return (unsigned int)prim2num(pe);
    else
	return *(unsigned int *)(&(pe->pe_prim[1]));
}

/*\
 *  ISODE's str2oid() is #define'd to max out at 20 OID elements.
 *  We could have people recompile their ISODE to fix this, but
 *  let's just offer our own instead.  Because it uses sscanf()
 *  it's probably a little slower.
 *
 *  Given a character string of integers in dot-notation, return
 *  the OID that corresponds to that string.  The OID is static.
\*/
OID fixed_str2oid(cp)
    char			*cp;
{
    char			*c;
    int				n, i;
    static struct OIDentifier	oid;
    static unsigned int		elements[OID_ELEMENTS];

    for (c = cp, n = 0; c;) {
	switch (*c) {
	case '1':
	case '2':
	case '3':
	case '4':
	case '5':
	case '6':
	case '7':
	case '8':
	case '9':
	case '0':
	case '.':
	case 0:
	    break;
	default:
	    return NULL;
	}
	if (sscanf(c, "%d", &i) == 1)
	    elements[n++] = i;
	else
	    break;
	while ((*c != '.') && (*c != 0))
	    c++;
	if (*c == '.')
	    c++;
    }
    if (n) {
	oid.oid_nelem = n;
	oid.oid_elements = elements;
	return &oid;
    }
    else
	return NULL;
}

/*\
 *  Convert an int to a 4 octet qbuf.  Space for the qbuf is created
 *  by this function.
\*/
struct qbuf *int2qb(intval)
    const int		intval;
{
    struct qbuf		*qb;

    return str2qb((char *)&intval, sizeof(int), 0);
}

/*\
 *  Convert a 4 byte qbuf to an int, given a pointer to the qbuf.
\*/
int qb2int(qb)
    const struct qbuf	*qb;
{
    char		*cp;
    int			rval;

    cp = qb2str(qb);
    rval = *(int *)cp;
    free(cp);
    return rval;
}

/*\
 *  Convert an ipAddress PE (application class tag 0) to a NULL-terminated
 *  string representing the ip address in dot-notation.  The string
 *  resides in a static location.
\*/
char *ipaddr_pe2str(value)
    const PE		value;
{
    static char		buf[16];
    unsigned int	ip_address;
    int			len;
    char		*cp;

    cp = prim2str(value, &len);
    ip_address = *(int *)cp;
    sprintf(buf, "%u.%u.%u.%u", ip_address >> 24,
	    (ip_address >> 16) & 0x000000ff, (ip_address >> 8) & 0x000000ff,
	    ip_address & 0x000000ff);
    free(cp);
    return buf;
}

/*\
 *  Convert an int to an ipAddress PE (application class tag 0).
 *  Space for the PE is created by this function.
\*/
PE ipaddr_int2pe(intval)
    const int		intval;
{
    struct qbuf		*qb;
    PE			value;

    qb = int2qb(intval);
    value = qb2prim(qb, PE_CLASS_APPL, 0);
    return value;
}

/*\
 *  Convert an ipAddress PE (application class tag 0) to an int.
\*/
int ipaddr_pe2int(value)
    const PE		value;
{
    char		*cp;
    int			len;
    int			rval;

    cp = prim2str(value, &len);
    rval = *(int *)cp;
    free(cp);
    return rval;
}

/*\
 *  Given an IP address as an int, return the corresponding dot-notation
 *  string in a static char buffer.
\*/
char *ipaddr_int2str(int_value)
    const unsigned int	int_value;
{
    struct in_addr	addr;

    addr.s_addr = int_value;
    return (inet_ntoa(addr));
}

/*\
 *  Compare the first n sub-identifiers of two OIDs.  Return 0 if they
 *  are the same, 1 if they are different.
\*/
int oid_cmp_n(oid1, oid2, n)
    const OID		oid1, oid2;
    const int		n;
{
    int			i;

    /*
     * This routine can probably be optimized.
     */
    for (i = 0; i < n; i++)
        if (oid1->oid_elements[i] != oid2->oid_elements[i])
            return 1;

    return 0;
}

/*\
 *  Compare two OIDs.  Return 0 if they are the same, -1 if the first
 *  comes prior to the second in MIB order, 1 if the first comes after
 *  the second in MIB order.
\*/
int oid_cmp_tree(oid1, oid2)
    const OID		oid1, oid2;
{
    unsigned int	*s1, *s2;
    int			cmp_len, i;

    /*
     * This routine can probably be optimized.
     */
    cmp_len = MIN(oid1->oid_nelem, oid2->oid_nelem);

    s1 = oid1->oid_elements;
    s2 = oid2->oid_elements;

    for (i = 0; i < cmp_len; i++) {
        if (s1[i] < s2[i])
            return -1;
        else if (s1[i] > s2[i])
            return 1;
    }
    if (oid1->oid_nelem < oid2->oid_nelem)
	return -1;
    else
	return (oid1->oid_nelem > oid2->oid_nelem);
}

/*\
 *  Test if an OID is in the HNMS variable tree.
 *  Return 1 if it is, 0 otherwise.
\*/
int oid_is_hnms(oid)
    const OID		oid;
{
    if (!oid_cmp_n(oid, oid_hnmsmib, 6))
	return 1;
    else
	return 0;
}

/*\
 *  Return an integer containing the current time, expressed in seconds
 *  since Jan. 1, 1970, GMT.
\*/
unsigned int get_int_time()
{
    struct timeval	time;

    gettimeofday(&time, (struct timezone *)0);
    return (unsigned int)time.tv_sec;
}

/*\
 *  Convert an integer time value to a string hh:mm:ss, in local time.
 *  A pointer to a static buffer is returned.
\*/
char *time_int2str(int_time)
    const int	int_time;
{
    static char	buf[9];
    struct tm 	*time;

    bzero(buf, 9);
    if (int_time == 0)
	strcpy(buf, "__:__:__");
    else {
	time = localtime((time_t *)&(int_time));
	sprintf(buf, "%02d:%02d:%02d",
		time->tm_hour, time->tm_min, time->tm_sec);
    }
    return buf;
}


/*\
 *  Convert seconds to hours, minutes, and seconds.  No need to use MODs,
 *  since MULs are faster than DIVs.
\*/
void time_int2hms(orig_seconds, hours, minutes, seconds)
    const unsigned long		orig_seconds;
    long			*hours, *minutes, *seconds;
{
    unsigned int	tmp;

    *hours = orig_seconds / 3600;
    tmp = orig_seconds - *hours * 3600;
    *minutes = tmp / 60;
    *seconds = tmp - *minutes * 60;
}

/*\
 *  Copy a time string in the format hhmmss to a struct tm which is passed
 *  in.  ONLY write to the fields tm_hour, tm_min, and tm_sec.
\*/
void time_str2tm(str_time, tm_time)
    const char		*str_time;
    struct tm		*tm_time;
{
    sscanf(str_time, "%02d%02d%02d", &tm_time->tm_hour, &tm_time->tm_min,
	   &tm_time->tm_sec);
}

/*\
 *  Copy a date string in the format yymmdd to a struct tm which is passed
 *  in.  The date_str is interpreted as being in local daylight time.
 *  The resulting values are written to the fields tm_year, tm_mon, and
 *  tm_mday; all other fields are cleared.  Subtract 1 from the month to make
 *  it conform to the struct tm representation (0 to 11).
\*/
void date_str2tm(str_date, tm_date)
    const char		*str_date;
    struct tm		*tm_date;
{
    bzero(tm_date, sizeof(struct tm));
    sscanf(str_date, "%02d%02d%02d", &tm_date->tm_year, &tm_date->tm_mon,
	   &tm_date->tm_mday);
    tm_date->tm_mon--;
}
