/*
// 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) {
}