/
Genesis-1.0p36-DEV/
Genesis-1.0p36-DEV/bin/
Genesis-1.0p36-DEV/doc/
Genesis-1.0p36-DEV/etc/
Genesis-1.0p36-DEV/src/data/
/*
// Full copyright information is available in the file ../doc/CREDITS
//
// General utilities.
*/

#include "defs.h"

#include <ctype.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdarg.h>
#include <fcntl.h>
#include "util.h"
#include "token.h"
#include "macros.h"

#define FORMAT_BUF_INITIAL_LENGTH 48
#define MAX_SCRATCH 2

int lowercase[NUM_CHARS];
int uppercase[NUM_CHARS];

static Int  reserve_fds[MAX_SCRATCH];
static Int  fds_used;

INTERNAL void claim_fd(Int i);

void init_util(void) {
    int i;

    for (i = 0; i < NUM_CHARS; i++) {
	lowercase[i] = (int) (isupper(i) ? tolower(i) : i);
	uppercase[i] = (int) (islower(i) ? toupper(i) : i);
    }
    srand(time(NULL) + getpid());
}

uLong hash_nullchar(char * s) {
    uLong hashval = 0, g;

    /* Algorithm by Peter J. Weinberger. */
    for (; *s; s++) {
	hashval = (hashval << 4) + *s;
	g = hashval & 0xf0000000;
	if (g) {
	    hashval ^= g >> 24;
	    hashval ^= g;
	}
    }
    return hashval;
}

uLong hash_string(cStr * str) {
    uLong hashval = 0, g;
    int len;
    char * s;

    s = string_chars(str);
    len = string_length(str);

    /* Algorithm by Peter J. Weinberger. */
    for (; len; len--, s++) {
	hashval = (hashval << 4) + *s;
	g = hashval & 0xf0000000;
	if (g) {
	    hashval ^= g >> 24;
	    hashval ^= g;
	}
    }

    return hashval;
}

uLong hash_string_nocase(cStr * str) {
    uLong hashval = 0, g;
    int len;
    char * s;

    s = string_chars(str);
    len = string_length(str);

    /* Algorithm by Peter J. Weinberger. */
    for (; len; len--, s++) {
	hashval = (hashval << 4) + (*s & 0x5f);
	g = hashval & 0xf0000000;
	if (g) {
	    hashval ^= g >> 24;
	    hashval ^= g;
	}
    }

    return hashval;
}


Long atoln(char *s, Int n) {
    Long val = 0;

    while (n-- && isdigit(*s))
	val = val * 10 + *s++ - '0';
    return val;
}

char *long_to_ascii(Long num, Number_buf nbuf) {
#if DISABLED /* why?? */
    char *p = &nbuf[NUMBER_BUF_SIZE - 1];
    Int sign = 0;

    *p-- = 0;
    if (num < 0) {
	sign = 1;
	num = -num;
    } else if (!num) {
	*p-- = '0';
    }
    while (num) {
	*p-- = num % 10 + '0';
	num /= 10;
    }
    if (sign)
	*p-- = '-';
    return p + 1;
#else
    *nbuf++ = (char) 0;
    sprintf(nbuf, "%ld", (long) num);
    return nbuf;
#endif
}

char * float_to_ascii(float num, Number_buf nbuf) {
    int i;
    sprintf (nbuf, "%g", num);
    for (i=0; nbuf[i]; i++)
      if (nbuf[i]=='.' || nbuf[i]=='e')
           return nbuf;
    nbuf[i]='.';
    nbuf[i+1]='0';
    nbuf[i+2]='\0';
    return nbuf;
}

/* Compare two strings, ignoring case. */
Int strccmp(char *s1, char *s2) {
    while (*s1 && LCASE(*s1) == LCASE(*s2))
	s1++, s2++;
    return LCASE(*s1) - LCASE(*s2);
}

/* Compare two strings up to n characters, ignoring case. */
Int strnccmp(char *s1, char *s2, Int n) {
    while (n-- && *s1 && LCASE(*s1) == LCASE(*s2))
	s1++, s2++;
    return (n >= 0) ? LCASE(*s1) - LCASE(*s2) : 0;
}

/* Look for c in s, ignoring case. */
char *strcchr(char *s, Int c) {
    c = LCASE(c);

    for (; *s; s++) {
	if (LCASE(*s) == c)
	    return s;
    }
    return (c) ? NULL : s;
}

char *strcstr(char *s, char *search) {
    char *p;
    Int search_len = strlen(search);

    if (!search_len)
        return NULL;

    for (p = strcchr(s, *search); p; p = strcchr(p + 1, *search)) {
	if (strnccmp(p, search, search_len) == 0)
	    return p;
    }

    return NULL;
}

