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> 1878acc472SPeter Tyser 1978acc472SPeter Tyser #include <common.h> 205ec68503SRob Clark #include <charset.h> 21*aa5728ffSRob Clark #include <uuid.h> 2278acc472SPeter Tyser 2378acc472SPeter Tyser #include <div64.h> 2478acc472SPeter Tyser #define noinline __attribute__((noinline)) 2578acc472SPeter Tyser 2678acc472SPeter Tyser /* we use this so that we can do without the ctype library */ 2778acc472SPeter Tyser #define is_digit(c) ((c) >= '0' && (c) <= '9') 2878acc472SPeter Tyser 2978acc472SPeter Tyser static int skip_atoi(const char **s) 3078acc472SPeter Tyser { 3178acc472SPeter Tyser int i = 0; 3278acc472SPeter Tyser 3378acc472SPeter Tyser while (is_digit(**s)) 3478acc472SPeter Tyser i = i * 10 + *((*s)++) - '0'; 358acdae68SDaniel Schwierzeck 3678acc472SPeter Tyser return i; 3778acc472SPeter Tyser } 3878acc472SPeter Tyser 3978acc472SPeter Tyser /* Decimal conversion is by far the most typical, and is used 4078acc472SPeter Tyser * for /proc and /sys data. This directly impacts e.g. top performance 4178acc472SPeter Tyser * with many processes running. We optimize it for speed 4278acc472SPeter Tyser * using code from 4378acc472SPeter Tyser * http://www.cs.uiowa.edu/~jones/bcd/decimal.html 4478acc472SPeter Tyser * (with permission from the author, Douglas W. Jones). */ 4578acc472SPeter Tyser 4678acc472SPeter Tyser /* Formats correctly any integer in [0,99999]. 4778acc472SPeter Tyser * Outputs from one to five digits depending on input. 4878acc472SPeter Tyser * On i386 gcc 4.1.2 -O2: ~250 bytes of code. */ 4978acc472SPeter Tyser static char *put_dec_trunc(char *buf, unsigned q) 5078acc472SPeter Tyser { 5178acc472SPeter Tyser unsigned d3, d2, d1, d0; 5278acc472SPeter Tyser d1 = (q>>4) & 0xf; 5378acc472SPeter Tyser d2 = (q>>8) & 0xf; 5478acc472SPeter Tyser d3 = (q>>12); 5578acc472SPeter Tyser 5678acc472SPeter Tyser d0 = 6*(d3 + d2 + d1) + (q & 0xf); 5778acc472SPeter Tyser q = (d0 * 0xcd) >> 11; 5878acc472SPeter Tyser d0 = d0 - 10*q; 5978acc472SPeter Tyser *buf++ = d0 + '0'; /* least significant digit */ 6078acc472SPeter Tyser d1 = q + 9*d3 + 5*d2 + d1; 6178acc472SPeter Tyser if (d1 != 0) { 6278acc472SPeter Tyser q = (d1 * 0xcd) >> 11; 6378acc472SPeter Tyser d1 = d1 - 10*q; 6478acc472SPeter Tyser *buf++ = d1 + '0'; /* next digit */ 6578acc472SPeter Tyser 6678acc472SPeter Tyser d2 = q + 2*d2; 6778acc472SPeter Tyser if ((d2 != 0) || (d3 != 0)) { 6878acc472SPeter Tyser q = (d2 * 0xd) >> 7; 6978acc472SPeter Tyser d2 = d2 - 10*q; 7078acc472SPeter Tyser *buf++ = d2 + '0'; /* next digit */ 7178acc472SPeter Tyser 7278acc472SPeter Tyser d3 = q + 4*d3; 7378acc472SPeter Tyser if (d3 != 0) { 7478acc472SPeter Tyser q = (d3 * 0xcd) >> 11; 7578acc472SPeter Tyser d3 = d3 - 10*q; 7678acc472SPeter Tyser *buf++ = d3 + '0'; /* next digit */ 7778acc472SPeter Tyser if (q != 0) 7878acc472SPeter Tyser *buf++ = q + '0'; /* most sign. digit */ 7978acc472SPeter Tyser } 8078acc472SPeter Tyser } 8178acc472SPeter Tyser } 8278acc472SPeter Tyser return buf; 8378acc472SPeter Tyser } 8478acc472SPeter Tyser /* Same with if's removed. Always emits five digits */ 8578acc472SPeter Tyser static char *put_dec_full(char *buf, unsigned q) 8678acc472SPeter Tyser { 8778acc472SPeter Tyser /* BTW, if q is in [0,9999], 8-bit ints will be enough, */ 8878acc472SPeter Tyser /* but anyway, gcc produces better code with full-sized ints */ 8978acc472SPeter Tyser unsigned d3, d2, d1, d0; 9078acc472SPeter Tyser d1 = (q>>4) & 0xf; 9178acc472SPeter Tyser d2 = (q>>8) & 0xf; 9278acc472SPeter Tyser d3 = (q>>12); 9378acc472SPeter Tyser 9478acc472SPeter Tyser /* 9578acc472SPeter Tyser * Possible ways to approx. divide by 10 9678acc472SPeter Tyser * gcc -O2 replaces multiply with shifts and adds 9778acc472SPeter Tyser * (x * 0xcd) >> 11: 11001101 - shorter code than * 0x67 (on i386) 9878acc472SPeter Tyser * (x * 0x67) >> 10: 1100111 9978acc472SPeter Tyser * (x * 0x34) >> 9: 110100 - same 10078acc472SPeter Tyser * (x * 0x1a) >> 8: 11010 - same 10178acc472SPeter Tyser * (x * 0x0d) >> 7: 1101 - same, shortest code (on i386) 10278acc472SPeter Tyser */ 10378acc472SPeter Tyser 10478acc472SPeter Tyser d0 = 6*(d3 + d2 + d1) + (q & 0xf); 10578acc472SPeter Tyser q = (d0 * 0xcd) >> 11; 10678acc472SPeter Tyser d0 = d0 - 10*q; 10778acc472SPeter Tyser *buf++ = d0 + '0'; 10878acc472SPeter Tyser d1 = q + 9*d3 + 5*d2 + d1; 10978acc472SPeter Tyser q = (d1 * 0xcd) >> 11; 11078acc472SPeter Tyser d1 = d1 - 10*q; 11178acc472SPeter Tyser *buf++ = d1 + '0'; 11278acc472SPeter Tyser 11378acc472SPeter Tyser d2 = q + 2*d2; 11478acc472SPeter Tyser q = (d2 * 0xd) >> 7; 11578acc472SPeter Tyser d2 = d2 - 10*q; 11678acc472SPeter Tyser *buf++ = d2 + '0'; 11778acc472SPeter Tyser 11878acc472SPeter Tyser d3 = q + 4*d3; 11978acc472SPeter Tyser q = (d3 * 0xcd) >> 11; /* - shorter code */ 12078acc472SPeter Tyser /* q = (d3 * 0x67) >> 10; - would also work */ 12178acc472SPeter Tyser d3 = d3 - 10*q; 12278acc472SPeter Tyser *buf++ = d3 + '0'; 12378acc472SPeter Tyser *buf++ = q + '0'; 12478acc472SPeter Tyser return buf; 12578acc472SPeter Tyser } 12678acc472SPeter Tyser /* No inlining helps gcc to use registers better */ 1276bf67259SSimon Glass static noinline char *put_dec(char *buf, uint64_t num) 12878acc472SPeter Tyser { 12978acc472SPeter Tyser while (1) { 13078acc472SPeter Tyser unsigned rem; 13178acc472SPeter Tyser if (num < 100000) 13278acc472SPeter Tyser return put_dec_trunc(buf, num); 13378acc472SPeter Tyser rem = do_div(num, 100000); 13478acc472SPeter Tyser buf = put_dec_full(buf, rem); 13578acc472SPeter Tyser } 13678acc472SPeter Tyser } 13778acc472SPeter Tyser 13878acc472SPeter Tyser #define ZEROPAD 1 /* pad with zero */ 13978acc472SPeter Tyser #define SIGN 2 /* unsigned/signed long */ 14078acc472SPeter Tyser #define PLUS 4 /* show plus */ 14178acc472SPeter Tyser #define SPACE 8 /* space if plus */ 14278acc472SPeter Tyser #define LEFT 16 /* left justified */ 14378acc472SPeter Tyser #define SMALL 32 /* Must be 32 == 0x20 */ 14478acc472SPeter Tyser #define SPECIAL 64 /* 0x */ 14578acc472SPeter Tyser 146046a37bdSSonny Rao /* 147046a37bdSSonny Rao * Macro to add a new character to our output string, but only if it will 148046a37bdSSonny Rao * fit. The macro moves to the next character position in the output string. 149046a37bdSSonny Rao */ 150046a37bdSSonny Rao #define ADDCH(str, ch) do { \ 151046a37bdSSonny Rao if ((str) < end) \ 152046a37bdSSonny Rao *(str) = (ch); \ 153046a37bdSSonny Rao ++str; \ 154046a37bdSSonny Rao } while (0) 155046a37bdSSonny Rao 1567b64f66cSDaniel Schwierzeck static char *number(char *buf, char *end, u64 num, 157046a37bdSSonny Rao int base, int size, int precision, int type) 15878acc472SPeter Tyser { 15978acc472SPeter Tyser /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ 1608acdae68SDaniel Schwierzeck static const char digits[16] = "0123456789ABCDEF"; 16178acc472SPeter Tyser 16278acc472SPeter Tyser char tmp[66]; 16378acc472SPeter Tyser char sign; 16478acc472SPeter Tyser char locase; 16578acc472SPeter Tyser int need_pfx = ((type & SPECIAL) && base != 10); 16678acc472SPeter Tyser int i; 16778acc472SPeter Tyser 16878acc472SPeter Tyser /* locase = 0 or 0x20. ORing digits or letters with 'locase' 16978acc472SPeter Tyser * produces same digits or (maybe lowercased) letters */ 17078acc472SPeter Tyser locase = (type & SMALL); 17178acc472SPeter Tyser if (type & LEFT) 17278acc472SPeter Tyser type &= ~ZEROPAD; 17378acc472SPeter Tyser sign = 0; 17478acc472SPeter Tyser if (type & SIGN) { 1757b64f66cSDaniel Schwierzeck if ((s64) num < 0) { 17678acc472SPeter Tyser sign = '-'; 1777b64f66cSDaniel Schwierzeck num = -(s64) num; 17878acc472SPeter Tyser size--; 17978acc472SPeter Tyser } else if (type & PLUS) { 18078acc472SPeter Tyser sign = '+'; 18178acc472SPeter Tyser size--; 18278acc472SPeter Tyser } else if (type & SPACE) { 18378acc472SPeter Tyser sign = ' '; 18478acc472SPeter Tyser size--; 18578acc472SPeter Tyser } 18678acc472SPeter Tyser } 18778acc472SPeter Tyser if (need_pfx) { 18878acc472SPeter Tyser size--; 18978acc472SPeter Tyser if (base == 16) 19078acc472SPeter Tyser size--; 19178acc472SPeter Tyser } 19278acc472SPeter Tyser 19378acc472SPeter Tyser /* generate full string in tmp[], in reverse order */ 19478acc472SPeter Tyser i = 0; 19578acc472SPeter Tyser if (num == 0) 19678acc472SPeter Tyser tmp[i++] = '0'; 19778acc472SPeter Tyser /* Generic code, for any base: 19878acc472SPeter Tyser else do { 19978acc472SPeter Tyser tmp[i++] = (digits[do_div(num,base)] | locase); 20078acc472SPeter Tyser } while (num != 0); 20178acc472SPeter Tyser */ 20278acc472SPeter Tyser else if (base != 10) { /* 8 or 16 */ 20378acc472SPeter Tyser int mask = base - 1; 20478acc472SPeter Tyser int shift = 3; 2058acdae68SDaniel Schwierzeck 2068acdae68SDaniel Schwierzeck if (base == 16) 2078acdae68SDaniel Schwierzeck shift = 4; 2088acdae68SDaniel Schwierzeck 20978acc472SPeter Tyser do { 2108acdae68SDaniel Schwierzeck tmp[i++] = (digits[((unsigned char)num) & mask] 2118acdae68SDaniel Schwierzeck | locase); 21278acc472SPeter Tyser num >>= shift; 21378acc472SPeter Tyser } while (num); 21478acc472SPeter Tyser } else { /* base 10 */ 21578acc472SPeter Tyser i = put_dec(tmp, num) - tmp; 21678acc472SPeter Tyser } 21778acc472SPeter Tyser 21878acc472SPeter Tyser /* printing 100 using %2d gives "100", not "00" */ 21978acc472SPeter Tyser if (i > precision) 22078acc472SPeter Tyser precision = i; 22178acc472SPeter Tyser /* leading space padding */ 22278acc472SPeter Tyser size -= precision; 223046a37bdSSonny Rao if (!(type & (ZEROPAD + LEFT))) { 22478acc472SPeter Tyser while (--size >= 0) 225046a37bdSSonny Rao ADDCH(buf, ' '); 226046a37bdSSonny Rao } 22778acc472SPeter Tyser /* sign */ 22878acc472SPeter Tyser if (sign) 229046a37bdSSonny Rao ADDCH(buf, sign); 23078acc472SPeter Tyser /* "0x" / "0" prefix */ 23178acc472SPeter Tyser if (need_pfx) { 232046a37bdSSonny Rao ADDCH(buf, '0'); 23378acc472SPeter Tyser if (base == 16) 234046a37bdSSonny Rao ADDCH(buf, 'X' | locase); 23578acc472SPeter Tyser } 23678acc472SPeter Tyser /* zero or space padding */ 23778acc472SPeter Tyser if (!(type & LEFT)) { 23878acc472SPeter Tyser char c = (type & ZEROPAD) ? '0' : ' '; 239046a37bdSSonny Rao 24078acc472SPeter Tyser while (--size >= 0) 241046a37bdSSonny Rao ADDCH(buf, c); 24278acc472SPeter Tyser } 24378acc472SPeter Tyser /* hmm even more zero padding? */ 24478acc472SPeter Tyser while (i <= --precision) 245046a37bdSSonny Rao ADDCH(buf, '0'); 24678acc472SPeter Tyser /* actual digits of result */ 24778acc472SPeter Tyser while (--i >= 0) 248046a37bdSSonny Rao ADDCH(buf, tmp[i]); 24978acc472SPeter Tyser /* trailing space padding */ 25078acc472SPeter Tyser while (--size >= 0) 251046a37bdSSonny Rao ADDCH(buf, ' '); 25278acc472SPeter Tyser return buf; 25378acc472SPeter Tyser } 25478acc472SPeter Tyser 255046a37bdSSonny Rao static char *string(char *buf, char *end, char *s, int field_width, 256046a37bdSSonny Rao int precision, int flags) 25778acc472SPeter Tyser { 25878acc472SPeter Tyser int len, i; 25978acc472SPeter Tyser 2600eb25768SKim Phillips if (s == NULL) 26178acc472SPeter Tyser s = "<NULL>"; 26278acc472SPeter Tyser 26378acc472SPeter Tyser len = strnlen(s, precision); 26478acc472SPeter Tyser 26578acc472SPeter Tyser if (!(flags & LEFT)) 26678acc472SPeter Tyser while (len < field_width--) 267046a37bdSSonny Rao ADDCH(buf, ' '); 26878acc472SPeter Tyser for (i = 0; i < len; ++i) 269046a37bdSSonny Rao ADDCH(buf, *s++); 27078acc472SPeter Tyser while (len < field_width--) 271046a37bdSSonny Rao ADDCH(buf, ' '); 27278acc472SPeter Tyser return buf; 27378acc472SPeter Tyser } 27478acc472SPeter Tyser 2755ec68503SRob Clark static char *string16(char *buf, char *end, u16 *s, int field_width, 2765ec68503SRob Clark int precision, int flags) 2775ec68503SRob Clark { 2785ec68503SRob Clark u16 *str = s ? s : L"<NULL>"; 2795ec68503SRob Clark int utf16_len = utf16_strnlen(str, precision); 2805ec68503SRob Clark u8 utf8[utf16_len * MAX_UTF8_PER_UTF16]; 2815ec68503SRob Clark int utf8_len, i; 2825ec68503SRob Clark 2835ec68503SRob Clark utf8_len = utf16_to_utf8(utf8, str, utf16_len) - utf8; 2845ec68503SRob Clark 2855ec68503SRob Clark if (!(flags & LEFT)) 2865ec68503SRob Clark while (utf8_len < field_width--) 2875ec68503SRob Clark ADDCH(buf, ' '); 2885ec68503SRob Clark for (i = 0; i < utf8_len; ++i) 2895ec68503SRob Clark ADDCH(buf, utf8[i]); 2905ec68503SRob Clark while (utf8_len < field_width--) 2915ec68503SRob Clark ADDCH(buf, ' '); 2925ec68503SRob Clark return buf; 2935ec68503SRob Clark } 2945ec68503SRob Clark 29578acc472SPeter Tyser #ifdef CONFIG_CMD_NET 296d7b2d9dfSJeroen Hofstee static const char hex_asc[] = "0123456789abcdef"; 297d7b2d9dfSJeroen Hofstee #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] 298d7b2d9dfSJeroen Hofstee #define hex_asc_hi(x) hex_asc[((x) & 0xf0) >> 4] 299d7b2d9dfSJeroen Hofstee 300d7b2d9dfSJeroen Hofstee static inline char *pack_hex_byte(char *buf, u8 byte) 301d7b2d9dfSJeroen Hofstee { 302d7b2d9dfSJeroen Hofstee *buf++ = hex_asc_hi(byte); 303d7b2d9dfSJeroen Hofstee *buf++ = hex_asc_lo(byte); 304d7b2d9dfSJeroen Hofstee return buf; 305d7b2d9dfSJeroen Hofstee } 306d7b2d9dfSJeroen Hofstee 307046a37bdSSonny Rao static char *mac_address_string(char *buf, char *end, u8 *addr, int field_width, 30878acc472SPeter Tyser int precision, int flags) 30978acc472SPeter Tyser { 3108acdae68SDaniel Schwierzeck /* (6 * 2 hex digits), 5 colons and trailing zero */ 3118acdae68SDaniel Schwierzeck char mac_addr[6 * 3]; 31278acc472SPeter Tyser char *p = mac_addr; 31378acc472SPeter Tyser int i; 31478acc472SPeter Tyser 31578acc472SPeter Tyser for (i = 0; i < 6; i++) { 31678acc472SPeter Tyser p = pack_hex_byte(p, addr[i]); 31778acc472SPeter Tyser if (!(flags & SPECIAL) && i != 5) 31878acc472SPeter Tyser *p++ = ':'; 31978acc472SPeter Tyser } 32078acc472SPeter Tyser *p = '\0'; 32178acc472SPeter Tyser 322046a37bdSSonny Rao return string(buf, end, mac_addr, field_width, precision, 323046a37bdSSonny Rao flags & ~SPECIAL); 32478acc472SPeter Tyser } 32578acc472SPeter Tyser 326046a37bdSSonny Rao static char *ip6_addr_string(char *buf, char *end, u8 *addr, int field_width, 32778acc472SPeter Tyser int precision, int flags) 32878acc472SPeter Tyser { 3298acdae68SDaniel Schwierzeck /* (8 * 4 hex digits), 7 colons and trailing zero */ 3308acdae68SDaniel Schwierzeck char ip6_addr[8 * 5]; 33178acc472SPeter Tyser char *p = ip6_addr; 33278acc472SPeter Tyser int i; 33378acc472SPeter Tyser 33478acc472SPeter Tyser for (i = 0; i < 8; i++) { 33578acc472SPeter Tyser p = pack_hex_byte(p, addr[2 * i]); 33678acc472SPeter Tyser p = pack_hex_byte(p, addr[2 * i + 1]); 33778acc472SPeter Tyser if (!(flags & SPECIAL) && i != 7) 33878acc472SPeter Tyser *p++ = ':'; 33978acc472SPeter Tyser } 34078acc472SPeter Tyser *p = '\0'; 34178acc472SPeter Tyser 342046a37bdSSonny Rao return string(buf, end, ip6_addr, field_width, precision, 343046a37bdSSonny Rao flags & ~SPECIAL); 34478acc472SPeter Tyser } 34578acc472SPeter Tyser 346046a37bdSSonny Rao static char *ip4_addr_string(char *buf, char *end, u8 *addr, int field_width, 34778acc472SPeter Tyser int precision, int flags) 34878acc472SPeter Tyser { 3498acdae68SDaniel Schwierzeck /* (4 * 3 decimal digits), 3 dots and trailing zero */ 3508acdae68SDaniel Schwierzeck char ip4_addr[4 * 4]; 35178acc472SPeter Tyser char temp[3]; /* hold each IP quad in reverse order */ 35278acc472SPeter Tyser char *p = ip4_addr; 35378acc472SPeter Tyser int i, digits; 35478acc472SPeter Tyser 35578acc472SPeter Tyser for (i = 0; i < 4; i++) { 35678acc472SPeter Tyser digits = put_dec_trunc(temp, addr[i]) - temp; 35778acc472SPeter Tyser /* reverse the digits in the quad */ 35878acc472SPeter Tyser while (digits--) 35978acc472SPeter Tyser *p++ = temp[digits]; 36078acc472SPeter Tyser if (i != 3) 36178acc472SPeter Tyser *p++ = '.'; 36278acc472SPeter Tyser } 36378acc472SPeter Tyser *p = '\0'; 36478acc472SPeter Tyser 365046a37bdSSonny Rao return string(buf, end, ip4_addr, field_width, precision, 366046a37bdSSonny Rao flags & ~SPECIAL); 36778acc472SPeter Tyser } 36878acc472SPeter Tyser #endif 36978acc472SPeter Tyser 370*aa5728ffSRob Clark #ifdef CONFIG_LIB_UUID 371*aa5728ffSRob Clark /* 372*aa5728ffSRob Clark * This works (roughly) the same way as linux's, but we currently always 373*aa5728ffSRob Clark * print lower-case (ie. we just keep %pUB and %pUL for compat with linux), 374*aa5728ffSRob Clark * mostly just because that is what uuid_bin_to_str() supports. 375*aa5728ffSRob Clark * 376*aa5728ffSRob Clark * %pUb: 01020304-0506-0708-090a-0b0c0d0e0f10 377*aa5728ffSRob Clark * %pUl: 04030201-0605-0807-090a-0b0c0d0e0f10 378*aa5728ffSRob Clark */ 379*aa5728ffSRob Clark static char *uuid_string(char *buf, char *end, u8 *addr, int field_width, 380*aa5728ffSRob Clark int precision, int flags, const char *fmt) 381*aa5728ffSRob Clark { 382*aa5728ffSRob Clark char uuid[UUID_STR_LEN + 1]; 383*aa5728ffSRob Clark int str_format = UUID_STR_FORMAT_STD; 384*aa5728ffSRob Clark 385*aa5728ffSRob Clark switch (*(++fmt)) { 386*aa5728ffSRob Clark case 'L': 387*aa5728ffSRob Clark case 'l': 388*aa5728ffSRob Clark str_format = UUID_STR_FORMAT_GUID; 389*aa5728ffSRob Clark break; 390*aa5728ffSRob Clark case 'B': 391*aa5728ffSRob Clark case 'b': 392*aa5728ffSRob Clark /* this is the default */ 393*aa5728ffSRob Clark break; 394*aa5728ffSRob Clark default: 395*aa5728ffSRob Clark break; 396*aa5728ffSRob Clark } 397*aa5728ffSRob Clark 398*aa5728ffSRob Clark uuid_bin_to_str(addr, uuid, str_format); 399*aa5728ffSRob Clark 400*aa5728ffSRob Clark return string(buf, end, uuid, field_width, precision, flags); 401*aa5728ffSRob Clark } 402*aa5728ffSRob Clark #endif 403*aa5728ffSRob Clark 40478acc472SPeter Tyser /* 40578acc472SPeter Tyser * Show a '%p' thing. A kernel extension is that the '%p' is followed 40678acc472SPeter Tyser * by an extra set of alphanumeric characters that are extended format 40778acc472SPeter Tyser * specifiers. 40878acc472SPeter Tyser * 40978acc472SPeter Tyser * Right now we handle: 41078acc472SPeter Tyser * 41178acc472SPeter Tyser * - 'M' For a 6-byte MAC address, it prints the address in the 41278acc472SPeter Tyser * usual colon-separated hex notation 41378acc472SPeter Tyser * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way (dot-separated 41478acc472SPeter Tyser * decimal for v4 and colon separated network-order 16 bit hex for v6) 41578acc472SPeter Tyser * - 'i' [46] for 'raw' IPv4/IPv6 addresses, IPv6 omits the colons, IPv4 is 41678acc472SPeter Tyser * currently the same 41778acc472SPeter Tyser * 41878acc472SPeter Tyser * Note: The difference between 'S' and 'F' is that on ia64 and ppc64 41978acc472SPeter Tyser * function pointers are really function descriptors, which contain a 42078acc472SPeter Tyser * pointer to the real address. 42178acc472SPeter Tyser */ 422046a37bdSSonny Rao static char *pointer(const char *fmt, char *buf, char *end, void *ptr, 423046a37bdSSonny Rao int field_width, int precision, int flags) 42478acc472SPeter Tyser { 4251eebd14bSThierry Reding u64 num = (uintptr_t)ptr; 4261eebd14bSThierry Reding 427d266f669SWolfgang Denk /* 428d266f669SWolfgang Denk * Being a boot loader, we explicitly allow pointers to 429d266f669SWolfgang Denk * (physical) address null. 430d266f669SWolfgang Denk */ 431d266f669SWolfgang Denk #if 0 43278acc472SPeter Tyser if (!ptr) 433046a37bdSSonny Rao return string(buf, end, "(null)", field_width, precision, 434046a37bdSSonny Rao flags); 435d266f669SWolfgang Denk #endif 43678acc472SPeter Tyser 43778acc472SPeter Tyser switch (*fmt) { 438*aa5728ffSRob Clark #ifdef CONFIG_CMD_NET 4391eebd14bSThierry Reding case 'a': 4401eebd14bSThierry Reding flags |= SPECIAL | ZEROPAD; 4411eebd14bSThierry Reding 4421eebd14bSThierry Reding switch (fmt[1]) { 4431eebd14bSThierry Reding case 'p': 4441eebd14bSThierry Reding default: 4451eebd14bSThierry Reding field_width = sizeof(phys_addr_t) * 2 + 2; 4461eebd14bSThierry Reding num = *(phys_addr_t *)ptr; 4471eebd14bSThierry Reding break; 4481eebd14bSThierry Reding } 4491eebd14bSThierry Reding break; 45078acc472SPeter Tyser case 'm': 45178acc472SPeter Tyser flags |= SPECIAL; 45278acc472SPeter Tyser /* Fallthrough */ 45378acc472SPeter Tyser case 'M': 454046a37bdSSonny Rao return mac_address_string(buf, end, ptr, field_width, 455046a37bdSSonny Rao precision, flags); 45678acc472SPeter Tyser case 'i': 45778acc472SPeter Tyser flags |= SPECIAL; 45878acc472SPeter Tyser /* Fallthrough */ 45978acc472SPeter Tyser case 'I': 46078acc472SPeter Tyser if (fmt[1] == '6') 461046a37bdSSonny Rao return ip6_addr_string(buf, end, ptr, field_width, 462046a37bdSSonny Rao precision, flags); 46378acc472SPeter Tyser if (fmt[1] == '4') 464046a37bdSSonny Rao return ip4_addr_string(buf, end, ptr, field_width, 465046a37bdSSonny Rao precision, flags); 46678acc472SPeter Tyser flags &= ~SPECIAL; 46778acc472SPeter Tyser break; 46878acc472SPeter Tyser #endif 469*aa5728ffSRob Clark #ifdef CONFIG_LIB_UUID 470*aa5728ffSRob Clark case 'U': 471*aa5728ffSRob Clark return uuid_string(buf, end, ptr, field_width, precision, 472*aa5728ffSRob Clark flags, fmt); 473*aa5728ffSRob Clark #endif 474*aa5728ffSRob Clark default: 475*aa5728ffSRob Clark break; 476*aa5728ffSRob Clark } 47778acc472SPeter Tyser flags |= SMALL; 47878acc472SPeter Tyser if (field_width == -1) { 47978acc472SPeter Tyser field_width = 2*sizeof(void *); 48078acc472SPeter Tyser flags |= ZEROPAD; 48178acc472SPeter Tyser } 4821eebd14bSThierry Reding return number(buf, end, num, 16, field_width, precision, flags); 48378acc472SPeter Tyser } 48478acc472SPeter Tyser 485046a37bdSSonny Rao static int vsnprintf_internal(char *buf, size_t size, const char *fmt, 486046a37bdSSonny Rao va_list args) 48778acc472SPeter Tyser { 4887b64f66cSDaniel Schwierzeck u64 num; 48978acc472SPeter Tyser int base; 49078acc472SPeter Tyser char *str; 49178acc472SPeter Tyser 49278acc472SPeter Tyser int flags; /* flags to number() */ 49378acc472SPeter Tyser 49478acc472SPeter Tyser int field_width; /* width of output field */ 49578acc472SPeter Tyser int precision; /* min. # of digits for integers; max 49678acc472SPeter Tyser number of chars for from string */ 49778acc472SPeter Tyser int qualifier; /* 'h', 'l', or 'L' for integer fields */ 49878acc472SPeter Tyser /* 'z' support added 23/7/1999 S.H. */ 49978acc472SPeter Tyser /* 'z' changed to 'Z' --davidm 1/25/99 */ 50078acc472SPeter Tyser /* 't' added for ptrdiff_t */ 501046a37bdSSonny Rao char *end = buf + size; 50278acc472SPeter Tyser 503046a37bdSSonny Rao /* Make sure end is always >= buf - do we want this in U-Boot? */ 504046a37bdSSonny Rao if (end < buf) { 505046a37bdSSonny Rao end = ((void *)-1); 506046a37bdSSonny Rao size = end - buf; 507046a37bdSSonny Rao } 50878acc472SPeter Tyser str = buf; 50978acc472SPeter Tyser 51078acc472SPeter Tyser for (; *fmt ; ++fmt) { 51178acc472SPeter Tyser if (*fmt != '%') { 512046a37bdSSonny Rao ADDCH(str, *fmt); 51378acc472SPeter Tyser continue; 51478acc472SPeter Tyser } 51578acc472SPeter Tyser 51678acc472SPeter Tyser /* process flags */ 51778acc472SPeter Tyser flags = 0; 51878acc472SPeter Tyser repeat: 51978acc472SPeter Tyser ++fmt; /* this also skips first '%' */ 52078acc472SPeter Tyser switch (*fmt) { 5218acdae68SDaniel Schwierzeck case '-': 5228acdae68SDaniel Schwierzeck flags |= LEFT; 5238acdae68SDaniel Schwierzeck goto repeat; 5248acdae68SDaniel Schwierzeck case '+': 5258acdae68SDaniel Schwierzeck flags |= PLUS; 5268acdae68SDaniel Schwierzeck goto repeat; 5278acdae68SDaniel Schwierzeck case ' ': 5288acdae68SDaniel Schwierzeck flags |= SPACE; 5298acdae68SDaniel Schwierzeck goto repeat; 5308acdae68SDaniel Schwierzeck case '#': 5318acdae68SDaniel Schwierzeck flags |= SPECIAL; 5328acdae68SDaniel Schwierzeck goto repeat; 5338acdae68SDaniel Schwierzeck case '0': 5348acdae68SDaniel Schwierzeck flags |= ZEROPAD; 5358acdae68SDaniel Schwierzeck goto repeat; 53678acc472SPeter Tyser } 53778acc472SPeter Tyser 53878acc472SPeter Tyser /* get field width */ 53978acc472SPeter Tyser field_width = -1; 54078acc472SPeter Tyser if (is_digit(*fmt)) 54178acc472SPeter Tyser field_width = skip_atoi(&fmt); 54278acc472SPeter Tyser else if (*fmt == '*') { 54378acc472SPeter Tyser ++fmt; 54478acc472SPeter Tyser /* it's the next argument */ 54578acc472SPeter Tyser field_width = va_arg(args, int); 54678acc472SPeter Tyser if (field_width < 0) { 54778acc472SPeter Tyser field_width = -field_width; 54878acc472SPeter Tyser flags |= LEFT; 54978acc472SPeter Tyser } 55078acc472SPeter Tyser } 55178acc472SPeter Tyser 55278acc472SPeter Tyser /* get the precision */ 55378acc472SPeter Tyser precision = -1; 55478acc472SPeter Tyser if (*fmt == '.') { 55578acc472SPeter Tyser ++fmt; 55678acc472SPeter Tyser if (is_digit(*fmt)) 55778acc472SPeter Tyser precision = skip_atoi(&fmt); 55878acc472SPeter Tyser else if (*fmt == '*') { 55978acc472SPeter Tyser ++fmt; 56078acc472SPeter Tyser /* it's the next argument */ 56178acc472SPeter Tyser precision = va_arg(args, int); 56278acc472SPeter Tyser } 56378acc472SPeter Tyser if (precision < 0) 56478acc472SPeter Tyser precision = 0; 56578acc472SPeter Tyser } 56678acc472SPeter Tyser 56778acc472SPeter Tyser /* get the conversion qualifier */ 56878acc472SPeter Tyser qualifier = -1; 56978acc472SPeter Tyser if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || 57078acc472SPeter Tyser *fmt == 'Z' || *fmt == 'z' || *fmt == 't') { 57178acc472SPeter Tyser qualifier = *fmt; 57278acc472SPeter Tyser ++fmt; 57378acc472SPeter Tyser if (qualifier == 'l' && *fmt == 'l') { 57478acc472SPeter Tyser qualifier = 'L'; 57578acc472SPeter Tyser ++fmt; 57678acc472SPeter Tyser } 57778acc472SPeter Tyser } 57878acc472SPeter Tyser 57978acc472SPeter Tyser /* default base */ 58078acc472SPeter Tyser base = 10; 58178acc472SPeter Tyser 58278acc472SPeter Tyser switch (*fmt) { 58378acc472SPeter Tyser case 'c': 584046a37bdSSonny Rao if (!(flags & LEFT)) { 58578acc472SPeter Tyser while (--field_width > 0) 586046a37bdSSonny Rao ADDCH(str, ' '); 587046a37bdSSonny Rao } 588046a37bdSSonny Rao ADDCH(str, (unsigned char) va_arg(args, int)); 58978acc472SPeter Tyser while (--field_width > 0) 590046a37bdSSonny Rao ADDCH(str, ' '); 59178acc472SPeter Tyser continue; 59278acc472SPeter Tyser 59378acc472SPeter Tyser case 's': 5945ec68503SRob Clark if (qualifier == 'l' && !IS_ENABLED(CONFIG_SPL_BUILD)) { 5955ec68503SRob Clark str = string16(str, end, va_arg(args, u16 *), 5965ec68503SRob Clark field_width, precision, flags); 5975ec68503SRob Clark } else { 598046a37bdSSonny Rao str = string(str, end, va_arg(args, char *), 599046a37bdSSonny Rao field_width, precision, flags); 6005ec68503SRob Clark } 60178acc472SPeter Tyser continue; 60278acc472SPeter Tyser 60378acc472SPeter Tyser case 'p': 604046a37bdSSonny Rao str = pointer(fmt + 1, str, end, 60578acc472SPeter Tyser va_arg(args, void *), 60678acc472SPeter Tyser field_width, precision, flags); 60778acc472SPeter Tyser /* Skip all alphanumeric pointer suffixes */ 60878acc472SPeter Tyser while (isalnum(fmt[1])) 60978acc472SPeter Tyser fmt++; 61078acc472SPeter Tyser continue; 61178acc472SPeter Tyser 61278acc472SPeter Tyser case 'n': 61378acc472SPeter Tyser if (qualifier == 'l') { 61478acc472SPeter Tyser long *ip = va_arg(args, long *); 61578acc472SPeter Tyser *ip = (str - buf); 61678acc472SPeter Tyser } else { 61778acc472SPeter Tyser int *ip = va_arg(args, int *); 61878acc472SPeter Tyser *ip = (str - buf); 61978acc472SPeter Tyser } 62078acc472SPeter Tyser continue; 62178acc472SPeter Tyser 62278acc472SPeter Tyser case '%': 623046a37bdSSonny Rao ADDCH(str, '%'); 62478acc472SPeter Tyser continue; 62578acc472SPeter Tyser 62678acc472SPeter Tyser /* integer number formats - set up the flags and "break" */ 62778acc472SPeter Tyser case 'o': 62878acc472SPeter Tyser base = 8; 62978acc472SPeter Tyser break; 63078acc472SPeter Tyser 63178acc472SPeter Tyser case 'x': 63278acc472SPeter Tyser flags |= SMALL; 63378acc472SPeter Tyser case 'X': 63478acc472SPeter Tyser base = 16; 63578acc472SPeter Tyser break; 63678acc472SPeter Tyser 63778acc472SPeter Tyser case 'd': 63878acc472SPeter Tyser case 'i': 63978acc472SPeter Tyser flags |= SIGN; 64078acc472SPeter Tyser case 'u': 64178acc472SPeter Tyser break; 64278acc472SPeter Tyser 64378acc472SPeter Tyser default: 644046a37bdSSonny Rao ADDCH(str, '%'); 64578acc472SPeter Tyser if (*fmt) 646046a37bdSSonny Rao ADDCH(str, *fmt); 64778acc472SPeter Tyser else 64878acc472SPeter Tyser --fmt; 64978acc472SPeter Tyser continue; 65078acc472SPeter Tyser } 65178acc472SPeter Tyser if (qualifier == 'L') /* "quad" for 64 bit variables */ 65278acc472SPeter Tyser num = va_arg(args, unsigned long long); 65378acc472SPeter Tyser else if (qualifier == 'l') { 65478acc472SPeter Tyser num = va_arg(args, unsigned long); 65578acc472SPeter Tyser if (flags & SIGN) 65678acc472SPeter Tyser num = (signed long) num; 65778acc472SPeter Tyser } else if (qualifier == 'Z' || qualifier == 'z') { 65878acc472SPeter Tyser num = va_arg(args, size_t); 65978acc472SPeter Tyser } else if (qualifier == 't') { 66078acc472SPeter Tyser num = va_arg(args, ptrdiff_t); 66178acc472SPeter Tyser } else if (qualifier == 'h') { 66278acc472SPeter Tyser num = (unsigned short) va_arg(args, int); 66378acc472SPeter Tyser if (flags & SIGN) 66478acc472SPeter Tyser num = (signed short) num; 66578acc472SPeter Tyser } else { 66678acc472SPeter Tyser num = va_arg(args, unsigned int); 66778acc472SPeter Tyser if (flags & SIGN) 66878acc472SPeter Tyser num = (signed int) num; 66978acc472SPeter Tyser } 670046a37bdSSonny Rao str = number(str, end, num, base, field_width, precision, 671046a37bdSSonny Rao flags); 67278acc472SPeter Tyser } 673046a37bdSSonny Rao 674046a37bdSSonny Rao if (size > 0) { 675046a37bdSSonny Rao ADDCH(str, '\0'); 676046a37bdSSonny Rao if (str > end) 677046a37bdSSonny Rao end[-1] = '\0'; 678686f60f5SDarwin Rambo --str; 679046a37bdSSonny Rao } 680046a37bdSSonny Rao /* the trailing null byte doesn't count towards the total */ 68178acc472SPeter Tyser return str - buf; 68278acc472SPeter Tyser } 68378acc472SPeter Tyser 684046a37bdSSonny Rao int vsnprintf(char *buf, size_t size, const char *fmt, 685046a37bdSSonny Rao va_list args) 686046a37bdSSonny Rao { 687046a37bdSSonny Rao return vsnprintf_internal(buf, size, fmt, args); 688046a37bdSSonny Rao } 689046a37bdSSonny Rao 690046a37bdSSonny Rao int vscnprintf(char *buf, size_t size, const char *fmt, va_list args) 691046a37bdSSonny Rao { 692046a37bdSSonny Rao int i; 693046a37bdSSonny Rao 694046a37bdSSonny Rao i = vsnprintf(buf, size, fmt, args); 695046a37bdSSonny Rao 696046a37bdSSonny Rao if (likely(i < size)) 697046a37bdSSonny Rao return i; 698046a37bdSSonny Rao if (size != 0) 699046a37bdSSonny Rao return size - 1; 700046a37bdSSonny Rao return 0; 701046a37bdSSonny Rao } 702046a37bdSSonny Rao 703046a37bdSSonny Rao int snprintf(char *buf, size_t size, const char *fmt, ...) 704046a37bdSSonny Rao { 705046a37bdSSonny Rao va_list args; 706046a37bdSSonny Rao int i; 707046a37bdSSonny Rao 708046a37bdSSonny Rao va_start(args, fmt); 709046a37bdSSonny Rao i = vsnprintf(buf, size, fmt, args); 710046a37bdSSonny Rao va_end(args); 711046a37bdSSonny Rao 712046a37bdSSonny Rao return i; 713046a37bdSSonny Rao } 714046a37bdSSonny Rao 715046a37bdSSonny Rao int scnprintf(char *buf, size_t size, const char *fmt, ...) 716046a37bdSSonny Rao { 717046a37bdSSonny Rao va_list args; 718046a37bdSSonny Rao int i; 719046a37bdSSonny Rao 720046a37bdSSonny Rao va_start(args, fmt); 721046a37bdSSonny Rao i = vscnprintf(buf, size, fmt, args); 722046a37bdSSonny Rao va_end(args); 723046a37bdSSonny Rao 724046a37bdSSonny Rao return i; 725046a37bdSSonny Rao } 726046a37bdSSonny Rao 727046a37bdSSonny Rao /** 728046a37bdSSonny Rao * Format a string and place it in a buffer (va_list version) 729046a37bdSSonny Rao * 730046a37bdSSonny Rao * @param buf The buffer to place the result into 731046a37bdSSonny Rao * @param fmt The format string to use 732046a37bdSSonny Rao * @param args Arguments for the format string 733046a37bdSSonny Rao * 734046a37bdSSonny Rao * The function returns the number of characters written 735046a37bdSSonny Rao * into @buf. Use vsnprintf() or vscnprintf() in order to avoid 736046a37bdSSonny Rao * buffer overflows. 737046a37bdSSonny Rao * 738046a37bdSSonny Rao * If you're not already dealing with a va_list consider using sprintf(). 739046a37bdSSonny Rao */ 740046a37bdSSonny Rao int vsprintf(char *buf, const char *fmt, va_list args) 741046a37bdSSonny Rao { 742046a37bdSSonny Rao return vsnprintf_internal(buf, INT_MAX, fmt, args); 743046a37bdSSonny Rao } 744046a37bdSSonny Rao 74578acc472SPeter Tyser int sprintf(char *buf, const char *fmt, ...) 74678acc472SPeter Tyser { 74778acc472SPeter Tyser va_list args; 74878acc472SPeter Tyser int i; 74978acc472SPeter Tyser 75078acc472SPeter Tyser va_start(args, fmt); 75178acc472SPeter Tyser i = vsprintf(buf, fmt, args); 75278acc472SPeter Tyser va_end(args); 75378acc472SPeter Tyser return i; 75478acc472SPeter Tyser } 75578acc472SPeter Tyser 7567d9cde10SStefan Roese int printf(const char *fmt, ...) 7577d9cde10SStefan Roese { 7587d9cde10SStefan Roese va_list args; 7597d9cde10SStefan Roese uint i; 7607d9cde10SStefan Roese char printbuffer[CONFIG_SYS_PBSIZE]; 7617d9cde10SStefan Roese 7627d9cde10SStefan Roese va_start(args, fmt); 7637d9cde10SStefan Roese 7647d9cde10SStefan Roese /* 7657d9cde10SStefan Roese * For this to work, printbuffer must be larger than 7667d9cde10SStefan Roese * anything we ever want to print. 7677d9cde10SStefan Roese */ 7687d9cde10SStefan Roese i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); 7697d9cde10SStefan Roese va_end(args); 7707d9cde10SStefan Roese 7717d9cde10SStefan Roese /* Print the string */ 7727d9cde10SStefan Roese puts(printbuffer); 7737d9cde10SStefan Roese return i; 7747d9cde10SStefan Roese } 7757d9cde10SStefan Roese 7767d9cde10SStefan Roese int vprintf(const char *fmt, va_list args) 7777d9cde10SStefan Roese { 7787d9cde10SStefan Roese uint i; 7797d9cde10SStefan Roese char printbuffer[CONFIG_SYS_PBSIZE]; 7807d9cde10SStefan Roese 7817d9cde10SStefan Roese /* 7827d9cde10SStefan Roese * For this to work, printbuffer must be larger than 7837d9cde10SStefan Roese * anything we ever want to print. 7847d9cde10SStefan Roese */ 7857d9cde10SStefan Roese i = vscnprintf(printbuffer, sizeof(printbuffer), fmt, args); 7867d9cde10SStefan Roese 7877d9cde10SStefan Roese /* Print the string */ 7887d9cde10SStefan Roese puts(printbuffer); 7897d9cde10SStefan Roese return i; 7907d9cde10SStefan Roese } 7917d9cde10SStefan Roese 79266312374SSimon Glass 79321726a7aSSimon Glass void __assert_fail(const char *assertion, const char *file, unsigned line, 79421726a7aSSimon Glass const char *function) 79521726a7aSSimon Glass { 79621726a7aSSimon Glass /* This will not return */ 79721726a7aSSimon Glass panic("%s:%u: %s: Assertion `%s' failed.", file, line, function, 79821726a7aSSimon Glass assertion); 79921726a7aSSimon Glass } 8003cce8a54SSimon Glass 8013cce8a54SSimon Glass char *simple_itoa(ulong i) 8023cce8a54SSimon Glass { 8033cce8a54SSimon Glass /* 21 digits plus null terminator, good for 64-bit or smaller ints */ 8043cce8a54SSimon Glass static char local[22]; 8053cce8a54SSimon Glass char *p = &local[21]; 8063cce8a54SSimon Glass 8073cce8a54SSimon Glass *p-- = '\0'; 8083cce8a54SSimon Glass do { 8093cce8a54SSimon Glass *p-- = '0' + i % 10; 8103cce8a54SSimon Glass i /= 10; 8113cce8a54SSimon Glass } while (i > 0); 8123cce8a54SSimon Glass return p + 1; 8133cce8a54SSimon Glass } 814b8bcaa3aSSimon Glass 815b8bcaa3aSSimon Glass /* We don't seem to have %'d in U-Boot */ 816b8bcaa3aSSimon Glass void print_grouped_ull(unsigned long long int_val, int digits) 817b8bcaa3aSSimon Glass { 818b8bcaa3aSSimon Glass char str[21], *s; 819b8bcaa3aSSimon Glass int grab = 3; 820b8bcaa3aSSimon Glass 821b8bcaa3aSSimon Glass digits = (digits + 2) / 3; 822b8bcaa3aSSimon Glass sprintf(str, "%*llu", digits * 3, int_val); 823b8bcaa3aSSimon Glass for (s = str; *s; s += grab) { 824b8bcaa3aSSimon Glass if (s != str) 825b8bcaa3aSSimon Glass putc(s[-1] != ' ' ? ',' : ' '); 826b8bcaa3aSSimon Glass printf("%.*s", grab, s); 827b8bcaa3aSSimon Glass grab = 3; 828b8bcaa3aSSimon Glass } 829b8bcaa3aSSimon Glass } 83009c32807SHeiko Schocher 83109c32807SHeiko Schocher bool str2off(const char *p, loff_t *num) 83209c32807SHeiko Schocher { 83309c32807SHeiko Schocher char *endptr; 83409c32807SHeiko Schocher 83509c32807SHeiko Schocher *num = simple_strtoull(p, &endptr, 16); 83609c32807SHeiko Schocher return *p != '\0' && *endptr == '\0'; 83709c32807SHeiko Schocher } 83809c32807SHeiko Schocher 83909c32807SHeiko Schocher bool str2long(const char *p, ulong *num) 84009c32807SHeiko Schocher { 84109c32807SHeiko Schocher char *endptr; 84209c32807SHeiko Schocher 84309c32807SHeiko Schocher *num = simple_strtoul(p, &endptr, 16); 84409c32807SHeiko Schocher return *p != '\0' && *endptr == '\0'; 84509c32807SHeiko Schocher } 846