/*
 * $Header: /san01/cvs/ashpool/csrc/continuity/mecha/Attic/utl.c,v 1.31 2004/05/13 14:11:25 aleigh Exp $ 
 */

/*
 * Copyright (c) 1994, 2004 Alex Leigh, All Rights Reserved
 * 
 * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF ALEX LEIGH The copyright
 * notice above does not evidence any actual or intended publication of such
 * source code.
 * 
 * Disclosure, copying, distribution, or transmission of of this code without
 * express permission is prohibited.
 */
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <pwd.h>
#include <uuid/uuid.h>
#include <sys/time.h>
#include <ctype.h>

#include "mecha.h"

struct utlSswatch {
    unsigned int sa_sec;
    unsigned int sa_usec;
    unsigned int so_sec;
    unsigned int so_usec;
    unsigned int e_sec;
    unsigned int e_usec;
    unsigned int e_msec;
};

/*
 * Stolen from Eric Lindvall 5stops.com
 */
int utlFfd_printf(int fd, const char *fmt, ...)
{
    va_list ap;
    char *buf;
    int size = 1024;
    int e;

    assert(fmt != NULL);

    buf = (char *) malloc(size);

    assert(buf != NULL);

    va_start(ap, fmt);

    while (1) {
        int nchars = vsnprintf(buf, size, fmt, ap);

        if (nchars > -1 && nchars < size)
            break;
        if (nchars > -1)
            size = nchars + 1;
        else
            size *= 2;
        buf = (char *) realloc(buf, size);
    }

    va_end(ap);

    if ((e = write(fd, buf, strlen(buf))) == -1)
        e = -errno;

    free(buf);

    return (e);
}

unsigned int utlFtime(void)
{
    struct timeval tv;

    gettimeofday(&tv, NULL);

    return tv.tv_sec;
}

/*
 * under solaris 2.7, localtime is not MT-safe. localtime_r instead.
 * 
 * this is very crude, and not necessarily the kind of thing that we really want
 * to endorse, but it seems effective enough at 560/sec rates.
 */
volatile const struct tm *utlFlocaltime(void)
{
    static struct tm cache_tm;
    static unsigned int cache_time;
    static unsigned int processing = 0;
    unsigned int now;

    now = utlFtime();

    if (now != cache_time && processing == 0) {
        processing = 1;
        cache_time = now;
        localtime_r((time_t *) & now, &cache_tm);
        processing = 0;
    }
    return &cache_tm;
}

static const char utlGmonths[48] =
    "Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ";
static const char utlGdays[] = "Sun,Mon,Tue,Wed,Thu,Fri,Sat,";

/*
 * Returns the time in common log format. This returned string is always 29
 * characters long. This looks kind of dubious, but it seems to work quite
 * well.
 */
volatile const char *utlFcommonlog_time(void)
{
    volatile const struct tm *t = utlFlocaltime();
    char *p;
    unsigned int a;
    static char buf[30];
    int time_offset = 0;

    assert(t != NULL);

    p = buf + 29;
    *p-- = '\0';
    *p-- = ' ';
    *p-- = ']';
    a = abs(time_offset / 60);
    *p-- = '0' + a % 10;
    a /= 10;
    *p-- = '0' + a % 6;
    a /= 6;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p-- = (time_offset > 0) ? '-' : '+';
    *p-- = ' ';

    a = t->tm_sec;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p-- = ':';
    a = t->tm_min;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p-- = ':';
    a = t->tm_hour;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p-- = ':';
    a = 1900 + t->tm_year;
    while (a) {
        *p-- = '0' + a % 10;
        a /= 10;
    }
    /* p points to an unused spot */
    *p-- = '/';
    p -= 2;
    memcpy(p--, utlGmonths + 4 * (t->tm_mon), 3);
    *p-- = '/';
    a = t->tm_mday;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p = '[';
    return p;                   /* should be same as returning buf */
}

volatile const char *utlFlocal_time(void)
{
    volatile const struct tm *t;
    char *p;
    unsigned int a;
    static char buf[30];

    t = utlFlocaltime();

    assert(t != NULL);

    p = buf + 29;
    *p-- = '\0';
    *p-- = ']';

    a = t->tm_sec;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p-- = ':';
    a = t->tm_min;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p-- = ':';
    a = t->tm_hour;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p-- = ':';
    a = 1900 + t->tm_year;
    while (a) {
        *p-- = '0' + a % 10;
        a /= 10;
    }
    /* p points to an unused spot */
    *p-- = '/';
    p -= 2;
    memcpy(p--, utlGmonths + 4 * (t->tm_mon), 3);
    *p-- = '/';
    a = t->tm_mday;
    *p-- = '0' + a % 10;
    *p-- = '0' + a / 10;
    *p = '[';
    return p;                   /* should be same as returning buf */
}

