12c7d1e30SArvind Sankar // SPDX-License-Identifier: GPL-2.0-only 22c7d1e30SArvind Sankar /* -*- linux-c -*- ------------------------------------------------------- * 32c7d1e30SArvind Sankar * 42c7d1e30SArvind Sankar * Copyright (C) 1991, 1992 Linus Torvalds 52c7d1e30SArvind Sankar * Copyright 2007 rPath, Inc. - All Rights Reserved 62c7d1e30SArvind Sankar * 72c7d1e30SArvind Sankar * ----------------------------------------------------------------------- */ 82c7d1e30SArvind Sankar 92c7d1e30SArvind Sankar /* 10ce5e3f90SArvind Sankar * Oh, it's a waste of space, but oh-so-yummy for debugging. 112c7d1e30SArvind Sankar */ 122c7d1e30SArvind Sankar 132c7d1e30SArvind Sankar #include <stdarg.h> 142c7d1e30SArvind Sankar 152c7d1e30SArvind Sankar #include <linux/compiler.h> 162c7d1e30SArvind Sankar #include <linux/ctype.h> 176c4bcd8aSArvind Sankar #include <linux/kernel.h> 18fb031937SArvind Sankar #include <linux/limits.h> 192c7d1e30SArvind Sankar #include <linux/string.h> 208fb331e1SArvind Sankar #include <linux/types.h> 212c7d1e30SArvind Sankar 226c4bcd8aSArvind Sankar static 236c4bcd8aSArvind Sankar int skip_atoi(const char **s) 242c7d1e30SArvind Sankar { 252c7d1e30SArvind Sankar int i = 0; 262c7d1e30SArvind Sankar 272c7d1e30SArvind Sankar while (isdigit(**s)) 282c7d1e30SArvind Sankar i = i * 10 + *((*s)++) - '0'; 292c7d1e30SArvind Sankar return i; 302c7d1e30SArvind Sankar } 312c7d1e30SArvind Sankar 32ce5e3f90SArvind Sankar /* 33ce5e3f90SArvind Sankar * put_dec_full4 handles numbers in the range 0 <= r < 10000. 34ce5e3f90SArvind Sankar * The multiplier 0xccd is round(2^15/10), and the approximation 35ce5e3f90SArvind Sankar * r/10 == (r * 0xccd) >> 15 is exact for all r < 16389. 36ce5e3f90SArvind Sankar */ 37ce5e3f90SArvind Sankar static 386c4bcd8aSArvind Sankar void put_dec_full4(char *end, unsigned int r) 39ce5e3f90SArvind Sankar { 40ce5e3f90SArvind Sankar int i; 41ce5e3f90SArvind Sankar 42ce5e3f90SArvind Sankar for (i = 0; i < 3; i++) { 43ce5e3f90SArvind Sankar unsigned int q = (r * 0xccd) >> 15; 446c4bcd8aSArvind Sankar *--end = '0' + (r - q * 10); 45ce5e3f90SArvind Sankar r = q; 46ce5e3f90SArvind Sankar } 476c4bcd8aSArvind Sankar *--end = '0' + r; 48ce5e3f90SArvind Sankar } 49ce5e3f90SArvind Sankar 50ce5e3f90SArvind Sankar /* put_dec is copied from lib/vsprintf.c with small modifications */ 51ce5e3f90SArvind Sankar 52ce5e3f90SArvind Sankar /* 53ce5e3f90SArvind Sankar * Call put_dec_full4 on x % 10000, return x / 10000. 54ce5e3f90SArvind Sankar * The approximation x/10000 == (x * 0x346DC5D7) >> 43 55ce5e3f90SArvind Sankar * holds for all x < 1,128,869,999. The largest value this 56ce5e3f90SArvind Sankar * helper will ever be asked to convert is 1,125,520,955. 57ce5e3f90SArvind Sankar * (second call in the put_dec code, assuming n is all-ones). 58ce5e3f90SArvind Sankar */ 59ce5e3f90SArvind Sankar static 606c4bcd8aSArvind Sankar unsigned int put_dec_helper4(char *end, unsigned int x) 61ce5e3f90SArvind Sankar { 62ce5e3f90SArvind Sankar unsigned int q = (x * 0x346DC5D7ULL) >> 43; 63ce5e3f90SArvind Sankar 646c4bcd8aSArvind Sankar put_dec_full4(end, x - q * 10000); 65ce5e3f90SArvind Sankar return q; 66ce5e3f90SArvind Sankar } 67ce5e3f90SArvind Sankar 68ce5e3f90SArvind Sankar /* Based on code by Douglas W. Jones found at 69ce5e3f90SArvind Sankar * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour> 70ce5e3f90SArvind Sankar * (with permission from the author). 71ce5e3f90SArvind Sankar * Performs no 64-bit division and hence should be fast on 32-bit machines. 72ce5e3f90SArvind Sankar */ 73ce5e3f90SArvind Sankar static 746c4bcd8aSArvind Sankar char *put_dec(char *end, unsigned long long n) 75ce5e3f90SArvind Sankar { 76ce5e3f90SArvind Sankar unsigned int d3, d2, d1, q, h; 776c4bcd8aSArvind Sankar char *p = end; 78ce5e3f90SArvind Sankar 79ce5e3f90SArvind Sankar d1 = ((unsigned int)n >> 16); /* implicit "& 0xffff" */ 80ce5e3f90SArvind Sankar h = (n >> 32); 81ce5e3f90SArvind Sankar d2 = (h ) & 0xffff; 82ce5e3f90SArvind Sankar d3 = (h >> 16); /* implicit "& 0xffff" */ 83ce5e3f90SArvind Sankar 84ce5e3f90SArvind Sankar /* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0 85ce5e3f90SArvind Sankar = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */ 86ce5e3f90SArvind Sankar q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((unsigned int)n & 0xffff); 87ce5e3f90SArvind Sankar q = put_dec_helper4(p, q); 886c4bcd8aSArvind Sankar p -= 4; 89ce5e3f90SArvind Sankar 90ce5e3f90SArvind Sankar q += 7671 * d3 + 9496 * d2 + 6 * d1; 91ce5e3f90SArvind Sankar q = put_dec_helper4(p, q); 926c4bcd8aSArvind Sankar p -= 4; 93ce5e3f90SArvind Sankar 94ce5e3f90SArvind Sankar q += 4749 * d3 + 42 * d2; 95ce5e3f90SArvind Sankar q = put_dec_helper4(p, q); 966c4bcd8aSArvind Sankar p -= 4; 97ce5e3f90SArvind Sankar 98ce5e3f90SArvind Sankar q += 281 * d3; 99ce5e3f90SArvind Sankar q = put_dec_helper4(p, q); 1006c4bcd8aSArvind Sankar p -= 4; 101ce5e3f90SArvind Sankar 102ce5e3f90SArvind Sankar put_dec_full4(p, q); 1036c4bcd8aSArvind Sankar p -= 4; 104ce5e3f90SArvind Sankar 105ce5e3f90SArvind Sankar /* strip off the extra 0's we printed */ 1066c4bcd8aSArvind Sankar while (p < end && *p == '0') 1076c4bcd8aSArvind Sankar ++p; 108ce5e3f90SArvind Sankar 1096c4bcd8aSArvind Sankar return p; 1106c4bcd8aSArvind Sankar } 1116c4bcd8aSArvind Sankar 1126c4bcd8aSArvind Sankar static 1136c4bcd8aSArvind Sankar char *number(char *end, unsigned long long num, int base, char locase) 1146c4bcd8aSArvind Sankar { 1156c4bcd8aSArvind Sankar /* 1166c4bcd8aSArvind Sankar * locase = 0 or 0x20. ORing digits or letters with 'locase' 1176c4bcd8aSArvind Sankar * produces same digits or (maybe lowercased) letters 1186c4bcd8aSArvind Sankar */ 1196c4bcd8aSArvind Sankar 1206c4bcd8aSArvind Sankar /* we are called with base 8, 10 or 16, only, thus don't need "G..." */ 1216c4bcd8aSArvind Sankar static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */ 1226c4bcd8aSArvind Sankar 1236c4bcd8aSArvind Sankar switch (base) { 1246c4bcd8aSArvind Sankar case 10: 1256c4bcd8aSArvind Sankar if (num != 0) 1266c4bcd8aSArvind Sankar end = put_dec(end, num); 1276c4bcd8aSArvind Sankar break; 1286c4bcd8aSArvind Sankar case 8: 1296c4bcd8aSArvind Sankar for (; num != 0; num >>= 3) 1306c4bcd8aSArvind Sankar *--end = '0' + (num & 07); 1316c4bcd8aSArvind Sankar break; 1326c4bcd8aSArvind Sankar case 16: 1336c4bcd8aSArvind Sankar for (; num != 0; num >>= 4) 1346c4bcd8aSArvind Sankar *--end = digits[num & 0xf] | locase; 1356c4bcd8aSArvind Sankar break; 1366c4bcd8aSArvind Sankar default: 1376c4bcd8aSArvind Sankar unreachable(); 1386c4bcd8aSArvind Sankar }; 1396c4bcd8aSArvind Sankar 1406c4bcd8aSArvind Sankar return end; 141ce5e3f90SArvind Sankar } 142ce5e3f90SArvind Sankar 1432c7d1e30SArvind Sankar #define ZEROPAD 1 /* pad with zero */ 1442c7d1e30SArvind Sankar #define SIGN 2 /* unsigned/signed long */ 1452c7d1e30SArvind Sankar #define PLUS 4 /* show plus */ 1462c7d1e30SArvind Sankar #define SPACE 8 /* space if plus */ 1472c7d1e30SArvind Sankar #define LEFT 16 /* left justified */ 1482c7d1e30SArvind Sankar #define SMALL 32 /* Must be 32 == 0x20 */ 1492c7d1e30SArvind Sankar #define SPECIAL 64 /* 0x */ 150*d850a2ffSArvind Sankar #define WIDE 128 /* UTF-16 string */ 1512c7d1e30SArvind Sankar 1523b835095SArvind Sankar static 1533b835095SArvind Sankar int get_flags(const char **fmt) 1543b835095SArvind Sankar { 1553b835095SArvind Sankar int flags = 0; 1563b835095SArvind Sankar 1573b835095SArvind Sankar do { 1583b835095SArvind Sankar switch (**fmt) { 1593b835095SArvind Sankar case '-': 1603b835095SArvind Sankar flags |= LEFT; 1613b835095SArvind Sankar break; 1623b835095SArvind Sankar case '+': 1633b835095SArvind Sankar flags |= PLUS; 1643b835095SArvind Sankar break; 1653b835095SArvind Sankar case ' ': 1663b835095SArvind Sankar flags |= SPACE; 1673b835095SArvind Sankar break; 1683b835095SArvind Sankar case '#': 1693b835095SArvind Sankar flags |= SPECIAL; 1703b835095SArvind Sankar break; 1713b835095SArvind Sankar case '0': 1723b835095SArvind Sankar flags |= ZEROPAD; 1733b835095SArvind Sankar break; 1743b835095SArvind Sankar default: 1753b835095SArvind Sankar return flags; 1763b835095SArvind Sankar } 1773b835095SArvind Sankar ++(*fmt); 1783b835095SArvind Sankar } while (1); 1793b835095SArvind Sankar } 1803b835095SArvind Sankar 1813fbcf75bSArvind Sankar static 1823fbcf75bSArvind Sankar int get_int(const char **fmt, va_list *ap) 1833fbcf75bSArvind Sankar { 1843fbcf75bSArvind Sankar if (isdigit(**fmt)) 1853fbcf75bSArvind Sankar return skip_atoi(fmt); 1863fbcf75bSArvind Sankar if (**fmt == '*') { 1873fbcf75bSArvind Sankar ++(*fmt); 1883fbcf75bSArvind Sankar /* it's the next argument */ 1893fbcf75bSArvind Sankar return va_arg(*ap, int); 1903fbcf75bSArvind Sankar } 1913fbcf75bSArvind Sankar return 0; 1923fbcf75bSArvind Sankar } 1933fbcf75bSArvind Sankar 194dec61199SArvind Sankar static 195dec61199SArvind Sankar unsigned long long get_number(int sign, int qualifier, va_list *ap) 196dec61199SArvind Sankar { 197dec61199SArvind Sankar if (sign) { 198dec61199SArvind Sankar switch (qualifier) { 199dec61199SArvind Sankar case 'L': 200dec61199SArvind Sankar return va_arg(*ap, long long); 201dec61199SArvind Sankar case 'l': 202dec61199SArvind Sankar return va_arg(*ap, long); 203dec61199SArvind Sankar case 'h': 204dec61199SArvind Sankar return (short)va_arg(*ap, int); 205dec61199SArvind Sankar case 'H': 206dec61199SArvind Sankar return (signed char)va_arg(*ap, int); 207dec61199SArvind Sankar default: 208dec61199SArvind Sankar return va_arg(*ap, int); 209dec61199SArvind Sankar }; 210dec61199SArvind Sankar } else { 211dec61199SArvind Sankar switch (qualifier) { 212dec61199SArvind Sankar case 'L': 213dec61199SArvind Sankar return va_arg(*ap, unsigned long long); 214dec61199SArvind Sankar case 'l': 215dec61199SArvind Sankar return va_arg(*ap, unsigned long); 216dec61199SArvind Sankar case 'h': 217dec61199SArvind Sankar return (unsigned short)va_arg(*ap, int); 218dec61199SArvind Sankar case 'H': 219dec61199SArvind Sankar return (unsigned char)va_arg(*ap, int); 220dec61199SArvind Sankar default: 221dec61199SArvind Sankar return va_arg(*ap, unsigned int); 222dec61199SArvind Sankar } 223dec61199SArvind Sankar } 224dec61199SArvind Sankar } 225dec61199SArvind Sankar 2266c4bcd8aSArvind Sankar static 2276c4bcd8aSArvind Sankar char get_sign(long long *num, int flags) 2286c4bcd8aSArvind Sankar { 2296c4bcd8aSArvind Sankar if (!(flags & SIGN)) 2306c4bcd8aSArvind Sankar return 0; 2316c4bcd8aSArvind Sankar if (*num < 0) { 2326c4bcd8aSArvind Sankar *num = -(*num); 2336c4bcd8aSArvind Sankar return '-'; 2346c4bcd8aSArvind Sankar } 2356c4bcd8aSArvind Sankar if (flags & PLUS) 2366c4bcd8aSArvind Sankar return '+'; 2376c4bcd8aSArvind Sankar if (flags & SPACE) 2386c4bcd8aSArvind Sankar return ' '; 2396c4bcd8aSArvind Sankar return 0; 2406c4bcd8aSArvind Sankar } 2416c4bcd8aSArvind Sankar 242*d850a2ffSArvind Sankar static 243*d850a2ffSArvind Sankar size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen) 244*d850a2ffSArvind Sankar { 245*d850a2ffSArvind Sankar size_t len, clen; 246*d850a2ffSArvind Sankar 247*d850a2ffSArvind Sankar for (len = 0; len < maxlen && *s16; len += clen) { 248*d850a2ffSArvind Sankar u16 c0 = *s16++; 249*d850a2ffSArvind Sankar 250*d850a2ffSArvind Sankar /* First, get the length for a BMP character */ 251*d850a2ffSArvind Sankar clen = 1 + (c0 >= 0x80) + (c0 >= 0x800); 252*d850a2ffSArvind Sankar if (len + clen > maxlen) 253*d850a2ffSArvind Sankar break; 254*d850a2ffSArvind Sankar /* 255*d850a2ffSArvind Sankar * If this is a high surrogate, and we're already at maxlen, we 256*d850a2ffSArvind Sankar * can't include the character if it's a valid surrogate pair. 257*d850a2ffSArvind Sankar * Avoid accessing one extra word just to check if it's valid 258*d850a2ffSArvind Sankar * or not. 259*d850a2ffSArvind Sankar */ 260*d850a2ffSArvind Sankar if ((c0 & 0xfc00) == 0xd800) { 261*d850a2ffSArvind Sankar if (len + clen == maxlen) 262*d850a2ffSArvind Sankar break; 263*d850a2ffSArvind Sankar if ((*s16 & 0xfc00) == 0xdc00) { 264*d850a2ffSArvind Sankar ++s16; 265*d850a2ffSArvind Sankar ++clen; 266*d850a2ffSArvind Sankar } 267*d850a2ffSArvind Sankar } 268*d850a2ffSArvind Sankar } 269*d850a2ffSArvind Sankar 270*d850a2ffSArvind Sankar return len; 271*d850a2ffSArvind Sankar } 272*d850a2ffSArvind Sankar 273*d850a2ffSArvind Sankar static 274*d850a2ffSArvind Sankar u32 utf16_to_utf32(const u16 **s16) 275*d850a2ffSArvind Sankar { 276*d850a2ffSArvind Sankar u16 c0, c1; 277*d850a2ffSArvind Sankar 278*d850a2ffSArvind Sankar c0 = *(*s16)++; 279*d850a2ffSArvind Sankar /* not a surrogate */ 280*d850a2ffSArvind Sankar if ((c0 & 0xf800) != 0xd800) 281*d850a2ffSArvind Sankar return c0; 282*d850a2ffSArvind Sankar /* invalid: low surrogate instead of high */ 283*d850a2ffSArvind Sankar if (c0 & 0x0400) 284*d850a2ffSArvind Sankar return 0xfffd; 285*d850a2ffSArvind Sankar c1 = **s16; 286*d850a2ffSArvind Sankar /* invalid: missing low surrogate */ 287*d850a2ffSArvind Sankar if ((c1 & 0xfc00) != 0xdc00) 288*d850a2ffSArvind Sankar return 0xfffd; 289*d850a2ffSArvind Sankar /* valid surrogate pair */ 290*d850a2ffSArvind Sankar ++(*s16); 291*d850a2ffSArvind Sankar return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1; 292*d850a2ffSArvind Sankar } 293*d850a2ffSArvind Sankar 2948fb331e1SArvind Sankar #define PUTC(c) \ 2958fb331e1SArvind Sankar do { \ 2968fb331e1SArvind Sankar if (pos < size) \ 2978fb331e1SArvind Sankar buf[pos] = (c); \ 2988fb331e1SArvind Sankar ++pos; \ 2998fb331e1SArvind Sankar } while (0); 3008fb331e1SArvind Sankar 3018fb331e1SArvind Sankar int vsnprintf(char *buf, size_t size, const char *fmt, va_list ap) 3022c7d1e30SArvind Sankar { 3036c4bcd8aSArvind Sankar /* The maximum space required is to print a 64-bit number in octal */ 3046c4bcd8aSArvind Sankar char tmp[(sizeof(unsigned long long) * 8 + 2) / 3]; 3056c4bcd8aSArvind Sankar char *tmp_end = &tmp[ARRAY_SIZE(tmp)]; 3066c4bcd8aSArvind Sankar long long num; 3076c4bcd8aSArvind Sankar int base; 3082c7d1e30SArvind Sankar const char *s; 3098fb331e1SArvind Sankar size_t len, pos; 3106c4bcd8aSArvind Sankar char sign; 3112c7d1e30SArvind Sankar 3122c7d1e30SArvind Sankar int flags; /* flags to number() */ 3132c7d1e30SArvind Sankar 3142c7d1e30SArvind Sankar int field_width; /* width of output field */ 3152c7d1e30SArvind Sankar int precision; /* min. # of digits for integers; max 3162c7d1e30SArvind Sankar number of chars for from string */ 317ce5e3f90SArvind Sankar int qualifier; /* 'h', 'hh', 'l' or 'll' for integer fields */ 3182c7d1e30SArvind Sankar 3193fbcf75bSArvind Sankar va_list args; 3203fbcf75bSArvind Sankar 3213fbcf75bSArvind Sankar /* 3223fbcf75bSArvind Sankar * We want to pass our input va_list to helper functions by reference, 3233fbcf75bSArvind Sankar * but there's an annoying edge case. If va_list was originally passed 3243fbcf75bSArvind Sankar * to us by value, we could just pass &ap down to the helpers. This is 3253fbcf75bSArvind Sankar * the case on, for example, X86_32. 3263fbcf75bSArvind Sankar * However, on X86_64 (and possibly others), va_list is actually a 3273fbcf75bSArvind Sankar * size-1 array containing a structure. Our function parameter ap has 3283fbcf75bSArvind Sankar * decayed from T[1] to T*, and &ap has type T** rather than T(*)[1], 3293fbcf75bSArvind Sankar * which is what will be expected by a function taking a va_list * 3303fbcf75bSArvind Sankar * parameter. 3313fbcf75bSArvind Sankar * One standard way to solve this mess is by creating a copy in a local 3323fbcf75bSArvind Sankar * variable of type va_list and then passing a pointer to that local 3333fbcf75bSArvind Sankar * copy instead, which is what we do here. 3343fbcf75bSArvind Sankar */ 3353fbcf75bSArvind Sankar va_copy(args, ap); 3363fbcf75bSArvind Sankar 3378fb331e1SArvind Sankar for (pos = 0; *fmt; ++fmt) { 3383b835095SArvind Sankar if (*fmt != '%' || *++fmt == '%') { 3398fb331e1SArvind Sankar PUTC(*fmt); 3402c7d1e30SArvind Sankar continue; 3412c7d1e30SArvind Sankar } 3422c7d1e30SArvind Sankar 3432c7d1e30SArvind Sankar /* process flags */ 3443b835095SArvind Sankar flags = get_flags(&fmt); 3452c7d1e30SArvind Sankar 3462c7d1e30SArvind Sankar /* get field width */ 3473fbcf75bSArvind Sankar field_width = get_int(&fmt, &args); 3482c7d1e30SArvind Sankar if (field_width < 0) { 3492c7d1e30SArvind Sankar field_width = -field_width; 3502c7d1e30SArvind Sankar flags |= LEFT; 3512c7d1e30SArvind Sankar } 3522c7d1e30SArvind Sankar 3536c4bcd8aSArvind Sankar if (flags & LEFT) 3546c4bcd8aSArvind Sankar flags &= ~ZEROPAD; 3556c4bcd8aSArvind Sankar 3562c7d1e30SArvind Sankar /* get the precision */ 3572c7d1e30SArvind Sankar precision = -1; 3582c7d1e30SArvind Sankar if (*fmt == '.') { 3592c7d1e30SArvind Sankar ++fmt; 3603fbcf75bSArvind Sankar precision = get_int(&fmt, &args); 36177e48db0SArvind Sankar if (precision >= 0) 36277e48db0SArvind Sankar flags &= ~ZEROPAD; 36377e48db0SArvind Sankar } 3642c7d1e30SArvind Sankar 3652c7d1e30SArvind Sankar /* get the conversion qualifier */ 3662c7d1e30SArvind Sankar qualifier = -1; 36729a28066SArvind Sankar if (*fmt == 'h' || *fmt == 'l') { 3682c7d1e30SArvind Sankar qualifier = *fmt; 3692c7d1e30SArvind Sankar ++fmt; 370ce5e3f90SArvind Sankar if (qualifier == *fmt) { 371ce5e3f90SArvind Sankar qualifier -= 'a'-'A'; 372ce5e3f90SArvind Sankar ++fmt; 373ce5e3f90SArvind Sankar } 3742c7d1e30SArvind Sankar } 3752c7d1e30SArvind Sankar 3766c4bcd8aSArvind Sankar sign = 0; 3776c4bcd8aSArvind Sankar 3782c7d1e30SArvind Sankar switch (*fmt) { 3792c7d1e30SArvind Sankar case 'c': 3806c4bcd8aSArvind Sankar flags &= LEFT; 3816c4bcd8aSArvind Sankar s = tmp; 382*d850a2ffSArvind Sankar if (qualifier == 'l') { 383*d850a2ffSArvind Sankar ((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int); 384*d850a2ffSArvind Sankar ((u16 *)tmp)[1] = L'\0'; 385*d850a2ffSArvind Sankar precision = INT_MAX; 386*d850a2ffSArvind Sankar goto wstring; 387*d850a2ffSArvind Sankar } else { 388*d850a2ffSArvind Sankar tmp[0] = (unsigned char)va_arg(args, int); 3896c4bcd8aSArvind Sankar precision = len = 1; 390*d850a2ffSArvind Sankar } 3916c4bcd8aSArvind Sankar goto output; 3922c7d1e30SArvind Sankar 3932c7d1e30SArvind Sankar case 's': 3946c4bcd8aSArvind Sankar flags &= LEFT; 395fb031937SArvind Sankar if (precision < 0) 396fb031937SArvind Sankar precision = INT_MAX; 397*d850a2ffSArvind Sankar s = va_arg(args, void *); 398fb031937SArvind Sankar if (!s) 399fb031937SArvind Sankar s = precision < 6 ? "" : "(null)"; 400*d850a2ffSArvind Sankar else if (qualifier == 'l') { 401*d850a2ffSArvind Sankar wstring: 402*d850a2ffSArvind Sankar flags |= WIDE; 403*d850a2ffSArvind Sankar precision = len = utf16s_utf8nlen((const u16 *)s, precision); 404*d850a2ffSArvind Sankar goto output; 405*d850a2ffSArvind Sankar } 4066c4bcd8aSArvind Sankar precision = len = strnlen(s, precision); 4076c4bcd8aSArvind Sankar goto output; 4082c7d1e30SArvind Sankar 4092c7d1e30SArvind Sankar /* integer number formats - set up the flags and "break" */ 4102c7d1e30SArvind Sankar case 'o': 4112c7d1e30SArvind Sankar base = 8; 4122c7d1e30SArvind Sankar break; 4132c7d1e30SArvind Sankar 4147c30fd79SArvind Sankar case 'p': 4157c30fd79SArvind Sankar if (precision < 0) 4167c30fd79SArvind Sankar precision = 2 * sizeof(void *); 4177c30fd79SArvind Sankar fallthrough; 4182c7d1e30SArvind Sankar case 'x': 4192c7d1e30SArvind Sankar flags |= SMALL; 4202c7d1e30SArvind Sankar fallthrough; 4212c7d1e30SArvind Sankar case 'X': 4222c7d1e30SArvind Sankar base = 16; 4232c7d1e30SArvind Sankar break; 4242c7d1e30SArvind Sankar 4252c7d1e30SArvind Sankar case 'd': 4262c7d1e30SArvind Sankar case 'i': 4272c7d1e30SArvind Sankar flags |= SIGN; 4282c7d1e30SArvind Sankar fallthrough; 4292c7d1e30SArvind Sankar case 'u': 4306c4bcd8aSArvind Sankar flags &= ~SPECIAL; 4317c30fd79SArvind Sankar base = 10; 4322c7d1e30SArvind Sankar break; 4332c7d1e30SArvind Sankar 4342c7d1e30SArvind Sankar default: 435f97ca2c8SArvind Sankar /* 436f97ca2c8SArvind Sankar * Bail out if the conversion specifier is invalid. 437f97ca2c8SArvind Sankar * There's probably a typo in the format string and the 438f97ca2c8SArvind Sankar * remaining specifiers are unlikely to match up with 439f97ca2c8SArvind Sankar * the arguments. 440f97ca2c8SArvind Sankar */ 441f97ca2c8SArvind Sankar goto fail; 4422c7d1e30SArvind Sankar } 4437c30fd79SArvind Sankar if (*fmt == 'p') { 4447c30fd79SArvind Sankar num = (unsigned long)va_arg(args, void *); 4452c7d1e30SArvind Sankar } else { 446dec61199SArvind Sankar num = get_number(flags & SIGN, qualifier, &args); 447ce5e3f90SArvind Sankar } 4486c4bcd8aSArvind Sankar 4496c4bcd8aSArvind Sankar sign = get_sign(&num, flags); 4506c4bcd8aSArvind Sankar if (sign) 4516c4bcd8aSArvind Sankar --field_width; 4526c4bcd8aSArvind Sankar 4536c4bcd8aSArvind Sankar s = number(tmp_end, num, base, flags & SMALL); 4546c4bcd8aSArvind Sankar len = tmp_end - s; 4556c4bcd8aSArvind Sankar /* default precision is 1 */ 4566c4bcd8aSArvind Sankar if (precision < 0) 4576c4bcd8aSArvind Sankar precision = 1; 4586c4bcd8aSArvind Sankar /* precision is minimum number of digits to print */ 4596c4bcd8aSArvind Sankar if (precision < len) 4606c4bcd8aSArvind Sankar precision = len; 4616c4bcd8aSArvind Sankar if (flags & SPECIAL) { 4626c4bcd8aSArvind Sankar /* 4636c4bcd8aSArvind Sankar * For octal, a leading 0 is printed only if necessary, 4646c4bcd8aSArvind Sankar * i.e. if it's not already there because of the 4656c4bcd8aSArvind Sankar * precision. 4666c4bcd8aSArvind Sankar */ 4676c4bcd8aSArvind Sankar if (base == 8 && precision == len) 4686c4bcd8aSArvind Sankar ++precision; 4696c4bcd8aSArvind Sankar /* 4706c4bcd8aSArvind Sankar * For hexadecimal, the leading 0x is skipped if the 4716c4bcd8aSArvind Sankar * output is empty, i.e. both the number and the 4726c4bcd8aSArvind Sankar * precision are 0. 4736c4bcd8aSArvind Sankar */ 4746c4bcd8aSArvind Sankar if (base == 16 && precision > 0) 4756c4bcd8aSArvind Sankar field_width -= 2; 4766c4bcd8aSArvind Sankar else 4776c4bcd8aSArvind Sankar flags &= ~SPECIAL; 4786c4bcd8aSArvind Sankar } 4796c4bcd8aSArvind Sankar /* 4806c4bcd8aSArvind Sankar * For zero padding, increase the precision to fill the field 4816c4bcd8aSArvind Sankar * width. 4826c4bcd8aSArvind Sankar */ 4836c4bcd8aSArvind Sankar if ((flags & ZEROPAD) && field_width > precision) 4846c4bcd8aSArvind Sankar precision = field_width; 4856c4bcd8aSArvind Sankar 4866c4bcd8aSArvind Sankar output: 4876c4bcd8aSArvind Sankar /* Calculate the padding necessary */ 4886c4bcd8aSArvind Sankar field_width -= precision; 4896c4bcd8aSArvind Sankar /* Leading padding with ' ' */ 4906c4bcd8aSArvind Sankar if (!(flags & LEFT)) 4916c4bcd8aSArvind Sankar while (field_width-- > 0) 4928fb331e1SArvind Sankar PUTC(' '); 4936c4bcd8aSArvind Sankar /* sign */ 4946c4bcd8aSArvind Sankar if (sign) 4958fb331e1SArvind Sankar PUTC(sign); 4966c4bcd8aSArvind Sankar /* 0x/0X for hexadecimal */ 4976c4bcd8aSArvind Sankar if (flags & SPECIAL) { 4988fb331e1SArvind Sankar PUTC('0'); 4998fb331e1SArvind Sankar PUTC( 'X' | (flags & SMALL)); 5006c4bcd8aSArvind Sankar } 5016c4bcd8aSArvind Sankar /* Zero padding and excess precision */ 5026c4bcd8aSArvind Sankar while (precision-- > len) 5038fb331e1SArvind Sankar PUTC('0'); 5046c4bcd8aSArvind Sankar /* Actual output */ 505*d850a2ffSArvind Sankar if (flags & WIDE) { 506*d850a2ffSArvind Sankar const u16 *ws = (const u16 *)s; 507*d850a2ffSArvind Sankar 508*d850a2ffSArvind Sankar while (len-- > 0) { 509*d850a2ffSArvind Sankar u32 c32 = utf16_to_utf32(&ws); 510*d850a2ffSArvind Sankar u8 *s8; 511*d850a2ffSArvind Sankar size_t clen; 512*d850a2ffSArvind Sankar 513*d850a2ffSArvind Sankar if (c32 < 0x80) { 514*d850a2ffSArvind Sankar PUTC(c32); 515*d850a2ffSArvind Sankar continue; 516*d850a2ffSArvind Sankar } 517*d850a2ffSArvind Sankar 518*d850a2ffSArvind Sankar /* Number of trailing octets */ 519*d850a2ffSArvind Sankar clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000); 520*d850a2ffSArvind Sankar 521*d850a2ffSArvind Sankar len -= clen; 522*d850a2ffSArvind Sankar s8 = (u8 *)&buf[pos]; 523*d850a2ffSArvind Sankar 524*d850a2ffSArvind Sankar /* Avoid writing partial character */ 525*d850a2ffSArvind Sankar PUTC('\0'); 526*d850a2ffSArvind Sankar pos += clen; 527*d850a2ffSArvind Sankar if (pos >= size) 528*d850a2ffSArvind Sankar continue; 529*d850a2ffSArvind Sankar 530*d850a2ffSArvind Sankar /* Set high bits of leading octet */ 531*d850a2ffSArvind Sankar *s8 = (0xf00 >> 1) >> clen; 532*d850a2ffSArvind Sankar /* Write trailing octets in reverse order */ 533*d850a2ffSArvind Sankar for (s8 += clen; clen; --clen, c32 >>= 6) 534*d850a2ffSArvind Sankar *s8-- = 0x80 | (c32 & 0x3f); 535*d850a2ffSArvind Sankar /* Set low bits of leading octet */ 536*d850a2ffSArvind Sankar *s8 |= c32; 537*d850a2ffSArvind Sankar } 538*d850a2ffSArvind Sankar } else { 5396c4bcd8aSArvind Sankar while (len-- > 0) 5408fb331e1SArvind Sankar PUTC(*s++); 541*d850a2ffSArvind Sankar } 5426c4bcd8aSArvind Sankar /* Trailing padding with ' ' */ 5436c4bcd8aSArvind Sankar while (field_width-- > 0) 5448fb331e1SArvind Sankar PUTC(' '); 5452c7d1e30SArvind Sankar } 546f97ca2c8SArvind Sankar fail: 5473fbcf75bSArvind Sankar va_end(args); 5483fbcf75bSArvind Sankar 5498fb331e1SArvind Sankar if (size) 5508fb331e1SArvind Sankar buf[min(pos, size-1)] = '\0'; 5518fb331e1SArvind Sankar 5528fb331e1SArvind Sankar return pos; 5532c7d1e30SArvind Sankar } 5542c7d1e30SArvind Sankar 5558fb331e1SArvind Sankar int snprintf(char *buf, size_t size, const char *fmt, ...) 5562c7d1e30SArvind Sankar { 5572c7d1e30SArvind Sankar va_list args; 5582c7d1e30SArvind Sankar int i; 5592c7d1e30SArvind Sankar 5602c7d1e30SArvind Sankar va_start(args, fmt); 5618fb331e1SArvind Sankar i = vsnprintf(buf, size, fmt, args); 5622c7d1e30SArvind Sankar va_end(args); 5632c7d1e30SArvind Sankar return i; 5642c7d1e30SArvind Sankar } 565