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