utlTswatch *utlFstart_swatch(void)
{
    struct timeval tv;
    utlTswatch *sw;

    gettimeofday(&tv, NULL);

    sw = malloc(sizeof(utlTswatch));
    memFclear(sw, sizeof(utlTswatch));

    sw->sa_sec = tv.tv_sec;
    sw->sa_usec = tv.tv_usec;

    return sw;
}

unsigned int utlFstop_swatch(utlTswatch * sw)
{
    struct timeval tv;
    unsigned int x;

    assert(sw != NULL);

    gettimeofday(&tv, NULL);

    sw->so_sec = tv.tv_sec;
    sw->so_usec = tv.tv_usec;

    sw->e_sec = sw->so_sec - sw->sa_sec;
    sw->e_usec = sw->so_usec - sw->sa_usec;

    x = (sw->e_sec * 1000) * 1000 + (sw->e_usec);

    free(sw);

    return x;
}

int utlFtv_sub(struct timeval *r, const struct timeval *x,
               const struct timeval *y)
{
    struct timeval working;

    assert(r != NULL);
    assert(x != NULL);
    assert(y != NULL);


    memcpy(&working, y, sizeof(struct timeval));

    if (x->tv_usec < working.tv_usec) {
        int nsec = (working.tv_usec - x->tv_usec) / 1000000L + 1;
        working.tv_usec -= 1000000L * nsec;
        working.tv_sec += nsec;
    }
    if (x->tv_usec - working.tv_usec > 1000000L) {
        int nsec = (x->tv_usec - working.tv_usec) / 1000000L;
        working.tv_usec += 1000000L * nsec;
        working.tv_sec -= nsec;
    }
    r->tv_sec = x->tv_sec - working.tv_sec;
    r->tv_usec = x->tv_usec - working.tv_usec;

    /* Return 1 if result is negative. */
    return x->tv_sec < working.tv_sec;
}

int utlFip_to_str(unsigned int ip, char *buf, size_t buflen)
{
    int a, b, c, d;
    int carry;

    assert(buf != NULL);

    a = ip / 16777216;
    carry = ip % 16777216;

    b = carry / 65536;
    carry = carry % 65536;

    c = carry / 256;
    d = carry % 256;

    return snprintf(buf, buflen, "%d.%d.%d.%d", a, b, c, d);
}

unsigned int utlFstr_to_ip(const char *ip)
{
  return ntohl(inet_addr(ip));
}

int utlFisnum(const char c)
{
    return (c >= '0' && c <= '9' ? 1 : 0);
}

/* check to see if the given string contains any alphas or is just numbers */
int utlFstr_has_alphas(const char *p)
{
    const char *c;
    int ret = 0;

    assert(p != NULL);

    for (c = p; *c != 0; c++) {
        if (utlFisnum(*c) == 0)
            ret = 1;
    }

    return ret;
}


void utlFtv_add(struct timeval *res, struct timeval *t1,
                struct timeval *t2)
{
    assert(res != NULL);
    assert(t1 != NULL);
    assert(t2 != NULL);

    res->tv_sec = t1->tv_sec + t2->tv_sec;
    res->tv_usec = t1->tv_usec + t2->tv_usec;

    res->tv_sec += res->tv_usec / 1000000L;

    res->tv_usec %= 1000000L;
}


long utlFtv_to_ms(struct timeval *tv)
{
    assert(tv != NULL);
    return (tv->tv_sec * 1000 + tv->tv_usec / 1000);
}

/*
 * Encode an URL in the URL encoding format. Arguments are an empty dynamic
 * string that will contain the output encoding, and string, which is the
 * string to be encoded. A pointer to the string content of the ds will be
 * returned on success.
 */

int utlFurl_encode(dynTstring * dyn, const char *string)
{
    static char safechars[] =
        "abcdefghijklmnopqrstuvwxyz"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789:/.";

    assert(dyn != NULL);

    if (string == NULL)
        return -1;

    assert(dyn != NULL);

    while (*string != '\0') {
        if (strchr(safechars, *string) != NULL) {
            dynFappend(dyn, string, 1);
        } else {
            char buf[4];
            sprintf(buf, "%%%02x", (unsigned char) *string);
            dynFappend(dyn, buf, 3);
        }
        ++string;
    }
    return 0;
}

