OSDN Git Service

PR c++/9367
[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 *base;
340   memoized_dirlist_entry *dent;
341   void **slot;
342   
343   /* If the hashtable has not already been created, create it now.  */
344   if (!memoized_dirlists)
345     memoized_dirlists = htab_create (37,
346                                      htab_hash_string,
347                                      memoized_dirlist_lookup_eq,
348                                      NULL);
349
350   /* Get the name of the directory.  */
351   sep = strrchr (filename, DIR_SEPARATOR);
352   if (sep)
353     {
354       *sep = '\0';
355       base = sep + 1;
356     }
357   else
358     base = filename;
359
360   /* Obtain the entry for this directory form the hash table.  */
361   slot = htab_find_slot (memoized_dirlists, filename, INSERT);
362   if (!*slot)
363     {
364       /* We have not already scanned this directory; scan it now.  */
365       dent = ((memoized_dirlist_entry *) 
366               ALLOC (sizeof (memoized_dirlist_entry)));
367       dent->dir = xstrdup (filename);
368       /* Unfortunately, scandir is not fully standardized.  In
369          particular, the type of the function pointer passed as the
370          third argument sometimes takes a "const struct dirent *"
371          parameter, and sometimes just a "struct dirent *".  We cast
372          to (void *) so that either way it is quietly accepted.  */
373       dent->num_files = scandir (filename, &dent->files, 
374                                  (void *) java_or_class_file, 
375                                  alphasort);
376       *slot = dent;
377     }
378   else
379     dent = *((memoized_dirlist_entry **) slot);
380
381   /* Put the separator back.  */
382   if (sep)
383     *sep = DIR_SEPARATOR;
384
385   /* If the file is not in the list, there is no need to stat it; it
386      does not exist.  */
387   if (dent->num_files != -1
388       && !bsearch (base, dent->files, dent->num_files,
389                    sizeof (struct dirent *), compare_path))
390     return -1;
391 #endif
392   
393   return stat (filename, buf);
394 }
395
396 /* Returns 1 if the CLASSNAME (really a char *) matches the name
397    stored in TABLE_ENTRY (also a char *).  */
398
399 static int
400 memoized_class_lookup_eq (const void *table_entry, const void *classname)
401 {
402   return strcmp ((const char *)classname, (const char *)table_entry) == 0;
403 }
404
405 /* A hash table keeping track of class names that were not found
406    during class lookup.  (There is no need to cache the values
407    associated with names that were found; they are saved in
408    IDENTIFIER_CLASS_VALUE.)  */
409 static htab_t memoized_class_lookups;
410
411 /* Returns a freshly malloc'd string with the fully qualified pathname
412    of the .class file for the class CLASSNAME.  CLASSNAME must be
413    allocated in permanent storage; this function may retain a pointer
414    to it.  Returns NULL on failure.  If JCF != NULL, it is suitably
415    initialized.  SOURCE_OK is true if we should also look for .java
416    file. */
417
418 const char *
419 find_class (const char *classname, int classname_length, JCF *jcf,
420             int source_ok)
421 {
422   int fd;
423   int i, k, java = -1, class = -1;
424   struct stat java_buf, class_buf;
425   char *dep_file;
426   void *entry;
427   char *java_buffer;
428   int buflen;
429   char *buffer;
430   hashval_t hash;
431
432   /* Create the hash table, if it does not already exist.  */
433   if (!memoized_class_lookups)
434     memoized_class_lookups = htab_create (37, 
435                                           htab_hash_string, 
436                                           memoized_class_lookup_eq,
437                                           NULL);
438
439   /* Loop for this class in the hashtable.  If it is present, we've
440      already looked for this class and failed to find it.  */
441   hash = htab_hash_string (classname);
442   if (htab_find_with_hash (memoized_class_lookups, classname, hash))
443     return NULL;
444
445   /* Allocate and zero out the buffer, since we don't explicitly put a
446      null pointer when we're copying it below.  */
447   buflen = jcf_path_max_len () + classname_length + 10;
448   buffer = ALLOC (buflen);
449   memset (buffer, 0, buflen);
450
451   java_buffer = alloca (buflen);
452
453   jcf->java_source = 0;
454
455   for (entry = jcf_path_start (); entry != NULL; entry = jcf_path_next (entry))
456     {
457       const char *path_name = jcf_path_name (entry);
458       if (class != 0)
459         {
460           int dir_len;
461
462           strcpy (buffer, path_name);
463           i = strlen (buffer);
464
465           /* This is right because we know that `.zip' entries will have a
466              trailing slash.  See jcf-path.c.  */
467           dir_len = i - 1;
468
469           for (k = 0; k < classname_length; k++, i++)
470             {
471               char ch = classname[k];
472               buffer[i] = ch == '.' ? '/' : ch;
473             }
474           strcpy (buffer+i, ".class");
475
476           if (jcf_path_is_zipfile (entry))
477             {
478               int err_code;
479               JCF _jcf;
480               buffer[dir_len] = '\0';
481               SOURCE_FRONTEND_DEBUG 
482                 (("Trying [...%s]:%s", 
483                   &buffer[dir_len-(dir_len > 15 ? 15 : dir_len)], 
484                   buffer+dir_len+1));
485               if (jcf == NULL)
486                 jcf = &_jcf;
487               err_code = open_in_zip (jcf, buffer, buffer+dir_len+1,
488                                       jcf_path_is_system (entry));
489               if (err_code == 0)
490                 {
491                   /* Should we check if .zip is out-of-date wrt .java? */
492                   buffer[dir_len] = '(';
493                   strcpy (buffer+i, ".class)");
494                   if (jcf == &_jcf)
495                     JCF_FINISH (jcf);
496                   return buffer;
497                 }
498               else
499                 continue;
500             }
501           class = caching_stat(buffer, &class_buf);
502         }
503
504       if (source_ok)
505         {
506           /* Compute name of .java file.  */
507           int l, m;
508           strcpy (java_buffer, path_name);
509           l = strlen (java_buffer);
510           for (m = 0; m < classname_length; ++m)
511             java_buffer[m + l] = (classname[m] == '.' ? '/' : classname[m]);
512           strcpy (java_buffer + m + l, ".java");
513           java = caching_stat (java_buffer, &java_buf);
514           if (java == 0)
515             break;
516         }
517     }
518
519   /* We preferably pick a class file if we have a chance. If the source
520      file is newer than the class file, we issue a warning and parse the
521      source file instead.
522      There should be a flag to allow people have the class file picked
523      up no matter what. FIXME. */
524   if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime)
525     {
526       if (flag_newer)
527         warning ("source file for class `%s' is newer than its matching class file.  Source file `%s' used instead", classname, java_buffer);
528       class = -1;
529     }
530
531   if (! java)
532     dep_file = java_buffer;
533   else
534     dep_file = buffer;
535   if (!class)
536     {
537       SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n",
538                               classname+classname_length-
539                               (classname_length <= 30 ? 
540                                classname_length : 30)));
541       fd = open (buffer, O_RDONLY | O_BINARY);
542       if (fd >= 0)
543         goto found;
544     }
545   /* Give .java a try, if necessary */
546   if (!java)
547     {
548       strcpy (buffer, java_buffer);
549       SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n",
550                               classname+classname_length-
551                               (classname_length <= 30 ? 
552                                classname_length : 30)));
553       fd = open (buffer, O_RDONLY);
554       if (fd >= 0)
555         {
556           jcf->java_source = 1;
557           goto found;
558         }
559     }
560
561   free (buffer);
562
563   /* Remember that this class could not be found so that we do not
564      have to look again.  */
565   *htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT) 
566     = (void *) classname;
567
568   return NULL;
569  found:
570   if (jcf->java_source)
571     {
572       JCF_ZERO (jcf);           /* JCF_FINISH relies on this */
573       jcf->java_source = 1;
574       jcf->filename = xstrdup (buffer);
575       close (fd);               /* We use STDIO for source file */
576     }
577   else
578     buffer = (char *) open_class (buffer, jcf, fd, dep_file);
579   jcf->classname = xstrdup (classname);
580   return buffer;
581 }
582
583 void
584 jcf_print_char (FILE *stream, int ch)
585 {
586   switch (ch)
587     {
588     case '\'':
589     case '\\':
590     case '\"':
591       fprintf (stream, "\\%c", ch);
592       break;
593     case '\n':
594       fprintf (stream, "\\n");
595       break;
596     case '\t':
597       fprintf (stream, "\\t");
598       break;
599     case '\r':
600       fprintf (stream, "\\r");
601       break;
602     default:
603       if (ch >= ' ' && ch < 127)
604         putc (ch, stream);
605       else if (ch < 256)
606         fprintf (stream, "\\%03x", ch);
607       else
608         fprintf (stream, "\\u%04x", ch);
609     }
610 }
611
612 /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */
613
614 void
615 jcf_print_utf8 (FILE *stream, register const unsigned char *str, int length)
616 {
617   const unsigned char * limit = str + length;
618   while (str < limit)
619     {
620       int ch = UTF8_GET (str, limit);
621       if (ch < 0)
622         {
623           fprintf (stream, "\\<invalid>");
624           return;
625         }
626       jcf_print_char (stream, ch);
627     }
628 }
629
630 /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */
631
632 void
633 jcf_print_utf8_replace (FILE *stream, const unsigned char *str, int length,
634                         int in_char, int out_char)
635 {
636   const unsigned char *limit = str + length;
637   while (str < limit)
638     {
639       int ch = UTF8_GET (str, limit);
640       if (ch < 0)
641         {
642           fprintf (stream, "\\<invalid>");
643           return;
644         }
645       jcf_print_char (stream, ch == in_char ? out_char : ch);
646     }
647 }
648
649 /* Check that all the cross-references in the constant pool are
650    valid.  Returns 0 on success.
651    Otherwise, returns the index of the (first) invalid entry.
652    Only checks internal consistency, but does not check that
653    any classes, fields, or methods are valid.*/
654
655 int
656 verify_constant_pool (JCF *jcf)
657 {
658   int i, n;
659   for (i = 1; i < JPOOL_SIZE (jcf); i++)
660     {
661       switch (JPOOL_TAG (jcf, i))
662         {
663         case CONSTANT_NameAndType:
664           n = JPOOL_USHORT2 (jcf, i);
665           if (n <= 0 || n >= JPOOL_SIZE(jcf)
666               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
667             return i;
668           /* ... fall through ... */
669         case CONSTANT_Class:
670         case CONSTANT_String:
671           n = JPOOL_USHORT1 (jcf, i);
672           if (n <= 0 || n >= JPOOL_SIZE(jcf)
673               || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
674             return i;
675           break;
676         case CONSTANT_Fieldref:
677         case CONSTANT_Methodref:
678         case CONSTANT_InterfaceMethodref:
679           n = JPOOL_USHORT1 (jcf, i);
680           if (n <= 0 || n >= JPOOL_SIZE(jcf)
681               || JPOOL_TAG (jcf, n) != CONSTANT_Class)
682             return i;
683           n = JPOOL_USHORT2 (jcf, i);
684           if (n <= 0 || n >= JPOOL_SIZE(jcf)
685               || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
686             return i;
687           break;
688         case CONSTANT_Long:
689         case CONSTANT_Double:
690           i++;
691           break;
692         case CONSTANT_Float:
693         case CONSTANT_Integer:
694         case CONSTANT_Utf8:
695         case CONSTANT_Unicode:
696           break;
697         default:
698           return i;
699         }
700     }
701   return 0;
702 }
703
704 void
705 format_uint (char *buffer, uint64 value, int base)
706 {
707 #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
708   char buf[WRITE_BUF_SIZE];
709   register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */
710   int chars_written;
711   int i;
712
713   /* Now do the actual conversion, placing the result at the *end* of buf. */
714   /* Note this code does not pretend to be optimized. */
715   do {
716     int digit = value % base;
717     static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
718     *--buf_ptr = digit_chars[digit];
719     value /= base;
720   } while (value != 0);
721
722   chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
723   for (i = 0; i < chars_written; i++)
724     buffer[i] = *buf_ptr++;
725   buffer[i] = 0;
726 }
727
728 void
729 format_int (char *buffer, jlong value, int base)
730 {
731   uint64 abs_value;
732   if (value < 0)
733     {
734       abs_value = -(uint64)value;
735       *buffer++ = '-';
736     }
737   else
738     abs_value = (uint64) value;
739   format_uint (buffer, abs_value, base);
740 }