Make-lang.in, [...]: Replace "GNU CC" with "GCC" in the copyright header.
[gcc.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 }