int utlFurl_decode(dynTstring * dyn, const char *string)
{
    unsigned char decoded;
    char twobytes[3];

    assert(dyn != NULL);
    assert(string != NULL);

    twobytes[2] = '\0';
    while (*string != '\0') {

        if (*string == '+') {
            dynFappend(dyn, " ", 1);
            string++;
            continue;
        }
        if (*string != '%') {
            dynFappend(dyn, string, 1);
            string++;
        } else {
            if (utlFhexdigit(string[1]) && utlFhexdigit(string[2])) {
                twobytes[0] = string[1];
                twobytes[1] = string[2];
                decoded =
                    (unsigned char) strtol(twobytes, (char **) NULL, 16);
                dynFappend(dyn, (char *) &decoded, 1);
                string += 3;
            } else {
                return 0;
            }
        }
    }
    return 0;
}

int utlFhexdigit(char c)
{
    static char hexdigits[] = "0123456789ABCDEFabcdef";
    return (c != '\0' && (strchr(hexdigits, c) != NULL));
}

/*
 * return a 32-bit number identifying this machine. For Sun, this is the
 * hostid. For other PC platforms it should probably return the MAC address
 * of the 1st Ethernet controller.
 */
uint32_t utlFgethostid(void)
{
#ifdef SOLARIS
    return gethostid();
#else
    return 0;
#endif
}

/* Microsecond granularity */
#define MAX_ADJUSTMENT 10

int utlFclock_get(uint32_t * clock_high, uint32_t * clock_low,
                  uint16_t * ret_clock_seq)
{
    static int adjustment = 0;
    static struct timeval last = { 0, 0 };
    uint16_t clock_seq;
    struct timeval tv;
    unsigned long long clock_reg;

    assert(clock_high != NULL);
    assert(clock_low != NULL);
    assert(ret_clock_seq != NULL);

  try_again:
    gettimeofday(&tv, 0);
    if ((last.tv_sec == 0) && (last.tv_usec == 0)) {
        rndFrandom_bytes((unsigned char *) &clock_seq, sizeof(clock_seq));
        clock_seq &= 0x1FFF;
        last = tv;
        last.tv_sec--;
    }
    if ((tv.tv_sec < last.tv_sec) ||
        ((tv.tv_sec == last.tv_sec) && (tv.tv_usec < last.tv_usec))) {
        clock_seq = (clock_seq + 1) & 0x1FFF;
        adjustment = 0;
    } else if ((tv.tv_sec == last.tv_sec) && (tv.tv_usec == last.tv_usec)) {
        if (adjustment >= MAX_ADJUSTMENT)
            goto try_again;
        adjustment++;
    } else
        adjustment = 0;

    clock_reg = tv.tv_usec * 10 + adjustment;
    clock_reg += ((unsigned long long) tv.tv_sec) * 10000000;
    clock_reg += (((unsigned long long) 0x01B21DD2) << 32) + 0x13814000;

    *clock_high = clock_reg >> 32;
    *clock_low = clock_reg;
    *ret_clock_seq = clock_seq;
    return 0;
}

/*
 * Parse 'when' into HTTP format time, GMT. The receiving buffer must be at
 * least 40 characters to fully fit the time.
 */
size_t utlFhttp_time_print(time_t when, char *buf, size_t buflen)
{
    struct tm tmp;

    assert(buf != NULL);

    gmtime_r(&when, &tmp);
    tmp.tm_isdst = 0;

    return strftime(buf, buflen, "%a, %d %b %Y %H:%M:%S GMT", &tmp);
}


static const char *month_names[12] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};

static int MakeMonth(char *s)
{
    int i;

    assert(s != NULL);

    /*
     * Make sure it's capitalized like this: "Jan"
     */

    *s = toupper(*s);
    *(s + 1) = tolower(*(s + 1));
    *(s + 2) = tolower(*(s + 2));

    for (i = 0; i < 12; i++) {
        if (!strncmp(month_names[i], s, 3)) {
            return i;
        }
    }
    return 0;
}

static int MakeNum(char *s)
{
    assert(s != NULL);

    if (*s >= '0' && *s <= '9') {
        return (10 * (*s - '0')) + (*(s + 1) - '0');
    } else {
        return *(s + 1) - '0';
    }
}