/* A random number generator.  A lot of Unix rand() implementations don't
 * produce very random low bits, so we shift by eight bits if we can do that
 * without truncating the range. */
#ifndef RAND_MAX
#define RAND_MAX 256
#endif

Long random_number(Long n) {
    Long num = rand();
    if (!n)
      return 0;

#ifdef sys_sunos41
    if (n <= 256)
#else
    if (RAND_MAX >> 8 >= n)
#endif
	num >>= 8;
    return num % n;
}

/* Result must be copied before it can be re-used.  Non-reentrant. */
cStr * vformat(char * fmt, va_list arg) {
    cStr   * buf,
               * str;
    char       * p,
               * s;
    Number_buf   nbuf;

    buf = string_new(0);

    forever {

	/* Find % or end of string. */
	p = strchr(fmt, '%');
	if (!p || !p[1]) {
	    /* No more percents; copy rest and stop. */
	    buf = string_add_chars(buf, fmt, strlen(fmt));
	    break;
	}
	buf = string_add_chars(buf, fmt, p - fmt);

	switch (p[1]) {

	  case '%':
	    buf = string_addc(buf, '%');
	    break;

	  case 's':
	    s = va_arg(arg, char *);
	    buf = string_add_chars(buf, s, strlen(s));
	    break;

	  case 'S':
	    str = va_arg(arg, cStr *);
	    buf = string_add(buf, str);
	    break;

	  case 'd':
	    s = long_to_ascii(va_arg(arg, Int), nbuf);
	    buf = string_add_chars(buf, s, strlen(s));
	    break;

	  case 'l':
	    s = long_to_ascii(va_arg(arg, Long), nbuf);
	    buf = string_add_chars(buf, s, strlen(s));
	    break;

	  case 'D':
	    str = data_to_literal(va_arg(arg, cData *), TRUE);
	    if (string_length(str) > MAX_DATA_DISPLAY) {
		str = string_truncate(str, MAX_DATA_DISPLAY - 3);
		str = string_add_chars(str, "...", 3);
	    }
	    buf = string_add_chars(buf, string_chars(str), string_length(str));
	    string_discard(str);
	    break;

          case 'O': {
            cData d;
            d.type = OBJNUM;
            d.u.objnum = va_arg(arg, cObjnum);
            str = data_to_literal(&d, TRUE);
            buf = string_add_chars(buf, string_chars(str), string_length(str));
            string_discard(str);
          }
          break;

	  case 'I':
	    s = ident_name(va_arg(arg, Long));
	    if (is_valid_ident(s))
		buf = string_add_chars(buf, s, strlen(s));
	    else
		buf = string_add_unparsed(buf, s, strlen(s));
	    break;
	}

	fmt = p + 2;
    }

    va_end(arg);
    return buf;
}

cStr * format(char *fmt, ...) {
    va_list arg;
    cStr *str;

    va_start(arg, fmt);
    str = vformat(fmt, arg);
    va_end(arg);
    return str;
}

/* 
// builds a timestamp in the almost rfc850 format (DD MMM YY HH:MM)
//
// Added: 30 Jul 1995 - BJG
*/
char * timestamp (char * str) {
    time_t      t;
    struct tm * tms;
    static char tstr[LINE];
    char      * s;
    char      * months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
                            "Aug", "Sep", "Oct", "Nov", "Dec"};

    if (str != NULL)
        s = str;
    else
        s = tstr;

    time(&t);
    tms = localtime(&t);
    sprintf(s, "%d %3s %2d %d:%.2d",
            tms->tm_mday,
            months[tms->tm_mon],
            tms->tm_year,
            tms->tm_hour,
            tms->tm_min);

    return s;
}

void fformat(FILE *fp, char *fmt, ...) {
    va_list arg;
    cStr *str;

    va_start(arg, fmt);

    str = vformat(fmt, arg);
    fputs(string_chars(str), fp);
    string_discard(str);

    va_end(arg);
}

#if DISABLED
cStr *fgetstring(FILE *fp) {
    cStr *line;
    Int len;
    char buf[BIGBUF];

    line = string_new(0);  

    while (fgets(buf, BIGBUF, fp)) {
        len = strlen(buf);
        if (buf[len - 1] == '\n') {
            line = string_add_chars(line, buf, len-1);
            return line;
        } else
            line = string_add_chars(line, buf, len);
    }
        
    if (line->len) { 
        return line;
    } else {
        string_discard(line);
        return NULL;
    } 
}
#else
cStr *fgetstring(FILE *fp) {
    cStr        * line;
    Int           len;
    char        * p;

    /* we have a bigger line in general, but it reduces
       copies for the most common situation */
    line = string_new(BUF);
    p = string_chars(line);
    if (fgets(p, BUF, fp)) {
	len = strlen(p);
        if (p[len - 1] == '\n') {
            p[len - 1] = (char) NULL;
            line->len = len - 1;
            return line;
        } else {
            char   buf[BIGBUF];

            line->len = len;

            /* drop to something less efficient for bigger cases */
            while (fgets(buf, BIGBUF, fp)) {
        	len = strlen(buf);
        	if (buf[len - 1] == '\n') {
        	    line = string_add_chars(line, buf, len-1);
        	    return line;
        	} else {
        	    line = string_add_chars(line, buf, len);
                }
            }
        }
    }

    if (line->len > 0) {
	return line;
    } else {
	string_discard(line);
	return NULL;
    }
}
#endif

