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