OSDN Git Service

2003-03-10 Ranjit Mathew <rmathew@hotmail.com>
[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;
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] == '.' ? '/' : classname[m]);
518           strcpy (java_buffer + m + l, ".java");
519           java = caching_stat (java_buffer, &java_buf);
520           if (java == 0)
521             break;
522         }
523     }
524
525   /* We preferably pick a class file if we have a chance. If the source
526      file is newer than the class file, we issue a warning and parse the
527      source file instead.
528      There should be a flag to allow people have the class file picked
529      up no matter what. FIXME. */
530   if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime)
531     {
532       if (flag_newer)
533         warning ("source file for class `%s' is newer than its matching class file.  Source file `%s' used instead", classname, java_buffer);
534       class = -1;
535     }
536
537   if (! java)
538     dep_file = java_buffer;
539   else
540     dep_file = buffer;
541   if (!class)
542     {
543       SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n",
544                               classname+classname_length-
545                               (classname_length <= 30 ? 
546                                classname_length : 30)));
547       fd = open (buffer, O_RDONLY | O_BINARY);
548       if (fd >= 0)
549         goto found;
550     }
551   /* Give .java a try, if necessary */
552   if (!java)
553     {
554       strcpy (buffer, java_buffer);
555       SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n",
556                               classname+classname_length-
557                               (classname_length <= 30 ? 
558                                classname_length : 30)));
559       fd = open (buffer, O_RDONLY);
560       if (fd >= 0)
561         {
562           jcf->java_source = 1;
563           goto found;
564         }
565     }
566
567   free (buffer);
568
569   /* Remember that this class could not be found so that we do not
570      have to look again.  */
571   *htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT) 
572     = (void *) classname;
573
574   return NULL;
575  found:
576   if (jcf->java_source)
577     {
578       JCF_ZERO (jcf);           /* JCF_FINISH relies on this */
579       jcf->java_source = 1;
580       jcf->filename = xstrdup (buffer);
581       close (fd);               /* We use STDIO for source file */
582     }
583   else
584     buffer = (char *) open_class (buffer, jcf, fd, dep_file);
585   jcf->classname = xstrdup (classname);
586   return buffer;
587 }
588
589 void
590 jcf_print_char (FILE *stream, int ch)
591 {
592   switch (ch)
593     {
594     case '\'':
595     case '\\':
596     case '\"':
597       fprintf (stream, "\\%c", ch);
598       break;
599     case '\n':
600       fprintf (stream, "\\n");
601       break;
602     case '\t':
603       fprintf (stream, "\\t");
604       break;
605     case '\r':
606       fprintf (stream, "\\r");
607       break;
608     default:
609       if (ch >= ' ' && ch < 127)
610         putc (ch, stream);
611       else if (ch < 256)
612         fprintf (stream, "\\%03x", ch);
613       else
614         fprintf (stream, "\\u%04x", ch);
615     }
616 }
617
618 /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */
619
620 void
621 jcf_print_utf8 (FILE *stream, register const unsigned char *str, int length)
622 {
623   const unsigned char * limit = str + length;
624   while (str < limit)
625     {
626       int ch = UTF8_GET (str, limit);
627       if (ch < 0)
628         {
629           fprintf (stream, "\\<invalid>");
630           return;
631         }
632       jcf_print_char (stream, ch);
633     }
634 }
635
636 /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */
637
638 void
639 jcf_print_utf8_replace (FILE *stream, const unsigned char *str, int length,
640                         int in_char, int out_char)
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 == in_char ? out_char : ch);
652     }
653 }
654
655 /* Check that all the cross-references in the constant pool are
656    valid.  Returns 0 on success.
657    Otherwise, returns the index of the (first) invalid entry.
658    Only checks internal consistency, but does not check that
659    any classes, fields, or methods are valid.*/
660
661 int
662 verify_constant_pool (JCF *jcf)
663 {
664   int i, n;
665   for (i = 1; i < JPOOL_SIZE (jcf); i++)
666     {
667       switch (JPOOL_TAG (jcf, i))
668         {
669         case CONSTANT_NameAndType:
670           n = JPOOL_USHORT2 (jcf, i);
671           if (n <= 0 || n >= JPOOL_SIZE(jcf)
672               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
673             return i;
674           /* ... fall through ... */
675         case CONSTANT_Class:
676         case CONSTANT_String:
677           n = JPOOL_USHORT1 (jcf, i);
678           if (n <= 0 || n >= JPOOL_SIZE(jcf)
679               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
680             return i;
681           break;
682         case CONSTANT_Fieldref:
683         case CONSTANT_Methodref:
684         case CONSTANT_InterfaceMethodref:
685           n = JPOOL_USHORT1 (jcf, i);
686           if (n <= 0 || n >= JPOOL_SIZE(jcf)
687               || JPOOL_TAG (jcf, n) != CONSTANT_Class)
688             return i;
689           n = JPOOL_USHORT2 (jcf, i);
690           if (n <= 0 || n >= JPOOL_SIZE(jcf)
691               || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
692             return i;
693           break;
694         case CONSTANT_Long:
695         case CONSTANT_Double:
696           i++;
697           break;
698         case CONSTANT_Float:
699         case CONSTANT_Integer:
700         case CONSTANT_Utf8:
701         case CONSTANT_Unicode:
702           break;
703         default:
704           return i;
705         }
706     }
707   return 0;
708 }
709
710 void
711 format_uint (char *buffer, uint64 value, int base)
712 {
713 #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
714   char buf[WRITE_BUF_SIZE];
715   register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */
716   int chars_written;
717   int i;
718
719   /* Now do the actual conversion, placing the result at the *end* of buf. */
720   /* Note this code does not pretend to be optimized. */
721   do {
722     int digit = value % base;
723     static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
724     *--buf_ptr = digit_chars[digit];
725     value /= base;
726   } while (value != 0);
727
728   chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
729   for (i = 0; i < chars_written; i++)
730     buffer[i] = *buf_ptr++;
731   buffer[i] = 0;
732 }
733
734 void
735 format_int (char *buffer, jlong value, int base)
736 {
737   uint64 abs_value;
738   if (value < 0)
739     {
740       abs_value = -(uint64)value;
741       *buffer++ = '-';
742     }
743   else
744     abs_value = (uint64) value;
745   format_uint (buffer, abs_value, base);
746 }