OSDN Git Service

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