/* Utility routines for finding and reading Java(TM) .class files. Copyright (C) 1996, 1998 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Java and all Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. The Free Software Foundation is independent of Sun Microsystems, Inc. */ /* Written by Per Bothner , February 1996. */ #include "config.h" #include "system.h" #define ENABLE_UNZIP 1 #include "jcf.h" #include #include /* DOS brain-damage */ #ifndef O_BINARY #define O_BINARY 0 /* MS-DOS brain-damage */ #endif char *classpath; int DEFUN(jcf_unexpected_eof, (jcf, count), JCF *jcf AND int count) { if (jcf->filename) fprintf (stderr, "Premature end of .class file %s.\n", jcf->filename); else fprintf (stderr, "Premature end of .class file .\n"); exit (-1); } void DEFUN(jcf_trim_old_input, (jcf), 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 DEFUN(jcf_filbuf_from_stdio, (jcf, count), JCF *jcf AND 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 (fread (jcf->read_end, 1, count, file) != count) jcf_unexpected_eof (jcf, count); jcf->read_end += count; return 0; } #if ENABLE_UNZIP #include "zipfile.h" struct ZipFileCache *SeenZipFiles = NULL; int DEFUN(open_in_zip, (jcf, zipfile, zipmember), JCF *jcf AND const char *zipfile AND const char *zipmember) { struct ZipFileCache* zipf; ZipDirectory *zipd; int i, len; for (zipf = SeenZipFiles; ; zipf = zipf->next) { if (zipf == NULL) { char magic [4]; int fd = open (zipfile, O_RDONLY | O_BINARY); jcf_dependency_add_file (zipfile, 0); /* FIXME: system file? */ if (read (fd, magic, 4) != 4 || GET_u4 (magic) != (JCF_u4)ZIPMAGIC) return -1; lseek (fd, 0L, SEEK_SET); zipf = ALLOC (sizeof (struct ZipFileCache) + strlen (zipfile) + 1); zipf->next = SeenZipFiles; zipf->name = (char*)(zipf+1); strcpy (zipf->name, zipfile); SeenZipFiles = zipf; zipf->z.fd = fd; if (fd == -1) { /* A missing zip file is not considered an error. */ zipf->z.count = 0; zipf->z.dir_size = 0; zipf->z.central_directory = NULL; return -1; } else { if (read_zip_archive (&zipf->z) != 0) return -2; /* This however should be an error - FIXME */ } break; } if (strcmp (zipf->name, zipfile) == 0) break; } if (!zipmember) return 0; len = strlen (zipmember); zipd = (struct ZipDirectory*) zipf->z.central_directory; for (i = 0; i < zipf->z.count; i++, zipd = ZIPDIR_NEXT (zipd)) { if (len == zipd->filename_length && strncmp (ZIPDIR_FILENAME (zipd), zipmember, len) == 0) { JCF_ZERO (jcf); jcf->buffer = ALLOC (zipd->size); jcf->buffer_end = jcf->buffer + zipd->size; jcf->read_ptr = jcf->buffer; jcf->read_end = jcf->buffer_end; jcf->filbuf = jcf_unexpected_eof; jcf->filename = strdup (zipfile); jcf->classname = strdup (zipmember); jcf->zipd = (void *)zipd; if (lseek (zipf->z.fd, zipd->filestart, 0) < 0 || read (zipf->z.fd, jcf->buffer, zipd->size) != zipd->size) return -2; return 0; } } return -1; } #endif /* ENABLE_UNZIP */ #if JCF_USE_STDIO char* DEFUN(open_class, (filename, jcf, stream, dep_name), char *filename AND JCF *jcf AND FILE* stream AND char *dep_name) { if (jcf) { if (dep_name != NULL) jcf_dependency_add_file (dep_name, 0); JCF_ZERO (jcf); jcf->buffer = NULL; jcf->buffer_end = NULL; jcf->read_ptr = NULL; jcf->read_end = NULL; jcf->read_state = stream; jcf->filbuf = jcf_filbuf_from_stdio; } else fclose (stream); return filename; } #else char* DEFUN(open_class, (filename, jcf, fd, dep_name), char *filename AND JCF *jcf AND int fd AND 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; } #endif char * DEFUN(find_classfile, (filename, jcf, dep_name), char *filename AND JCF *jcf AND char *dep_name) { #if JCF_USE_STDIO FILE *stream = fopen (filename, "rb"); if (stream == NULL) return NULL; return open_class (arg, jcf, stream, dep_name); #else int fd = open (filename, O_RDONLY | O_BINARY); if (fd < 0) return NULL; return open_class (filename, jcf, fd, dep_name); #endif } /* Returns a freshly malloc'd string with the fully qualified pathname of the .class file for the class CLASSNAME. Returns NULL on failure. If JCF != NULL, it is suitably initialized. With DO_CLASS_FILE set to 1, search a .class/.java file named after CLASSNAME, otherwise, search a ZIP directory entry named after CLASSNAME. */ char * DEFUN(find_class, (classname, classname_length, jcf, do_class_file), const char *classname AND int classname_length AND JCF *jcf AND int do_class_file) { #if JCF_USE_STDIO FILE *stream; #else int fd; #endif int i, j, k, java, class; struct stat java_buf, class_buf; char *dep_file; /* A temporary buffer that we grow to be large enough to hold whatever class name we're working on. */ static int temp_len = 0; static char *temp_buffer = NULL; /* Allocate and zero out the buffer, since we don't explicitly put a null pointer when we're copying it below. */ int buflen = strlen (classpath) + classname_length + 10; char *buffer = (char *) ALLOC (buflen); bzero (buffer, buflen); if (buflen > temp_len) { temp_len = buflen; if (temp_buffer == NULL) temp_buffer = (char *) ALLOC (temp_len); else temp_buffer = (char *) REALLOC (temp_buffer, temp_len); } jcf->java_source = jcf->outofsynch = 0; for (j = 0; classpath[j] != '\0'; ) { for (i = 0; classpath[j] != ':' && classpath[j] != '\0'; i++, j++) buffer[i] = classpath[j]; if (classpath[j] == ':') j++; if (i > 0) /* Empty directory is redundant */ { int dir_len; if (buffer[i-1] != '/') buffer[i++] = '/'; dir_len = i-1; for (k = 0; k < classname_length; k++, i++) { char ch = classname[k]; buffer[i] = ch == '.' ? '/' : ch; } if (do_class_file) strcpy (buffer+i, ".class"); #if ENABLE_UNZIP if (dir_len > 4 && buffer[dir_len-4] == '.' && buffer[dir_len-3] == 'z' && buffer[dir_len-2] == 'i' && buffer[dir_len-1] == 'p') { int err_code; JCF _jcf; if (!do_class_file) strcpy (buffer+i, "/"); buffer[dir_len] = '\0'; if (do_class_file) 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); if (err_code == 0) { if (!do_class_file) jcf->seen_in_zip = 1; else { buffer[dir_len] = '('; strcpy (buffer+i, ".class)"); } if (jcf == &_jcf) JCF_FINISH (jcf); return buffer; } else continue; } #endif /* If we do directories, do them here */ if (!do_class_file) { struct stat dir_buff; int dir; buffer[i] = '\0'; /* Was previously unterminated here. */ if (!(dir = stat (buffer, &dir_buff))) { jcf->seen_in_zip = 0; goto found; } } /* Check for out of synch .class/.java files */ class = stat (buffer, &class_buf); strcpy (buffer+i, ".java"); /* Stash the name of the .java file in the temp buffer. */ strcpy (temp_buffer, buffer); java = stat (buffer, &java_buf); if ((!java && !class) && java_buf.st_mtime >= class_buf.st_mtime) jcf->outofsynch = 1; if (! java) dep_file = temp_buffer; else dep_file = buffer; #if JCF_USE_STDIO if (!class) { strcpy (buffer+i, ".class"); SOURCE_FRONTEND_DEBUG (("Trying %s", buffer)); stream = fopen (buffer, "rb"); if (stream) goto found; } /* Give .java a try, if necessary */ if (!java) { strcpy (buffer+i, ".java"); SOURCE_FRONTEND_DEBUG (("Trying %s", buffer)); stream = fopen (buffer, "r"); if (stream) { jcf->java_source = 1; goto found; } } #else if (!class) { strcpy (buffer+i, ".class"); SOURCE_FRONTEND_DEBUG (("Trying %s", buffer)); fd = open (buffer, O_RDONLY | O_BINARY); if (fd >= 0) goto found; } /* Give .java a try, if necessary */ if (!java) { if (do_class_file) strcpy (buffer+i, ".java"); SOURCE_FRONTEND_DEBUG (("Trying %s", buffer)); fd = open (buffer, O_RDONLY | O_BINARY); if (fd >= 0) { jcf->java_source = 1; goto found; } } #endif } } free (buffer); return NULL; found: #if JCF_USE_STDIO if (jcf->java_source) return NULL; /* FIXME */ else return open_class (buffer, jcf, stream, dep_file); #else if (jcf->java_source) { JCF_ZERO (jcf); /* JCF_FINISH relies on this */ jcf->java_source = 1; jcf->filename = (char *) strdup (buffer); close (fd); /* We use STDIO for source file */ } else if (do_class_file) buffer = open_class (buffer, jcf, fd, dep_file); jcf->classname = (char *) ALLOC (classname_length + 1); strncpy (jcf->classname, classname, classname_length + 1); jcf->classname = (char *) strdup (classname); return buffer; #endif } void DEFUN(jcf_print_char, (stream, ch), FILE *stream AND 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); } } /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */ void DEFUN(jcf_print_utf8, (stream, str, length), FILE *stream AND register unsigned char *str AND int length) { unsigned char* limit = str + length; while (str < limit) { int ch = UTF8_GET (str, limit); if (ch < 0) { fprintf (stream, "\\"); return; } jcf_print_char (stream, ch); } } /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */ void DEFUN(jcf_print_utf8_replace, (stream, str, length, in_char, out_char), FILE *stream AND unsigned char *str AND int length AND int in_char AND int out_char) { int i;/* FIXME - actually handle Unicode! */ for (i = 0; i < length; i++) { int ch = str[i]; jcf_print_char (stream, ch == in_char ? out_char : ch); } } /* Check that all the cross-references in the constant pool are valid. Returns 0 on success. Otherwise, returns the index of the (first) invalid entry. */ int DEFUN(verify_constant_pool, (jcf), 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; /* ... fall through ... */ 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 DEFUN(format_uint, (buffer, value, base), char *buffer AND uint64 value AND int base) { #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8) char buf[WRITE_BUF_SIZE]; register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */ int chars_written; int i; /* Now do the actual conversion, placing the result at the *end* of buf. */ /* Note this code does not pretend to be optimized. */ do { int digit = value % base; static 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 DEFUN(format_int, (buffer, value, base), char *buffer AND jlong value AND int base) { uint64 abs_value; if (value < 0) { abs_value = -(uint64)value; *buffer++ = '-'; } else abs_value = (uint64) value; format_uint (buffer, abs_value, base); }