OSDN Git Service

* configure.in (AC_CHECK_FUNCS): Add checks for scandir and
[pf3gnuchains/gcc-fork.git] / gcc / java / jcf-io.c
1 /* Utility routines for finding and reading Java(TM) .class files.
2    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002  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 "config.h"
26 #include "system.h"
27
28 #include "jcf.h"
29 #include "tree.h"
30 #include "toplev.h"
31 #include "java-tree.h"
32 #include "hashtab.h"
33 #if JCF_USE_SCANDIR
34 #include <dirent.h>
35 #include <fnmatch.h>
36 #endif
37
38 #include "zlib.h"
39
40 /* DOS brain-damage */
41 #ifndef O_BINARY
42 #define O_BINARY 0 /* MS-DOS brain-damage */
43 #endif
44
45 int
46 DEFUN(jcf_unexpected_eof, (jcf, count),
47       JCF *jcf AND int count ATTRIBUTE_UNUSED)
48 {
49   if (jcf->filename)
50     fprintf (stderr, "Premature end of .class file %s.\n", jcf->filename);
51   else
52     fprintf (stderr, "Premature end of .class file <stdin>.\n");
53   exit (-1);
54 }
55
56 void
57 DEFUN(jcf_trim_old_input, (jcf),
58       JCF *jcf)
59 {
60   int count = jcf->read_ptr - jcf->buffer;
61   if (count > 0)
62     {
63       memmove (jcf->buffer, jcf->read_ptr, jcf->read_end - jcf->read_ptr);
64       jcf->read_ptr -= count;
65       jcf->read_end -= count;
66     }
67 }
68
69 int
70 DEFUN(jcf_filbuf_from_stdio, (jcf, count),
71       JCF *jcf AND int count)
72 {
73   FILE *file = (FILE*) (jcf->read_state);
74   if (count > jcf->buffer_end - jcf->read_ptr)
75     {
76       JCF_u4 old_read_ptr = jcf->read_ptr - jcf->buffer;
77       JCF_u4 old_read_end = jcf->read_end - jcf->buffer;
78       JCF_u4 old_size = jcf->buffer_end - jcf->buffer;
79       JCF_u4 new_size = (old_size == 0 ? 2000 : 2 * old_size) + count;
80       unsigned char *new_buffer = jcf->buffer == NULL ? ALLOC (new_size)
81         : REALLOC (jcf->buffer, new_size);
82       jcf->buffer = new_buffer;
83       jcf->buffer_end = new_buffer + new_size;
84       jcf->read_ptr = new_buffer + old_read_ptr;
85       jcf->read_end = new_buffer + old_read_end;
86     }
87   count -= jcf->read_end - jcf->read_ptr;
88   if (count <= 0)
89     return 0;
90   if ((int) fread (jcf->read_end, 1, count, file) != count)
91     jcf_unexpected_eof (jcf, count);
92   jcf->read_end += count;
93   return 0;
94 }
95
96 #include "zipfile.h"
97
98 struct ZipFile *SeenZipFiles = NULL;
99
100 /* Open a zip file with the given name, and cache directory and file
101    descriptor.  If the file is missing, treat it as an empty archive.
102    Return NULL if the .zip file is malformed.
103 */
104
105 ZipFile *
106 DEFUN(opendir_in_zip, (zipfile, is_system),
107       const char *zipfile AND int is_system)
108 {
109   struct ZipFile* zipf;
110   char magic [4];
111   int fd;
112   for (zipf = SeenZipFiles;  zipf != NULL;  zipf = zipf->next)
113     {
114       if (strcmp (zipf->name, zipfile) == 0)
115         return zipf;
116     }
117
118   zipf = ALLOC (sizeof (struct ZipFile) + strlen (zipfile) + 1);
119   zipf->next = SeenZipFiles;
120   zipf->name = (char*)(zipf+1);
121   strcpy (zipf->name, zipfile);
122   SeenZipFiles = zipf;
123   fd = open (zipfile, O_RDONLY | O_BINARY);
124   zipf->fd = fd;
125   if (fd < 0)
126     {
127       /* A missing zip file is not considered an error.
128        We may want to re-consider that.  FIXME. */
129       zipf->count = 0;
130       zipf->dir_size = 0;
131       zipf->central_directory = NULL;
132     }
133   else
134     {
135       jcf_dependency_add_file (zipfile, is_system);
136       if (read (fd, magic, 4) != 4 || GET_u4 (magic) != (JCF_u4)ZIPMAGIC)
137         return NULL;
138       lseek (fd, 0L, SEEK_SET);
139       if (read_zip_archive (zipf) != 0)
140         return NULL;
141     }
142   return zipf;
143 }
144
145 /* Returns:
146    0:  OK - zipmember found.
147    -1: Not found.
148    -2: Malformed archive.
149 */
150
151 int
152 DEFUN(open_in_zip, (jcf, zipfile, zipmember, is_system),
153       JCF *jcf AND const char *zipfile AND const char *zipmember
154       AND int is_system)
155 {
156   ZipDirectory *zipd;
157   int i, len;
158   ZipFile *zipf = opendir_in_zip (zipfile, is_system);
159
160   if (zipf == NULL)
161     return -2;
162
163   if (!zipmember)
164     return 0;
165
166   len = strlen (zipmember);
167   
168   zipd = (struct ZipDirectory*) zipf->central_directory;
169   for (i = 0; i < zipf->count; i++, zipd = ZIPDIR_NEXT (zipd))
170     {
171       if (len == zipd->filename_length &&
172           strncmp (ZIPDIR_FILENAME (zipd), zipmember, len) == 0)
173         {
174           JCF_ZERO (jcf);
175
176           jcf->filename = xstrdup (zipfile);
177           jcf->classname = xstrdup (zipmember);
178           return read_zip_member(jcf, zipd, zipf);
179         }
180     }
181   return -1;
182 }
183
184 /* Read data from zip archive member. */
185
186 int
187 DEFUN(read_zip_member, (jcf, zipd, zipf),
188       JCF *jcf AND  ZipDirectory *zipd AND ZipFile *zipf)
189 {
190           jcf->filbuf = jcf_unexpected_eof;
191           jcf->zipd = (void *)zipd;
192
193           if (zipd->compression_method == Z_NO_COMPRESSION)
194             {
195               jcf->buffer = ALLOC (zipd->size);
196               jcf->buffer_end = jcf->buffer + zipd->size;
197               jcf->read_ptr = jcf->buffer;
198               jcf->read_end = jcf->buffer_end;
199               if (lseek (zipf->fd, zipd->filestart, 0) < 0
200                   || read (zipf->fd, jcf->buffer, zipd->size) != (long) zipd->size)
201                 return -2;
202             }
203           else
204             {
205               char *buffer;
206               z_stream d_stream; /* decompression stream */
207               d_stream.zalloc = (alloc_func) 0;
208               d_stream.zfree = (free_func) 0;
209               d_stream.opaque = (voidpf) 0;
210
211               jcf->buffer = ALLOC (zipd->uncompressed_size);
212               d_stream.next_out = jcf->buffer;
213               d_stream.avail_out = zipd->uncompressed_size;
214               jcf->buffer_end = jcf->buffer + zipd->uncompressed_size;
215               jcf->read_ptr = jcf->buffer;
216               jcf->read_end = jcf->buffer_end;
217               buffer = ALLOC (zipd->size);
218               d_stream.next_in = buffer;
219               d_stream.avail_in = zipd->size;
220               if (lseek (zipf->fd, zipd->filestart, 0) < 0
221                   || read (zipf->fd, buffer, zipd->size) != (long) zipd->size)
222                 return -2;
223               /* Handle NO_HEADER using undocumented zlib feature.
224                  This is a very common hack.  */
225               inflateInit2 (&d_stream, -MAX_WBITS);
226               inflate (&d_stream, Z_NO_FLUSH);
227               inflateEnd (&d_stream);
228               FREE (buffer);
229             }
230
231           return 0;
232 }
233
234 #if JCF_USE_STDIO
235 const char *
236 DEFUN(open_class, (filename, jcf, stream, dep_name),
237       const char *filename AND JCF *jcf AND FILE* stream
238       AND const char *dep_name)
239 {
240   if (jcf)
241     {
242       if (dep_name != NULL)
243         jcf_dependency_add_file (dep_name, 0);
244       JCF_ZERO (jcf);
245       jcf->buffer = NULL;
246       jcf->buffer_end = NULL;
247       jcf->read_ptr = NULL;
248       jcf->read_end = NULL;
249       jcf->read_state = stream;
250       jcf->filename = filename;
251       jcf->filbuf = jcf_filbuf_from_stdio;
252     }
253   else
254     fclose (stream);
255   return filename;
256 }
257 #else
258 const char *
259 DEFUN(open_class, (filename, jcf, fd, dep_name),
260       const char *filename AND JCF *jcf AND int fd AND const char *dep_name)
261 {
262   if (jcf)
263     {
264       struct stat stat_buf;
265       if (fstat (fd, &stat_buf) != 0
266           || ! S_ISREG (stat_buf.st_mode))
267         {
268           perror ("Could not figure length of .class file");
269           return NULL;
270         }
271       if (dep_name != NULL)
272         jcf_dependency_add_file (dep_name, 0);
273       JCF_ZERO (jcf);
274       jcf->buffer = ALLOC (stat_buf.st_size);
275       jcf->buffer_end = jcf->buffer + stat_buf.st_size;
276       jcf->read_ptr = jcf->buffer;
277       jcf->read_end = jcf->buffer_end;
278       jcf->read_state = NULL;
279       jcf->filename = filename;
280       if (read (fd, jcf->buffer, stat_buf.st_size) != stat_buf.st_size)
281         {
282           perror ("Failed to read .class file");
283           return NULL;
284         }
285       close (fd);
286       jcf->filbuf = jcf_unexpected_eof;
287     }
288   else
289     close (fd);
290   return filename;
291 }
292 #endif
293
294
295 const char *
296 DEFUN(find_classfile, (filename, jcf, dep_name),
297       char *filename AND JCF *jcf AND const char *dep_name)
298 {
299 #if JCF_USE_STDIO
300   FILE *stream = fopen (filename, "rb");
301   if (stream == NULL)
302     return NULL;
303   return open_class (arg, jcf, stream, dep_name);
304 #else
305   int fd = open (filename, O_RDONLY | O_BINARY);
306   if (fd < 0)
307     return NULL;
308   return open_class (filename, jcf, fd, dep_name);
309 #endif
310 }
311
312 #if JCF_USE_SCANDIR
313
314 /* A comparison function (as for qsort) that compares KEY (a char *
315    giving the basename of a file) with the name stored in ENTRY (a
316    dirent **).  */
317
318 static int
319 DEFUN(compare_path, (key, entry),
320       const void *key AND const void *entry)
321 {
322   return strcmp ((const char *) key, 
323                  (*((const struct dirent **) entry))->d_name);
324 }
325
326 /* Returns nonzero if ENTRY names a .java or .class file.  */
327
328 static int
329 DEFUN(java_or_class_file, (entry),
330       const struct dirent *entry)
331 {
332   const char *base = basename (entry->d_name);
333   return (fnmatch ("*.java", base, 0) == 0 || 
334           fnmatch ("*.class", base, 0) == 0);
335 }
336
337 /* Information about the files present in a particular directory.  */
338 typedef struct memoized_dirlist_entry 
339 {
340   /* The name of the directory.  */
341   const char *dir;
342   /* The number of .java and .class files present, or -1 if we could
343      not, for some reason, obtain the list.  */
344   int num_files;
345   /* The .java and .class files in the directory, in alphabetical
346      order.  */
347   struct dirent **files;
348 } memoized_dirlist_entry;
349
350 /* Returns true if ENTRY (a memoized_dirlist_entry *) correponds to
351    the directory given by KEY (a char *) giving the directory 
352    name.  */
353
354 static int
355 DEFUN(memoized_dirlist_lookup_eq, (entry, key),
356       const void *entry AND const void *key)
357 {
358   return strcmp ((const char *) key,
359                  ((const memoized_dirlist_entry *) entry)->dir) == 0;
360 }
361
362 /* A hash table mapping directory names to the lists of .java and
363    .class files in that directory.  */
364
365 static htab_t memoized_dirlists;
366
367 #endif
368
369 /* Like stat, but avoids actually making the stat system call if we
370    know that it cannot succeed.  FILENAME and BUF are as for stat.  */
371
372 static int
373 DEFUN(caching_stat, (filename, buf),
374       char *filename AND struct stat *buf)
375 {
376 #if JCF_USE_SCANDIR
377   char *sep;
378   char *base;
379   memoized_dirlist_entry *dent;
380   void **slot;
381   
382   /* If the hashtable has not already been created, create it now.  */
383   if (!memoized_dirlists)
384     memoized_dirlists = htab_create (37,
385                                      htab_hash_string,
386                                      memoized_dirlist_lookup_eq,
387                                      NULL);
388
389   /* Get the name of the directory.  */
390   sep = strrchr (filename, DIR_SEPARATOR);
391   if (sep)
392     {
393       *sep = '\0';
394       base = sep + 1;
395     }
396   else
397     base = filename;
398
399   /* Obtain the entry for this directory form the hash table.  */
400   slot = htab_find_slot (memoized_dirlists, filename, INSERT);
401   if (!*slot)
402     {
403       /* We have not already scanned this directory; scan it now.  */
404       dent = ((memoized_dirlist_entry *) 
405               ALLOC (sizeof (memoized_dirlist_entry)));
406       dent->dir = xstrdup (filename);
407       /* Unfortunately, scandir is not fully standardized.  In
408          particular, the type of the function pointer passed as the
409          third argument sometimes takes a "const struct dirent *"
410          parameter, and sometimes just a "struct dirent *".  We rely
411          on the ability to interchange these two types of function
412          pointers.  */
413       dent->num_files = scandir (filename, &dent->files, 
414                                  java_or_class_file, 
415                                  alphasort);
416       *slot = dent;
417     }
418   else
419     dent = *((memoized_dirlist_entry **) slot);
420
421   /* Put the spearator back.  */
422   if (sep)
423     *sep = DIR_SEPARATOR;
424
425   /* If the file is not in the list, there is no need to stat it; it
426      does not exist.  */
427   if (dent->num_files != -1
428       && !bsearch (base, dent->files, dent->num_files,
429                    sizeof (struct dirent *), compare_path))
430     return -1;
431 #endif
432   
433   return stat (filename, buf);
434 }
435
436 /* Returns 1 if the CLASSNAME (really a char *) matches the name
437    stored in TABLE_ENTRY (also a char *).  */
438
439 static int
440 DEFUN(memoized_class_lookup_eq, (table_entry, classname),
441       const void *table_entry AND const void *classname)
442 {
443   return strcmp ((const char *)classname, (const char *)table_entry) == 0;
444 }
445
446 /* A hash table keeping track of class names that were not found
447    during class lookup.  (There is no need to cache the values
448    associated with names that were found; they are saved in
449    IDENTIFIER_CLASS_VALUE.)  */
450 static htab_t memoized_class_lookups;
451
452 /* Returns a freshly malloc'd string with the fully qualified pathname
453    of the .class file for the class CLASSNAME.  CLASSNAME must be
454    allocated in permanent storage; this function may retain a pointer
455    to it.  Returns NULL on failure.  If JCF != NULL, it is suitably
456    initialized.  SOURCE_OK is true if we should also look for .java
457    file. */
458
459 const char *
460 DEFUN(find_class, (classname, classname_length, jcf, source_ok),
461       const char *classname AND int classname_length AND JCF *jcf AND int source_ok)
462
463 {
464 #if JCF_USE_STDIO
465   FILE *stream;
466 #else
467   int fd;
468 #endif
469   int i, k, java = -1, class = -1;
470   struct stat java_buf, class_buf;
471   char *dep_file;
472   void *entry;
473   char *java_buffer;
474   int buflen;
475   char *buffer;
476   hashval_t hash;
477
478   /* Create the hash table, if it does not already exist.  */
479   if (!memoized_class_lookups)
480     memoized_class_lookups = htab_create (37, 
481                                           htab_hash_string, 
482                                           memoized_class_lookup_eq,
483                                           NULL);
484
485   /* Loop for this class in the hashtable.  If it is present, we've
486      already looked for this class and failed to find it.  */
487   hash = htab_hash_string (classname);
488   if (htab_find_with_hash (memoized_class_lookups, classname, hash))
489     return NULL;
490
491   /* Allocate and zero out the buffer, since we don't explicitly put a
492      null pointer when we're copying it below.  */
493   buflen = jcf_path_max_len () + classname_length + 10;
494   buffer = (char *) ALLOC (buflen);
495   memset (buffer, 0, buflen);
496
497   java_buffer = (char *) alloca (buflen);
498
499   jcf->java_source = 0;
500
501   for (entry = jcf_path_start (); entry != NULL; entry = jcf_path_next (entry))
502     {
503       const char *path_name = jcf_path_name (entry);
504       if (class != 0)
505         {
506           int dir_len;
507
508           strcpy (buffer, path_name);
509           i = strlen (buffer);
510
511           /* This is right because we know that `.zip' entries will have a
512              trailing slash.  See jcf-path.c.  */
513           dir_len = i - 1;
514
515           for (k = 0; k < classname_length; k++, i++)
516             {
517               char ch = classname[k];
518               buffer[i] = ch == '.' ? '/' : ch;
519             }
520           strcpy (buffer+i, ".class");
521
522           if (jcf_path_is_zipfile (entry))
523             {
524               int err_code;
525               JCF _jcf;
526               buffer[dir_len] = '\0';
527               SOURCE_FRONTEND_DEBUG 
528                 (("Trying [...%s]:%s", 
529                   &buffer[dir_len-(dir_len > 15 ? 15 : dir_len)], 
530                   buffer+dir_len+1));
531               if (jcf == NULL)
532                 jcf = &_jcf;
533               err_code = open_in_zip (jcf, buffer, buffer+dir_len+1,
534                                       jcf_path_is_system (entry));
535               if (err_code == 0)
536                 {
537                   /* Should we check if .zip is out-of-date wrt .java? */
538                   buffer[dir_len] = '(';
539                   strcpy (buffer+i, ".class)");
540                   if (jcf == &_jcf)
541                     JCF_FINISH (jcf);
542                   return buffer;
543                 }
544               else
545                 continue;
546             }
547           class = caching_stat(buffer, &class_buf);
548         }
549
550       if (source_ok)
551         {
552           /* Compute name of .java file.  */
553           int l, m;
554           strcpy (java_buffer, path_name);
555           l = strlen (java_buffer);
556           for (m = 0; m < classname_length; ++m)
557             java_buffer[m + l] = (classname[m] == '.' ? '/' : classname[m]);
558           strcpy (java_buffer + m + l, ".java");
559           java = caching_stat (java_buffer, &java_buf);
560           if (java == 0)
561             break;
562         }
563     }
564
565   /* We preferably pick a class file if we have a chance. If the source
566      file is newer than the class file, we issue a warning and parse the
567      source file instead.
568      There should be a flag to allow people have the class file picked
569      up no matter what. FIXME. */
570   if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime)
571     {
572       if (flag_newer)
573         warning ("source file for class `%s' is newer than its matching class file.  Source file `%s' used instead", classname, java_buffer);
574       class = -1;
575     }
576
577   if (! java)
578     dep_file = java_buffer;
579   else
580     dep_file = buffer;
581 #if JCF_USE_STDIO
582   if (!class)
583     {
584       SOURCE_FRONTEND_DEBUG (("Trying %s", buffer));
585       stream = fopen (buffer, "rb");
586       if (stream)
587         goto found;
588     }
589   /* Give .java a try, if necessary */
590   if (!java)
591     {
592       strcpy (buffer, java_buffer);
593       SOURCE_FRONTEND_DEBUG (("Trying %s", buffer));
594       stream = fopen (buffer, "r");
595       if (stream)
596         {
597           jcf->java_source = 1;
598           goto found;
599         }
600     }
601 #else
602   if (!class)
603     {
604       SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n",
605                               classname+classname_length-
606                               (classname_length <= 30 ? 
607                                classname_length : 30)));
608       fd = open (buffer, O_RDONLY | O_BINARY);
609       if (fd >= 0)
610         goto found;
611     }
612   /* Give .java a try, if necessary */
613   if (!java)
614     {
615       strcpy (buffer, java_buffer);
616       SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n",
617                               classname+classname_length-
618                               (classname_length <= 30 ? 
619                                classname_length : 30)));
620       fd = open (buffer, O_RDONLY);
621       if (fd >= 0)
622         {
623           jcf->java_source = 1;
624           goto found;
625         }
626     }
627 #endif
628
629   free (buffer);
630
631   /* Remember that this class could not be found so that we do not
632      have to look again.  */
633   *htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT) 
634     = (void *) classname;
635
636   return NULL;
637  found:
638 #if JCF_USE_STDIO
639   if (jcf->java_source)
640     return NULL;                /* FIXME */
641   else
642     return open_class (buffer, jcf, stream, dep_file);
643 #else
644   if (jcf->java_source)
645     {
646       JCF_ZERO (jcf);           /* JCF_FINISH relies on this */
647       jcf->java_source = 1;
648       jcf->filename = xstrdup (buffer);
649       close (fd);               /* We use STDIO for source file */
650     }
651   else
652     buffer = (char *) open_class (buffer, jcf, fd, dep_file);
653   jcf->classname = xstrdup (classname);
654   return buffer;
655 #endif
656 }
657
658 void
659 DEFUN(jcf_print_char, (stream, ch),
660       FILE *stream AND int ch)
661 {
662   switch (ch)
663     {
664     case '\'':
665     case '\\':
666     case '\"':
667       fprintf (stream, "\\%c", ch);
668       break;
669     case '\n':
670       fprintf (stream, "\\n");
671       break;
672     case '\t':
673       fprintf (stream, "\\t");
674       break;
675     case '\r':
676       fprintf (stream, "\\r");
677       break;
678     default:
679       if (ch >= ' ' && ch < 127)
680         putc (ch, stream);
681       else if (ch < 256)
682         fprintf (stream, "\\%03x", ch);
683       else
684         fprintf (stream, "\\u%04x", ch);
685     }
686 }
687
688 /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */
689
690 void
691 DEFUN(jcf_print_utf8, (stream, str, length),
692       FILE *stream AND register const unsigned char *str AND int length)
693 {
694   const unsigned char * limit = str + length;
695   while (str < limit)
696     {
697       int ch = UTF8_GET (str, limit);
698       if (ch < 0)
699         {
700           fprintf (stream, "\\<invalid>");
701           return;
702         }
703       jcf_print_char (stream, ch);
704     }
705 }
706
707 /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */
708
709 void
710 DEFUN(jcf_print_utf8_replace, (stream, str, length, in_char, out_char),
711       FILE *stream AND const unsigned char *str AND int length
712       AND int in_char AND int out_char)
713 {
714   const unsigned char *limit = str + length;
715   while (str < limit)
716     {
717       int ch = UTF8_GET (str, limit);
718       if (ch < 0)
719         {
720           fprintf (stream, "\\<invalid>");
721           return;
722         }
723       jcf_print_char (stream, ch == in_char ? out_char : ch);
724     }
725 }
726
727 /* Check that all the cross-references in the constant pool are
728    valid.  Returns 0 on success.
729    Otherwise, returns the index of the (first) invalid entry.
730    Only checks internal consistency, but does not check that
731    any classes, fields, or methods are valid.*/
732
733 int
734 DEFUN(verify_constant_pool, (jcf),
735       JCF *jcf)
736 {
737   int i, n;
738   for (i = 1; i < JPOOL_SIZE (jcf); i++)
739     {
740       switch (JPOOL_TAG (jcf, i))
741         {
742         case CONSTANT_NameAndType:
743           n = JPOOL_USHORT2 (jcf, i);
744           if (n <= 0 || n >= JPOOL_SIZE(jcf)
745               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
746             return i;
747           /* ... fall through ... */
748         case CONSTANT_Class:
749         case CONSTANT_String:
750           n = JPOOL_USHORT1 (jcf, i);
751           if (n <= 0 || n >= JPOOL_SIZE(jcf)
752               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
753             return i;
754           break;
755         case CONSTANT_Fieldref:
756         case CONSTANT_Methodref:
757         case CONSTANT_InterfaceMethodref:
758           n = JPOOL_USHORT1 (jcf, i);
759           if (n <= 0 || n >= JPOOL_SIZE(jcf)
760               || JPOOL_TAG (jcf, n) != CONSTANT_Class)
761             return i;
762           n = JPOOL_USHORT2 (jcf, i);
763           if (n <= 0 || n >= JPOOL_SIZE(jcf)
764               || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
765             return i;
766           break;
767         case CONSTANT_Long:
768         case CONSTANT_Double:
769           i++;
770           break;
771         case CONSTANT_Float:
772         case CONSTANT_Integer:
773         case CONSTANT_Utf8:
774         case CONSTANT_Unicode:
775           break;
776         default:
777           return i;
778         }
779     }
780   return 0;
781 }
782
783 void
784 DEFUN(format_uint, (buffer, value, base),
785       char *buffer AND uint64 value AND int base)
786 {
787 #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
788   char buf[WRITE_BUF_SIZE];
789   register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */
790   int chars_written;
791   int i;
792
793   /* Now do the actual conversion, placing the result at the *end* of buf. */
794   /* Note this code does not pretend to be optimized. */
795   do {
796     int digit = value % base;
797     static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
798     *--buf_ptr = digit_chars[digit];
799     value /= base;
800   } while (value != 0);
801
802   chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
803   for (i = 0; i < chars_written; i++)
804     buffer[i] = *buf_ptr++;
805   buffer[i] = 0;
806 }
807
808 void
809 DEFUN(format_int, (buffer, value, base),
810       char *buffer AND jlong value AND int base)
811 {
812   uint64 abs_value;
813   if (value < 0)
814     {
815       abs_value = -(uint64)value;
816       *buffer++ = '-';
817     }
818   else
819     abs_value = (uint64) value;
820   format_uint (buffer, abs_value, base);
821 }