/* @@@HEAD@@@ // General utilities. */ #include <stdio.h> #include <ctype.h> #include <string.h> #include <time.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <stdarg.h> #include <fcntl.h> #include "config.h" #include "defs.h" #include "y.tab.h" #include "util.h" #include "cdc_string.h" #include "data.h" #include "ident.h" #include "token.h" #include "memory.h" #include "log.h" #define FORMAT_BUF_INITIAL_LENGTH 48 #define MAX_SCRATCH 2 /* crypt() is not POSIX. */ extern char * crypt(const char *, const char *); char lowercase[128]; char uppercase[128]; 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 < 128; i++) { lowercase[i] = (isupper(i) ? tolower(i) : i); uppercase[i] = (islower(i) ? toupper(i) : i); } srand(time(NULL) + getpid()); } unsigned long hash(char *s) { unsigned long 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; } unsigned long hash_case(char *s, int n) { unsigned long hashval = 0, g; int i; /* Algorithm by Peter J. Weinberger. */ for (i = 0; i < n; i++) { hashval = (hashval << 4) + (s[i] & 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) { 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; } char * float_to_ascii(float num, Number_buf nbuf) { sprintf (nbuf,"%f",num); 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); 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. */ long random_number(long n) { long num = rand(); if (!n) return 0; #ifdef SUNOS if (256 >= n) #else if (RAND_MAX >> 8 >= n) #endif num >>= 8; return num % n; } /* Encrypt a string. The salt can be NULL. */ char *crypt_string(char *key, char *salt) { char rsalt[2]; if (!salt) { rsalt[0] = random_number(95) + 32; rsalt[1] = random_number(95) + 32; salt = rsalt; } return crypt(key, salt); } /* Result must be copied before it can be re-used. Non-reentrant. */ string_t *vformat(char *fmt, va_list arg) { string_t * buf, * str; char * p, * s; Number_buf nbuf; buf = string_new(0); while (1) { /* 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, string_t *); buf = string_add(buf, str); break; case 'O': /* figure objname, if not that then use dbref */ 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, data_t *)); 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 '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; } string_t *format(char *fmt, ...) { va_list arg; string_t *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[WORD]; 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; string_t *str; va_start(arg, fmt); str = vformat(fmt, arg); fputs(string_chars(str), fp); string_discard(str); va_end(arg); } string_t *fgetstring(FILE *fp) { string_t *line; int len; char buf[1000]; line = string_new(0); while (fgets(buf, 1000, 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; } } char *english_type(int type) { switch (type) { case INTEGER: return "an integer"; case FLOAT: return "a float"; case STRING: return "a string"; case DBREF: return "a dbref"; case LIST: return "a list"; case SYMBOL: return "a symbol"; case ERROR: return "an error"; case FROB: return "a frob"; case DICT: return "a dictionary"; case BUFFER: return "a buffer"; default: return "a mistake"; } } 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); } long parse_ident(char **sptr) { string_t *str; char *s = *sptr; long id; if (*s == '"') { str = string_parse(&s); } else { 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) { reserve_fds[i] = open("/dev/null", O_WRONLY); 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) { int l = slen, len = slen; char * s = b2, * b = b1; while (l > 0) { if (*s == '\\') { s++, l--; switch (*s) { case 'n': add_char(b, '\n'); len--; 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(__s, __c)