#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "jcf.h"
#include "tree.h"
#include "toplev.h"
#include "java-tree.h"
#include "hashtab.h"
#if JCF_USE_SCANDIR
#include <dirent.h>
#include <fnmatch.h>
#endif
#include "zlib.h"
#ifndef O_BINARY
#define O_BINARY 0
#endif
int
jcf_unexpected_eof (JCF *jcf, int count ATTRIBUTE_UNUSED)
{
if (jcf->filename)
fprintf (stderr, "Premature end of .class file %s.\n", jcf->filename);
else
fprintf (stderr, "Premature end of .class file <stdin>.\n");
exit (-1);
}
void
jcf_trim_old_input (JCF *jcf)
{
int count = jcf->read_ptr - jcf->buffer;
if (count > 0)
{
memmove (jcf->buffer, jcf->read_ptr, jcf->read_end - jcf->read_ptr);
jcf->read_ptr -= count;
jcf->read_end -= count;
}
}
int
jcf_filbuf_from_stdio (JCF *jcf, int count)
{
FILE *file = (FILE*) (jcf->read_state);
if (count > jcf->buffer_end - jcf->read_ptr)
{
JCF_u4 old_read_ptr = jcf->read_ptr - jcf->buffer;
JCF_u4 old_read_end = jcf->read_end - jcf->buffer;
JCF_u4 old_size = jcf->buffer_end - jcf->buffer;
JCF_u4 new_size = (old_size == 0 ? 2000 : 2 * old_size) + count;
unsigned char *new_buffer = jcf->buffer == NULL ? ALLOC (new_size)
: REALLOC (jcf->buffer, new_size);
jcf->buffer = new_buffer;
jcf->buffer_end = new_buffer + new_size;
jcf->read_ptr = new_buffer + old_read_ptr;
jcf->read_end = new_buffer + old_read_end;
}
count -= jcf->read_end - jcf->read_ptr;
if (count <= 0)
return 0;
if ((int) fread (jcf->read_end, 1, count, file) != count)
jcf_unexpected_eof (jcf, count);
jcf->read_end += count;
return 0;
}
#include "zipfile.h"
struct ZipFile *SeenZipFiles = NULL;
ZipFile *
opendir_in_zip (const char *zipfile, int is_system)
{
struct ZipFile* zipf;
char magic [4];
int fd;
for (zipf = SeenZipFiles; zipf != NULL; zipf = zipf->next)
{
if (strcmp (zipf->name, zipfile) == 0)
return zipf;
}
zipf = ALLOC (sizeof (struct ZipFile) + strlen (zipfile) + 1);
zipf->next = SeenZipFiles;
zipf->name = (char*)(zipf+1);
strcpy (zipf->name, zipfile);
fd = open (zipfile, O_RDONLY | O_BINARY);
zipf->fd = fd;
if (fd < 0)
{
zipf->count = 0;
zipf->dir_size = 0;
zipf->central_directory = NULL;
}
else
{
jcf_dependency_add_file (zipfile, is_system);
if (read (fd, magic, 4) != 4 || GET_u4 (magic) != (JCF_u4)ZIPMAGIC)
return NULL;
lseek (fd, 0L, SEEK_SET);
if (read_zip_archive (zipf) != 0)
return NULL;
}
SeenZipFiles = zipf;
return zipf;
}
int
open_in_zip (JCF *jcf, const char *zipfile, const char *zipmember,
int is_system)
{
ZipDirectory *zipd;
int i, len;
ZipFile *zipf = opendir_in_zip (zipfile, is_system);
if (zipf == NULL)
return -2;
if (!zipmember)
return 0;
len = strlen (zipmember);
zipd = (struct ZipDirectory*) zipf->central_directory;
for (i = 0; i < zipf->count; i++, zipd = ZIPDIR_NEXT (zipd))
{
if (len == zipd->filename_length &&
strncmp (ZIPDIR_FILENAME (zipd), zipmember, len) == 0)
{
JCF_ZERO (jcf);
jcf->filename = xstrdup (zipfile);
jcf->classname = xstrdup (zipmember);
return read_zip_member(jcf, zipd, zipf);
}
}
return -1;
}
int
read_zip_member (JCF *jcf, ZipDirectory *zipd, ZipFile *zipf)
{
jcf->filbuf = jcf_unexpected_eof;
jcf->zipd = (void *)zipd;
if (zipd->compression_method == Z_NO_COMPRESSION)
{
jcf->buffer = ALLOC (zipd->size);
jcf->buffer_end = jcf->buffer + zipd->size;
jcf->read_ptr = jcf->buffer;
jcf->read_end = jcf->buffer_end;
if (lseek (zipf->fd, zipd->filestart, 0) < 0
|| read (zipf->fd, jcf->buffer, zipd->size) != (long) zipd->size)
return -2;
}
else
{
char *buffer;
z_stream d_stream;
d_stream.zalloc = (alloc_func) 0;
d_stream.zfree = (free_func) 0;
d_stream.opaque = (voidpf) 0;
jcf->buffer = ALLOC (zipd->uncompressed_size);
d_stream.next_out = jcf->buffer;
d_stream.avail_out = zipd->uncompressed_size;
jcf->buffer_end = jcf->buffer + zipd->uncompressed_size;
jcf->read_ptr = jcf->buffer;
jcf->read_end = jcf->buffer_end;
buffer = ALLOC (zipd->size);
d_stream.next_in = (unsigned char *) buffer;
d_stream.avail_in = zipd->size;
if (lseek (zipf->fd, zipd->filestart, 0) < 0
|| read (zipf->fd, buffer, zipd->size) != (long) zipd->size)
return -2;
inflateInit2 (&d_stream, -MAX_WBITS);
inflate (&d_stream, Z_NO_FLUSH);
inflateEnd (&d_stream);
FREE (buffer);
}
return 0;
}
const char *
open_class (const char *filename, JCF *jcf, int fd, const char *dep_name)
{
if (jcf)
{
struct stat stat_buf;
if (fstat (fd, &stat_buf) != 0
|| ! S_ISREG (stat_buf.st_mode))
{
perror ("Could not figure length of .class file");
return NULL;
}
if (dep_name != NULL)
jcf_dependency_add_file (dep_name, 0);
JCF_ZERO (jcf);
jcf->buffer = ALLOC (stat_buf.st_size);
jcf->buffer_end = jcf->buffer + stat_buf.st_size;
jcf->read_ptr = jcf->buffer;
jcf->read_end = jcf->buffer_end;
jcf->read_state = NULL;
jcf->filename = filename;
if (read (fd, jcf->buffer, stat_buf.st_size) != stat_buf.st_size)
{
perror ("Failed to read .class file");
return NULL;
}
close (fd);
jcf->filbuf = jcf_unexpected_eof;
}
else
close (fd);
return filename;
}
const char *
find_classfile (char *filename, JCF *jcf, const char *dep_name)
{
int fd = open (filename, O_RDONLY | O_BINARY);
if (fd < 0)
return NULL;
return open_class (filename, jcf, fd, dep_name);
}
#if JCF_USE_SCANDIR
static int
compare_path (const void *key, const void *entry)
{
return strcmp ((const char *) key,
(*((const struct dirent **) entry))->d_name);
}
static int
java_or_class_file (const struct dirent *entry)
{
const char *base = lbasename (entry->d_name);
return (fnmatch ("*.java", base, 0) == 0 ||
fnmatch ("*.class", base, 0) == 0);
}
typedef struct memoized_dirlist_entry
{
const char *dir;
int num_files;
struct dirent **files;
} memoized_dirlist_entry;
static int
memoized_dirlist_lookup_eq (const void *entry, const void *key)
{
return strcmp ((const char *) key,
((const memoized_dirlist_entry *) entry)->dir) == 0;
}
static htab_t memoized_dirlists;
#endif
static int
caching_stat (char *filename, struct stat *buf)
{
#if JCF_USE_SCANDIR
char *sep;
char origsep = 0;
char *base;
memoized_dirlist_entry *dent;
void **slot;
if (!memoized_dirlists)
memoized_dirlists = htab_create (37,
htab_hash_string,
memoized_dirlist_lookup_eq,
NULL);
sep = strrchr (filename, DIR_SEPARATOR);
#ifdef DIR_SEPARATOR_2
if (! sep)
sep = strrchr (filename, DIR_SEPARATOR_2);
#endif
if (sep)
{
origsep = *sep;
*sep = '\0';
base = sep + 1;
}
else
base = filename;
slot = htab_find_slot (memoized_dirlists, filename, INSERT);
if (!*slot)
{
dent = ((memoized_dirlist_entry *)
ALLOC (sizeof (memoized_dirlist_entry)));
dent->dir = xstrdup (filename);
dent->num_files = __extension__ scandir (filename, &dent->files,
(void *) java_or_class_file,
alphasort);
*slot = dent;
}
else
dent = *((memoized_dirlist_entry **) slot);
if (sep)
*sep = origsep;
if (dent->num_files != -1
&& !bsearch (base, dent->files, dent->num_files,
sizeof (struct dirent *), compare_path))
return -1;
#endif
return stat (filename, buf);
}
static int
memoized_class_lookup_eq (const void *table_entry, const void *classname)
{
return strcmp ((const char *)classname, (const char *)table_entry) == 0;
}
static htab_t memoized_class_lookups;
const char *
find_class (const char *classname, int classname_length, JCF *jcf,
int source_ok)
{
int fd;
int i, k, java = -1, class = -1;
struct stat java_buf, class_buf;
char *dep_file;
void *entry;
char *java_buffer;
int buflen;
char *buffer;
hashval_t hash;
if (!memoized_class_lookups)
memoized_class_lookups = htab_create (37,
htab_hash_string,
memoized_class_lookup_eq,
NULL);
hash = htab_hash_string (classname);
if (htab_find_with_hash (memoized_class_lookups, classname, hash))
return NULL;
buflen = jcf_path_max_len () + classname_length + 10;
buffer = ALLOC (buflen);
memset (buffer, 0, buflen);
java_buffer = alloca (buflen);
jcf->java_source = 0;
for (entry = jcf_path_start (); entry != NULL; entry = jcf_path_next (entry))
{
const char *path_name = jcf_path_name (entry);
if (class != 0)
{
int dir_len;
strcpy (buffer, path_name);
i = strlen (buffer);
dir_len = i - 1;
for (k = 0; k < classname_length; k++, i++)
{
char ch = classname[k];
buffer[i] = ch == '.' ? '/' : ch;
}
strcpy (buffer+i, ".class");
if (jcf_path_is_zipfile (entry))
{
int err_code;
JCF _jcf;
buffer[dir_len] = '\0';
SOURCE_FRONTEND_DEBUG
(("Trying [...%s]:%s",
&buffer[dir_len-(dir_len > 15 ? 15 : dir_len)],
buffer+dir_len+1));
if (jcf == NULL)
jcf = &_jcf;
err_code = open_in_zip (jcf, buffer, buffer+dir_len+1,
jcf_path_is_system (entry));
if (err_code == 0)
{
buffer[dir_len] = '(';
strcpy (buffer+i, ".class)");
if (jcf == &_jcf)
JCF_FINISH (jcf);
return buffer;
}
else
continue;
}
class = caching_stat(buffer, &class_buf);
}
if (source_ok)
{
int l, m;
strcpy (java_buffer, path_name);
l = strlen (java_buffer);
for (m = 0; m < classname_length; ++m)
java_buffer[m + l] = (classname[m] == '.'
? DIR_SEPARATOR : classname[m]);
strcpy (java_buffer + m + l, ".java");
java = caching_stat (java_buffer, &java_buf);
if (java == 0)
break;
}
}
if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime)
{
if (flag_newer)
warning ("source file for class %qs is newer than its matching class file. Source file %qs used instead", classname, java_buffer);
class = -1;
}
if (! java)
dep_file = java_buffer;
else
dep_file = buffer;
if (!class)
{
SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n",
classname+classname_length-
(classname_length <= 30 ?
classname_length : 30)));
fd = JCF_OPEN_EXACT_CASE (buffer, O_RDONLY | O_BINARY);
if (fd >= 0)
goto found;
}
if (!java)
{
strcpy (buffer, java_buffer);
SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n",
classname+classname_length-
(classname_length <= 30 ?
classname_length : 30)));
fd = JCF_OPEN_EXACT_CASE (buffer, O_RDONLY);
if (fd >= 0)
{
jcf->java_source = 1;
goto found;
}
}
free (buffer);
*htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT)
= (void *) classname;
return NULL;
found:
if (jcf->java_source)
{
JCF_ZERO (jcf);
jcf->java_source = 1;
jcf->filename = xstrdup (buffer);
close (fd);
}
else
buffer = (char *) open_class (buffer, jcf, fd, dep_file);
jcf->classname = xstrdup (classname);
return buffer;
}
void
jcf_print_char (FILE *stream, int ch)
{
switch (ch)
{
case '\'':
case '\\':
case '\"':
fprintf (stream, "\\%c", ch);
break;
case '\n':
fprintf (stream, "\\n");
break;
case '\t':
fprintf (stream, "\\t");
break;
case '\r':
fprintf (stream, "\\r");
break;
default:
if (ch >= ' ' && ch < 127)
putc (ch, stream);
else if (ch < 256)
fprintf (stream, "\\%03x", ch);
else
fprintf (stream, "\\u%04x", ch);
}
}
void
jcf_print_utf8 (FILE *stream, const unsigned char *str, int length)
{
const unsigned char * limit = str + length;
while (str < limit)
{
int ch = UTF8_GET (str, limit);
if (ch < 0)
{
fprintf (stream, "\\<invalid>");
return;
}
jcf_print_char (stream, ch);
}
}
void
jcf_print_utf8_replace (FILE *stream, const unsigned char *str, int length,
int in_char, int out_char)
{
const unsigned char *limit = str + length;
while (str < limit)
{
int ch = UTF8_GET (str, limit);
if (ch < 0)
{
fprintf (stream, "\\<invalid>");
return;
}
jcf_print_char (stream, ch == in_char ? out_char : ch);
}
}
int
verify_constant_pool (JCF *jcf)
{
int i, n;
for (i = 1; i < JPOOL_SIZE (jcf); i++)
{
switch (JPOOL_TAG (jcf, i))
{
case CONSTANT_NameAndType:
n = JPOOL_USHORT2 (jcf, i);
if (n <= 0 || n >= JPOOL_SIZE(jcf)
|| JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
return i;
case CONSTANT_Class:
case CONSTANT_String:
n = JPOOL_USHORT1 (jcf, i);
if (n <= 0 || n >= JPOOL_SIZE(jcf)
|| JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
return i;
break;
case CONSTANT_Fieldref:
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
n = JPOOL_USHORT1 (jcf, i);
if (n <= 0 || n >= JPOOL_SIZE(jcf)
|| JPOOL_TAG (jcf, n) != CONSTANT_Class)
return i;
n = JPOOL_USHORT2 (jcf, i);
if (n <= 0 || n >= JPOOL_SIZE(jcf)
|| JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
return i;
break;
case CONSTANT_Long:
case CONSTANT_Double:
i++;
break;
case CONSTANT_Float:
case CONSTANT_Integer:
case CONSTANT_Utf8:
case CONSTANT_Unicode:
break;
default:
return i;
}
}
return 0;
}
void
format_uint (char *buffer, uint64 value, int base)
{
#define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
char buf[WRITE_BUF_SIZE];
char *buf_ptr = buf+WRITE_BUF_SIZE;
int chars_written;
int i;
do {
int digit = value % base;
static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
*--buf_ptr = digit_chars[digit];
value /= base;
} while (value != 0);
chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
for (i = 0; i < chars_written; i++)
buffer[i] = *buf_ptr++;
buffer[i] = 0;
}
void
format_int (char *buffer, jlong value, int base)
{
uint64 abs_value;
if (value < 0)
{
abs_value = -(uint64)value;
*buffer++ = '-';
}
else
abs_value = (uint64) value;
format_uint (buffer, abs_value, base);
}