time_t utlFhttp_time_parse(char *str)
{
    char *s;
    struct tm tm;
    time_t t;
    time_t toff;

    if (str == NULL) {
        return 0;
    }
    /*
     * Find the comma after day-of-week
     * 
     * Thursday, 10-Jun-93 01:29:59 GMT ^ +-- s
     * 
     * Thu, 10 Jan 1993 01:29:59 GMT ^ +-- s
     */

    s = strchr(str, ',');
    if (s != NULL) {

        /*
         * Advance S to the first non-space after the comma which should be
         * the first digit of the day.
         */

        s++;
        while (*s && *s == ' ') {
            s++;
        }

        /*
         * Figure out which format it is in. If there is a hyphen, then it
         * must be the first format.
         */

        if (strchr(s, '-') != NULL) {
            if (strlen(s) < 18) {
                return 0;
            }
            /*
             * The format is:
             * 
             * Thursday, 10-Jun-93 01:29:59 GMT ^ +--s
             */

            tm.tm_mday = MakeNum(s);
            tm.tm_mon = MakeMonth(s + 3);
            tm.tm_year = MakeNum(s + 7);
            tm.tm_hour = MakeNum(s + 10);
            tm.tm_min = MakeNum(s + 13);
            tm.tm_sec = MakeNum(s + 16);
        } else {
            if ((int) strlen(s) < 20) {
                return 0;
            }
            /*
             * The format is:
             * 
             * Thu, 10 Jan 1993 01:29:59 GMT Thu, 10 Jan 1993 01:29:59 GMT ^
             * +--s
             */

            tm.tm_mday = MakeNum(s);
            tm.tm_mon = MakeMonth(s + 3);
            tm.tm_year = (100 * MakeNum(s + 7) - 1900) + MakeNum(s + 9);
            tm.tm_hour = MakeNum(s + 12);
            tm.tm_min = MakeNum(s + 15);
            tm.tm_sec = MakeNum(s + 18);
        }
    } else {

        /*
         * No commas, so it must be the third, fixed field, format:
         * 
         * Wed Jun  9 01:29:59 1993 GMT
         * 
         * Advance s to the first letter of the month.
         */

        s = str;
        while (*s && *s == ' ') {
            s++;
        }
        if ((int) strlen(s) < 24) {
            return 0;
        }
        tm.tm_mday = MakeNum(s + 8);
        tm.tm_mon = MakeMonth(s + 4);
        tm.tm_year = MakeNum(s + 22);
        tm.tm_hour = MakeNum(s + 11);
        tm.tm_min = MakeNum(s + 14);
        tm.tm_sec = MakeNum(s + 17);
    }

    /*
     * If there are any impossible values, then return an error.
     */

    if (tm.tm_sec < 0 || tm.tm_sec > 59 ||
        tm.tm_min < 0 || tm.tm_min > 59 ||
        tm.tm_hour < 0 || tm.tm_hour > 23 ||
        tm.tm_mday < 1 || tm.tm_mday > 31 ||
        tm.tm_mon < 0 || tm.tm_mon > 11 || tm.tm_year < 70
        || tm.tm_year > 120) {
        return 0;
    }
    tm.tm_isdst = 0;
    toff = (time_t) timezone;
    t = mktime(&tm) - toff;
    return t;
}

int utlFgetuid (const char *username)
{
    struct passwd pw;

    assert(username != NULL);

    if (utlFget_pwname(username, &pw) == -1) {
        return -1;
    } else {
        return pw.pw_uid;
    }
}

int utlFgetgid (const char *username)
{
    struct passwd pw;

    assert(username != NULL);

    if (utlFget_pwname(username, &pw) == -1) {
        return -1;
    } else {
        return pw.pw_gid;
    }
}

