OSDN Git Service

Merged gcj-eclipse branch to trunk.
[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, 2003, 2004, 2005, 2006
3    Free Software Foundation, Inc.
4
5 This file is part of GCC.
6
7 GCC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GCC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING.  If not, write to
19 the Free Software Foundation, 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.  
21
22 Java and all Java-based marks are trademarks or registered trademarks
23 of Sun Microsystems, Inc. in the United States and other countries.
24 The Free Software Foundation is independent of Sun Microsystems, Inc.  */
25
26 /* Written by Per Bothner <bothner@cygnus.com>, February 1996. */
27
28 #include "config.h"
29 #include "system.h"
30 #include "coretypes.h"
31 #include "tm.h"
32
33 #include "jcf.h"
34 #include "tree.h"
35 #include "toplev.h"
36 #include "java-tree.h"
37 #include "hashtab.h"
38 #if JCF_USE_SCANDIR
39 #include <dirent.h>
40 #include <fnmatch.h>
41 #endif
42
43 #include "zlib.h"
44
45 /* DOS brain-damage */
46 #ifndef O_BINARY
47 #define O_BINARY 0 /* MS-DOS brain-damage */
48 #endif
49
50 int
51 jcf_unexpected_eof (JCF *jcf, int count ATTRIBUTE_UNUSED)
52 {
53   if (jcf->filename)
54     fprintf (stderr, "Premature end of .class file %s.\n", jcf->filename);
55   else
56     fprintf (stderr, "Premature end of .class file <stdin>.\n");
57   exit (-1);
58 }
59
60 void
61 jcf_trim_old_input (JCF *jcf)
62 {
63   int count = jcf->read_ptr - jcf->buffer;
64   if (count > 0)
65     {
66       memmove (jcf->buffer, jcf->read_ptr, jcf->read_end - jcf->read_ptr);
67       jcf->read_ptr -= count;
68       jcf->read_end -= count;
69     }
70 }
71
72 int
73 jcf_filbuf_from_stdio (JCF *jcf, 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 ((int) 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 #include "zipfile.h"
99
100 struct ZipFile *SeenZipFiles = NULL;
101
102 /* Open a zip file with the given name, and cache directory and file
103    descriptor.  If the file is missing, treat it as an empty archive.
104    Return NULL if the .zip file is malformed.
105 */
106
107 ZipFile *
108 opendir_in_zip (const char *zipfile, int is_system)
109 {
110   struct ZipFile* zipf;
111   char magic [4];
112   int fd;
113   for (zipf = SeenZipFiles;  zipf != NULL;  zipf = zipf->next)
114     {
115       if (strcmp (zipf->name, zipfile) == 0)
116         return zipf;
117     }
118
119   zipf = ALLOC (sizeof (struct ZipFile) + strlen (zipfile) + 1);
120   zipf->next = SeenZipFiles;
121   zipf->name = (char*)(zipf+1);
122   strcpy (zipf->name, zipfile);
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
143   SeenZipFiles = zipf;  
144   return zipf;
145 }
146
147 /* Returns:
148    0:  OK - zipmember found.
149    -1: Not found.
150    -2: Malformed archive.
151 */
152
153 int
154 open_in_zip (JCF *jcf, const char *zipfile, const char *zipmember,
155              int is_system)
156 {
157   ZipDirectory *zipd;
158   int i, len;
159   ZipFile *zipf = opendir_in_zip (zipfile, is_system);
160
161   if (zipf == NULL)
162     return -2;
163
164   if (!zipmember)
165     return 0;
166
167   len = strlen (zipmember);
168   
169   zipd = (struct ZipDirectory*) zipf->central_directory;
170   for (i = 0; i < zipf->count; i++, zipd = ZIPDIR_NEXT (zipd))
171     {
172       if (len == zipd->filename_length &&
173           strncmp (ZIPDIR_FILENAME (zipd), zipmember, len) == 0)
174         {
175           JCF_ZERO (jcf);
176
177           jcf->filename = xstrdup (zipfile);
178           jcf->classname = xstrdup (zipmember);
179           return read_zip_member(jcf, zipd, zipf);
180         }
181     }
182   return -1;
183 }
184
185 /* Read data from zip archive member. */
186
187 int
188 read_zip_member (JCF *jcf,  ZipDirectory *zipd, ZipFile *zipf)
189 {
190   jcf->filbuf = jcf_unexpected_eof;
191   jcf->zipd = zipd;
192
193   if (zipd->compression_method == Z_NO_COMPRESSION)
194     {
195       jcf->buffer = XNEWVEC (unsigned char, 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 = XNEWVEC (unsigned char, 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 = XNEWVEC (char, zipd->size);
218       d_stream.next_in = (unsigned char *) 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 const char *
235 open_class (const char *filename, JCF *jcf, int fd, const char *dep_name)
236 {
237   if (jcf)
238     {
239       struct stat stat_buf;
240       if (fstat (fd, &stat_buf) != 0
241           || ! S_ISREG (stat_buf.st_mode))
242         {
243           perror ("Could not figure length of .class file");
244           return NULL;
245         }
246       if (dep_name != NULL)
247         jcf_dependency_add_file (dep_name, 0);
248       JCF_ZERO (jcf);
249       jcf->buffer = XNEWVEC (unsigned char, stat_buf.st_size);
250       jcf->buffer_end = jcf->buffer + stat_buf.st_size;
251       jcf->read_ptr = jcf->buffer;
252       jcf->read_end = jcf->buffer_end;
253       jcf->read_state = NULL;
254       jcf->filename = filename;
255       if (read (fd, jcf->buffer, stat_buf.st_size) != stat_buf.st_size)
256         {
257           perror ("Failed to read .class file");
258           return NULL;
259         }
260       close (fd);
261       jcf->filbuf = jcf_unexpected_eof;
262     }
263   else
264     close (fd);
265   return filename;
266 }
267
268
269 const char *
270 find_classfile (char *filename, JCF *jcf, const char *dep_name)
271 {
272   int fd = open (filename, O_RDONLY | O_BINARY);
273   if (fd < 0)
274     return NULL;
275   return open_class (filename, jcf, fd, dep_name);
276 }
277
278 #if JCF_USE_SCANDIR
279
280 /* A comparison function (as for qsort) that compares KEY (a char *
281    giving the basename of a file) with the name stored in ENTRY (a
282    dirent **).  */
283
284 static int
285 compare_path (const void *key, const void *entry)
286 {
287   return strcmp ((const char *) key, 
288                  (*((const struct dirent **) entry))->d_name);
289 }
290
291 /* Returns nonzero if ENTRY names a .java or .class file.  */
292
293 static int
294 java_or_class_file (const struct dirent *entry)
295 {
296   const char *base = lbasename (entry->d_name);
297   return (fnmatch ("*.java", base, 0) == 0 || 
298           fnmatch ("*.class", base, 0) == 0);
299 }
300
301 /* Information about the files present in a particular directory.  */
302 typedef struct memoized_dirlist_entry 
303 {
304   /* The name of the directory.  */
305   const char *dir;
306   /* The number of .java and .class files present, or -1 if we could
307      not, for some reason, obtain the list.  */
308   int num_files;
309   /* The .java and .class files in the directory, in alphabetical
310      order.  */
311   struct dirent **files;
312 } memoized_dirlist_entry;
313
314 /* A hash function for a memoized_dirlist_entry.  */
315 static hashval_t
316 memoized_dirlist_hash (const void *entry)
317 {
318   const memoized_dirlist_entry *mde = (const memoized_dirlist_entry *) entry;
319   return htab_hash_string (mde->dir);
320 }
321
322 /* Returns true if ENTRY (a memoized_dirlist_entry *) corresponds to
323    the directory given by KEY (a char *) giving the directory 
324    name.  */
325
326 static int
327 memoized_dirlist_lookup_eq (const void *entry, const void *key)
328 {
329   return strcmp ((const char *) key,
330                  ((const memoized_dirlist_entry *) entry)->dir) == 0;
331 }
332
333 /* A hash table mapping directory names to the lists of .java and
334    .class files in that directory.  */
335
336 static htab_t memoized_dirlists;
337
338 #endif
339
340 /* Like stat, but avoids actually making the stat system call if we
341    know that it cannot succeed.  FILENAME and BUF are as for stat.  */
342
343 static int
344 caching_stat (char *filename, struct stat *buf)
345 {
346 #if JCF_USE_SCANDIR
347   char *sep;
348   char origsep = 0;
349   char *base;
350   memoized_dirlist_entry *dent;
351   void **slot;
352   struct memoized_dirlist_entry temp;
353   
354   /* If the hashtable has not already been created, create it now.  */
355   if (!memoized_dirlists)
356     memoized_dirlists = htab_create (37,
357                                      memoized_dirlist_hash,
358                                      memoized_dirlist_lookup_eq,
359                                      NULL);
360
361   /* Get the name of the directory.  */
362   sep = strrchr (filename, DIR_SEPARATOR);
363 #ifdef DIR_SEPARATOR_2
364   if (! sep)
365     sep = strrchr (filename, DIR_SEPARATOR_2);
366 #endif
367   if (sep)
368     {
369       origsep = *sep;
370       *sep = '\0';
371       base = sep + 1;
372     }
373   else
374     base = filename;
375
376   /* Obtain the entry for this directory from the hash table.  This
377      approach is ok since we know that the hash function only looks at
378      the directory name.  */
379   temp.dir = filename;
380   temp.num_files = 0;
381   temp.files = NULL;
382   slot = htab_find_slot (memoized_dirlists, &temp, INSERT);
383   if (!*slot)
384     {
385       /* We have not already scanned this directory; scan it now.  */
386       dent = XNEW (memoized_dirlist_entry);
387       dent->dir = xstrdup (filename);
388       /* Unfortunately, scandir is not fully standardized.  In
389          particular, the type of the function pointer passed as the
390          third argument sometimes takes a "const struct dirent *"
391          parameter, and sometimes just a "struct dirent *".  We cast
392          to (void *) and use __extension__ so that either way it is
393          quietly accepted.  FIXME: scandir is not in POSIX.  */
394       dent->num_files = __extension__ scandir (filename, &dent->files, 
395                                                (void *) java_or_class_file, 
396                                                alphasort);
397       *slot = dent;
398     }
399   else
400     dent = *((memoized_dirlist_entry **) slot);
401
402   /* Put the separator back.  */
403   if (sep)
404     *sep = origsep;
405
406   /* If the file is not in the list, there is no need to stat it; it
407      does not exist.  */
408   if (dent->num_files != -1
409       && !bsearch (base, dent->files, dent->num_files,
410                    sizeof (struct dirent *), compare_path))
411     return -1;
412 #endif
413   
414   return stat (filename, buf);
415 }
416
417 /* Returns 1 if the CLASSNAME (really a char *) matches the name
418    stored in TABLE_ENTRY (also a char *).  */
419
420 static int
421 memoized_class_lookup_eq (const void *table_entry, const void *classname)
422 {
423   return strcmp ((const char *)classname, (const char *)table_entry) == 0;
424 }
425
426 /* A hash table keeping track of class names that were not found
427    during class lookup.  (There is no need to cache the values
428    associated with names that were found; they are saved in
429    IDENTIFIER_CLASS_VALUE.)  */
430 static htab_t memoized_class_lookups;
431
432 /* Returns a freshly malloc'd string with the fully qualified pathname
433    of the .class file for the class CLASSNAME.  CLASSNAME must be
434    allocated in permanent storage; this function may retain a pointer
435    to it.  Returns NULL on failure.  If JCF != NULL, it is suitably
436    initialized.  SOURCE_OK is true if we should also look for .java
437    file. */
438
439 const char *
440 find_class (const char *classname, int classname_length, JCF *jcf,
441             int source_ok)
442 {
443   int fd;
444   int i, k, java = -1, class = -1;
445   struct stat java_buf, class_buf;
446   char *dep_file;
447   void *entry;
448   char *java_buffer;
449   int buflen;
450   char *buffer;
451   hashval_t hash;
452
453   /* FIXME: ecj hack.  */
454   source_ok = 0;
455
456   /* Create the hash table, if it does not already exist.  */
457   if (!memoized_class_lookups)
458     memoized_class_lookups = htab_create (37, 
459                                           htab_hash_string, 
460                                           memoized_class_lookup_eq,
461                                           NULL);
462
463   /* Loop for this class in the hashtable.  If it is present, we've
464      already looked for this class and failed to find it.  */
465   hash = htab_hash_string (classname);
466   if (htab_find_with_hash (memoized_class_lookups, classname, hash))
467     return NULL;
468
469   /* Allocate and zero out the buffer, since we don't explicitly put a
470      null pointer when we're copying it below.  */
471   buflen = jcf_path_max_len () + classname_length + 10;
472   buffer = ALLOC (buflen);
473   memset (buffer, 0, buflen);
474
475   java_buffer = alloca (buflen);
476
477   jcf->java_source = 0;
478
479   for (entry = jcf_path_start (); entry != NULL; entry = jcf_path_next (entry))
480     {
481       const char *path_name = jcf_path_name (entry);
482       if (class != 0)
483         {
484           int dir_len;
485
486           strcpy (buffer, path_name);
487           i = strlen (buffer);
488
489           /* This is right because we know that `.zip' entries will have a
490              trailing slash.  See jcf-path.c.  */
491           dir_len = i - 1;
492
493           for (k = 0; k < classname_length; k++, i++)
494             {
495               char ch = classname[k];
496               buffer[i] = ch == '.' ? '/' : ch;
497             }
498           strcpy (buffer+i, ".class");
499
500           if (jcf_path_is_zipfile (entry))
501             {
502               int err_code;
503               JCF _jcf;
504               buffer[dir_len] = '\0';
505               SOURCE_FRONTEND_DEBUG 
506                 (("Trying [...%s]:%s", 
507                   &buffer[dir_len-(dir_len > 15 ? 15 : dir_len)], 
508                   buffer+dir_len+1));
509               if (jcf == NULL)
510                 jcf = &_jcf;
511               err_code = open_in_zip (jcf, buffer, buffer+dir_len+1,
512                                       jcf_path_is_system (entry));
513               if (err_code == 0)
514                 {
515                   /* Should we check if .zip is out-of-date wrt .java? */
516                   buffer[dir_len] = '(';
517                   strcpy (buffer+i, ".class)");
518                   if (jcf == &_jcf)
519                     JCF_FINISH (jcf);
520                   return buffer;
521                 }
522               else
523                 continue;
524             }
525           class = caching_stat(buffer, &class_buf);
526         }
527
528       if (source_ok)
529         {
530           /* Compute name of .java file.  */
531           int l, m;
532           strcpy (java_buffer, path_name);
533           l = strlen (java_buffer);
534           for (m = 0; m < classname_length; ++m)
535             java_buffer[m + l] = (classname[m] == '.'
536                                   ? DIR_SEPARATOR : classname[m]);
537           strcpy (java_buffer + m + l, ".java");
538           java = caching_stat (java_buffer, &java_buf);
539           if (java == 0)
540             break;
541         }
542     }
543
544   /* We preferably pick a class file if we have a chance. If the source
545      file is newer than the class file, we issue a warning and parse the
546      source file instead.
547      There should be a flag to allow people have the class file picked
548      up no matter what. FIXME. */
549   if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime)
550     {
551       if (flag_newer)
552         warning (0, "source file for class %qs is newer than its matching class file.  Source file %qs used instead", classname, java_buffer);
553       class = -1;
554     }
555
556   if (! java)
557     dep_file = java_buffer;
558   else
559     dep_file = buffer;
560   if (!class)
561     {
562       SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n",
563                               classname+classname_length-
564                               (classname_length <= 30 ? 
565                                classname_length : 30)));
566       fd = JCF_OPEN_EXACT_CASE (buffer, O_RDONLY | O_BINARY);
567       if (fd >= 0)
568         goto found;
569     }
570   /* Give .java a try, if necessary */
571   if (!java)
572     {
573       strcpy (buffer, java_buffer);
574       SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n",
575                               classname+classname_length-
576                               (classname_length <= 30 ? 
577                                classname_length : 30)));
578       fd = JCF_OPEN_EXACT_CASE (buffer, O_RDONLY);
579       if (fd >= 0)
580         {
581           jcf->java_source = 1;
582           goto found;
583         }
584     }
585
586   free (buffer);
587
588   /* Remember that this class could not be found so that we do not
589      have to look again.  */
590   *htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT) 
591     = (void *) classname;
592
593   return NULL;
594  found:
595   if (jcf->java_source)
596     {
597       JCF_ZERO (jcf);           /* JCF_FINISH relies on this */
598       jcf->java_source = 1;
599       jcf->filename = xstrdup (buffer);
600       close (fd);               /* We use STDIO for source file */
601     }
602   else
603     buffer = (char *) open_class (buffer, jcf, fd, dep_file);
604   jcf->classname = xstrdup (classname);
605   return buffer;
606 }
607
608 void
609 jcf_print_char (FILE *stream, int ch)
610 {
611   switch (ch)
612     {
613     case '\'':
614     case '\\':
615     case '\"':
616       fprintf (stream, "\\%c", ch);
617       break;
618     case '\n':
619       fprintf (stream, "\\n");
620       break;
621     case '\t':
622       fprintf (stream, "\\t");
623       break;
624     case '\r':
625       fprintf (stream, "\\r");
626       break;
627     default:
628       if (ch >= ' ' && ch < 127)
629         putc (ch, stream);
630       else if (ch < 256)
631         fprintf (stream, "\\%03x", ch);
632       else
633         fprintf (stream, "\\u%04x", ch);
634     }
635 }
636
637 /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */
638
639 void
640 jcf_print_utf8 (FILE *stream, const unsigned char *str, int length)
641 {
642   const unsigned char * limit = str + length;
643   while (str < limit)
644     {
645       int ch = UTF8_GET (str, limit);
646       if (ch < 0)
647         {
648           fprintf (stream, "\\<invalid>");
649           return;
650         }
651       jcf_print_char (stream, ch);
652     }
653 }
654
655 /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */
656
657 void
658 jcf_print_utf8_replace (FILE *stream, const unsigned char *str, int length,
659                         int in_char, int out_char)
660 {
661   const unsigned char *limit = str + length;
662   while (str < limit)
663     {
664       int ch = UTF8_GET (str, limit);
665       if (ch < 0)
666         {
667           fprintf (stream, "\\<invalid>");
668           return;
669         }
670       jcf_print_char (stream, ch == in_char ? out_char : ch);
671     }
672 }
673
674 /* Check that all the cross-references in the constant pool are
675    valid.  Returns 0 on success.
676    Otherwise, returns the index of the (first) invalid entry.
677    Only checks internal consistency, but does not check that
678    any classes, fields, or methods are valid.*/
679
680 int
681 verify_constant_pool (JCF *jcf)
682 {
683   int i, n;
684   for (i = 1; i < JPOOL_SIZE (jcf); i++)
685     {
686       switch (JPOOL_TAG (jcf, i))
687         {
688         case CONSTANT_NameAndType:
689           n = JPOOL_USHORT2 (jcf, i);
690           if (n <= 0 || n >= JPOOL_SIZE(jcf)
691               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
692             return i;
693           /* ... fall through ... */
694         case CONSTANT_Class:
695         case CONSTANT_String:
696           n = JPOOL_USHORT1 (jcf, i);
697           if (n <= 0 || n >= JPOOL_SIZE(jcf)
698               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
699             return i;
700           break;
701         case CONSTANT_Fieldref:
702         case CONSTANT_Methodref:
703         case CONSTANT_InterfaceMethodref:
704           n = JPOOL_USHORT1 (jcf, i);
705           if (n <= 0 || n >= JPOOL_SIZE(jcf)
706               || JPOOL_TAG (jcf, n) != CONSTANT_Class)
707             return i;
708           n = JPOOL_USHORT2 (jcf, i);
709           if (n <= 0 || n >= JPOOL_SIZE(jcf)
710               || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
711             return i;
712           break;
713         case CONSTANT_Long:
714         case CONSTANT_Double:
715           i++;
716           break;
717         case CONSTANT_Float:
718         case CONSTANT_Integer:
719         case CONSTANT_Utf8:
720         case CONSTANT_Unicode:
721           break;
722         default:
723           return i;
724         }
725     }
726   return 0;
727 }
728
729 void
730 format_uint (char *buffer, uint64 value, int base)
731 {
732 #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
733   char buf[WRITE_BUF_SIZE];
734   char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */
735   int chars_written;
736   int i;
737
738   /* Now do the actual conversion, placing the result at the *end* of buf. */
739   /* Note this code does not pretend to be optimized. */
740   do {
741     int digit = value % base;
742     static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
743     *--buf_ptr = digit_chars[digit];
744     value /= base;
745   } while (value != 0);
746
747   chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
748   for (i = 0; i < chars_written; i++)
749     buffer[i] = *buf_ptr++;
750   buffer[i] = 0;
751 }
752
753 void
754 format_int (char *buffer, jlong value, int base)
755 {
756   uint64 abs_value;
757   if (value < 0)
758     {
759       abs_value = -(uint64)value;
760       *buffer++ = '-';
761     }
762   else
763     abs_value = (uint64) value;
764   format_uint (buffer, abs_value, base);
765 }