OSDN Git Service

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