OSDN Git Service

* target-def.h: Remove usage of OBJECT_FORMAT_ROSE.
[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
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, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, 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   SeenZipFiles = zipf;
124   fd = open (zipfile, O_RDONLY | O_BINARY);
125   zipf->fd = fd;
126   if (fd < 0)
127     {
128       /* A missing zip file is not considered an error.
129        We may want to re-consider that.  FIXME. */
130       zipf->count = 0;
131       zipf->dir_size = 0;
132       zipf->central_directory = NULL;
133     }
134   else
135     {
136       jcf_dependency_add_file (zipfile, is_system);
137       if (read (fd, magic, 4) != 4 || GET_u4 (magic) != (JCF_u4)ZIPMAGIC)
138         return NULL;
139       lseek (fd, 0L, SEEK_SET);
140       if (read_zip_archive (zipf) != 0)
141         return NULL;
142     }
143   return zipf;
144 }
145
146 /* Returns:
147    0:  OK - zipmember found.
148    -1: Not found.
149    -2: Malformed archive.
150 */
151
152 int
153 open_in_zip (JCF *jcf, const char *zipfile, const char *zipmember,
154              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 read_zip_member (JCF *jcf,  ZipDirectory *zipd, ZipFile *zipf)
188 {
189   jcf->filbuf = jcf_unexpected_eof;
190   jcf->zipd = (void *)zipd;
191
192   if (zipd->compression_method == Z_NO_COMPRESSION)
193     {
194       jcf->buffer = ALLOC (zipd->size);
195       jcf->buffer_end = jcf->buffer + zipd->size;
196       jcf->read_ptr = jcf->buffer;
197       jcf->read_end = jcf->buffer_end;
198       if (lseek (zipf->fd, zipd->filestart, 0) < 0
199           || read (zipf->fd, jcf->buffer, zipd->size) != (long) zipd->size)
200         return -2;
201     }
202   else
203     {
204       char *buffer;
205       z_stream d_stream; /* decompression stream */
206       d_stream.zalloc = (alloc_func) 0;
207       d_stream.zfree = (free_func) 0;
208       d_stream.opaque = (voidpf) 0;
209
210       jcf->buffer = ALLOC (zipd->uncompressed_size);
211       d_stream.next_out = jcf->buffer;
212       d_stream.avail_out = zipd->uncompressed_size;
213       jcf->buffer_end = jcf->buffer + zipd->uncompressed_size;
214       jcf->read_ptr = jcf->buffer;
215       jcf->read_end = jcf->buffer_end;
216       buffer = ALLOC (zipd->size);
217       d_stream.next_in = buffer;
218       d_stream.avail_in = zipd->size;
219       if (lseek (zipf->fd, zipd->filestart, 0) < 0
220           || read (zipf->fd, buffer, zipd->size) != (long) zipd->size)
221         return -2;
222       /* Handle NO_HEADER using undocumented zlib feature.
223          This is a very common hack.  */
224       inflateInit2 (&d_stream, -MAX_WBITS);
225       inflate (&d_stream, Z_NO_FLUSH);
226       inflateEnd (&d_stream);
227       FREE (buffer);
228     }
229
230   return 0;
231 }
232
233 const char *
234 open_class (const char *filename, JCF *jcf, int fd, const char *dep_name)
235 {
236   if (jcf)
237     {
238       struct stat stat_buf;
239       if (fstat (fd, &stat_buf) != 0
240           || ! S_ISREG (stat_buf.st_mode))
241         {
242           perror ("Could not figure length of .class file");
243           return NULL;
244         }
245       if (dep_name != NULL)
246         jcf_dependency_add_file (dep_name, 0);
247       JCF_ZERO (jcf);
248       jcf->buffer = ALLOC (stat_buf.st_size);
249       jcf->buffer_end = jcf->buffer + stat_buf.st_size;
250       jcf->read_ptr = jcf->buffer;
251       jcf->read_end = jcf->buffer_end;
252       jcf->read_state = NULL;
253       jcf->filename = filename;
254       if (read (fd, jcf->buffer, stat_buf.st_size) != stat_buf.st_size)
255         {
256           perror ("Failed to read .class file");
257           return NULL;
258         }
259       close (fd);
260       jcf->filbuf = jcf_unexpected_eof;
261     }
262   else
263     close (fd);
264   return filename;
265 }
266
267
268 const char *
269 find_classfile (char *filename, JCF *jcf, const char *dep_name)
270 {
271   int fd = open (filename, O_RDONLY | O_BINARY);
272   if (fd < 0)
273     return NULL;
274   return open_class (filename, jcf, fd, dep_name);
275 }
276
277 #if JCF_USE_SCANDIR
278
279 /* A comparison function (as for qsort) that compares KEY (a char *
280    giving the basename of a file) with the name stored in ENTRY (a
281    dirent **).  */
282
283 static int
284 compare_path (const void *key, const void *entry)
285 {
286   return strcmp ((const char *) key, 
287                  (*((const struct dirent **) entry))->d_name);
288 }
289
290 /* Returns nonzero if ENTRY names a .java or .class file.  */
291
292 static int
293 java_or_class_file (const struct dirent *entry)
294 {
295   const char *base = lbasename (entry->d_name);
296   return (fnmatch ("*.java", base, 0) == 0 || 
297           fnmatch ("*.class", base, 0) == 0);
298 }
299
300 /* Information about the files present in a particular directory.  */
301 typedef struct memoized_dirlist_entry 
302 {
303   /* The name of the directory.  */
304   const char *dir;
305   /* The number of .java and .class files present, or -1 if we could
306      not, for some reason, obtain the list.  */
307   int num_files;
308   /* The .java and .class files in the directory, in alphabetical
309      order.  */
310   struct dirent **files;
311 } memoized_dirlist_entry;
312
313 /* Returns true if ENTRY (a memoized_dirlist_entry *) corresponds to
314    the directory given by KEY (a char *) giving the directory 
315    name.  */
316
317 static int
318 memoized_dirlist_lookup_eq (const void *entry, const void *key)
319 {
320   return strcmp ((const char *) key,
321                  ((const memoized_dirlist_entry *) entry)->dir) == 0;
322 }
323
324 /* A hash table mapping directory names to the lists of .java and
325    .class files in that directory.  */
326
327 static htab_t memoized_dirlists;
328
329 #endif
330
331 /* Like stat, but avoids actually making the stat system call if we
332    know that it cannot succeed.  FILENAME and BUF are as for stat.  */
333
334 static int
335 caching_stat (char *filename, struct stat *buf)
336 {
337 #if JCF_USE_SCANDIR
338   char *sep;
339   char origsep = 0;
340   char *base;
341   memoized_dirlist_entry *dent;
342   void **slot;
343   
344   /* If the hashtable has not already been created, create it now.  */
345   if (!memoized_dirlists)
346     memoized_dirlists = htab_create (37,
347                                      htab_hash_string,
348                                      memoized_dirlist_lookup_eq,
349                                      NULL);
350
351   /* Get the name of the directory.  */
352   sep = strrchr (filename, DIR_SEPARATOR);
353 #ifdef DIR_SEPARATOR_2
354   if (! sep)
355     sep = strrchr (filename, DIR_SEPARATOR_2);
356 #endif
357   if (sep)
358     {
359       origsep = *sep;
360       *sep = '\0';
361       base = sep + 1;
362     }
363   else
364     base = filename;
365
366   /* Obtain the entry for this directory from the hash table.  */
367   slot = htab_find_slot (memoized_dirlists, filename, INSERT);
368   if (!*slot)
369     {
370       /* We have not already scanned this directory; scan it now.  */
371       dent = ((memoized_dirlist_entry *) 
372               ALLOC (sizeof (memoized_dirlist_entry)));
373       dent->dir = xstrdup (filename);
374       /* Unfortunately, scandir is not fully standardized.  In
375          particular, the type of the function pointer passed as the
376          third argument sometimes takes a "const struct dirent *"
377          parameter, and sometimes just a "struct dirent *".  We cast
378          to (void *) so that either way it is quietly accepted.  */
379       dent->num_files = scandir (filename, &dent->files, 
380                                  (void *) java_or_class_file, 
381                                  alphasort);
382       *slot = dent;
383     }
384   else
385     dent = *((memoized_dirlist_entry **) slot);
386
387   /* Put the separator back.  */
388   if (sep)
389     *sep = origsep;
390
391   /* If the file is not in the list, there is no need to stat it; it
392      does not exist.  */
393   if (dent->num_files != -1
394       && !bsearch (base, dent->files, dent->num_files,
395                    sizeof (struct dirent *), compare_path))
396     return -1;
397 #endif
398   
399   return stat (filename, buf);
400 }
401
402 /* Returns 1 if the CLASSNAME (really a char *) matches the name
403    stored in TABLE_ENTRY (also a char *).  */
404
405 static int
406 memoized_class_lookup_eq (const void *table_entry, const void *classname)
407 {
408   return strcmp ((const char *)classname, (const char *)table_entry) == 0;
409 }
410
411 /* A hash table keeping track of class names that were not found
412    during class lookup.  (There is no need to cache the values
413    associated with names that were found; they are saved in
414    IDENTIFIER_CLASS_VALUE.)  */
415 static htab_t memoized_class_lookups;
416
417 /* Returns a freshly malloc'd string with the fully qualified pathname
418    of the .class file for the class CLASSNAME.  CLASSNAME must be
419    allocated in permanent storage; this function may retain a pointer
420    to it.  Returns NULL on failure.  If JCF != NULL, it is suitably
421    initialized.  SOURCE_OK is true if we should also look for .java
422    file. */
423
424 const char *
425 find_class (const char *classname, int classname_length, JCF *jcf,
426             int source_ok)
427 {
428   int fd;
429   int i, k, java = -1, class = -1;
430   struct stat java_buf, class_buf;
431   char *dep_file;
432   void *entry;
433   char *java_buffer;
434   int buflen;
435   char *buffer;
436   hashval_t hash;
437
438   /* Create the hash table, if it does not already exist.  */
439   if (!memoized_class_lookups)
440     memoized_class_lookups = htab_create (37, 
441                                           htab_hash_string, 
442                                           memoized_class_lookup_eq,
443                                           NULL);
444
445   /* Loop for this class in the hashtable.  If it is present, we've
446      already looked for this class and failed to find it.  */
447   hash = htab_hash_string (classname);
448   if (htab_find_with_hash (memoized_class_lookups, classname, hash))
449     return NULL;
450
451   /* Allocate and zero out the buffer, since we don't explicitly put a
452      null pointer when we're copying it below.  */
453   buflen = jcf_path_max_len () + classname_length + 10;
454   buffer = ALLOC (buflen);
455   memset (buffer, 0, buflen);
456
457   java_buffer = alloca (buflen);
458
459   jcf->java_source = 0;
460
461   for (entry = jcf_path_start (); entry != NULL; entry = jcf_path_next (entry))
462     {
463       const char *path_name = jcf_path_name (entry);
464       if (class != 0)
465         {
466           int dir_len;
467
468           strcpy (buffer, path_name);
469           i = strlen (buffer);
470
471           /* This is right because we know that `.zip' entries will have a
472              trailing slash.  See jcf-path.c.  */
473           dir_len = i - 1;
474
475           for (k = 0; k < classname_length; k++, i++)
476             {
477               char ch = classname[k];
478               buffer[i] = ch == '.' ? '/' : ch;
479             }
480           strcpy (buffer+i, ".class");
481
482           if (jcf_path_is_zipfile (entry))
483             {
484               int err_code;
485               JCF _jcf;
486               buffer[dir_len] = '\0';
487               SOURCE_FRONTEND_DEBUG 
488                 (("Trying [...%s]:%s", 
489                   &buffer[dir_len-(dir_len > 15 ? 15 : dir_len)], 
490                   buffer+dir_len+1));
491               if (jcf == NULL)
492                 jcf = &_jcf;
493               err_code = open_in_zip (jcf, buffer, buffer+dir_len+1,
494                                       jcf_path_is_system (entry));
495               if (err_code == 0)
496                 {
497                   /* Should we check if .zip is out-of-date wrt .java? */
498                   buffer[dir_len] = '(';
499                   strcpy (buffer+i, ".class)");
500                   if (jcf == &_jcf)
501                     JCF_FINISH (jcf);
502                   return buffer;
503                 }
504               else
505                 continue;
506             }
507           class = caching_stat(buffer, &class_buf);
508         }
509
510       if (source_ok)
511         {
512           /* Compute name of .java file.  */
513           int l, m;
514           strcpy (java_buffer, path_name);
515           l = strlen (java_buffer);
516           for (m = 0; m < classname_length; ++m)
517             java_buffer[m + l] = (classname[m] == '.'
518                                   ? DIR_SEPARATOR : classname[m]);
519           strcpy (java_buffer + m + l, ".java");
520           java = caching_stat (java_buffer, &java_buf);
521           if (java == 0)
522             break;
523         }
524     }
525
526   /* We preferably pick a class file if we have a chance. If the source
527      file is newer than the class file, we issue a warning and parse the
528      source file instead.
529      There should be a flag to allow people have the class file picked
530      up no matter what. FIXME. */
531   if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime)
532     {
533       if (flag_newer)
534         warning ("source file for class `%s' is newer than its matching class file.  Source file `%s' used instead", classname, java_buffer);
535       class = -1;
536     }
537
538   if (! java)
539     dep_file = java_buffer;
540   else
541     dep_file = buffer;
542   if (!class)
543     {
544       SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n",
545                               classname+classname_length-
546                               (classname_length <= 30 ? 
547                                classname_length : 30)));
548       fd = JCF_OPEN_EXACT_CASE (buffer, O_RDONLY | O_BINARY);
549       if (fd >= 0)
550         goto found;
551     }
552   /* Give .java a try, if necessary */
553   if (!java)
554     {
555       strcpy (buffer, java_buffer);
556       SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n",
557                               classname+classname_length-
558                               (classname_length <= 30 ? 
559                                classname_length : 30)));
560       fd = JCF_OPEN_EXACT_CASE (buffer, O_RDONLY);
561       if (fd >= 0)
562         {
563           jcf->java_source = 1;
564           goto found;
565         }
566     }
567
568   free (buffer);
569
570   /* Remember that this class could not be found so that we do not
571      have to look again.  */
572   *htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT) 
573     = (void *) classname;
574
575   return NULL;
576  found:
577   if (jcf->java_source)
578     {
579       JCF_ZERO (jcf);           /* JCF_FINISH relies on this */
580       jcf->java_source = 1;
581       jcf->filename = xstrdup (buffer);
582       close (fd);               /* We use STDIO for source file */
583     }
584   else
585     buffer = (char *) open_class (buffer, jcf, fd, dep_file);
586   jcf->classname = xstrdup (classname);
587   return buffer;
588 }
589
590 void
591 jcf_print_char (FILE *stream, int ch)
592 {
593   switch (ch)
594     {
595     case '\'':
596     case '\\':
597     case '\"':
598       fprintf (stream, "\\%c", ch);
599       break;
600     case '\n':
601       fprintf (stream, "\\n");
602       break;
603     case '\t':
604       fprintf (stream, "\\t");
605       break;
606     case '\r':
607       fprintf (stream, "\\r");
608       break;
609     default:
610       if (ch >= ' ' && ch < 127)
611         putc (ch, stream);
612       else if (ch < 256)
613         fprintf (stream, "\\%03x", ch);
614       else
615         fprintf (stream, "\\u%04x", ch);
616     }
617 }
618
619 /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */
620
621 void
622 jcf_print_utf8 (FILE *stream, register const unsigned char *str, int length)
623 {
624   const unsigned char * limit = str + length;
625   while (str < limit)
626     {
627       int ch = UTF8_GET (str, limit);
628       if (ch < 0)
629         {
630           fprintf (stream, "\\<invalid>");
631           return;
632         }
633       jcf_print_char (stream, ch);
634     }
635 }
636
637 /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */
638
639 void
640 jcf_print_utf8_replace (FILE *stream, const unsigned char *str, int length,
641                         int in_char, int out_char)
642 {
643   const unsigned char *limit = str + length;
644   while (str < limit)
645     {
646       int ch = UTF8_GET (str, limit);
647       if (ch < 0)
648         {
649           fprintf (stream, "\\<invalid>");
650           return;
651         }
652       jcf_print_char (stream, ch == in_char ? out_char : ch);
653     }
654 }
655
656 /* Check that all the cross-references in the constant pool are
657    valid.  Returns 0 on success.
658    Otherwise, returns the index of the (first) invalid entry.
659    Only checks internal consistency, but does not check that
660    any classes, fields, or methods are valid.*/
661
662 int
663 verify_constant_pool (JCF *jcf)
664 {
665   int i, n;
666   for (i = 1; i < JPOOL_SIZE (jcf); i++)
667     {
668       switch (JPOOL_TAG (jcf, i))
669         {
670         case CONSTANT_NameAndType:
671           n = JPOOL_USHORT2 (jcf, i);
672           if (n <= 0 || n >= JPOOL_SIZE(jcf)
673               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
674             return i;
675           /* ... fall through ... */
676         case CONSTANT_Class:
677         case CONSTANT_String:
678           n = JPOOL_USHORT1 (jcf, i);
679           if (n <= 0 || n >= JPOOL_SIZE(jcf)
680               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
681             return i;
682           break;
683         case CONSTANT_Fieldref:
684         case CONSTANT_Methodref:
685         case CONSTANT_InterfaceMethodref:
686           n = JPOOL_USHORT1 (jcf, i);
687           if (n <= 0 || n >= JPOOL_SIZE(jcf)
688               || JPOOL_TAG (jcf, n) != CONSTANT_Class)
689             return i;
690           n = JPOOL_USHORT2 (jcf, i);
691           if (n <= 0 || n >= JPOOL_SIZE(jcf)
692               || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
693             return i;
694           break;
695         case CONSTANT_Long:
696         case CONSTANT_Double:
697           i++;
698           break;
699         case CONSTANT_Float:
700         case CONSTANT_Integer:
701         case CONSTANT_Utf8:
702         case CONSTANT_Unicode:
703           break;
704         default:
705           return i;
706         }
707     }
708   return 0;
709 }
710
711 void
712 format_uint (char *buffer, uint64 value, int base)
713 {
714 #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
715   char buf[WRITE_BUF_SIZE];
716   register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */
717   int chars_written;
718   int i;
719
720   /* Now do the actual conversion, placing the result at the *end* of buf. */
721   /* Note this code does not pretend to be optimized. */
722   do {
723     int digit = value % base;
724     static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
725     *--buf_ptr = digit_chars[digit];
726     value /= base;
727   } while (value != 0);
728
729   chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
730   for (i = 0; i < chars_written; i++)
731     buffer[i] = *buf_ptr++;
732   buffer[i] = 0;
733 }
734
735 void
736 format_int (char *buffer, jlong value, int base)
737 {
738   uint64 abs_value;
739   if (value < 0)
740     {
741       abs_value = -(uint64)value;
742       *buffer++ = '-';
743     }
744   else
745     abs_value = (uint64) value;
746   format_uint (buffer, abs_value, base);
747 }