Daily bump.
[official-gcc.git] / gcc / java / jcf-io.c
blobb228bf576c5e8afbe6a6d62f4375dd776373ca2b
1 /* Utility routines for finding and reading Java(TM) .class files.
2 Copyright (C) 1996, 1997, 1998, 1999, 2000, 2002 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with GNU CC; see the file COPYING. If not, write to
16 the Free Software Foundation, 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA.
19 Java and all Java-based marks are trademarks or registered trademarks
20 of Sun Microsystems, Inc. in the United States and other countries.
21 The Free Software Foundation is independent of Sun Microsystems, Inc. */
23 /* Written by Per Bothner <bothner@cygnus.com>, February 1996. */
25 #include "config.h"
26 #include "system.h"
28 #include "jcf.h"
29 #include "tree.h"
30 #include "toplev.h"
31 #include "java-tree.h"
32 #include "hashtab.h"
33 #if JCF_USE_SCANDIR
34 #include <dirent.h>
35 #include <fnmatch.h>
36 #endif
38 #include "zlib.h"
40 /* DOS brain-damage */
41 #ifndef O_BINARY
42 #define O_BINARY 0 /* MS-DOS brain-damage */
43 #endif
45 int
46 DEFUN(jcf_unexpected_eof, (jcf, count),
47 JCF *jcf AND int count ATTRIBUTE_UNUSED)
49 if (jcf->filename)
50 fprintf (stderr, "Premature end of .class file %s.\n", jcf->filename);
51 else
52 fprintf (stderr, "Premature end of .class file <stdin>.\n");
53 exit (-1);
56 void
57 DEFUN(jcf_trim_old_input, (jcf),
58 JCF *jcf)
60 int count = jcf->read_ptr - jcf->buffer;
61 if (count > 0)
63 memmove (jcf->buffer, jcf->read_ptr, jcf->read_end - jcf->read_ptr);
64 jcf->read_ptr -= count;
65 jcf->read_end -= count;
69 int
70 DEFUN(jcf_filbuf_from_stdio, (jcf, count),
71 JCF *jcf AND int count)
73 FILE *file = (FILE*) (jcf->read_state);
74 if (count > jcf->buffer_end - jcf->read_ptr)
76 JCF_u4 old_read_ptr = jcf->read_ptr - jcf->buffer;
77 JCF_u4 old_read_end = jcf->read_end - jcf->buffer;
78 JCF_u4 old_size = jcf->buffer_end - jcf->buffer;
79 JCF_u4 new_size = (old_size == 0 ? 2000 : 2 * old_size) + count;
80 unsigned char *new_buffer = jcf->buffer == NULL ? ALLOC (new_size)
81 : REALLOC (jcf->buffer, new_size);
82 jcf->buffer = new_buffer;
83 jcf->buffer_end = new_buffer + new_size;
84 jcf->read_ptr = new_buffer + old_read_ptr;
85 jcf->read_end = new_buffer + old_read_end;
87 count -= jcf->read_end - jcf->read_ptr;
88 if (count <= 0)
89 return 0;
90 if ((int) fread (jcf->read_end, 1, count, file) != count)
91 jcf_unexpected_eof (jcf, count);
92 jcf->read_end += count;
93 return 0;
96 #include "zipfile.h"
98 struct ZipFile *SeenZipFiles = NULL;
100 /* Open a zip file with the given name, and cache directory and file
101 descriptor. If the file is missing, treat it as an empty archive.
102 Return NULL if the .zip file is malformed.
105 ZipFile *
106 DEFUN(opendir_in_zip, (zipfile, is_system),
107 const char *zipfile AND int is_system)
109 struct ZipFile* zipf;
110 char magic [4];
111 int fd;
112 for (zipf = SeenZipFiles; zipf != NULL; zipf = zipf->next)
114 if (strcmp (zipf->name, zipfile) == 0)
115 return zipf;
118 zipf = ALLOC (sizeof (struct ZipFile) + strlen (zipfile) + 1);
119 zipf->next = SeenZipFiles;
120 zipf->name = (char*)(zipf+1);
121 strcpy (zipf->name, zipfile);
122 SeenZipFiles = zipf;
123 fd = open (zipfile, O_RDONLY | O_BINARY);
124 zipf->fd = fd;
125 if (fd < 0)
127 /* A missing zip file is not considered an error.
128 We may want to re-consider that. FIXME. */
129 zipf->count = 0;
130 zipf->dir_size = 0;
131 zipf->central_directory = NULL;
133 else
135 jcf_dependency_add_file (zipfile, is_system);
136 if (read (fd, magic, 4) != 4 || GET_u4 (magic) != (JCF_u4)ZIPMAGIC)
137 return NULL;
138 lseek (fd, 0L, SEEK_SET);
139 if (read_zip_archive (zipf) != 0)
140 return NULL;
142 return zipf;
145 /* Returns:
146 0: OK - zipmember found.
147 -1: Not found.
148 -2: Malformed archive.
152 DEFUN(open_in_zip, (jcf, zipfile, zipmember, is_system),
153 JCF *jcf AND const char *zipfile AND const char *zipmember
154 AND int is_system)
156 ZipDirectory *zipd;
157 int i, len;
158 ZipFile *zipf = opendir_in_zip (zipfile, is_system);
160 if (zipf == NULL)
161 return -2;
163 if (!zipmember)
164 return 0;
166 len = strlen (zipmember);
168 zipd = (struct ZipDirectory*) zipf->central_directory;
169 for (i = 0; i < zipf->count; i++, zipd = ZIPDIR_NEXT (zipd))
171 if (len == zipd->filename_length &&
172 strncmp (ZIPDIR_FILENAME (zipd), zipmember, len) == 0)
174 JCF_ZERO (jcf);
176 jcf->filename = xstrdup (zipfile);
177 jcf->classname = xstrdup (zipmember);
178 return read_zip_member(jcf, zipd, zipf);
181 return -1;
184 /* Read data from zip archive member. */
187 DEFUN(read_zip_member, (jcf, zipd, zipf),
188 JCF *jcf AND ZipDirectory *zipd AND ZipFile *zipf)
190 jcf->filbuf = jcf_unexpected_eof;
191 jcf->zipd = (void *)zipd;
193 if (zipd->compression_method == Z_NO_COMPRESSION)
195 jcf->buffer = ALLOC (zipd->size);
196 jcf->buffer_end = jcf->buffer + zipd->size;
197 jcf->read_ptr = jcf->buffer;
198 jcf->read_end = jcf->buffer_end;
199 if (lseek (zipf->fd, zipd->filestart, 0) < 0
200 || read (zipf->fd, jcf->buffer, zipd->size) != (long) zipd->size)
201 return -2;
203 else
205 char *buffer;
206 z_stream d_stream; /* decompression stream */
207 d_stream.zalloc = (alloc_func) 0;
208 d_stream.zfree = (free_func) 0;
209 d_stream.opaque = (voidpf) 0;
211 jcf->buffer = ALLOC (zipd->uncompressed_size);
212 d_stream.next_out = jcf->buffer;
213 d_stream.avail_out = zipd->uncompressed_size;
214 jcf->buffer_end = jcf->buffer + zipd->uncompressed_size;
215 jcf->read_ptr = jcf->buffer;
216 jcf->read_end = jcf->buffer_end;
217 buffer = ALLOC (zipd->size);
218 d_stream.next_in = buffer;
219 d_stream.avail_in = zipd->size;
220 if (lseek (zipf->fd, zipd->filestart, 0) < 0
221 || read (zipf->fd, buffer, zipd->size) != (long) zipd->size)
222 return -2;
223 /* Handle NO_HEADER using undocumented zlib feature.
224 This is a very common hack. */
225 inflateInit2 (&d_stream, -MAX_WBITS);
226 inflate (&d_stream, Z_NO_FLUSH);
227 inflateEnd (&d_stream);
228 FREE (buffer);
231 return 0;
234 #if JCF_USE_STDIO
235 const char *
236 DEFUN(open_class, (filename, jcf, stream, dep_name),
237 const char *filename AND JCF *jcf AND FILE* stream
238 AND const char *dep_name)
240 if (jcf)
242 if (dep_name != NULL)
243 jcf_dependency_add_file (dep_name, 0);
244 JCF_ZERO (jcf);
245 jcf->buffer = NULL;
246 jcf->buffer_end = NULL;
247 jcf->read_ptr = NULL;
248 jcf->read_end = NULL;
249 jcf->read_state = stream;
250 jcf->filename = filename;
251 jcf->filbuf = jcf_filbuf_from_stdio;
253 else
254 fclose (stream);
255 return filename;
257 #else
258 const char *
259 DEFUN(open_class, (filename, jcf, fd, dep_name),
260 const char *filename AND JCF *jcf AND int fd AND const char *dep_name)
262 if (jcf)
264 struct stat stat_buf;
265 if (fstat (fd, &stat_buf) != 0
266 || ! S_ISREG (stat_buf.st_mode))
268 perror ("Could not figure length of .class file");
269 return NULL;
271 if (dep_name != NULL)
272 jcf_dependency_add_file (dep_name, 0);
273 JCF_ZERO (jcf);
274 jcf->buffer = ALLOC (stat_buf.st_size);
275 jcf->buffer_end = jcf->buffer + stat_buf.st_size;
276 jcf->read_ptr = jcf->buffer;
277 jcf->read_end = jcf->buffer_end;
278 jcf->read_state = NULL;
279 jcf->filename = filename;
280 if (read (fd, jcf->buffer, stat_buf.st_size) != stat_buf.st_size)
282 perror ("Failed to read .class file");
283 return NULL;
285 close (fd);
286 jcf->filbuf = jcf_unexpected_eof;
288 else
289 close (fd);
290 return filename;
292 #endif
295 const char *
296 DEFUN(find_classfile, (filename, jcf, dep_name),
297 char *filename AND JCF *jcf AND const char *dep_name)
299 #if JCF_USE_STDIO
300 FILE *stream = fopen (filename, "rb");
301 if (stream == NULL)
302 return NULL;
303 return open_class (arg, jcf, stream, dep_name);
304 #else
305 int fd = open (filename, O_RDONLY | O_BINARY);
306 if (fd < 0)
307 return NULL;
308 return open_class (filename, jcf, fd, dep_name);
309 #endif
312 #if JCF_USE_SCANDIR
314 /* A comparison function (as for qsort) that compares KEY (a char *
315 giving the basename of a file) with the name stored in ENTRY (a
316 dirent **). */
318 static int
319 DEFUN(compare_path, (key, entry),
320 const void *key AND const void *entry)
322 return strcmp ((const char *) key,
323 (*((const struct dirent **) entry))->d_name);
326 /* Returns nonzero if ENTRY names a .java or .class file. */
328 static int
329 DEFUN(java_or_class_file, (entry),
330 const struct dirent *entry)
332 const char *base = basename (entry->d_name);
333 return (fnmatch ("*.java", base, 0) == 0 ||
334 fnmatch ("*.class", base, 0) == 0);
337 /* Information about the files present in a particular directory. */
338 typedef struct memoized_dirlist_entry
340 /* The name of the directory. */
341 const char *dir;
342 /* The number of .java and .class files present, or -1 if we could
343 not, for some reason, obtain the list. */
344 int num_files;
345 /* The .java and .class files in the directory, in alphabetical
346 order. */
347 struct dirent **files;
348 } memoized_dirlist_entry;
350 /* Returns true if ENTRY (a memoized_dirlist_entry *) correponds to
351 the directory given by KEY (a char *) giving the directory
352 name. */
354 static int
355 DEFUN(memoized_dirlist_lookup_eq, (entry, key),
356 const void *entry AND const void *key)
358 return strcmp ((const char *) key,
359 ((const memoized_dirlist_entry *) entry)->dir) == 0;
362 /* A hash table mapping directory names to the lists of .java and
363 .class files in that directory. */
365 static htab_t memoized_dirlists;
367 #endif
369 /* Like stat, but avoids actually making the stat system call if we
370 know that it cannot succeed. FILENAME and BUF are as for stat. */
372 static int
373 DEFUN(caching_stat, (filename, buf),
374 char *filename AND struct stat *buf)
376 #if JCF_USE_SCANDIR
377 char *sep;
378 char *base;
379 memoized_dirlist_entry *dent;
380 void **slot;
382 /* If the hashtable has not already been created, create it now. */
383 if (!memoized_dirlists)
384 memoized_dirlists = htab_create (37,
385 htab_hash_string,
386 memoized_dirlist_lookup_eq,
387 NULL);
389 /* Get the name of the directory. */
390 sep = strrchr (filename, DIR_SEPARATOR);
391 if (sep)
393 *sep = '\0';
394 base = sep + 1;
396 else
397 base = filename;
399 /* Obtain the entry for this directory form the hash table. */
400 slot = htab_find_slot (memoized_dirlists, filename, INSERT);
401 if (!*slot)
403 /* We have not already scanned this directory; scan it now. */
404 dent = ((memoized_dirlist_entry *)
405 ALLOC (sizeof (memoized_dirlist_entry)));
406 dent->dir = xstrdup (filename);
407 /* Unfortunately, scandir is not fully standardized. In
408 particular, the type of the function pointer passed as the
409 third argument sometimes takes a "const struct dirent *"
410 parameter, and sometimes just a "struct dirent *". We rely
411 on the ability to interchange these two types of function
412 pointers. */
413 dent->num_files = scandir (filename, &dent->files,
414 java_or_class_file,
415 alphasort);
416 *slot = dent;
418 else
419 dent = *((memoized_dirlist_entry **) slot);
421 /* Put the spearator back. */
422 if (sep)
423 *sep = DIR_SEPARATOR;
425 /* If the file is not in the list, there is no need to stat it; it
426 does not exist. */
427 if (dent->num_files != -1
428 && !bsearch (base, dent->files, dent->num_files,
429 sizeof (struct dirent *), compare_path))
430 return -1;
431 #endif
433 return stat (filename, buf);
436 /* Returns 1 if the CLASSNAME (really a char *) matches the name
437 stored in TABLE_ENTRY (also a char *). */
439 static int
440 DEFUN(memoized_class_lookup_eq, (table_entry, classname),
441 const void *table_entry AND const void *classname)
443 return strcmp ((const char *)classname, (const char *)table_entry) == 0;
446 /* A hash table keeping track of class names that were not found
447 during class lookup. (There is no need to cache the values
448 associated with names that were found; they are saved in
449 IDENTIFIER_CLASS_VALUE.) */
450 static htab_t memoized_class_lookups;
452 /* Returns a freshly malloc'd string with the fully qualified pathname
453 of the .class file for the class CLASSNAME. CLASSNAME must be
454 allocated in permanent storage; this function may retain a pointer
455 to it. Returns NULL on failure. If JCF != NULL, it is suitably
456 initialized. SOURCE_OK is true if we should also look for .java
457 file. */
459 const char *
460 DEFUN(find_class, (classname, classname_length, jcf, source_ok),
461 const char *classname AND int classname_length AND JCF *jcf AND int source_ok)
464 #if JCF_USE_STDIO
465 FILE *stream;
466 #else
467 int fd;
468 #endif
469 int i, k, java = -1, class = -1;
470 struct stat java_buf, class_buf;
471 char *dep_file;
472 void *entry;
473 char *java_buffer;
474 int buflen;
475 char *buffer;
476 hashval_t hash;
478 /* Create the hash table, if it does not already exist. */
479 if (!memoized_class_lookups)
480 memoized_class_lookups = htab_create (37,
481 htab_hash_string,
482 memoized_class_lookup_eq,
483 NULL);
485 /* Loop for this class in the hashtable. If it is present, we've
486 already looked for this class and failed to find it. */
487 hash = htab_hash_string (classname);
488 if (htab_find_with_hash (memoized_class_lookups, classname, hash))
489 return NULL;
491 /* Allocate and zero out the buffer, since we don't explicitly put a
492 null pointer when we're copying it below. */
493 buflen = jcf_path_max_len () + classname_length + 10;
494 buffer = (char *) ALLOC (buflen);
495 memset (buffer, 0, buflen);
497 java_buffer = (char *) alloca (buflen);
499 jcf->java_source = 0;
501 for (entry = jcf_path_start (); entry != NULL; entry = jcf_path_next (entry))
503 const char *path_name = jcf_path_name (entry);
504 if (class != 0)
506 int dir_len;
508 strcpy (buffer, path_name);
509 i = strlen (buffer);
511 /* This is right because we know that `.zip' entries will have a
512 trailing slash. See jcf-path.c. */
513 dir_len = i - 1;
515 for (k = 0; k < classname_length; k++, i++)
517 char ch = classname[k];
518 buffer[i] = ch == '.' ? '/' : ch;
520 strcpy (buffer+i, ".class");
522 if (jcf_path_is_zipfile (entry))
524 int err_code;
525 JCF _jcf;
526 buffer[dir_len] = '\0';
527 SOURCE_FRONTEND_DEBUG
528 (("Trying [...%s]:%s",
529 &buffer[dir_len-(dir_len > 15 ? 15 : dir_len)],
530 buffer+dir_len+1));
531 if (jcf == NULL)
532 jcf = &_jcf;
533 err_code = open_in_zip (jcf, buffer, buffer+dir_len+1,
534 jcf_path_is_system (entry));
535 if (err_code == 0)
537 /* Should we check if .zip is out-of-date wrt .java? */
538 buffer[dir_len] = '(';
539 strcpy (buffer+i, ".class)");
540 if (jcf == &_jcf)
541 JCF_FINISH (jcf);
542 return buffer;
544 else
545 continue;
547 class = caching_stat(buffer, &class_buf);
550 if (source_ok)
552 /* Compute name of .java file. */
553 int l, m;
554 strcpy (java_buffer, path_name);
555 l = strlen (java_buffer);
556 for (m = 0; m < classname_length; ++m)
557 java_buffer[m + l] = (classname[m] == '.' ? '/' : classname[m]);
558 strcpy (java_buffer + m + l, ".java");
559 java = caching_stat (java_buffer, &java_buf);
560 if (java == 0)
561 break;
565 /* We preferably pick a class file if we have a chance. If the source
566 file is newer than the class file, we issue a warning and parse the
567 source file instead.
568 There should be a flag to allow people have the class file picked
569 up no matter what. FIXME. */
570 if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime)
572 if (flag_newer)
573 warning ("source file for class `%s' is newer than its matching class file. Source file `%s' used instead", classname, java_buffer);
574 class = -1;
577 if (! java)
578 dep_file = java_buffer;
579 else
580 dep_file = buffer;
581 #if JCF_USE_STDIO
582 if (!class)
584 SOURCE_FRONTEND_DEBUG (("Trying %s", buffer));
585 stream = fopen (buffer, "rb");
586 if (stream)
587 goto found;
589 /* Give .java a try, if necessary */
590 if (!java)
592 strcpy (buffer, java_buffer);
593 SOURCE_FRONTEND_DEBUG (("Trying %s", buffer));
594 stream = fopen (buffer, "r");
595 if (stream)
597 jcf->java_source = 1;
598 goto found;
601 #else
602 if (!class)
604 SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n",
605 classname+classname_length-
606 (classname_length <= 30 ?
607 classname_length : 30)));
608 fd = open (buffer, O_RDONLY | O_BINARY);
609 if (fd >= 0)
610 goto found;
612 /* Give .java a try, if necessary */
613 if (!java)
615 strcpy (buffer, java_buffer);
616 SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n",
617 classname+classname_length-
618 (classname_length <= 30 ?
619 classname_length : 30)));
620 fd = open (buffer, O_RDONLY);
621 if (fd >= 0)
623 jcf->java_source = 1;
624 goto found;
627 #endif
629 free (buffer);
631 /* Remember that this class could not be found so that we do not
632 have to look again. */
633 *htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT)
634 = (void *) classname;
636 return NULL;
637 found:
638 #if JCF_USE_STDIO
639 if (jcf->java_source)
640 return NULL; /* FIXME */
641 else
642 return open_class (buffer, jcf, stream, dep_file);
643 #else
644 if (jcf->java_source)
646 JCF_ZERO (jcf); /* JCF_FINISH relies on this */
647 jcf->java_source = 1;
648 jcf->filename = xstrdup (buffer);
649 close (fd); /* We use STDIO for source file */
651 else
652 buffer = (char *) open_class (buffer, jcf, fd, dep_file);
653 jcf->classname = xstrdup (classname);
654 return buffer;
655 #endif
658 void
659 DEFUN(jcf_print_char, (stream, ch),
660 FILE *stream AND int ch)
662 switch (ch)
664 case '\'':
665 case '\\':
666 case '\"':
667 fprintf (stream, "\\%c", ch);
668 break;
669 case '\n':
670 fprintf (stream, "\\n");
671 break;
672 case '\t':
673 fprintf (stream, "\\t");
674 break;
675 case '\r':
676 fprintf (stream, "\\r");
677 break;
678 default:
679 if (ch >= ' ' && ch < 127)
680 putc (ch, stream);
681 else if (ch < 256)
682 fprintf (stream, "\\%03x", ch);
683 else
684 fprintf (stream, "\\u%04x", ch);
688 /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */
690 void
691 DEFUN(jcf_print_utf8, (stream, str, length),
692 FILE *stream AND register const unsigned char *str AND int length)
694 const unsigned char * limit = str + length;
695 while (str < limit)
697 int ch = UTF8_GET (str, limit);
698 if (ch < 0)
700 fprintf (stream, "\\<invalid>");
701 return;
703 jcf_print_char (stream, ch);
707 /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */
709 void
710 DEFUN(jcf_print_utf8_replace, (stream, str, length, in_char, out_char),
711 FILE *stream AND const unsigned char *str AND int length
712 AND int in_char AND int out_char)
714 const unsigned char *limit = str + length;
715 while (str < limit)
717 int ch = UTF8_GET (str, limit);
718 if (ch < 0)
720 fprintf (stream, "\\<invalid>");
721 return;
723 jcf_print_char (stream, ch == in_char ? out_char : ch);
727 /* Check that all the cross-references in the constant pool are
728 valid. Returns 0 on success.
729 Otherwise, returns the index of the (first) invalid entry.
730 Only checks internal consistency, but does not check that
731 any classes, fields, or methods are valid.*/
734 DEFUN(verify_constant_pool, (jcf),
735 JCF *jcf)
737 int i, n;
738 for (i = 1; i < JPOOL_SIZE (jcf); i++)
740 switch (JPOOL_TAG (jcf, i))
742 case CONSTANT_NameAndType:
743 n = JPOOL_USHORT2 (jcf, i);
744 if (n <= 0 || n >= JPOOL_SIZE(jcf)
745 || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
746 return i;
747 /* ... fall through ... */
748 case CONSTANT_Class:
749 case CONSTANT_String:
750 n = JPOOL_USHORT1 (jcf, i);
751 if (n <= 0 || n >= JPOOL_SIZE(jcf)
752 || JPOOL_TAG (jcf, n) != CONSTANT_Utf8)
753 return i;
754 break;
755 case CONSTANT_Fieldref:
756 case CONSTANT_Methodref:
757 case CONSTANT_InterfaceMethodref:
758 n = JPOOL_USHORT1 (jcf, i);
759 if (n <= 0 || n >= JPOOL_SIZE(jcf)
760 || JPOOL_TAG (jcf, n) != CONSTANT_Class)
761 return i;
762 n = JPOOL_USHORT2 (jcf, i);
763 if (n <= 0 || n >= JPOOL_SIZE(jcf)
764 || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType)
765 return i;
766 break;
767 case CONSTANT_Long:
768 case CONSTANT_Double:
769 i++;
770 break;
771 case CONSTANT_Float:
772 case CONSTANT_Integer:
773 case CONSTANT_Utf8:
774 case CONSTANT_Unicode:
775 break;
776 default:
777 return i;
780 return 0;
783 void
784 DEFUN(format_uint, (buffer, value, base),
785 char *buffer AND uint64 value AND int base)
787 #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8)
788 char buf[WRITE_BUF_SIZE];
789 register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */
790 int chars_written;
791 int i;
793 /* Now do the actual conversion, placing the result at the *end* of buf. */
794 /* Note this code does not pretend to be optimized. */
795 do {
796 int digit = value % base;
797 static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
798 *--buf_ptr = digit_chars[digit];
799 value /= base;
800 } while (value != 0);
802 chars_written = buf+WRITE_BUF_SIZE - buf_ptr;
803 for (i = 0; i < chars_written; i++)
804 buffer[i] = *buf_ptr++;
805 buffer[i] = 0;
808 void
809 DEFUN(format_int, (buffer, value, base),
810 char *buffer AND jlong value AND int base)
812 uint64 abs_value;
813 if (value < 0)
815 abs_value = -(uint64)value;
816 *buffer++ = '-';
818 else
819 abs_value = (uint64) value;
820 format_uint (buffer, abs_value, base);