1/* Utility routines for finding and reading Java(TM) .class files.
2   Copyright (C) 1996, 97-98, 1999  Free Software Foundation, Inc.
3
4This program is free software; you can redistribute it and/or modify
5it under the terms of the GNU General Public License as published by
6the Free Software Foundation; either version 2, or (at your option)
7any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with GNU CC; see the file COPYING.  If not, write to
16the Free Software Foundation, 59 Temple Place - Suite 330,
17Boston, MA 02111-1307, USA.
18
19Java and all Java-based marks are trademarks or registered trademarks
20of Sun Microsystems, Inc. in the United States and other countries.
21The Free Software Foundation is independent of Sun Microsystems, Inc.  */
22
23/* Written by Per Bothner <bothner@cygnus.com>, February 1996. */
24
25#include "config.h"
26#include "system.h"
27
28#include "jcf.h"
29#include "tree.h"
30#include "java-tree.h"
31
32/* DOS brain-damage */
33#ifndef O_BINARY
34#define O_BINARY 0 /* MS-DOS brain-damage */
35#endif
36
37int
38DEFUN(jcf_unexpected_eof, (jcf, count),
39      JCF *jcf AND int count ATTRIBUTE_UNUSED)
40{
41  if (jcf->filename)
42    fprintf (stderr, "Premature end of .class file %s.\n", jcf->filename);
43  else
44    fprintf (stderr, "Premature end of .class file <stdin>.\n");
45  exit (-1);
46}
47
48void
49DEFUN(jcf_trim_old_input, (jcf),
50      JCF *jcf)
51{
52  int count = jcf->read_ptr - jcf->buffer;
53  if (count > 0)
54    {
55      memmove (jcf->buffer, jcf->read_ptr, jcf->read_end - jcf->read_ptr);
56      jcf->read_ptr -= count;
57      jcf->read_end -= count;
58    }
59}
60
61int
62DEFUN(jcf_filbuf_from_stdio, (jcf, count),
63      JCF *jcf AND int count)
64{
65  FILE *file = (FILE*) (jcf->read_state);
66  if (count > jcf->buffer_end - jcf->read_ptr)
67    {
68      JCF_u4 old_read_ptr = jcf->read_ptr - jcf->buffer;
69      JCF_u4 old_read_end = jcf->read_end - jcf->buffer;
70      JCF_u4 old_size = jcf->buffer_end - jcf->buffer;
71      JCF_u4 new_size = (old_size == 0 ? 2000 : 2 * old_size) + count;
72      unsigned char *new_buffer = jcf->buffer == NULL ? ALLOC (new_size)
73	: REALLOC (jcf->buffer, new_size);
74      jcf->buffer = new_buffer;
75      jcf->buffer_end = new_buffer + new_size;
76      jcf->read_ptr = new_buffer + old_read_ptr;
77      jcf->read_end = new_buffer + old_read_end;
78    }
79  count -= jcf->read_end - jcf->read_ptr;
80  if (count <= 0)
81    return 0;
82  if ((int) fread (jcf->read_end, 1, count, file) != count)
83    jcf_unexpected_eof (jcf, count);
84  jcf->read_end += count;
85  return 0;
86}
87
88#include "zipfile.h"
89
90struct ZipFileCache *SeenZipFiles = NULL;
91
92/* Open a zip file with the given name, and cache directory and file
93   descriptor.  If the file is missing, treat it as an empty archive.
94   Return NULL if the .zip file is malformed.
95*/
96
97ZipFile *
98DEFUN(opendir_in_zip, (zipfile, is_system),
99      const char *zipfile AND int is_system)
100{
101  struct ZipFileCache* zipf;
102  char magic [4];
103  int fd;
104  for (zipf = SeenZipFiles;  zipf != NULL;  zipf = zipf->next)
105    {
106      if (strcmp (zipf->name, zipfile) == 0)
107	return &zipf->z;
108    }
109
110  zipf = ALLOC (sizeof (struct ZipFileCache) + strlen (zipfile) + 1);
111  zipf->next = SeenZipFiles;
112  zipf->name = (char*)(zipf+1);
113  strcpy (zipf->name, zipfile);
114  SeenZipFiles = zipf;
115  fd = open (zipfile, O_RDONLY | O_BINARY);
116  zipf->z.fd = fd;
117  if (fd < 0)
118    {
119      /* A missing zip file is not considered an error.
120       We may want to re-consider that.  FIXME. */
121      zipf->z.count = 0;
122      zipf->z.dir_size = 0;
123      zipf->z.central_directory = NULL;
124    }
125  else
126    {
127      jcf_dependency_add_file (zipfile, is_system);
128      if (read (fd, magic, 4) != 4 || GET_u4 (magic) != (JCF_u4)ZIPMAGIC)
129	return NULL;
130      lseek (fd, 0L, SEEK_SET);
131      if (read_zip_archive (&zipf->z) != 0)
132	return NULL;
133    }
134  return &zipf->z;
135}
136
137/* Returns:
138   0:  OK - zipmember found.
139   -1: Not found.
140   -2: Malformed archive.
141*/
142
143int
144DEFUN(open_in_zip, (jcf, zipfile, zipmember, is_system),
145      JCF *jcf AND const char *zipfile AND const char *zipmember
146      AND int is_system)
147{
148  ZipDirectory *zipd;
149  int i, len;
150  ZipFile *zipf = opendir_in_zip (zipfile, is_system);
151
152  if (zipf == NULL)
153    return -2;
154
155  if (!zipmember)
156    return 0;
157
158  len = strlen (zipmember);
159
160  zipd = (struct ZipDirectory*) zipf->central_directory;
161  for (i = 0; i < zipf->count; i++, zipd = ZIPDIR_NEXT (zipd))
162    {
163      if (len == zipd->filename_length &&
164	  strncmp (ZIPDIR_FILENAME (zipd), zipmember, len) == 0)
165	{
166	  JCF_ZERO (jcf);
167	  jcf->buffer = ALLOC (zipd->size);
168	  jcf->buffer_end = jcf->buffer + zipd->size;
169	  jcf->read_ptr = jcf->buffer;
170	  jcf->read_end = jcf->buffer_end;
171	  jcf->filbuf = jcf_unexpected_eof;
172	  jcf->filename = strdup (zipfile);
173	  jcf->classname = strdup (zipmember);
174	  jcf->zipd = (void *)zipd;
175	  if (lseek (zipf->fd, zipd->filestart, 0) < 0
176	      || read (zipf->fd, jcf->buffer, zipd->size) != zipd->size)
177	    return -2;
178	  return 0;
179	}
180    }
181  return -1;
182}
183
184#if JCF_USE_STDIO
185char*
186DEFUN(open_class, (filename, jcf, stream, dep_name),
187      char *filename AND JCF *jcf AND FILE* stream AND const char *dep_name)
188{
189  if (jcf)
190    {
191      if (dep_name != NULL)
192	jcf_dependency_add_file (dep_name, 0);
193      JCF_ZERO (jcf);
194      jcf->buffer = NULL;
195      jcf->buffer_end = NULL;
196      jcf->read_ptr = NULL;
197      jcf->read_end = NULL;
198      jcf->read_state = stream;
199      jcf->filbuf = jcf_filbuf_from_stdio;
200    }
201  else
202    fclose (stream);
203  return filename;
204}
205#else
206char*
207DEFUN(open_class, (filename, jcf, fd, dep_name),
208      char *filename AND JCF *jcf AND int fd AND const char *dep_name)
209{
210  if (jcf)
211    {
212      struct stat stat_buf;
213      if (fstat (fd, &stat_buf) != 0
214	  || ! S_ISREG (stat_buf.st_mode))
215	{
216	  perror ("Could not figure length of .class file");
217	  return NULL;
218	}
219      if (dep_name != NULL)
220	jcf_dependency_add_file (dep_name, 0);
221      JCF_ZERO (jcf);
222      jcf->buffer = ALLOC (stat_buf.st_size);
223      jcf->buffer_end = jcf->buffer + stat_buf.st_size;
224      jcf->read_ptr = jcf->buffer;
225      jcf->read_end = jcf->buffer_end;
226      jcf->read_state = NULL;
227      jcf->filename = filename;
228      if (read (fd, jcf->buffer, stat_buf.st_size) != stat_buf.st_size)
229	{
230	  perror ("Failed to read .class file");
231	  return NULL;
232	}
233      close (fd);
234      jcf->filbuf = jcf_unexpected_eof;
235    }
236  else
237    close (fd);
238  return filename;
239}
240#endif
241
242
243char *
244DEFUN(find_classfile, (filename, jcf, dep_name),
245      char *filename AND JCF *jcf AND const char *dep_name)
246{
247#if JCF_USE_STDIO
248  FILE *stream = fopen (filename, "rb");
249  if (stream == NULL)
250    return NULL;
251  return open_class (arg, jcf, stream, dep_name);
252#else
253  int fd = open (filename, O_RDONLY | O_BINARY);
254  if (fd < 0)
255    return NULL;
256  return open_class (filename, jcf, fd, dep_name);
257#endif
258}
259
260/* Returns a freshly malloc'd string with the fully qualified pathname
261   of the .class file for the class CLASSNAME.  Returns NULL on
262   failure.  If JCF != NULL, it is suitably initialized.
263   SOURCE_OK is true if we should also look for .java file. */
264
265char *
266DEFUN(find_class, (classname, classname_length, jcf, source_ok),
267      const char *classname AND int classname_length AND JCF *jcf AND int source_ok)
268
269{
270#if JCF_USE_STDIO
271  FILE *stream;
272#else
273  int fd;
274#endif
275  int i, k, java = -1, class = -1;
276  struct stat java_buf, class_buf;
277  char *dep_file;
278  void *entry;
279  char *java_buffer;
280
281  /* Allocate and zero out the buffer, since we don't explicitly put a
282     null pointer when we're copying it below.  */
283  int buflen = jcf_path_max_len () + classname_length + 10;
284  char *buffer = (char *) ALLOC (buflen);
285  bzero (buffer, buflen);
286
287  java_buffer = (char *) alloca (buflen);
288
289  jcf->java_source = jcf->outofsynch = 0;
290
291  for (entry = jcf_path_start (); entry != NULL; entry = jcf_path_next (entry))
292    {
293      char *path_name = jcf_path_name (entry);
294      if (class != 0)
295	{
296	  int dir_len;
297
298	  strcpy (buffer, path_name);
299	  i = strlen (buffer);
300
301	  /* This is right because we know that `.zip' entries will have a
302	     trailing slash.  See jcf-path.c.  */
303	  dir_len = i - 1;
304
305	  for (k = 0; k < classname_length; k++, i++)
306	    {
307	      char ch = classname[k];
308	      buffer[i] = ch == '.' ? '/' : ch;
309	    }
310	  strcpy (buffer+i, ".class");
311
312	  if (jcf_path_is_zipfile (entry))
313	    {
314	      int err_code;
315	      JCF _jcf;
316	      buffer[dir_len] = '\0';
317	      SOURCE_FRONTEND_DEBUG
318		(("Trying [...%s]:%s",
319		  &buffer[dir_len-(dir_len > 15 ? 15 : dir_len)],
320		  buffer+dir_len+1));
321	      if (jcf == NULL)
322		jcf = &_jcf;
323	      err_code = open_in_zip (jcf, buffer, buffer+dir_len+1,
324				      jcf_path_is_system (entry));
325	      if (err_code == 0)
326		{
327		  /* Should we check if .zip is out-of-date wrt .java? */
328		  buffer[dir_len] = '(';
329		  strcpy (buffer+i, ".class)");
330		  if (jcf == &_jcf)
331		    JCF_FINISH (jcf);
332		  return buffer;
333		}
334	      else
335		continue;
336	    }
337	  class = stat (buffer, &class_buf);
338	}
339
340      if (source_ok)
341	{
342	  /* Compute name of .java file.  */
343	  int l, m;
344	  strcpy (java_buffer, path_name);
345	  l = strlen (java_buffer);
346	  for (m = 0; m < classname_length; ++m)
347	    java_buffer[m + l] = (classname[m] == '.' ? '/' : classname[m]);
348	  strcpy (java_buffer + m + l, ".java");
349	  java = stat (java_buffer, &java_buf);
350	  if (java == 0)
351	    break;
352	}
353    }
354
355  if (! java && ! class && java_buf.st_mtime >= class_buf.st_mtime)
356    jcf->outofsynch = 1;
357
358  if (! java)
359    dep_file = java_buffer;
360  else
361    dep_file = buffer;
362#if JCF_USE_STDIO
363  if (!class)
364    {
365      SOURCE_FRONTEND_DEBUG (("Trying %s", buffer));
366      stream = fopen (buffer, "rb");
367      if (stream)
368	goto found;
369    }
370  /* Give .java a try, if necessary */
371  if (!java)
372    {
373      strcpy (buffer, java_buffer);
374      SOURCE_FRONTEND_DEBUG (("Trying %s", buffer));
375      stream = fopen (buffer, "r");
376      if (stream)
377	{
378	  jcf->java_source = 1;
379	  goto found;
380	}
381    }
382#else
383  if (!class)
384    {
385      SOURCE_FRONTEND_DEBUG (("Trying %s", buffer));
386      fd = open (buffer, O_RDONLY | O_BINARY);
387      if (fd >= 0)
388	goto found;
389    }
390  /* Give .java a try, if necessary */
391  if (!java)
392    {
393      strcpy (buffer, java_buffer);
394      SOURCE_FRONTEND_DEBUG (("Trying %s", buffer));
395      fd = open (buffer, O_RDONLY);
396      if (fd >= 0)
397	{
398	  jcf->java_source = 1;
399	  goto found;
400	}
401    }
402#endif
403
404  free (buffer);
405  return NULL;
406 found:
407#if JCF_USE_STDIO
408  if (jcf->java_source)
409    return NULL;		/* FIXME */
410  else
411    return open_class (buffer, jcf, stream, dep_file);
412#else
413  if (jcf->java_source)
414    {
415      JCF_ZERO (jcf);		/* JCF_FINISH relies on this */
416      jcf->java_source = 1;
417      jcf->filename = (char *) strdup (buffer);
418      close (fd);		/* We use STDIO for source file */
419    }
420  else
421    buffer = open_class (buffer, jcf, fd, dep_file);
422  jcf->classname = (char *) ALLOC (classname_length + 1);
423  strncpy (jcf->classname, classname, classname_length + 1);
424  jcf->classname = (char *) strdup (classname);
425  return buffer;
426#endif
427}
428
429void
430DEFUN(jcf_print_char, (stream, ch),
431      FILE *stream AND int ch)
432{
433  switch (ch)
434    {
435    case '\'':
436    case '\\':
437    case '\"':
438      fprintf (stream, "\\%c", ch);
439      break;
440    case '\n':
441      fprintf (stream, "\\n");
442      break;
443    case '\t':
444      fprintf (stream, "\\t");
445      break;
446    case '\r':
447      fprintf (stream, "\\r");
448      break;
449    default:
450      if (ch >= ' ' && ch < 127)
451	putc (ch, stream);
452      else if (ch < 256)
453	fprintf (stream, "\\%03x", ch);
454      else
455	fprintf (stream, "\\u%04x", ch);
456    }
457}
458
459/* Print UTF8 string at STR of length LENGTH bytes to STREAM. */
460
461void
462DEFUN(jcf_print_utf8, (stream, str, length),
463      FILE *stream AND register const unsigned char *str AND int length)
464{
465  const unsigned char * limit = str + length;
466  while (str < limit)
467    {
468      int ch = UTF8_GET (str, limit);
469      if (ch < 0)
470	{
471	  fprintf (stream, "\\<invalid>");
472	  return;
473	}
474      jcf_print_char (stream, ch);
475    }
476}
477
478/* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */
479
480void
481DEFUN(jcf_print_utf8_replace, (stream, str, length, in_char, out_char),
482      FILE *stream AND const unsigned char *str AND int length
483      AND int in_char AND int out_char)
484{
485
486  int i;/* FIXME - actually handle Unicode! */
487  for (i = 0; i < length; i++)
488    {
489      int ch = str[i];
490      jcf_print_char (stream, ch == in_char ? out_char : ch);
491    }
492}
493
494/* Check that all the cross-references in the constant pool are
495   valid.  Returns 0 on success.
496   Otherwise, returns the index of the (first) invalid entry. */
497
498int
499DEFUN(verify_constant_pool, (jcf),
500      JCF *jcf)
501{
502  int i, n;
503  for (i = 1; i < JPOOL_SIZE (jcf); i++)
504    {
505      switch (JPOOL_TAG (jcf, i))
506	{
507	case CONSTANT_NameAndType:
508	  n = JPOOL_USHORT2 (jcf, i);
509	  if (n <= 0 || n >= JPOOL_SIZE(jcf)
510	      || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
511	    return i;
512	  /* ... fall through ... */
513	case CONSTANT_Class:
514	case CONSTANT_String:
515	  n = JPOOL_USHORT1 (jcf, i);
516	  if (n <= 0 || n >= JPOOL_SIZE(jcf)
517	      || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
518	    return i;
519	  break;
520	case CONSTANT_Fieldref:
521	case CONSTANT_Methodref:
522	case CONSTANT_InterfaceMethodref:
523	  n = JPOOL_USHORT1 (jcf, i);
524	  if (n <= 0 || n >= JPOOL_SIZE(jcf)
525	      || JPOOL_TAG (jcf, n) != CONSTANT_Class)
526	    return i;
527	  n = JPOOL_USHORT2 (jcf, i);
528	  if (n <= 0 || n >= JPOOL_SIZE(jcf)
529	      || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
530	    return i;
531	  break;
532	case CONSTANT_Long:
533	case CONSTANT_Double:
534	  i++;
535	  break;
536	case CONSTANT_Float:
537	case CONSTANT_Integer:
538	case CONSTANT_Utf8:
539	case CONSTANT_Unicode:
540	  break;
541	default:
542	  return i;
543	}
544    }
545  return 0;
546}
547
548void
549DEFUN(format_uint, (buffer, value, base),
550      char *buffer AND uint64 value AND int base)
551{
552#define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
553  char buf[WRITE_BUF_SIZE];
554  register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */
555  int chars_written;
556  int i;
557
558  /* Now do the actual conversion, placing the result at the *end* of buf. */
559  /* Note this code does not pretend to be optimized. */
560  do {
561    int digit = value % base;
562    static char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
563    *--buf_ptr = digit_chars[digit];
564    value /= base;
565  } while (value != 0);
566
567  chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
568  for (i = 0; i < chars_written; i++)
569    buffer[i] = *buf_ptr++;
570  buffer[i] = 0;
571}
572
573void
574DEFUN(format_int, (buffer, value, base),
575      char *buffer AND jlong value AND int base)
576{
577  uint64 abs_value;
578  if (value < 0)
579    {
580      abs_value = -(uint64)value;
581      *buffer++ = '-';
582    }
583  else
584    abs_value = (uint64) value;
585  format_uint (buffer, abs_value, base);
586}
587