int utlFget_pwname(const char *username, struct passwd *pw)
{
    struct passwd *pwptr;
    char pwbuf[512];

    assert(username != NULL);
    assert(pw != NULL);

#ifdef SOLARIS
    if ((pwptr = getpwnam_r(username, pw, pwbuf, 512)) != NULL) {
#else
    if (!getpwnam_r(username, pw, pwbuf, 512, &pwptr) && pwptr) {
#endif

        /* Do Nothing */
    } else {
        if (errno == 0) {
            /* this can happen with getpwnam() on FreeBSD 4.3 */
            return -1;
        }
        return -1;
    }
    return 0;
}

int utlFget_homedir(const char *username, char *dir, size_t dir_len)
{
#ifndef WIN32
    struct passwd pw;
    size_t len;

    assert(username != NULL);
    assert(dir != NULL);
    assert(dir_len > 0);

    if (utlFget_pwname(username, &pw) != 0)
        return -1;

    len = strlen(pw.pw_dir);

    /* NULL */
    len++;
    if (len > dir_len)
        return -1;

    memcpy(dir, pw.pw_dir, len);

    return len;
#else
    return -1;
#endif
}

void utlFhours_to_hourmin(double in_time, int *hours, int *mins)
{
    *mins = in_time * 60;
    *hours = *mins / 60;
    *mins = *mins % 60;
}

double utlFhourmin_to_hours(const int hours, const int mins)
{
    return hours + ((double) mins / 60.0);
}

int utlFip_match(unsigned int ipa, unsigned int ipb, unsigned int netmask)
{
    if ((ipa & netmask) == (ipb & netmask))
        return 1;
    return 0;
}

int utlFip_validate(const char *addr) {
    int dot_count;
    int digit_count;
    
    dot_count = 0;
    digit_count = 0;
    while(*addr != '\0' && *addr != ' ') {
        if(*addr == '.') {
            dot_count++;
            digit_count = 0;
        }
        else if(!isdigit(*addr)) {
            dot_count = 5;
        }
        else {
            digit_count++;
            if(digit_count > 3) {
                dot_count = 5;
            }
        }
        addr++;
    }
    if(dot_count != 3) {
        return(-1);
    }
    else {
        return(0);
    }
}

static const char *utlGnumber_lut[] = {
    "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
    "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23",
    "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35",
    "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47",
    "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59",
    "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "70", "71",
    "72", "73", "74", "75", "76", "77", "78", "79", "80", "81", "82", "83",
    "84", "85", "86", "87", "88", "89", "90", "91", "92", "93", "94", "95",
    "96", "97", "98", "99", "100", "101", "102", "103", "104", "105",
        "106",
    "107", "108", "109", "110", "111", "112", "113", "114", "115", "116",
    "117", "118", "119", "120", "121", "122", "123", "124", "125", "126",
    "127", "128", "129", "130", "131", "132", "133", "134", "135", "136",
    "137", "138", "139", "140", "141", "142", "143", "144", "145", "146",
    "147", "148", "149", "150", "151", "152", "153", "154", "155", "156",
    "157", "158", "159", "160", "161", "162", "163", "164", "165", "166",
    "167", "168", "169", "170", "171", "172", "173", "174", "175", "176",
    "177", "178", "179", "180", "181", "182", "183", "184", "185", "186",
    "187", "188", "189", "190", "191", "192", "193", "194", "195", "196",
    "197", "198", "199", "200", "201", "202", "203", "204", "205", "206",
    "207", "208", "209", "210", "211", "212", "213", "214", "215", "216",
    "217", "218", "219", "220", "221", "222", "223", "224", "225", "226",
    "227", "228", "229", "230", "231", "232", "233", "234", "235", "236",
    "237", "238", "239", "240", "241", "242", "243", "244", "245", "246",
    "247", "248", "249", "250", "251", "252", "253", "254", "255"
};

const char *utlFshort_to_str(unsigned short n)
{
    return utlGnumber_lut[n];
}

unsigned int utlFatoui(const char *s) {
  assert(sizeof(unsigned long)==sizeof(unsigned int));

  return (unsigned int)strtoul(s, (char **)NULL, 0);
}

int utlFatoi(const char *s) {
  return (int) strtol (s, (char **)NULL, 10);
}

void utlFhexdump(void *data, size_t size)
{
    /* dumps size bytes of *data to stdout. Looks like:
     * [0000] 75 6E 6B 6E 6F 77 6E 20
     *                  30 FF 00 00 00 00 39 00 unknown 0.....9.
     * (in a single line of course)
     */

    unsigned char *p = data;
    unsigned char c;
    int n;
    char bytestr[4] = {0};
    char addrstr[10] = {0};
    char hexstr[ 16*3 + 5] = {0};
    char charstr[16*1 + 5] = {0};
    for(n=1;n<=size;n++) {
        if (n%16 == 1) {
            /* store address for this line */
            snprintf(addrstr, sizeof(addrstr), "%.4x",
               ((unsigned int)p-(unsigned int)data) );
        }
            
        c = *p;
        if (isalnum(c) == 0) {
            c = '.';
        }

        /* store hex str (for left side) */
        snprintf(bytestr, sizeof(bytestr), "%02X ", *p);
        strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1);

        /* store char str (for right side) */
        snprintf(bytestr, sizeof(bytestr), "%c", c);
        strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1);

        if(n%16 == 0) { 
            /* line completed */
            printf("[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
            hexstr[0] = 0;
            charstr[0] = 0;
        } else if(n%8 == 0) {
            /* half line: add whitespaces */
            strncat(hexstr, "  ", sizeof(hexstr)-strlen(hexstr)-1);
            strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1);
        }
        p++; /* next byte */
    }

    if (strlen(hexstr) > 0) {
        /* print rest of buffer if not empty */
        printf("[%4.4s]   %-50.50s  %s\n", addrstr, hexstr, charstr);
    }
}


