netscape-revival
sun-java/runtime/util.c
/*
* @(#)util.c 1.27 95/12/08 Chris Warth
*
* Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies. Please refer to the file "copyright.html"
* for further important copyright and licensing information.
*
* SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
* THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
* ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*/
/*-
utility routines needed by both the compiler and the interpreter.
*/
#include "oobj.h"
#include "interpreter.h"
#include "utf.h"
#include "path_md.h"
#include "sys_api.h"
#include "str2id.h"
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "io_md.h" /* sysWrite() */
unicode *
str2unicode(char *str, unicode *ustr, long len)
{
unicode *dst = ustr;
/* XXX rjp - revisit */
#if defined(_WINDOWS) && !defined(_WIN32)
while (*str && --len >= 0)
*ustr++ = (unicode) *str++;
while (--len >= 0)
*ustr++ = (unicode) 0;
#else
memset((char *) dst, 0, len * sizeof(*dst));
while (*str && --len >= 0)
*ustr++ = *str++;
#endif
return dst;
}
void
unicode2str(unicode *src, char *dst, long len)
{
while (--len >= 0)
*dst++ = (char) *src++;
*dst = '\0';
}
/*
* Print s null terminated C-style character string.
*/
void
prints(char *s)
{
#ifdef PRINTS_STDIO
int c;
while (c = *s++)
putchar(c);
#else
if (s)
sysWrite(1, s, strlen(s));
#endif
}
/*
* Print an Java-style unicode string
*/
void
printus(unicode *str, long len)
{
#define UNISTRLEN 100
char obuf[UNISTRLEN+1];
register int p;
if (str == 0 || len <= 0)
return;
while (len > 0) {
unicode c;
for (p = 0; p < sizeof obuf - 10 && --len >= 0;)
if ((c = *str++) >= 256) {
/*
* Make unicode characters readable. Should not be
* possible to overflow, but truncate silently if so.
*/
(void) jio_snprintf(obuf + p, UNISTRLEN+1 - p, "\\<%X>", c);
p += strlen(obuf + p);
} else
obuf[p++] = (char) c;
if (p)
#ifdef PRINTS_STDIO
fwrite(obuf, 1, p, stdout);
#else
sysWrite(1, obuf, p);
#endif
}
}
#define STR2ID_HTOVERFLOWPOINT ((int)(STR2ID_HTSIZE*0.8))
/* Given a string, return a unique 16 bit id number. If param
* isn't null, we also set up an array of void * for holding info about each
* object. The address of this void * is stored into param
*/
/* Given a string, return a unique 16 bit id number. If param
* isn't null, we also set up an array of void * for holding info about each
* object. The address of this void * is stored into param
*/
hash_t
Str2ID(StrIDhash **idhroot, register char *s, void ***param, int CopyNeeded)
{
/*
* The database is a hash table. When the hash table overflows, a new
* hashtable is created chained onto the previous one. This is done so
* that we can use the hashtable slot index as the ID, without worrying
* about having the IDs change when the hashtable grows.
*/
StrIDhash *h;
PRArenaPool *stringArena;
char *s2;
long i;
uint32_t raw_hash, hash;
uint32_t secondary_hash;
for (raw_hash = 0, s2 = s; (i = *s2) != 0; s2++)
raw_hash = raw_hash * 37 + i;
hash = raw_hash % STR2ID_HTSIZE;
secondary_hash = (raw_hash & 7) + 1; /* between 1 and 8 */
if ((h = *idhroot) == 0) {
h = (StrIDhash *) sysCalloc(1, sizeof(*h));
if (!h) {
/* REMIND: Should throw an exception? */
out_of_memory();
return 0;
}
*idhroot = h;
PR_InitArenaPool(&(h->stringArena), "string pool", kHashTableArenaIncrementSize, 1);
h->baseid = 1; /* guarantee that no ID is 0 */
}
stringArena = &(h->stringArena);
while (1) {
i = hash;
while ((s2 = h->hash[i]) != 0) {
if (strcmp(h->hash[i], s) == 0) {
if (param)
*param = &h->param[i];
return h->baseid + i;
}
if ((i -= secondary_hash) < 0)
i += STR2ID_HTSIZE;
}
if (h->next == 0)
break;
h = h->next;
}
/* Couldn't find the string */
if (h->used >= STR2ID_HTOVERFLOWPOINT) {
/* have to create a new hashtable */
h->next = (StrIDhash *) sysCalloc(1, sizeof(*h));
if (!h->next) {
/* REMIND: Should throw an exception? */
out_of_memory();
return 0;
}
h->next->baseid = h->baseid + STR2ID_HTSIZE;
h = h->next;
i = hash;
}
if (CopyNeeded) {
PR_ARENA_ALLOCATE(h->hash[i], stringArena, strlen(s) + 1);
strcpy(h->hash[i], s);
h->is_malloced[i >> 5] |= (1 << (i & 31));
} else {
h->hash[i] = s;
}
h->used++;
if (param) {
if (h->param == 0) {
h->param = (void **)sysCalloc(STR2ID_HTSIZE, sizeof(*param));
if (!h->param) {
/* REMIND: Should throw an exception? */
out_of_memory();
return 0;
}
}
*param = &h->param[i];
}
return (hash_t) (h->baseid + i);
}
void
Str2IDFree(StrIDhash **hash_ptr)
{
StrIDhash *hash, *next;
hash = *hash_ptr;
PR_FreeArenaPool(&(hash->stringArena));
while (hash) {
next = hash->next;
if (hash->param)
sysFree(hash->param);
sysFree(hash);
hash = next;
}
*hash_ptr = 0;
}
void
Str2IDApply(StrIDhash *hash_ptr,
void (*f)(StrIDhash *h, void *arg, void *param),
void *arg) {
StrIDhash *hash, *next;
int i;
hash = hash_ptr;
while (hash) {
next = hash->next;
for (i = 0; i < STR2ID_HTSIZE; i++) {
if (hash->param[i]) {
(*f)(hash_ptr, arg, hash->param[i]);
}
}
hash = next;
}
}
char *
ID2Str(StrIDhash *h, unsigned short ID, void ***param) {
while ((long)(ID - h->baseid) >= STR2ID_HTSIZE)
h = h->next;
if (param) {
if (h->param == 0) {
h->param = (void **)sysCalloc(STR2ID_HTSIZE, sizeof(*param));
if (!h->param) {
/* REMIND: Should throw an exception? */
out_of_memory();
return 0;
}
}
*param = &h->param[ID - h->baseid];
}
return h->hash[ID - h->baseid];
}
char *
addstr(char *s, char *buf,
char *limit, char term)
{
char c;
while ((c = *s) && c != term && buf < limit) {
*buf++ = c;
s++;
}
return buf;
}
char *
unicode2rd(unicode *s, long len)
{ /* unicode string to readable C string */
#define CSTRLEN 40
static char buf[CSTRLEN+1];
char *dp = buf;
int c = 0;
if (s == 0)
return "NULL";
*dp++ = '"';
while (--len>=0 && (c = *s++) != 0 && dp < buf + sizeof buf - 10)
if (040 <= c && c < 0177)
*dp++ = c;
else
switch (c) {
case '\n':
*dp++ = '\\';
*dp++ = 'n';
break;
case '\t':
*dp++ = '\\';
*dp++ = 't';
break;
case '\r':
*dp++ = '\\';
*dp++ = 'r';
break;
case '\b':
*dp++ = '\\';
*dp++ = 'b';
break;
case '\f':
*dp++ = '\\';
*dp++ = 'f';
break;
default:
/* Should not be possible to overflow, truncate if so */
(void) jio_snprintf(dp, CSTRLEN+1 - (dp - buf), "\\%X", c);
dp += strlen(dp);
break;
}
*dp++ = '"';
if (len >= 0 && c != 0)
*dp++ = '.', *dp++ = '.', *dp++ = '.';
*dp++ = 0;
return buf;
}
char *
int642CString(int64_t number, char *buf, int buflen)
{
ll2str(number, buf, buf + buflen);
return buf;
}
void
out_of_memory()
{
sysWrite(2, "**Out of memory, exiting**\n", 27);
sysAbort();
}
#if 0
JAVA_PUBLIC_API(void)
panic(const char *format, ...)
{
va_list ap;
char buf[256];
va_start(ap, format);
printf("\n*** panic: ");
/* If buffer overflow, quietly truncate */
(void) jio_vsnprintf(buf, sizeof(buf), format, ap);
prints(buf);
sysAbort();
}
#endif
char *
classname2string(char *src, char *dst, int size) {
char *buf = dst;
for (; (--size > 0) && (*src != '\0') ; src++, dst++) {
if (*src == '/') {
*dst = '.';
} else {
*dst = *src;
}
}
*dst = '\0';
return buf;
}
/*
* Warning: the buffer overflow checking here is pretty bogus although
* should prevent overruns.
*/
void
mangleMethodName(struct methodblock *mb, char *buffer, int buflen, char *suffix)
{
ClassClass *cb = fieldclass(&mb->fb);
static char prefix[] = "Java_";
char *bufptr = buffer;
char *bufend = buffer + buflen;
/* Copy over Java_ */
(void) jio_snprintf(bufptr, bufend - bufptr, prefix);
bufptr += strlen(bufptr);
/* Mangle the class name */
bufptr += mangleUTFString(classname(cb), bufptr, bufend - bufptr,
MangleUTF_Class);
if (bufend - bufptr > 1)
*bufptr++ = '_';
bufptr += mangleUTFString(mb->fb.name, bufptr, bufend - bufptr,
MangleUTF_Field);
(void) jio_snprintf(bufptr, bufend - bufptr, "%s", suffix);
}
int
mangleUTFString(char *name, char *buffer, int buflen, int type) {
char *ptr = name;
char *bufptr = buffer;
char *bufend = buffer + buflen - 1; /* save space for NULL */
int ch;
while (((ch = next_utf2unicode(&ptr)) != '\0') && (bufend - bufptr > 0)) {
if (ch <= 0x7f && isalnum(ch)) {
*bufptr++ = ch;
} else if (ch == '/' && type == MangleUTF_Class) {
*bufptr++ = '_';
} else if (ch == '_' && type == MangleUTF_FieldStub) {
*bufptr++ = '_';
} else {
/* DANGER.
* DO NOT USE jio_snprintf directly with format "%.5x"
* It doesn't understand it, and you will lose horribly.
* REGNAD
*/
char temp[10];
sprintf(temp, "_%.5x", ch); /* six characters always plus null */
(void) jio_snprintf(bufptr, bufend - bufptr, "%s", temp);
bufptr += strlen(bufptr);
}
}
*bufptr = '\0';
return bufptr - buffer;
}
/*
* jio_snprintf(): sprintf with buffer limit.
*/
#include <stdarg.h>
#include "oobj.h"
#include "interpreter.h"
typedef struct InstanceData {
char *buffer;
char *end;
} InstanceData;
#undef ERROR
#define ERROR -1
#undef SUCCESS
#define SUCCESS 0
#undef CheckRet
#define CheckRet(x) { if ((x) == ERROR) return ERROR; }
static int put_char(InstanceData *this, int c)
{
if (iscntrl(c) && c != '\n' && c != '\t') {
c = '@' + (c & 0x1F);
if (this->buffer >= this->end) {
return ERROR;
}
*this->buffer++ = '^';
}
if (this->buffer >= this->end) {
return ERROR;
}
*this->buffer++ = c;
return SUCCESS;
}
static int
format_string(InstanceData *this, char *str, int left_justify, int min_width,
int precision)
{
int pad_length;
char *p;
if (str == 0) {
return ERROR;
}
if ((int)strlen(str) < precision) {
pad_length = min_width - strlen(str);
} else {
pad_length = min_width - precision;
}
if (pad_length < 0)
pad_length = 0;
if (left_justify) {
while (pad_length > 0) {
CheckRet(put_char(this, ' '));
--pad_length;
}
}
for (p = str; *p != '\0' && --precision >= 0; p++) {
CheckRet(put_char(this, *p));
}
if (!left_justify) {
while (pad_length > 0) {
CheckRet(put_char(this, ' '));
--pad_length;
}
}
return SUCCESS;
}
#define MAX_DIGITS 32
static int
format_number(InstanceData *this, long value, int format_type, int left_justify,
int min_width, int precision, bool_t zero_pad)
{
int sign_value = 0;
unsigned long uvalue;
char convert[MAX_DIGITS+1];
int place = 0;
int pad_length = 0;
static char digits[] = "0123456789abcdef";
int base = 0;
bool_t caps = FALSE;
bool_t add_sign = FALSE;
switch (format_type) {
case 'o': case 'O':
base = 8;
break;
case 'd': case 'D':
add_sign = TRUE; /* fall through */
case 'u': case 'U':
base = 10;
break;
case 'X':
caps = TRUE; /* fall through */
case 'x':
base = 16;
break;
}
sysAssert(base > 0 && base <= 16);
uvalue = value;
if (add_sign) {
if (value < 0) {
sign_value = '-';
uvalue = -value;
}
}
do {
convert[place] = digits[uvalue % (unsigned)base];
if (caps) {
convert[place] = toupper(convert[place]);
}
place++;
uvalue = (uvalue / (unsigned)base);
if (place > MAX_DIGITS) {
return ERROR;
}
} while(uvalue);
convert[place] = 0;
pad_length = min_width - place;
if (pad_length < 0) {
pad_length = 0;
}
if (left_justify) {
if (zero_pad && pad_length > 0) {
if (sign_value) {
CheckRet(put_char(this, sign_value));
--pad_length;
sign_value = 0;
}
while (pad_length > 0) {
CheckRet(put_char(this, '0'));
--pad_length;
}
} else {
while (pad_length > 0) {
CheckRet(put_char(this, ' '));
--pad_length;
}
}
}
if (sign_value) {
CheckRet(put_char(this, sign_value));
}
while (place > 0 && --precision >= 0) {
CheckRet(put_char(this, convert[--place]));
}
if (!left_justify) {
while (pad_length > 0) {
CheckRet(put_char(this, ' '));
--pad_length;
}
}
return SUCCESS;
}
int
jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
{
char *strvalue;
long value;
InstanceData this;
bool_t left_justify, zero_pad, long_flag, add_sign, fPrecision;
int min_width, precision, ch;
if (str == NULL) {
return ERROR;
}
str[0] = '\0';
this.buffer = str;
this.end = str + count - 1;
*this.end = '\0'; /* ensure null-termination in case of failure */
while ((ch = *fmt++) != 0) {
if (ch == '%') {
zero_pad = long_flag = add_sign = fPrecision = FALSE;
left_justify = TRUE;
min_width = 0;
precision = this.end - this.buffer;
next_char:
ch = *fmt++;
switch (ch) {
case 0:
return ERROR;
case '-':
left_justify = FALSE;
goto next_char;
case '0': /* set zero padding if min_width not set */
if (min_width == 0)
zero_pad = TRUE;
case '1': case '2': case '3':
case '4': case '5': case '6':
case '7': case '8': case '9':
if (fPrecision == TRUE) {
precision = precision * 10 + (ch - '0');
} else {
min_width = min_width * 10 + (ch - '0');
}
goto next_char;
case '.':
fPrecision = TRUE;
precision = 0;
goto next_char;
case 'l':
long_flag = TRUE;
goto next_char;
case 's':
strvalue = va_arg(args, char *);
CheckRet(format_string(&this, strvalue, left_justify,
min_width, precision));
break;
case 'c':
ch = va_arg(args, int);
CheckRet(put_char(&this, ch));
break;
case '%':
CheckRet(put_char(&this, '%'));
break;
case 'd': case 'D':
case 'u': case 'U':
case 'o': case 'O':
case 'x': case 'X':
value = long_flag ? va_arg(args, long) : va_arg(args, int);
CheckRet(format_number(&this, value, ch, left_justify,
min_width, precision, zero_pad));
break;
default:
return ERROR;
}
} else {
CheckRet(put_char(&this, ch));
}
}
*this.buffer = '\0';
return strlen(str);
}
int jio_snprintf(char *str, size_t count, const char *fmt, ...)
{
va_list args;
int len;
va_start(args, fmt);
len = jio_vsnprintf(str, count, fmt, args);
va_end(args);
return len;
}
/* Return true if the two classes are in the same class package */
bool_t
IsSameClassPackage(ClassClass *class1, ClassClass *class2)
{
if (cbLoader(class1) != cbLoader(class2))
return FALSE;
else {
char *name1 = classname(class1);
char *name2 = classname(class2);
char *last_slash1 = strrchr(name1, DIR_SEPARATOR);
char *last_slash2 = strrchr(name2, DIR_SEPARATOR);
if ((last_slash1 == NULL) || (last_slash2 == NULL)) {
return (last_slash1 == last_slash2);
} else {
int length1 = last_slash1 - name1;
int length2 = last_slash2 - name2;
return ((length1 == length2)
&& (strncmp(name1, name2, length1) == 0));
}
}
}