char * english_type(Int type) {
    switch (type) {
      case INTEGER:	return "an integer";
      case FLOAT:	return "a float";
      case STRING:	return "a string";
      case OBJNUM:	return "a objnum";
      case LIST:	return "a list";
      case SYMBOL:	return "a symbol";
      case T_ERROR:	return "an error";
      case FROB:	return "a frob";
      case DICT:	return "a dictionary";
      case BUFFER:	return "a buffer";
    default:		{INSTANCE_RECORD(type, r); return r->name; }
    }
}

char *english_integer(Int n, Number_buf nbuf) {
    static char *first_eleven[] = {
	"no", "one", "two", "three", "four", "five", "six", "seven",
	"eight", "nine", "ten" };

    if (n <= 10)
	return first_eleven[n];
    else
	return long_to_ascii(n, nbuf);
}

Ident parse_ident(char **sptr) {
    cStr *str;
    char *s = *sptr;
    Long id;

    while (isalnum(*s) || *s == '_')
        s++;

    str = string_from_chars(*sptr, s - *sptr);

    id = ident_get(string_chars(str));
    string_discard(str);

    *sptr = s;

    return id;
}

FILE *open_scratch_file(char *name, char *type) {
    FILE *fp;

    if (fds_used == MAX_SCRATCH)
	return NULL;

    close(reserve_fds[fds_used++]);
    fp = fopen(name, type);
    if (!fp) {
	claim_fd(--fds_used);
	return NULL;
    }
    return fp;
}

void close_scratch_file(FILE *fp) {
    fclose(fp);
    claim_fd(--fds_used);
}

void init_scratch_file(void) {
    Int i;

    for (i = 0; i < MAX_SCRATCH; i++)
	claim_fd(i);
}

INTERNAL void claim_fd(Int i) {
#ifdef __Win32__
    reserve_fds[i] = open("null_file", O_WRONLY | O_CREAT);
#else
    reserve_fds[i] = open("/dev/null", O_WRONLY);
#endif
    if (reserve_fds[i] == -1)
	panic("Couldn't reset reserved fd.");
}

#define add_char(__s, __c) { *__s = __c; __s++; }

Int parse_strcpy(char * b1, char * b2, Int slen) {
    register Int l = slen, len = slen;
    register char * s = b2, * b = b1;

    while (l > 0) {
        if (*s == '\\') {
            s++, l--;
            switch (*s) {
                case 't':
                    add_char(b, '\t');
                    len--;
                    break;
                case 'n':
                    add_char(b, '\r');
                    add_char(b, '\n');
                    break;
                case 'r':
                    add_char(b, '\r');
                    len--;
                    break;
                case '\\':
                    add_char(b, '\\');
                    len--;
                    break;
                default:
                    add_char(b, '\\');
                    add_char(b, *s);
                    break;
            }
            s++, l--;
        } else {
            add_char(b, *s);
            s++, l--;
        }
    }

    return len;
}

#undef add_char

Int getarg(char * n,
           char **buf,
           char * opt,
           char **argv,
           Int  * argc,
           void (*usage)(char *))
{
    char * p = opt; 
    
    p++;
    if (*p != (char) NULL) {
        *buf = p;

        return 0;
    }

    if ((*argc - 1) <= 0) {
        usage(n);
        fprintf(stderr, "** No followup argument to -%s.\n", opt);
        exit(1);
    }

    argv++;
    *buf = *argv;
    *argc -= 1;

    /* we don't want spaces */
    p = *buf;
    while (isspace(*p)) p++;

    /* not likely, but possible */
    if (strlen(p) == 0) {
        usage(n);
        fprintf(stderr, "** Invalid followup argument to -%s.\n", opt);
        exit(1);
    }

    *buf = p;

    return 1;
}

Int is_valid_id(char * str, Int len) {
    while (len--) {
        if (!isalnum(*str) && *str != '_')
            return 0;
        str++;
     }
     return 1;
}

void set_argv0(char * str) {
    
}