178acc472SPeter Tyser /* 278acc472SPeter Tyser * linux/lib/vsprintf.c 378acc472SPeter Tyser * 478acc472SPeter Tyser * Copyright (C) 1991, 1992 Linus Torvalds 578acc472SPeter Tyser */ 678acc472SPeter Tyser 778acc472SPeter Tyser /* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */ 878acc472SPeter Tyser /* 978acc472SPeter Tyser * Wirzenius wrote this portably, Torvalds fucked it up :-) 103cce8a54SSimon Glass * 113cce8a54SSimon Glass * from hush: simple_itoa() was lifted from boa-0.93.15 1278acc472SPeter Tyser */ 1378acc472SPeter Tyser 1478acc472SPeter Tyser #include <stdarg.h> 1578acc472SPeter Tyser #include <linux/types.h> 1678acc472SPeter Tyser #include <linux/string.h> 1778acc472SPeter Tyser #include <linux/ctype.h> 18a7fd0d9fSHeiko Schocher #include <errno.h> 1978acc472SPeter Tyser 2078acc472SPeter Tyser #include <common.h> 2178acc472SPeter Tyser #if !defined(CONFIG_PANIC_HANG) 2278acc472SPeter Tyser #include <command.h> 2378acc472SPeter Tyser #endif 2478acc472SPeter Tyser 2578acc472SPeter Tyser #include <div64.h> 2678acc472SPeter Tyser # define NUM_TYPE long long 2778acc472SPeter Tyser #define noinline __attribute__((noinline)) 2878acc472SPeter Tyser 29046a37bdSSonny Rao /* some reluctance to put this into a new limits.h, so it is here */ 30046a37bdSSonny Rao #define INT_MAX ((int)(~0U>>1)) 31046a37bdSSonny Rao 3278acc472SPeter Tyser const char hex_asc[] = "0123456789abcdef"; 3378acc472SPeter Tyser #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] 3478acc472SPeter Tyser #define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] 3578acc472SPeter Tyser 3678acc472SPeter Tyser static inline char *pack_hex_byte(char *buf, u8 byte) 3778acc472SPeter Tyser { 3878acc472SPeter Tyser *buf++ = hex_asc_hi(byte); 3978acc472SPeter Tyser *buf++ = hex_asc_lo(byte); 4078acc472SPeter Tyser return buf; 4178acc472SPeter Tyser } 4278acc472SPeter Tyser 43*8acdae68SDaniel Schwierzeck unsigned long simple_strtoul(const char *cp, char **endp, 44*8acdae68SDaniel Schwierzeck unsigned int base) 4578acc472SPeter Tyser { 46*8acdae68SDaniel Schwierzeck unsigned long result = 0; 47*8acdae68SDaniel Schwierzeck unsigned long value; 4878acc472SPeter Tyser 4978acc472SPeter Tyser if (*cp == '0') { 5078acc472SPeter Tyser cp++; 5178acc472SPeter Tyser if ((*cp == 'x') && isxdigit(cp[1])) { 5278acc472SPeter Tyser base = 16; 5378acc472SPeter Tyser cp++; 5478acc472SPeter Tyser } 55*8acdae68SDaniel Schwierzeck 56*8acdae68SDaniel Schwierzeck if (!base) 5778acc472SPeter Tyser base = 8; 5878acc472SPeter Tyser } 59*8acdae68SDaniel Schwierzeck 60*8acdae68SDaniel Schwierzeck if (!base) 6178acc472SPeter Tyser base = 10; 62*8acdae68SDaniel Schwierzeck 6378acc472SPeter Tyser while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) 6478acc472SPeter Tyser ? toupper(*cp) : *cp)-'A'+10) < base) { 6578acc472SPeter Tyser result = result*base + value; 6678acc472SPeter Tyser cp++; 6778acc472SPeter Tyser } 68*8acdae68SDaniel Schwierzeck 6978acc472SPeter Tyser if (endp) 7078acc472SPeter Tyser *endp = (char *)cp; 71*8acdae68SDaniel Schwierzeck 7278acc472SPeter Tyser return result; 7378acc472SPeter Tyser } 7478acc472SPeter Tyser 75a7fd0d9fSHeiko Schocher int strict_strtoul(const char *cp, unsigned int base, unsigned long *res) 76a7fd0d9fSHeiko Schocher { 77a7fd0d9fSHeiko Schocher char *tail; 78a7fd0d9fSHeiko Schocher unsigned long val; 79a7fd0d9fSHeiko Schocher size_t len; 80a7fd0d9fSHeiko Schocher 81a7fd0d9fSHeiko Schocher *res = 0; 82a7fd0d9fSHeiko Schocher len = strlen(cp); 83a7fd0d9fSHeiko Schocher if (len == 0) 84a7fd0d9fSHeiko Schocher return -EINVAL; 85a7fd0d9fSHeiko Schocher 86a7fd0d9fSHeiko Schocher val = simple_strtoul(cp, &tail, base); 87a7fd0d9fSHeiko Schocher if (tail == cp) 88a7fd0d9fSHeiko Schocher return -EINVAL; 89a7fd0d9fSHeiko Schocher 90a7fd0d9fSHeiko Schocher if ((*tail == '\0') || 91a7fd0d9fSHeiko Schocher ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) { 92a7fd0d9fSHeiko Schocher *res = val; 93a7fd0d9fSHeiko Schocher return 0; 94a7fd0d9fSHeiko Schocher } 95a7fd0d9fSHeiko Schocher 96a7fd0d9fSHeiko Schocher return -EINVAL; 97a7fd0d9fSHeiko Schocher } 98a7fd0d9fSHeiko Schocher 9978acc472SPeter Tyser long simple_strtol(const char *cp, char **endp, unsigned int base) 10078acc472SPeter Tyser { 10178acc472SPeter Tyser if (*cp == '-') 10278acc472SPeter Tyser return -simple_strtoul(cp + 1, endp, base); 103*8acdae68SDaniel Schwierzeck 10478acc472SPeter Tyser return simple_strtoul(cp, endp, base); 10578acc472SPeter Tyser } 10678acc472SPeter Tyser 10778acc472SPeter Tyser int ustrtoul(const char *cp, char **endp, unsigned int base) 10878acc472SPeter Tyser { 10978acc472SPeter Tyser unsigned long result = simple_strtoul(cp, endp, base); 11078acc472SPeter Tyser switch (**endp) { 11178acc472SPeter Tyser case 'G': 11278acc472SPeter Tyser result *= 1024; 11378acc472SPeter Tyser /* fall through */ 11478acc472SPeter Tyser case 'M': 11578acc472SPeter Tyser result *= 1024; 11678acc472SPeter Tyser /* fall through */ 11778acc472SPeter Tyser case 'K': 11878acc472SPeter Tyser case 'k': 11978acc472SPeter Tyser result *= 1024; 12078acc472SPeter Tyser if ((*endp)[1] == 'i') { 12178acc472SPeter Tyser if ((*endp)[2] == 'B') 12278acc472SPeter Tyser (*endp) += 3; 12378acc472SPeter Tyser else 12478acc472SPeter Tyser (*endp) += 2; 12578acc472SPeter Tyser } 12678acc472SPeter Tyser } 12778acc472SPeter Tyser return result; 12878acc472SPeter Tyser } 12978acc472SPeter Tyser 130*8acdae68SDaniel Schwierzeck unsigned long long simple_strtoull(const char *cp, char **endp, 131*8acdae68SDaniel Schwierzeck unsigned int base) 13278acc472SPeter Tyser { 13378acc472SPeter Tyser unsigned long long result = 0, value; 13478acc472SPeter Tyser 13578acc472SPeter Tyser if (*cp == '0') { 13678acc472SPeter Tyser cp++; 13778acc472SPeter Tyser if ((*cp == 'x') && isxdigit(cp[1])) { 13878acc472SPeter Tyser base = 16; 13978acc472SPeter Tyser cp++; 14078acc472SPeter Tyser } 141*8acdae68SDaniel Schwierzeck 142*8acdae68SDaniel Schwierzeck if (!base) 14378acc472SPeter Tyser base = 8; 14478acc472SPeter Tyser } 145*8acdae68SDaniel Schwierzeck 146*8acdae68SDaniel Schwierzeck if (!base) 14778acc472SPeter Tyser base = 10; 148*8acdae68SDaniel Schwierzeck 149*8acdae68SDaniel Schwierzeck while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp - '0' 15078acc472SPeter Tyser : (islower(*cp) ? toupper(*cp) : *cp) - 'A' + 10) < base) { 15178acc472SPeter Tyser result = result * base + value; 15278acc472SPeter Tyser cp++; 15378acc472SPeter Tyser } 154*8acdae68SDaniel Schwierzeck 15578acc472SPeter Tyser if (endp) 15678acc472SPeter Tyser *endp = (char *) cp; 157*8acdae68SDaniel Schwierzeck 15878acc472SPeter Tyser return result; 15978acc472SPeter Tyser } 16078acc472SPeter Tyser 16178acc472SPeter Tyser /* we use this so that we can do without the ctype library */ 16278acc472SPeter Tyser #define is_digit(c) ((c) >= '0' && (c) <= '9') 16378acc472SPeter Tyser 16478acc472SPeter Tyser static int skip_atoi(const char **s) 16578acc472SPeter Tyser { 16678acc472SPeter Tyser int i = 0; 16778acc472SPeter Tyser 16878acc472SPeter Tyser while (is_digit(**s)) 16978acc472SPeter Tyser i = i * 10 + *((*s)++) - '0'; 170*8acdae68SDaniel Schwierzeck 17178acc472SPeter Tyser return i; 17278acc472SPeter Tyser } 17378acc472SPeter Tyser 17478acc472SPeter Tyser /* Decimal conversion is by far the most typical, and is used 17578acc472SPeter Tyser * for /proc and /sys data. This directly impacts e.g. top performance 17678acc472SPeter Tyser * with many processes running. We optimize it for speed 17778acc472SPeter Tyser * using code from 17878acc472SPeter Tyser * http://www.cs.uiowa.edu/~jones/bcd/decimal.html 17978acc472SPeter Tyser * (with permission from the author, Douglas W. Jones). */ 18078acc472SPeter Tyser 18178acc472SPeter Tyser /* Formats correctly any integer in [0,99999]. 18278acc472SPeter Tyser * Outputs from one to five digits depending on input. 18378acc472SPeter Tyser * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */ 18478acc472SPeter Tyser static char *put_dec_trunc(char *buf, unsigned q) 18578acc472SPeter Tyser { 18678acc472SPeter Tyser unsigned d3, d2, d1, d0; 18778acc472SPeter Tyser d1 = (q>>4) & 0xf; 18878acc472SPeter Tyser d2 = (q>>8) & 0xf; 18978acc472SPeter Tyser d3 = (q>>12); 19078acc472SPeter Tyser 19178acc472SPeter Tyser d0 = 6*(d3 + d2 + d1) + (q & 0xf); 19278acc472SPeter Tyser q = (d0 * 0xcd) >> 11; 19378acc472SPeter Tyser d0 = d0 - 10*q; 19478acc472SPeter Tyser *buf++ = d0 + '0'; /* least significant digit */ 19578acc472SPeter Tyser d1 = q + 9*d3 + 5*d2 + d1; 19678acc472SPeter Tyser if (d1 != 0) { 19778acc472SPeter Tyser q = (d1 * 0xcd) >> 11; 19878acc472SPeter Tyser d1 = d1 - 10*q; 19978acc472SPeter Tyser *buf++ = d1 + '0'; /* next digit */ 20078acc472SPeter Tyser 20178acc472SPeter Tyser d2 = q + 2*d2; 20278acc472SPeter Tyser if ((d2 != 0) || (d3 != 0)) { 20378acc472SPeter Tyser q = (d2 * 0xd) >> 7; 20478acc472SPeter Tyser d2 = d2 - 10*q; 20578acc472SPeter Tyser *buf++ = d2 + '0'; /* next digit */ 20678acc472SPeter Tyser 20778acc472SPeter Tyser d3 = q + 4*d3; 20878acc472SPeter Tyser if (d3 != 0) { 20978acc472SPeter Tyser q = (d3 * 0xcd) >> 11; 21078acc472SPeter Tyser d3 = d3 - 10*q; 21178acc472SPeter Tyser *buf++ = d3 + '0'; /* next digit */ 21278acc472SPeter Tyser if (q != 0) 21378acc472SPeter Tyser *buf++ = q + '0'; /* most sign. digit */ 21478acc472SPeter Tyser } 21578acc472SPeter Tyser } 21678acc472SPeter Tyser } 21778acc472SPeter Tyser return buf; 21878acc472SPeter Tyser } 21978acc472SPeter Tyser /* Same with if's removed. Always emits five digits */ 22078acc472SPeter Tyser static char *put_dec_full(char *buf, unsigned q) 22178acc472SPeter Tyser { 22278acc472SPeter Tyser /* BTW, if q is in [0,9999], 8-bit ints will be enough, */ 22378acc472SPeter Tyser /* but anyway, gcc produces better code with full-sized ints */ 22478acc472SPeter Tyser unsigned d3, d2, d1, d0; 22578acc472SPeter Tyser d1 = (q>>4) & 0xf; 22678acc472SPeter Tyser d2 = (q>>8) & 0xf; 22778acc472SPeter Tyser d3 = (q>>12); 22878acc472SPeter Tyser 22978acc472SPeter Tyser /* 23078acc472SPeter Tyser * Possible ways to approx. divide by 10 23178acc472SPeter Tyser * gcc -O2 replaces multiply with shifts and adds 23278acc472SPeter Tyser * (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386) 23378acc472SPeter Tyser * (x * 0x67) >> 10: 1100111 23478acc472SPeter Tyser * (x * 0x34) >> 9: 110100 - same 23578acc472SPeter Tyser * (x * 0x1a) >> 8: 11010 - same 23678acc472SPeter Tyser * (x * 0x0d) >> 7: 1101 - same, shortest code (on i386) 23778acc472SPeter Tyser */ 23878acc472SPeter Tyser 23978acc472SPeter Tyser d0 = 6*(d3 + d2 + d1) + (q & 0xf); 24078acc472SPeter Tyser q = (d0 * 0xcd) >> 11; 24178acc472SPeter Tyser d0 = d0 - 10*q; 24278acc472SPeter Tyser *buf++ = d0 + '0'; 24378acc472SPeter Tyser d1 = q + 9*d3 + 5*d2 + d1; 24478acc472SPeter Tyser q = (d1 * 0xcd) >> 11; 24578acc472SPeter Tyser d1 = d1 - 10*q; 24678acc472SPeter Tyser *buf++ = d1 + '0'; 24778acc472SPeter Tyser 24878acc472SPeter Tyser d2 = q + 2*d2; 24978acc472SPeter Tyser q = (d2 * 0xd) >> 7; 25078acc472SPeter Tyser d2 = d2 - 10*q; 25178acc472SPeter Tyser *buf++ = d2 + '0'; 25278acc472SPeter Tyser 25378acc472SPeter Tyser d3 = q + 4*d3; 25478acc472SPeter Tyser q = (d3 * 0xcd) >> 11; /* - shorter code */ 25578acc472SPeter Tyser /* q = (d3 * 0x67) >> 10; - would also work */ 25678acc472SPeter Tyser d3 = d3 - 10*q; 25778acc472SPeter Tyser *buf++ = d3 + '0'; 25878acc472SPeter Tyser *buf++ = q + '0'; 25978acc472SPeter Tyser return buf; 26078acc472SPeter Tyser } 26178acc472SPeter Tyser /* No inlining helps gcc to use registers better */ 26278acc472SPeter Tyser static noinline char *put_dec(char *buf, unsigned NUM_TYPE num) 26378acc472SPeter Tyser { 26478acc472SPeter Tyser while (1) { 26578acc472SPeter Tyser unsigned rem; 26678acc472SPeter Tyser if (num < 100000) 26778acc472SPeter Tyser return put_dec_trunc(buf, num); 26878acc472SPeter Tyser rem = do_div(num, 100000); 26978acc472SPeter Tyser buf = put_dec_full(buf, rem); 27078acc472SPeter Tyser } 27178acc472SPeter Tyser } 27278acc472SPeter Tyser 27378acc472SPeter Tyser #define ZEROPAD 1 /* pad with zero */ 27478acc472SPeter Tyser #define SIGN 2 /* unsigned/signed long */ 27578acc472SPeter Tyser #define PLUS 4 /* show plus */ 27678acc472SPeter Tyser #define SPACE 8 /* space if plus */ 27778acc472SPeter Tyser #define LEFT 16 /* left justified */ 27878acc472SPeter Tyser #define SMALL 32 /* Must be 32 == 0x20 */ 27978acc472SPeter Tyser #define SPECIAL 64 /* 0x */ 28078acc472SPeter Tyser 281046a37bdSSonny Rao #ifdef CONFIG_SYS_VSNPRINTF 282046a37bdSSonny Rao /* 283046a37bdSSonny Rao * Macro to add a new character to our output string, but only if it will 284046a37bdSSonny Rao * fit. The macro moves to the next character position in the output string. 285046a37bdSSonny Rao */ 286046a37bdSSonny Rao #define ADDCH(str, ch) do { \ 287046a37bdSSonny Rao if ((str) < end) \ 288046a37bdSSonny Rao *(str) = (ch); \ 289046a37bdSSonny Rao ++str; \ 290046a37bdSSonny Rao } while (0) 291046a37bdSSonny Rao #else 292046a37bdSSonny Rao #define ADDCH(str, ch) (*(str)++ = (ch)) 293046a37bdSSonny Rao #endif 294046a37bdSSonny Rao 295046a37bdSSonny Rao static char *number(char *buf, char *end, unsigned NUM_TYPE num, 296046a37bdSSonny Rao int base, int size, int precision, int type) 29778acc472SPeter Tyser { 29878acc472SPeter Tyser /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ 299*8acdae68SDaniel Schwierzeck static const char digits[16] = "0123456789ABCDEF"; 30078acc472SPeter Tyser 30178acc472SPeter Tyser char tmp[66]; 30278acc472SPeter Tyser char sign; 30378acc472SPeter Tyser char locase; 30478acc472SPeter Tyser int need_pfx = ((type & SPECIAL) && base != 10); 30578acc472SPeter Tyser int i; 30678acc472SPeter Tyser 30778acc472SPeter Tyser /* locase = 0 or 0x20. ORing digits or letters with 'locase' 30878acc472SPeter Tyser * produces same digits or (maybe lowercased) letters */ 30978acc472SPeter Tyser locase = (type & SMALL); 31078acc472SPeter Tyser if (type & LEFT) 31178acc472SPeter Tyser type &= ~ZEROPAD; 31278acc472SPeter Tyser sign = 0; 31378acc472SPeter Tyser if (type & SIGN) { 31478acc472SPeter Tyser if ((signed NUM_TYPE) num < 0) { 31578acc472SPeter Tyser sign = '-'; 31678acc472SPeter Tyser num = -(signed NUM_TYPE) num; 31778acc472SPeter Tyser size--; 31878acc472SPeter Tyser } else if (type & PLUS) { 31978acc472SPeter Tyser sign = '+'; 32078acc472SPeter Tyser size--; 32178acc472SPeter Tyser } else if (type & SPACE) { 32278acc472SPeter Tyser sign = ' '; 32378acc472SPeter Tyser size--; 32478acc472SPeter Tyser } 32578acc472SPeter Tyser } 32678acc472SPeter Tyser if (need_pfx) { 32778acc472SPeter Tyser size--; 32878acc472SPeter Tyser if (base == 16) 32978acc472SPeter Tyser size--; 33078acc472SPeter Tyser } 33178acc472SPeter Tyser 33278acc472SPeter Tyser /* generate full string in tmp[], in reverse order */ 33378acc472SPeter Tyser i = 0; 33478acc472SPeter Tyser if (num == 0) 33578acc472SPeter Tyser tmp[i++] = '0'; 33678acc472SPeter Tyser /* Generic code, for any base: 33778acc472SPeter Tyser else do { 33878acc472SPeter Tyser tmp[i++] = (digits[do_div(num,base)] | locase); 33978acc472SPeter Tyser } while (num != 0); 34078acc472SPeter Tyser */ 34178acc472SPeter Tyser else if (base != 10) { /* 8 or 16 */ 34278acc472SPeter Tyser int mask = base - 1; 34378acc472SPeter Tyser int shift = 3; 344*8acdae68SDaniel Schwierzeck 345*8acdae68SDaniel Schwierzeck if (base == 16) 346*8acdae68SDaniel Schwierzeck shift = 4; 347*8acdae68SDaniel Schwierzeck 34878acc472SPeter Tyser do { 349*8acdae68SDaniel Schwierzeck tmp[i++] = (digits[((unsigned char)num) & mask] 350*8acdae68SDaniel Schwierzeck | locase); 35178acc472SPeter Tyser num >>= shift; 35278acc472SPeter Tyser } while (num); 35378acc472SPeter Tyser } else { /* base 10 */ 35478acc472SPeter Tyser i = put_dec(tmp, num) - tmp; 35578acc472SPeter Tyser } 35678acc472SPeter Tyser 35778acc472SPeter Tyser /* printing 100 using %2d gives "100", not "00" */ 35878acc472SPeter Tyser if (i > precision) 35978acc472SPeter Tyser precision = i; 36078acc472SPeter Tyser /* leading space padding */ 36178acc472SPeter Tyser size -= precision; 362046a37bdSSonny Rao if (!(type & (ZEROPAD + LEFT))) { 36378acc472SPeter Tyser while (--size >= 0) 364046a37bdSSonny Rao ADDCH(buf, ' '); 365046a37bdSSonny Rao } 36678acc472SPeter Tyser /* sign */ 36778acc472SPeter Tyser if (sign) 368046a37bdSSonny Rao ADDCH(buf, sign); 36978acc472SPeter Tyser /* "0x" / "0" prefix */ 37078acc472SPeter Tyser if (need_pfx) { 371046a37bdSSonny Rao ADDCH(buf, '0'); 37278acc472SPeter Tyser if (base == 16) 373046a37bdSSonny Rao ADDCH(buf, 'X' | locase); 37478acc472SPeter Tyser } 37578acc472SPeter Tyser /* zero or space padding */ 37678acc472SPeter Tyser if (!(type & LEFT)) { 37778acc472SPeter Tyser char c = (type & ZEROPAD) ? '0' : ' '; 378046a37bdSSonny Rao 37978acc472SPeter Tyser while (--size >= 0) 380046a37bdSSonny Rao ADDCH(buf, c); 38178acc472SPeter Tyser } 38278acc472SPeter Tyser /* hmm even more zero padding? */ 38378acc472SPeter Tyser while (i <= --precision) 384046a37bdSSonny Rao ADDCH(buf, '0'); 38578acc472SPeter Tyser /* actual digits of result */ 38678acc472SPeter Tyser while (--i >= 0) 387046a37bdSSonny Rao ADDCH(buf, tmp[i]); 38878acc472SPeter Tyser /* trailing space padding */ 38978acc472SPeter Tyser while (--size >= 0) 390046a37bdSSonny Rao ADDCH(buf, ' '); 39178acc472SPeter Tyser return buf; 39278acc472SPeter Tyser } 39378acc472SPeter Tyser 394046a37bdSSonny Rao static char *string(char *buf, char *end, char *s, int field_width, 395046a37bdSSonny Rao int precision, int flags) 39678acc472SPeter Tyser { 39778acc472SPeter Tyser int len, i; 39878acc472SPeter Tyser 39978acc472SPeter Tyser if (s == 0) 40078acc472SPeter Tyser s = "<NULL>"; 40178acc472SPeter Tyser 40278acc472SPeter Tyser len = strnlen(s, precision); 40378acc472SPeter Tyser 40478acc472SPeter Tyser if (!(flags & LEFT)) 40578acc472SPeter Tyser while (len < field_width--) 406046a37bdSSonny Rao ADDCH(buf, ' '); 40778acc472SPeter Tyser for (i = 0; i < len; ++i) 408046a37bdSSonny Rao ADDCH(buf, *s++); 40978acc472SPeter Tyser while (len < field_width--) 410046a37bdSSonny Rao ADDCH(buf, ' '); 41178acc472SPeter Tyser return buf; 41278acc472SPeter Tyser } 41378acc472SPeter Tyser 41478acc472SPeter Tyser #ifdef CONFIG_CMD_NET 415046a37bdSSonny Rao static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width, 41678acc472SPeter Tyser int precision, int flags) 41778acc472SPeter Tyser { 418*8acdae68SDaniel Schwierzeck /* (6 * 2 hex digits), 5 colons and trailing zero */ 419*8acdae68SDaniel Schwierzeck char mac_addr[6 * 3]; 42078acc472SPeter Tyser char *p = mac_addr; 42178acc472SPeter Tyser int i; 42278acc472SPeter Tyser 42378acc472SPeter Tyser for (i = 0; i < 6; i++) { 42478acc472SPeter Tyser p = pack_hex_byte(p, addr[i]); 42578acc472SPeter Tyser if (!(flags & SPECIAL) && i != 5) 42678acc472SPeter Tyser *p++ = ':'; 42778acc472SPeter Tyser } 42878acc472SPeter Tyser *p = '\0'; 42978acc472SPeter Tyser 430046a37bdSSonny Rao return string(buf, end, mac_addr, field_width, precision, 431046a37bdSSonny Rao flags & ~SPECIAL); 43278acc472SPeter Tyser } 43378acc472SPeter Tyser 434046a37bdSSonny Rao static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width, 43578acc472SPeter Tyser int precision, int flags) 43678acc472SPeter Tyser { 437*8acdae68SDaniel Schwierzeck /* (8 * 4 hex digits), 7 colons and trailing zero */ 438*8acdae68SDaniel Schwierzeck char ip6_addr[8 * 5]; 43978acc472SPeter Tyser char *p = ip6_addr; 44078acc472SPeter Tyser int i; 44178acc472SPeter Tyser 44278acc472SPeter Tyser for (i = 0; i < 8; i++) { 44378acc472SPeter Tyser p = pack_hex_byte(p, addr[2 * i]); 44478acc472SPeter Tyser p = pack_hex_byte(p, addr[2 * i + 1]); 44578acc472SPeter Tyser if (!(flags & SPECIAL) && i != 7) 44678acc472SPeter Tyser *p++ = ':'; 44778acc472SPeter Tyser } 44878acc472SPeter Tyser *p = '\0'; 44978acc472SPeter Tyser 450046a37bdSSonny Rao return string(buf, end, ip6_addr, field_width, precision, 451046a37bdSSonny Rao flags & ~SPECIAL); 45278acc472SPeter Tyser } 45378acc472SPeter Tyser 454046a37bdSSonny Rao static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, 45578acc472SPeter Tyser int precision, int flags) 45678acc472SPeter Tyser { 457*8acdae68SDaniel Schwierzeck /* (4 * 3 decimal digits), 3 dots and trailing zero */ 458*8acdae68SDaniel Schwierzeck char ip4_addr[4 * 4]; 45978acc472SPeter Tyser char temp[3]; /* hold each IP quad in reverse order */ 46078acc472SPeter Tyser char *p = ip4_addr; 46178acc472SPeter Tyser int i, digits; 46278acc472SPeter Tyser 46378acc472SPeter Tyser for (i = 0; i < 4; i++) { 46478acc472SPeter Tyser digits = put_dec_trunc(temp, addr[i]) - temp; 46578acc472SPeter Tyser /* reverse the digits in the quad */ 46678acc472SPeter Tyser while (digits--) 46778acc472SPeter Tyser *p++ = temp[digits]; 46878acc472SPeter Tyser if (i != 3) 46978acc472SPeter Tyser *p++ = '.'; 47078acc472SPeter Tyser } 47178acc472SPeter Tyser *p = '\0'; 47278acc472SPeter Tyser 473046a37bdSSonny Rao return string(buf, end, ip4_addr, field_width, precision, 474046a37bdSSonny Rao flags & ~SPECIAL); 47578acc472SPeter Tyser } 47678acc472SPeter Tyser #endif 47778acc472SPeter Tyser 47878acc472SPeter Tyser /* 47978acc472SPeter Tyser * Show a '%p' thing. A kernel extension is that the '%p' is followed 48078acc472SPeter Tyser * by an extra set of alphanumeric characters that are extended format 48178acc472SPeter Tyser * specifiers. 48278acc472SPeter Tyser * 48378acc472SPeter Tyser * Right now we handle: 48478acc472SPeter Tyser * 48578acc472SPeter Tyser * - 'M' For a 6-byte MAC address, it prints the address in the 48678acc472SPeter Tyser * usual colon-separated hex notation 48778acc472SPeter Tyser * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way (dot-separated 48878acc472SPeter Tyser * decimal for v4 and colon separated network-order 16 bit hex for v6) 48978acc472SPeter Tyser * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is 49078acc472SPeter Tyser * currently the same 49178acc472SPeter Tyser * 49278acc472SPeter Tyser * Note: The difference between 'S' and 'F' is that on ia64 and ppc64 49378acc472SPeter Tyser * function pointers are really function descriptors, which contain a 49478acc472SPeter Tyser * pointer to the real address. 49578acc472SPeter Tyser */ 496046a37bdSSonny Rao static char *pointer(const char *fmt, char *buf, char *end, void *ptr, 497046a37bdSSonny Rao int field_width, int precision, int flags) 49878acc472SPeter Tyser { 49978acc472SPeter Tyser if (!ptr) 500046a37bdSSonny Rao return string(buf, end, "(null)", field_width, precision, 501046a37bdSSonny Rao flags); 50278acc472SPeter Tyser 50378acc472SPeter Tyser #ifdef CONFIG_CMD_NET 50478acc472SPeter Tyser switch (*fmt) { 50578acc472SPeter Tyser case 'm': 50678acc472SPeter Tyser flags |= SPECIAL; 50778acc472SPeter Tyser /* Fallthrough */ 50878acc472SPeter Tyser case 'M': 509046a37bdSSonny Rao return mac_address_string(buf, end, ptr, field_width, 510046a37bdSSonny Rao precision, flags); 51178acc472SPeter Tyser case 'i': 51278acc472SPeter Tyser flags |= SPECIAL; 51378acc472SPeter Tyser /* Fallthrough */ 51478acc472SPeter Tyser case 'I': 51578acc472SPeter Tyser if (fmt[1] == '6') 516046a37bdSSonny Rao return ip6_addr_string(buf, end, ptr, field_width, 517046a37bdSSonny Rao precision, flags); 51878acc472SPeter Tyser if (fmt[1] == '4') 519046a37bdSSonny Rao return ip4_addr_string(buf, end, ptr, field_width, 520046a37bdSSonny Rao precision, flags); 52178acc472SPeter Tyser flags &= ~SPECIAL; 52278acc472SPeter Tyser break; 52378acc472SPeter Tyser } 52478acc472SPeter Tyser #endif 52578acc472SPeter Tyser flags |= SMALL; 52678acc472SPeter Tyser if (field_width == -1) { 52778acc472SPeter Tyser field_width = 2*sizeof(void *); 52878acc472SPeter Tyser flags |= ZEROPAD; 52978acc472SPeter Tyser } 530046a37bdSSonny Rao return number(buf, end, (unsigned long)ptr, 16, field_width, 531046a37bdSSonny Rao precision, flags); 53278acc472SPeter Tyser } 53378acc472SPeter Tyser 534046a37bdSSonny Rao static int vsnprintf_internal(char *buf, size_t size, const char *fmt, 535046a37bdSSonny Rao va_list args) 53678acc472SPeter Tyser { 53778acc472SPeter Tyser unsigned NUM_TYPE num; 53878acc472SPeter Tyser int base; 53978acc472SPeter Tyser char *str; 54078acc472SPeter Tyser 54178acc472SPeter Tyser int flags; /* flags to number() */ 54278acc472SPeter Tyser 54378acc472SPeter Tyser int field_width; /* width of output field */ 54478acc472SPeter Tyser int precision; /* min. # of digits for integers; max 54578acc472SPeter Tyser number of chars for from string */ 54678acc472SPeter Tyser int qualifier; /* 'h', 'l', or 'L' for integer fields */ 54778acc472SPeter Tyser /* 'z' support added 23/7/1999 S.H. */ 54878acc472SPeter Tyser /* 'z' changed to 'Z' --davidm 1/25/99 */ 54978acc472SPeter Tyser /* 't' added for ptrdiff_t */ 550046a37bdSSonny Rao char *end = buf + size; 55178acc472SPeter Tyser 552046a37bdSSonny Rao #ifdef CONFIG_SYS_VSNPRINTF 553046a37bdSSonny Rao /* Make sure end is always >= buf - do we want this in U-Boot? */ 554046a37bdSSonny Rao if (end < buf) { 555046a37bdSSonny Rao end = ((void *)-1); 556046a37bdSSonny Rao size = end - buf; 557046a37bdSSonny Rao } 558046a37bdSSonny Rao #endif 55978acc472SPeter Tyser str = buf; 56078acc472SPeter Tyser 56178acc472SPeter Tyser for (; *fmt ; ++fmt) { 56278acc472SPeter Tyser if (*fmt != '%') { 563046a37bdSSonny Rao ADDCH(str, *fmt); 56478acc472SPeter Tyser continue; 56578acc472SPeter Tyser } 56678acc472SPeter Tyser 56778acc472SPeter Tyser /* process flags */ 56878acc472SPeter Tyser flags = 0; 56978acc472SPeter Tyser repeat: 57078acc472SPeter Tyser ++fmt; /* this also skips first '%' */ 57178acc472SPeter Tyser switch (*fmt) { 572*8acdae68SDaniel Schwierzeck case '-': 573*8acdae68SDaniel Schwierzeck flags |= LEFT; 574*8acdae68SDaniel Schwierzeck goto repeat; 575*8acdae68SDaniel Schwierzeck case '+': 576*8acdae68SDaniel Schwierzeck flags |= PLUS; 577*8acdae68SDaniel Schwierzeck goto repeat; 578*8acdae68SDaniel Schwierzeck case ' ': 579*8acdae68SDaniel Schwierzeck flags |= SPACE; 580*8acdae68SDaniel Schwierzeck goto repeat; 581*8acdae68SDaniel Schwierzeck case '#': 582*8acdae68SDaniel Schwierzeck flags |= SPECIAL; 583*8acdae68SDaniel Schwierzeck goto repeat; 584*8acdae68SDaniel Schwierzeck case '0': 585*8acdae68SDaniel Schwierzeck flags |= ZEROPAD; 586*8acdae68SDaniel Schwierzeck goto repeat; 58778acc472SPeter Tyser } 58878acc472SPeter Tyser 58978acc472SPeter Tyser /* get field width */ 59078acc472SPeter Tyser field_width = -1; 59178acc472SPeter Tyser if (is_digit(*fmt)) 59278acc472SPeter Tyser field_width = skip_atoi(&fmt); 59378acc472SPeter Tyser else if (*fmt == '*') { 59478acc472SPeter Tyser ++fmt; 59578acc472SPeter Tyser /* it's the next argument */ 59678acc472SPeter Tyser field_width = va_arg(args, int); 59778acc472SPeter Tyser if (field_width < 0) { 59878acc472SPeter Tyser field_width = -field_width; 59978acc472SPeter Tyser flags |= LEFT; 60078acc472SPeter Tyser } 60178acc472SPeter Tyser } 60278acc472SPeter Tyser 60378acc472SPeter Tyser /* get the precision */ 60478acc472SPeter Tyser precision = -1; 60578acc472SPeter Tyser if (*fmt == '.') { 60678acc472SPeter Tyser ++fmt; 60778acc472SPeter Tyser if (is_digit(*fmt)) 60878acc472SPeter Tyser precision = skip_atoi(&fmt); 60978acc472SPeter Tyser else if (*fmt == '*') { 61078acc472SPeter Tyser ++fmt; 61178acc472SPeter Tyser /* it's the next argument */ 61278acc472SPeter Tyser precision = va_arg(args, int); 61378acc472SPeter Tyser } 61478acc472SPeter Tyser if (precision < 0) 61578acc472SPeter Tyser precision = 0; 61678acc472SPeter Tyser } 61778acc472SPeter Tyser 61878acc472SPeter Tyser /* get the conversion qualifier */ 61978acc472SPeter Tyser qualifier = -1; 62078acc472SPeter Tyser if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || 62178acc472SPeter Tyser *fmt == 'Z' || *fmt == 'z' || *fmt == 't') { 62278acc472SPeter Tyser qualifier = *fmt; 62378acc472SPeter Tyser ++fmt; 62478acc472SPeter Tyser if (qualifier == 'l' && *fmt == 'l') { 62578acc472SPeter Tyser qualifier = 'L'; 62678acc472SPeter Tyser ++fmt; 62778acc472SPeter Tyser } 62878acc472SPeter Tyser } 62978acc472SPeter Tyser 63078acc472SPeter Tyser /* default base */ 63178acc472SPeter Tyser base = 10; 63278acc472SPeter Tyser 63378acc472SPeter Tyser switch (*fmt) { 63478acc472SPeter Tyser case 'c': 635046a37bdSSonny Rao if (!(flags & LEFT)) { 63678acc472SPeter Tyser while (--field_width > 0) 637046a37bdSSonny Rao ADDCH(str, ' '); 638046a37bdSSonny Rao } 639046a37bdSSonny Rao ADDCH(str, (unsigned char) va_arg(args, int)); 64078acc472SPeter Tyser while (--field_width > 0) 641046a37bdSSonny Rao ADDCH(str, ' '); 64278acc472SPeter Tyser continue; 64378acc472SPeter Tyser 64478acc472SPeter Tyser case 's': 645046a37bdSSonny Rao str = string(str, end, va_arg(args, char *), 646046a37bdSSonny Rao field_width, precision, flags); 64778acc472SPeter Tyser continue; 64878acc472SPeter Tyser 64978acc472SPeter Tyser case 'p': 650046a37bdSSonny Rao str = pointer(fmt + 1, str, end, 65178acc472SPeter Tyser va_arg(args, void *), 65278acc472SPeter Tyser field_width, precision, flags); 65378acc472SPeter Tyser /* Skip all alphanumeric pointer suffixes */ 65478acc472SPeter Tyser while (isalnum(fmt[1])) 65578acc472SPeter Tyser fmt++; 65678acc472SPeter Tyser continue; 65778acc472SPeter Tyser 65878acc472SPeter Tyser case 'n': 65978acc472SPeter Tyser if (qualifier == 'l') { 66078acc472SPeter Tyser long *ip = va_arg(args, long *); 66178acc472SPeter Tyser *ip = (str - buf); 66278acc472SPeter Tyser } else { 66378acc472SPeter Tyser int *ip = va_arg(args, int *); 66478acc472SPeter Tyser *ip = (str - buf); 66578acc472SPeter Tyser } 66678acc472SPeter Tyser continue; 66778acc472SPeter Tyser 66878acc472SPeter Tyser case '%': 669046a37bdSSonny Rao ADDCH(str, '%'); 67078acc472SPeter Tyser continue; 67178acc472SPeter Tyser 67278acc472SPeter Tyser /* integer number formats - set up the flags and "break" */ 67378acc472SPeter Tyser case 'o': 67478acc472SPeter Tyser base = 8; 67578acc472SPeter Tyser break; 67678acc472SPeter Tyser 67778acc472SPeter Tyser case 'x': 67878acc472SPeter Tyser flags |= SMALL; 67978acc472SPeter Tyser case 'X': 68078acc472SPeter Tyser base = 16; 68178acc472SPeter Tyser break; 68278acc472SPeter Tyser 68378acc472SPeter Tyser case 'd': 68478acc472SPeter Tyser case 'i': 68578acc472SPeter Tyser flags |= SIGN; 68678acc472SPeter Tyser case 'u': 68778acc472SPeter Tyser break; 68878acc472SPeter Tyser 68978acc472SPeter Tyser default: 690046a37bdSSonny Rao ADDCH(str, '%'); 69178acc472SPeter Tyser if (*fmt) 692046a37bdSSonny Rao ADDCH(str, *fmt); 69378acc472SPeter Tyser else 69478acc472SPeter Tyser --fmt; 69578acc472SPeter Tyser continue; 69678acc472SPeter Tyser } 69778acc472SPeter Tyser if (qualifier == 'L') /* "quad" for 64 bit variables */ 69878acc472SPeter Tyser num = va_arg(args, unsigned long long); 69978acc472SPeter Tyser else if (qualifier == 'l') { 70078acc472SPeter Tyser num = va_arg(args, unsigned long); 70178acc472SPeter Tyser if (flags & SIGN) 70278acc472SPeter Tyser num = (signed long) num; 70378acc472SPeter Tyser } else if (qualifier == 'Z' || qualifier == 'z') { 70478acc472SPeter Tyser num = va_arg(args, size_t); 70578acc472SPeter Tyser } else if (qualifier == 't') { 70678acc472SPeter Tyser num = va_arg(args, ptrdiff_t); 70778acc472SPeter Tyser } else if (qualifier == 'h') { 70878acc472SPeter Tyser num = (unsigned short) va_arg(args, int); 70978acc472SPeter Tyser if (flags & SIGN) 71078acc472SPeter Tyser num = (signed short) num; 71178acc472SPeter Tyser } else { 71278acc472SPeter Tyser num = va_arg(args, unsigned int); 71378acc472SPeter Tyser if (flags & SIGN) 71478acc472SPeter Tyser num = (signed int) num; 71578acc472SPeter Tyser } 716046a37bdSSonny Rao str = number(str, end, num, base, field_width, precision, 717046a37bdSSonny Rao flags); 71878acc472SPeter Tyser } 719046a37bdSSonny Rao 720046a37bdSSonny Rao #ifdef CONFIG_SYS_VSNPRINTF 721046a37bdSSonny Rao if (size > 0) { 722046a37bdSSonny Rao ADDCH(str, '\0'); 723046a37bdSSonny Rao if (str > end) 724046a37bdSSonny Rao end[-1] = '\0'; 725046a37bdSSonny Rao } 726046a37bdSSonny Rao #else 72778acc472SPeter Tyser *str = '\0'; 728046a37bdSSonny Rao #endif 729046a37bdSSonny Rao /* the trailing null byte doesn't count towards the total */ 73078acc472SPeter Tyser return str - buf; 73178acc472SPeter Tyser } 73278acc472SPeter Tyser 733046a37bdSSonny Rao #ifdef CONFIG_SYS_VSNPRINTF 734046a37bdSSonny Rao int vsnprintf(char *buf, size_t size, const char *fmt, 735046a37bdSSonny Rao va_list args) 736046a37bdSSonny Rao { 737046a37bdSSonny Rao return vsnprintf_internal(buf, size, fmt, args); 738046a37bdSSonny Rao } 739046a37bdSSonny Rao 740046a37bdSSonny Rao int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) 741046a37bdSSonny Rao { 742046a37bdSSonny Rao int i; 743046a37bdSSonny Rao 744046a37bdSSonny Rao i = vsnprintf(buf, size, fmt, args); 745046a37bdSSonny Rao 746046a37bdSSonny Rao if (likely(i < size)) 747046a37bdSSonny Rao return i; 748046a37bdSSonny Rao if (size != 0) 749046a37bdSSonny Rao return size - 1; 750046a37bdSSonny Rao return 0; 751046a37bdSSonny Rao } 752046a37bdSSonny Rao 753046a37bdSSonny Rao int snprintf(char *buf, size_t size, const char *fmt, ...) 754046a37bdSSonny Rao { 755046a37bdSSonny Rao va_list args; 756046a37bdSSonny Rao int i; 757046a37bdSSonny Rao 758046a37bdSSonny Rao va_start(args, fmt); 759046a37bdSSonny Rao i = vsnprintf(buf, size, fmt, args); 760046a37bdSSonny Rao va_end(args); 761046a37bdSSonny Rao 762046a37bdSSonny Rao return i; 763046a37bdSSonny Rao } 764046a37bdSSonny Rao 765046a37bdSSonny Rao int scnprintf(char *buf, size_t size, const char *fmt, ...) 766046a37bdSSonny Rao { 767046a37bdSSonny Rao va_list args; 768046a37bdSSonny Rao int i; 769046a37bdSSonny Rao 770046a37bdSSonny Rao va_start(args, fmt); 771046a37bdSSonny Rao i = vscnprintf(buf, size, fmt, args); 772046a37bdSSonny Rao va_end(args); 773046a37bdSSonny Rao 774046a37bdSSonny Rao return i; 775046a37bdSSonny Rao } 776046a37bdSSonny Rao #endif /* CONFIG_SYS_VSNPRINT */ 777046a37bdSSonny Rao 778046a37bdSSonny Rao /** 779046a37bdSSonny Rao * Format a string and place it in a buffer (va_list version) 780046a37bdSSonny Rao * 781046a37bdSSonny Rao * @param buf The buffer to place the result into 782046a37bdSSonny Rao * @param fmt The format string to use 783046a37bdSSonny Rao * @param args Arguments for the format string 784046a37bdSSonny Rao * 785046a37bdSSonny Rao * The function returns the number of characters written 786046a37bdSSonny Rao * into @buf. Use vsnprintf() or vscnprintf() in order to avoid 787046a37bdSSonny Rao * buffer overflows. 788046a37bdSSonny Rao * 789046a37bdSSonny Rao * If you're not already dealing with a va_list consider using sprintf(). 790046a37bdSSonny Rao */ 791046a37bdSSonny Rao int vsprintf(char *buf, const char *fmt, va_list args) 792046a37bdSSonny Rao { 793046a37bdSSonny Rao return vsnprintf_internal(buf, INT_MAX, fmt, args); 794046a37bdSSonny Rao } 795046a37bdSSonny Rao 79678acc472SPeter Tyser int sprintf(char *buf, const char *fmt, ...) 79778acc472SPeter Tyser { 79878acc472SPeter Tyser va_list args; 79978acc472SPeter Tyser int i; 80078acc472SPeter Tyser 80178acc472SPeter Tyser va_start(args, fmt); 80278acc472SPeter Tyser i = vsprintf(buf, fmt, args); 80378acc472SPeter Tyser va_end(args); 80478acc472SPeter Tyser return i; 80578acc472SPeter Tyser } 80678acc472SPeter Tyser 80778acc472SPeter Tyser void panic(const char *fmt, ...) 80878acc472SPeter Tyser { 80978acc472SPeter Tyser va_list args; 81078acc472SPeter Tyser va_start(args, fmt); 81178acc472SPeter Tyser vprintf(fmt, args); 81278acc472SPeter Tyser putc('\n'); 81378acc472SPeter Tyser va_end(args); 81478acc472SPeter Tyser #if defined(CONFIG_PANIC_HANG) 81578acc472SPeter Tyser hang(); 81678acc472SPeter Tyser #else 81778acc472SPeter Tyser udelay(100000); /* allow messages to go out */ 81878acc472SPeter Tyser do_reset(NULL, 0, 0, NULL); 81978acc472SPeter Tyser #endif 82040e01881SHeiko Schocher while (1) 82140e01881SHeiko Schocher ; 82278acc472SPeter Tyser } 82321726a7aSSimon Glass 82421726a7aSSimon Glass void __assert_fail(const char *assertion, const char *file, unsigned line, 82521726a7aSSimon Glass const char *function) 82621726a7aSSimon Glass { 82721726a7aSSimon Glass /* This will not return */ 82821726a7aSSimon Glass panic("%s:%u: %s: Assertion `%s' failed.", file, line, function, 82921726a7aSSimon Glass assertion); 83021726a7aSSimon Glass } 8313cce8a54SSimon Glass 8323cce8a54SSimon Glass char *simple_itoa(ulong i) 8333cce8a54SSimon Glass { 8343cce8a54SSimon Glass /* 21 digits plus null terminator, good for 64-bit or smaller ints */ 8353cce8a54SSimon Glass static char local[22]; 8363cce8a54SSimon Glass char *p = &local[21]; 8373cce8a54SSimon Glass 8383cce8a54SSimon Glass *p-- = '\0'; 8393cce8a54SSimon Glass do { 8403cce8a54SSimon Glass *p-- = '0' + i % 10; 8413cce8a54SSimon Glass i /= 10; 8423cce8a54SSimon Glass } while (i > 0); 8433cce8a54SSimon Glass return p + 1; 8443cce8a54SSimon Glass } 845