1 /* 2 * linux/lib/vsprintf.c 3 * 4 * Copyright (C) 1991, 1992 Linus Torvalds 5 */ 6 7 /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ 8 /* 9 * Wirzenius wrote this portably, Torvalds fucked it up :-) 10 * 11 * from hush: simple_itoa() was lifted from boa-0.93.15 12 */ 13 14 #include <stdarg.h> 15 #include <linux/types.h> 16 #include <linux/string.h> 17 #include <linux/ctype.h> 18 #include <errno.h> 19 20 #include <common.h> 21 #if !defined(CONFIG_PANIC_HANG) 22 #include <command.h> 23 #endif 24 25 #include <div64.h> 26 #define noinline __attribute__((noinline)) 27 28 /* some reluctance to put this into a new limits.h, so it is here */ 29 #define INT_MAX ((int)(~0U>>1)) 30 31 const char hex_asc[] = "0123456789abcdef"; 32 #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] 33 #define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] 34 35 static inline char *pack_hex_byte(char *buf, u8 byte) 36 { 37 *buf++ = hex_asc_hi(byte); 38 *buf++ = hex_asc_lo(byte); 39 return buf; 40 } 41 42 unsigned long simple_strtoul(const char *cp, char **endp, 43 unsigned int base) 44 { 45 unsigned long result = 0; 46 unsigned long value; 47 48 if (*cp == '0') { 49 cp++; 50 if ((*cp == 'x') && isxdigit(cp[1])) { 51 base = 16; 52 cp++; 53 } 54 55 if (!base) 56 base = 8; 57 } 58 59 if (!base) 60 base = 10; 61 62 while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) 63 ? toupper(*cp) : *cp)-'A'+10) < base) { 64 result = result*base + value; 65 cp++; 66 } 67 68 if (endp) 69 *endp = (char *)cp; 70 71 return result; 72 } 73 74 int strict_strtoul(const char *cp, unsigned int base, unsigned long *res) 75 { 76 char *tail; 77 unsigned long val; 78 size_t len; 79 80 *res = 0; 81 len = strlen(cp); 82 if (len == 0) 83 return -EINVAL; 84 85 val = simple_strtoul(cp, &tail, base); 86 if (tail == cp) 87 return -EINVAL; 88 89 if ((*tail == '\0') || 90 ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) { 91 *res = val; 92 return 0; 93 } 94 95 return -EINVAL; 96 } 97 98 long simple_strtol(const char *cp, char **endp, unsigned int base) 99 { 100 if (*cp == '-') 101 return -simple_strtoul(cp + 1, endp, base); 102 103 return simple_strtoul(cp, endp, base); 104 } 105 106 int ustrtoul(const char *cp, char **endp, unsigned int base) 107 { 108 unsigned long result = simple_strtoul(cp, endp, base); 109 switch (**endp) { 110 case 'G': 111 result *= 1024; 112 /* fall through */ 113 case 'M': 114 result *= 1024; 115 /* fall through */ 116 case 'K': 117 case 'k': 118 result *= 1024; 119 if ((*endp)[1] == 'i') { 120 if ((*endp)[2] == 'B') 121 (*endp) += 3; 122 else 123 (*endp) += 2; 124 } 125 } 126 return result; 127 } 128 129 unsigned long long simple_strtoull(const char *cp, char **endp, 130 unsigned int base) 131 { 132 unsigned long long result = 0, value; 133 134 if (*cp == '0') { 135 cp++; 136 if ((*cp == 'x') && isxdigit(cp[1])) { 137 base = 16; 138 cp++; 139 } 140 141 if (!base) 142 base = 8; 143 } 144 145 if (!base) 146 base = 10; 147 148 while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0' 149 : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base) { 150 result = result * base + value; 151 cp++; 152 } 153 154 if (endp) 155 *endp = (char *) cp; 156 157 return result; 158 } 159 160 /* we use this so that we can do without the ctype library */ 161 #define is_digit(c) ((c) >= '0' && (c) <= '9') 162 163 static int skip_atoi(const char **s) 164 { 165 int i = 0; 166 167 while (is_digit(**s)) 168 i = i * 10 + *((*s)++) - '0'; 169 170 return i; 171 } 172 173 /* Decimal conversion is by far the most typical, and is used 174 * for /proc and /sys data. This directly impacts e.g. top performance 175 * with many processes running. We optimize it for speed 176 * using code from 177 * http://www.cs.uiowa.edu/~jones/bcd/decimal.html 178 * (with permission from the author, Douglas W. Jones). */ 179 180 /* Formats correctly any integer in [0,99999]. 181 * Outputs from one to five digits depending on input. 182 * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */ 183 static char *put_dec_trunc(char *buf, unsigned q) 184 { 185 unsigned d3, d2, d1, d0; 186 d1 = (q>>4) & 0xf; 187 d2 = (q>>8) & 0xf; 188 d3 = (q>>12); 189 190 d0 = 6*(d3 + d2 + d1) + (q & 0xf); 191 q = (d0 * 0xcd) >> 11; 192 d0 = d0 - 10*q; 193 *buf++ = d0 + '0'; /* least significant digit */ 194 d1 = q + 9*d3 + 5*d2 + d1; 195 if (d1 != 0) { 196 q = (d1 * 0xcd) >> 11; 197 d1 = d1 - 10*q; 198 *buf++ = d1 + '0'; /* next digit */ 199 200 d2 = q + 2*d2; 201 if ((d2 != 0) || (d3 != 0)) { 202 q = (d2 * 0xd) >> 7; 203 d2 = d2 - 10*q; 204 *buf++ = d2 + '0'; /* next digit */ 205 206 d3 = q + 4*d3; 207 if (d3 != 0) { 208 q = (d3 * 0xcd) >> 11; 209 d3 = d3 - 10*q; 210 *buf++ = d3 + '0'; /* next digit */ 211 if (q != 0) 212 *buf++ = q + '0'; /* most sign. digit */ 213 } 214 } 215 } 216 return buf; 217 } 218 /* Same with if's removed. Always emits five digits */ 219 static char *put_dec_full(char *buf, unsigned q) 220 { 221 /* BTW, if q is in [0,9999], 8-bit ints will be enough, */ 222 /* but anyway, gcc produces better code with full-sized ints */ 223 unsigned d3, d2, d1, d0; 224 d1 = (q>>4) & 0xf; 225 d2 = (q>>8) & 0xf; 226 d3 = (q>>12); 227 228 /* 229 * Possible ways to approx. divide by 10 230 * gcc -O2 replaces multiply with shifts and adds 231 * (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386) 232 * (x * 0x67) >> 10: 1100111 233 * (x * 0x34) >> 9: 110100 - same 234 * (x * 0x1a) >> 8: 11010 - same 235 * (x * 0x0d) >> 7: 1101 - same, shortest code (on i386) 236 */ 237 238 d0 = 6*(d3 + d2 + d1) + (q & 0xf); 239 q = (d0 * 0xcd) >> 11; 240 d0 = d0 - 10*q; 241 *buf++ = d0 + '0'; 242 d1 = q + 9*d3 + 5*d2 + d1; 243 q = (d1 * 0xcd) >> 11; 244 d1 = d1 - 10*q; 245 *buf++ = d1 + '0'; 246 247 d2 = q + 2*d2; 248 q = (d2 * 0xd) >> 7; 249 d2 = d2 - 10*q; 250 *buf++ = d2 + '0'; 251 252 d3 = q + 4*d3; 253 q = (d3 * 0xcd) >> 11; /* - shorter code */ 254 /* q = (d3 * 0x67) >> 10; - would also work */ 255 d3 = d3 - 10*q; 256 *buf++ = d3 + '0'; 257 *buf++ = q + '0'; 258 return buf; 259 } 260 /* No inlining helps gcc to use registers better */ 261 static noinline char *put_dec(char *buf, u64 num) 262 { 263 while (1) { 264 unsigned rem; 265 if (num < 100000) 266 return put_dec_trunc(buf, num); 267 rem = do_div(num, 100000); 268 buf = put_dec_full(buf, rem); 269 } 270 } 271 272 #define ZEROPAD 1 /* pad with zero */ 273 #define SIGN 2 /* unsigned/signed long */ 274 #define PLUS 4 /* show plus */ 275 #define SPACE 8 /* space if plus */ 276 #define LEFT 16 /* left justified */ 277 #define SMALL 32 /* Must be 32 == 0x20 */ 278 #define SPECIAL 64 /* 0x */ 279 280 #ifdef CONFIG_SYS_VSNPRINTF 281 /* 282 * Macro to add a new character to our output string, but only if it will 283 * fit. The macro moves to the next character position in the output string. 284 */ 285 #define ADDCH(str, ch) do { \ 286 if ((str) < end) \ 287 *(str) = (ch); \ 288 ++str; \ 289 } while (0) 290 #else 291 #define ADDCH(str, ch) (*(str)++ = (ch)) 292 #endif 293 294 static char *number(char *buf, char *end, u64 num, 295 int base, int size, int precision, int type) 296 { 297 /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ 298 static const char digits[16] = "0123456789ABCDEF"; 299 300 char tmp[66]; 301 char sign; 302 char locase; 303 int need_pfx = ((type & SPECIAL) && base != 10); 304 int i; 305 306 /* locase = 0 or 0x20. ORing digits or letters with 'locase' 307 * produces same digits or (maybe lowercased) letters */ 308 locase = (type & SMALL); 309 if (type & LEFT) 310 type &= ~ZEROPAD; 311 sign = 0; 312 if (type & SIGN) { 313 if ((s64) num < 0) { 314 sign = '-'; 315 num = -(s64) num; 316 size--; 317 } else if (type & PLUS) { 318 sign = '+'; 319 size--; 320 } else if (type & SPACE) { 321 sign = ' '; 322 size--; 323 } 324 } 325 if (need_pfx) { 326 size--; 327 if (base == 16) 328 size--; 329 } 330 331 /* generate full string in tmp[], in reverse order */ 332 i = 0; 333 if (num == 0) 334 tmp[i++] = '0'; 335 /* Generic code, for any base: 336 else do { 337 tmp[i++] = (digits[do_div(num,base)] | locase); 338 } while (num != 0); 339 */ 340 else if (base != 10) { /* 8 or 16 */ 341 int mask = base - 1; 342 int shift = 3; 343 344 if (base == 16) 345 shift = 4; 346 347 do { 348 tmp[i++] = (digits[((unsigned char)num) & mask] 349 | locase); 350 num >>= shift; 351 } while (num); 352 } else { /* base 10 */ 353 i = put_dec(tmp, num) - tmp; 354 } 355 356 /* printing 100 using %2d gives "100", not "00" */ 357 if (i > precision) 358 precision = i; 359 /* leading space padding */ 360 size -= precision; 361 if (!(type & (ZEROPAD + LEFT))) { 362 while (--size >= 0) 363 ADDCH(buf, ' '); 364 } 365 /* sign */ 366 if (sign) 367 ADDCH(buf, sign); 368 /* "0x" / "0" prefix */ 369 if (need_pfx) { 370 ADDCH(buf, '0'); 371 if (base == 16) 372 ADDCH(buf, 'X' | locase); 373 } 374 /* zero or space padding */ 375 if (!(type & LEFT)) { 376 char c = (type & ZEROPAD) ? '0' : ' '; 377 378 while (--size >= 0) 379 ADDCH(buf, c); 380 } 381 /* hmm even more zero padding? */ 382 while (i <= --precision) 383 ADDCH(buf, '0'); 384 /* actual digits of result */ 385 while (--i >= 0) 386 ADDCH(buf, tmp[i]); 387 /* trailing space padding */ 388 while (--size >= 0) 389 ADDCH(buf, ' '); 390 return buf; 391 } 392 393 static char *string(char *buf, char *end, char *s, int field_width, 394 int precision, int flags) 395 { 396 int len, i; 397 398 if (s == 0) 399 s = "<NULL>"; 400 401 len = strnlen(s, precision); 402 403 if (!(flags & LEFT)) 404 while (len < field_width--) 405 ADDCH(buf, ' '); 406 for (i = 0; i < len; ++i) 407 ADDCH(buf, *s++); 408 while (len < field_width--) 409 ADDCH(buf, ' '); 410 return buf; 411 } 412 413 #ifdef CONFIG_CMD_NET 414 static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width, 415 int precision, int flags) 416 { 417 /* (6 * 2 hex digits), 5 colons and trailing zero */ 418 char mac_addr[6 * 3]; 419 char *p = mac_addr; 420 int i; 421 422 for (i = 0; i < 6; i++) { 423 p = pack_hex_byte(p, addr[i]); 424 if (!(flags & SPECIAL) && i != 5) 425 *p++ = ':'; 426 } 427 *p = '\0'; 428 429 return string(buf, end, mac_addr, field_width, precision, 430 flags & ~SPECIAL); 431 } 432 433 static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width, 434 int precision, int flags) 435 { 436 /* (8 * 4 hex digits), 7 colons and trailing zero */ 437 char ip6_addr[8 * 5]; 438 char *p = ip6_addr; 439 int i; 440 441 for (i = 0; i < 8; i++) { 442 p = pack_hex_byte(p, addr[2 * i]); 443 p = pack_hex_byte(p, addr[2 * i + 1]); 444 if (!(flags & SPECIAL) && i != 7) 445 *p++ = ':'; 446 } 447 *p = '\0'; 448 449 return string(buf, end, ip6_addr, field_width, precision, 450 flags & ~SPECIAL); 451 } 452 453 static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, 454 int precision, int flags) 455 { 456 /* (4 * 3 decimal digits), 3 dots and trailing zero */ 457 char ip4_addr[4 * 4]; 458 char temp[3]; /* hold each IP quad in reverse order */ 459 char *p = ip4_addr; 460 int i, digits; 461 462 for (i = 0; i < 4; i++) { 463 digits = put_dec_trunc(temp, addr[i]) - temp; 464 /* reverse the digits in the quad */ 465 while (digits--) 466 *p++ = temp[digits]; 467 if (i != 3) 468 *p++ = '.'; 469 } 470 *p = '\0'; 471 472 return string(buf, end, ip4_addr, field_width, precision, 473 flags & ~SPECIAL); 474 } 475 #endif 476 477 /* 478 * Show a '%p' thing. A kernel extension is that the '%p' is followed 479 * by an extra set of alphanumeric characters that are extended format 480 * specifiers. 481 * 482 * Right now we handle: 483 * 484 * - 'M' For a 6-byte MAC address, it prints the address in the 485 * usual colon-separated hex notation 486 * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way (dot-separated 487 * decimal for v4 and colon separated network-order 16 bit hex for v6) 488 * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is 489 * currently the same 490 * 491 * Note: The difference between 'S' and 'F' is that on ia64 and ppc64 492 * function pointers are really function descriptors, which contain a 493 * pointer to the real address. 494 */ 495 static char *pointer(const char *fmt, char *buf, char *end, void *ptr, 496 int field_width, int precision, int flags) 497 { 498 if (!ptr) 499 return string(buf, end, "(null)", field_width, precision, 500 flags); 501 502 #ifdef CONFIG_CMD_NET 503 switch (*fmt) { 504 case 'm': 505 flags |= SPECIAL; 506 /* Fallthrough */ 507 case 'M': 508 return mac_address_string(buf, end, ptr, field_width, 509 precision, flags); 510 case 'i': 511 flags |= SPECIAL; 512 /* Fallthrough */ 513 case 'I': 514 if (fmt[1] == '6') 515 return ip6_addr_string(buf, end, ptr, field_width, 516 precision, flags); 517 if (fmt[1] == '4') 518 return ip4_addr_string(buf, end, ptr, field_width, 519 precision, flags); 520 flags &= ~SPECIAL; 521 break; 522 } 523 #endif 524 flags |= SMALL; 525 if (field_width == -1) { 526 field_width = 2*sizeof(void *); 527 flags |= ZEROPAD; 528 } 529 return number(buf, end, (unsigned long)ptr, 16, field_width, 530 precision, flags); 531 } 532 533 static int vsnprintf_internal(char *buf, size_t size, const char *fmt, 534 va_list args) 535 { 536 u64 num; 537 int base; 538 char *str; 539 540 int flags; /* flags to number() */ 541 542 int field_width; /* width of output field */ 543 int precision; /* min. # of digits for integers; max 544 number of chars for from string */ 545 int qualifier; /* 'h', 'l', or 'L' for integer fields */ 546 /* 'z' support added 23/7/1999 S.H. */ 547 /* 'z' changed to 'Z' --davidm 1/25/99 */ 548 /* 't' added for ptrdiff_t */ 549 char *end = buf + size; 550 551 #ifdef CONFIG_SYS_VSNPRINTF 552 /* Make sure end is always >= buf - do we want this in U-Boot? */ 553 if (end < buf) { 554 end = ((void *)-1); 555 size = end - buf; 556 } 557 #endif 558 str = buf; 559 560 for (; *fmt ; ++fmt) { 561 if (*fmt != '%') { 562 ADDCH(str, *fmt); 563 continue; 564 } 565 566 /* process flags */ 567 flags = 0; 568 repeat: 569 ++fmt; /* this also skips first '%' */ 570 switch (*fmt) { 571 case '-': 572 flags |= LEFT; 573 goto repeat; 574 case '+': 575 flags |= PLUS; 576 goto repeat; 577 case ' ': 578 flags |= SPACE; 579 goto repeat; 580 case '#': 581 flags |= SPECIAL; 582 goto repeat; 583 case '0': 584 flags |= ZEROPAD; 585 goto repeat; 586 } 587 588 /* get field width */ 589 field_width = -1; 590 if (is_digit(*fmt)) 591 field_width = skip_atoi(&fmt); 592 else if (*fmt == '*') { 593 ++fmt; 594 /* it's the next argument */ 595 field_width = va_arg(args, int); 596 if (field_width < 0) { 597 field_width = -field_width; 598 flags |= LEFT; 599 } 600 } 601 602 /* get the precision */ 603 precision = -1; 604 if (*fmt == '.') { 605 ++fmt; 606 if (is_digit(*fmt)) 607 precision = skip_atoi(&fmt); 608 else if (*fmt == '*') { 609 ++fmt; 610 /* it's the next argument */ 611 precision = va_arg(args, int); 612 } 613 if (precision < 0) 614 precision = 0; 615 } 616 617 /* get the conversion qualifier */ 618 qualifier = -1; 619 if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || 620 *fmt == 'Z' || *fmt == 'z' || *fmt == 't') { 621 qualifier = *fmt; 622 ++fmt; 623 if (qualifier == 'l' && *fmt == 'l') { 624 qualifier = 'L'; 625 ++fmt; 626 } 627 } 628 629 /* default base */ 630 base = 10; 631 632 switch (*fmt) { 633 case 'c': 634 if (!(flags & LEFT)) { 635 while (--field_width > 0) 636 ADDCH(str, ' '); 637 } 638 ADDCH(str, (unsigned char) va_arg(args, int)); 639 while (--field_width > 0) 640 ADDCH(str, ' '); 641 continue; 642 643 case 's': 644 str = string(str, end, va_arg(args, char *), 645 field_width, precision, flags); 646 continue; 647 648 case 'p': 649 str = pointer(fmt + 1, str, end, 650 va_arg(args, void *), 651 field_width, precision, flags); 652 /* Skip all alphanumeric pointer suffixes */ 653 while (isalnum(fmt[1])) 654 fmt++; 655 continue; 656 657 case 'n': 658 if (qualifier == 'l') { 659 long *ip = va_arg(args, long *); 660 *ip = (str - buf); 661 } else { 662 int *ip = va_arg(args, int *); 663 *ip = (str - buf); 664 } 665 continue; 666 667 case '%': 668 ADDCH(str, '%'); 669 continue; 670 671 /* integer number formats - set up the flags and "break" */ 672 case 'o': 673 base = 8; 674 break; 675 676 case 'x': 677 flags |= SMALL; 678 case 'X': 679 base = 16; 680 break; 681 682 case 'd': 683 case 'i': 684 flags |= SIGN; 685 case 'u': 686 break; 687 688 default: 689 ADDCH(str, '%'); 690 if (*fmt) 691 ADDCH(str, *fmt); 692 else 693 --fmt; 694 continue; 695 } 696 if (qualifier == 'L') /* "quad" for 64 bit variables */ 697 num = va_arg(args, unsigned long long); 698 else if (qualifier == 'l') { 699 num = va_arg(args, unsigned long); 700 if (flags & SIGN) 701 num = (signed long) num; 702 } else if (qualifier == 'Z' || qualifier == 'z') { 703 num = va_arg(args, size_t); 704 } else if (qualifier == 't') { 705 num = va_arg(args, ptrdiff_t); 706 } else if (qualifier == 'h') { 707 num = (unsigned short) va_arg(args, int); 708 if (flags & SIGN) 709 num = (signed short) num; 710 } else { 711 num = va_arg(args, unsigned int); 712 if (flags & SIGN) 713 num = (signed int) num; 714 } 715 str = number(str, end, num, base, field_width, precision, 716 flags); 717 } 718 719 #ifdef CONFIG_SYS_VSNPRINTF 720 if (size > 0) { 721 ADDCH(str, '\0'); 722 if (str > end) 723 end[-1] = '\0'; 724 } 725 #else 726 *str = '\0'; 727 #endif 728 /* the trailing null byte doesn't count towards the total */ 729 return str - buf; 730 } 731 732 #ifdef CONFIG_SYS_VSNPRINTF 733 int vsnprintf(char *buf, size_t size, const char *fmt, 734 va_list args) 735 { 736 return vsnprintf_internal(buf, size, fmt, args); 737 } 738 739 int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) 740 { 741 int i; 742 743 i = vsnprintf(buf, size, fmt, args); 744 745 if (likely(i < size)) 746 return i; 747 if (size != 0) 748 return size - 1; 749 return 0; 750 } 751 752 int snprintf(char *buf, size_t size, const char *fmt, ...) 753 { 754 va_list args; 755 int i; 756 757 va_start(args, fmt); 758 i = vsnprintf(buf, size, fmt, args); 759 va_end(args); 760 761 return i; 762 } 763 764 int scnprintf(char *buf, size_t size, const char *fmt, ...) 765 { 766 va_list args; 767 int i; 768 769 va_start(args, fmt); 770 i = vscnprintf(buf, size, fmt, args); 771 va_end(args); 772 773 return i; 774 } 775 #endif /* CONFIG_SYS_VSNPRINT */ 776 777 /** 778 * Format a string and place it in a buffer (va_list version) 779 * 780 * @param buf The buffer to place the result into 781 * @param fmt The format string to use 782 * @param args Arguments for the format string 783 * 784 * The function returns the number of characters written 785 * into @buf. Use vsnprintf() or vscnprintf() in order to avoid 786 * buffer overflows. 787 * 788 * If you're not already dealing with a va_list consider using sprintf(). 789 */ 790 int vsprintf(char *buf, const char *fmt, va_list args) 791 { 792 return vsnprintf_internal(buf, INT_MAX, fmt, args); 793 } 794 795 int sprintf(char *buf, const char *fmt, ...) 796 { 797 va_list args; 798 int i; 799 800 va_start(args, fmt); 801 i = vsprintf(buf, fmt, args); 802 va_end(args); 803 return i; 804 } 805 806 void panic(const char *fmt, ...) 807 { 808 va_list args; 809 va_start(args, fmt); 810 vprintf(fmt, args); 811 putc('\n'); 812 va_end(args); 813 #if defined(CONFIG_PANIC_HANG) 814 hang(); 815 #else 816 udelay(100000); /* allow messages to go out */ 817 do_reset(NULL, 0, 0, NULL); 818 #endif 819 while (1) 820 ; 821 } 822 823 void __assert_fail(const char *assertion, const char *file, unsigned line, 824 const char *function) 825 { 826 /* This will not return */ 827 panic("%s:%u: %s: Assertion `%s' failed.", file, line, function, 828 assertion); 829 } 830 831 char *simple_itoa(ulong i) 832 { 833 /* 21 digits plus null terminator, good for 64-bit or smaller ints */ 834 static char local[22]; 835 char *p = &local[21]; 836 837 *p-- = '\0'; 838 do { 839 *p-- = '0' + i % 10; 840 i /= 10; 841 } while (i > 0); 842 return p + 1; 843 } 844