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 origsep = 0; 347 char *base; 348 memoized_dirlist_entry *dent; 349 void **slot; 350 351 /* If the hashtable has not already been created, create it now. */ 352 if (!memoized_dirlists) 353 memoized_dirlists = htab_create (37, 354 htab_hash_string, 355 memoized_dirlist_lookup_eq, 356 NULL); 357 358 /* Get the name of the directory. */ 359 sep = strrchr (filename, DIR_SEPARATOR); 360 #ifdef DIR_SEPARATOR_2 361 if (! sep) 362 sep = strrchr (filename, DIR_SEPARATOR_2); 363 #endif 364 if (sep) 365 { 366 origsep = *sep; 367 *sep = '\0'; 368 base = sep + 1; 369 } 370 else 371 base = filename; 372 373 /* Obtain the entry for this directory from the hash table. */ 374 slot = htab_find_slot (memoized_dirlists, filename, INSERT); 375 if (!*slot) 376 { 377 /* We have not already scanned this directory; scan it now. */ 378 dent = ((memoized_dirlist_entry *) 379 ALLOC (sizeof (memoized_dirlist_entry))); 380 dent->dir = xstrdup (filename); 381 /* Unfortunately, scandir is not fully standardized. In 382 particular, the type of the function pointer passed as the 383 third argument sometimes takes a "const struct dirent *" 384 parameter, and sometimes just a "struct dirent *". We cast 385 to (void *) so that either way it is quietly accepted. */ 386 dent->num_files = scandir (filename, &dent->files, 387 (void *) java_or_class_file, 388 alphasort); 389 *slot = dent; 390 } 391 else 392 dent = *((memoized_dirlist_entry **) slot); 393 394 /* Put the separator back. */ 395 if (sep) 396 *sep = origsep; 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] == '.' 527 ? DIR_SEPARATOR : classname[m]); 528 strcpy (java_buffer + m + l, ".java"); 529 java = caching_stat (java_buffer, &java_buf); 530 if (java == 0) 531 break; 532 } 533 } 534 535 /* We preferably pick a class file if we have a chance. If the source 536 file is newer than the class file, we issue a warning and parse the 537 source file instead. 538 There should be a flag to allow people have the class file picked 539 up no matter what. FIXME. */ 540 if (! java && ! class && java_buf.st_mtime > class_buf.st_mtime) 541 { 542 if (flag_newer) 543 warning ("source file for class `%s' is newer than its matching class file. Source file `%s' used instead", classname, java_buffer); 544 class = -1; 545 } 546 547 if (! java) 548 dep_file = java_buffer; 549 else 550 dep_file = buffer; 551 if (!class) 552 { 553 SOURCE_FRONTEND_DEBUG ((stderr, "[Class selected: %s]\n", 554 classname+classname_length- 555 (classname_length <= 30 ? 556 classname_length : 30))); 557 fd = JCF_OPEN_EXACT_CASE (buffer, O_RDONLY | O_BINARY); 558 if (fd >= 0) 559 goto found; 560 } 561 /* Give .java a try, if necessary */ 562 if (!java) 563 { 564 strcpy (buffer, java_buffer); 565 SOURCE_FRONTEND_DEBUG ((stderr, "[Source selected: %s]\n", 566 classname+classname_length- 567 (classname_length <= 30 ? 568 classname_length : 30))); 569 fd = JCF_OPEN_EXACT_CASE (buffer, O_RDONLY); 570 if (fd >= 0) 571 { 572 jcf->java_source = 1; 573 goto found; 574 } 575 } 576 577 free (buffer); 578 579 /* Remember that this class could not be found so that we do not 580 have to look again. */ 581 *htab_find_slot_with_hash (memoized_class_lookups, classname, hash, INSERT) 582 = (void *) classname; 583 584 return NULL; 585 found: 586 if (jcf->java_source) 587 { 588 JCF_ZERO (jcf); /* JCF_FINISH relies on this */ 589 jcf->java_source = 1; 590 jcf->filename = xstrdup (buffer); 591 close (fd); /* We use STDIO for source file */ 592 } 593 else 594 buffer = (char *) open_class (buffer, jcf, fd, dep_file); 595 jcf->classname = xstrdup (classname); 596 return buffer; 597 } 598 599 void 600 DEFUN(jcf_print_char, (stream, ch), 601 FILE *stream AND int ch) 602 { 603 switch (ch) 604 { 605 case '\'': 606 case '\\': 607 case '\"': 608 fprintf (stream, "\\%c", ch); 609 break; 610 case '\n': 611 fprintf (stream, "\\n"); 612 break; 613 case '\t': 614 fprintf (stream, "\\t"); 615 break; 616 case '\r': 617 fprintf (stream, "\\r"); 618 break; 619 default: 620 if (ch >= ' ' && ch < 127) 621 putc (ch, stream); 622 else if (ch < 256) 623 fprintf (stream, "\\%03x", ch); 624 else 625 fprintf (stream, "\\u%04x", ch); 626 } 627 } 628 629 /* Print UTF8 string at STR of length LENGTH bytes to STREAM. */ 630 631 void 632 DEFUN(jcf_print_utf8, (stream, str, length), 633 FILE *stream AND register const unsigned char *str AND int length) 634 { 635 const unsigned char * limit = str + length; 636 while (str < limit) 637 { 638 int ch = UTF8_GET (str, limit); 639 if (ch < 0) 640 { 641 fprintf (stream, "\\<invalid>"); 642 return; 643 } 644 jcf_print_char (stream, ch); 645 } 646 } 647 648 /* Same as jcf_print_utf8, but print IN_CHAR as OUT_CHAR. */ 649 650 void 651 DEFUN(jcf_print_utf8_replace, (stream, str, length, in_char, out_char), 652 FILE *stream AND const unsigned char *str AND int length 653 AND int in_char AND int out_char) 654 { 655 const unsigned char *limit = str + length; 656 while (str < limit) 657 { 658 int ch = UTF8_GET (str, limit); 659 if (ch < 0) 660 { 661 fprintf (stream, "\\<invalid>"); 662 return; 663 } 664 jcf_print_char (stream, ch == in_char ? out_char : ch); 665 } 666 } 667 668 /* Check that all the cross-references in the constant pool are 669 valid. Returns 0 on success. 670 Otherwise, returns the index of the (first) invalid entry. 671 Only checks internal consistency, but does not check that 672 any classes, fields, or methods are valid.*/ 673 674 int 675 DEFUN(verify_constant_pool, (jcf), 676 JCF *jcf) 677 { 678 int i, n; 679 for (i = 1; i < JPOOL_SIZE (jcf); i++) 680 { 681 switch (JPOOL_TAG (jcf, i)) 682 { 683 case CONSTANT_NameAndType: 684 n = JPOOL_USHORT2 (jcf, i); 685 if (n <= 0 || n >= JPOOL_SIZE(jcf) 686 || JPOOL_TAG (jcf, n) != CONSTANT_Utf8) 687 return i; 688 /* ... fall through ... */ 689 case CONSTANT_Class: 690 case CONSTANT_String: 691 n = JPOOL_USHORT1 (jcf, i); 692 if (n <= 0 || n >= JPOOL_SIZE(jcf) 693 || JPOOL_TAG (jcf, n) != CONSTANT_Utf8) 694 return i; 695 break; 696 case CONSTANT_Fieldref: 697 case CONSTANT_Methodref: 698 case CONSTANT_InterfaceMethodref: 699 n = JPOOL_USHORT1 (jcf, i); 700 if (n <= 0 || n >= JPOOL_SIZE(jcf) 701 || JPOOL_TAG (jcf, n) != CONSTANT_Class) 702 return i; 703 n = JPOOL_USHORT2 (jcf, i); 704 if (n <= 0 || n >= JPOOL_SIZE(jcf) 705 || JPOOL_TAG (jcf, n) != CONSTANT_NameAndType) 706 return i; 707 break; 708 case CONSTANT_Long: 709 case CONSTANT_Double: 710 i++; 711 break; 712 case CONSTANT_Float: 713 case CONSTANT_Integer: 714 case CONSTANT_Utf8: 715 case CONSTANT_Unicode: 716 break; 717 default: 718 return i; 719 } 720 } 721 return 0; 722 } 723 724 void 725 DEFUN(format_uint, (buffer, value, base), 726 char *buffer AND uint64 value AND int base) 727 { 728 #define WRITE_BUF_SIZE (4 + sizeof(uint64) * 8) 729 char buf[WRITE_BUF_SIZE]; 730 register char *buf_ptr = buf+WRITE_BUF_SIZE; /* End of buf. */ 731 int chars_written; 732 int i; 733 734 /* Now do the actual conversion, placing the result at the *end* of buf. */ 735 /* Note this code does not pretend to be optimized. */ 736 do { 737 int digit = value % base; 738 static const char digit_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 739 *--buf_ptr = digit_chars[digit]; 740 value /= base; 741 } while (value != 0); 742 743 chars_written = buf+WRITE_BUF_SIZE - buf_ptr; 744 for (i = 0; i < chars_written; i++) 745 buffer[i] = *buf_ptr++; 746 buffer[i] = 0; 747 } 748 749 void 750 DEFUN(format_int, (buffer, value, base), 751 char *buffer AND jlong value AND int base) 752 { 753 uint64 abs_value; 754 if (value < 0) 755 { 756 abs_value = -(uint64)value; 757 *buffer++ = '-'; 758 } 759 else 760 abs_value = (uint64) value; 761 format_uint (buffer, abs_value, base); 762 } 763