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
13*c0891ac1SAlexey Dobriyan #include <linux/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
skip_atoi(const char ** s)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
put_dec_full4(char * end,unsigned int r)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
put_dec_helper4(char * end,unsigned int x)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
put_dec(char * end,unsigned long long n)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
number(char * end,unsigned long long num,int base,char locase)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();
1385c4c30f4STian Tao }
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 */
150d850a2ffSArvind Sankar #define WIDE 128 /* UTF-16 string */
1512c7d1e30SArvind Sankar
1523b835095SArvind Sankar static
get_flags(const char ** fmt)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
get_int(const char ** fmt,va_list * ap)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
get_number(int sign,int qualifier,va_list * ap)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
get_sign(long long * num,int flags)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
242d850a2ffSArvind Sankar static
utf16s_utf8nlen(const u16 * s16,size_t maxlen)243d850a2ffSArvind Sankar size_t utf16s_utf8nlen(const u16 *s16, size_t maxlen)
244d850a2ffSArvind Sankar {
245d850a2ffSArvind Sankar size_t len, clen;
246d850a2ffSArvind Sankar
247d850a2ffSArvind Sankar for (len = 0; len < maxlen && *s16; len += clen) {
248d850a2ffSArvind Sankar u16 c0 = *s16++;
249d850a2ffSArvind Sankar
250d850a2ffSArvind Sankar /* First, get the length for a BMP character */
251d850a2ffSArvind Sankar clen = 1 + (c0 >= 0x80) + (c0 >= 0x800);
252d850a2ffSArvind Sankar if (len + clen > maxlen)
253d850a2ffSArvind Sankar break;
254d850a2ffSArvind Sankar /*
255d850a2ffSArvind Sankar * If this is a high surrogate, and we're already at maxlen, we
256d850a2ffSArvind Sankar * can't include the character if it's a valid surrogate pair.
257d850a2ffSArvind Sankar * Avoid accessing one extra word just to check if it's valid
258d850a2ffSArvind Sankar * or not.
259d850a2ffSArvind Sankar */
260d850a2ffSArvind Sankar if ((c0 & 0xfc00) == 0xd800) {
261d850a2ffSArvind Sankar if (len + clen == maxlen)
262d850a2ffSArvind Sankar break;
263d850a2ffSArvind Sankar if ((*s16 & 0xfc00) == 0xdc00) {
264d850a2ffSArvind Sankar ++s16;
265d850a2ffSArvind Sankar ++clen;
266d850a2ffSArvind Sankar }
267d850a2ffSArvind Sankar }
268d850a2ffSArvind Sankar }
269d850a2ffSArvind Sankar
270d850a2ffSArvind Sankar return len;
271d850a2ffSArvind Sankar }
272d850a2ffSArvind Sankar
273d850a2ffSArvind Sankar static
utf16_to_utf32(const u16 ** s16)274d850a2ffSArvind Sankar u32 utf16_to_utf32(const u16 **s16)
275d850a2ffSArvind Sankar {
276d850a2ffSArvind Sankar u16 c0, c1;
277d850a2ffSArvind Sankar
278d850a2ffSArvind Sankar c0 = *(*s16)++;
279d850a2ffSArvind Sankar /* not a surrogate */
280d850a2ffSArvind Sankar if ((c0 & 0xf800) != 0xd800)
281d850a2ffSArvind Sankar return c0;
282d850a2ffSArvind Sankar /* invalid: low surrogate instead of high */
283d850a2ffSArvind Sankar if (c0 & 0x0400)
284d850a2ffSArvind Sankar return 0xfffd;
285d850a2ffSArvind Sankar c1 = **s16;
286d850a2ffSArvind Sankar /* invalid: missing low surrogate */
287d850a2ffSArvind Sankar if ((c1 & 0xfc00) != 0xdc00)
288d850a2ffSArvind Sankar return 0xfffd;
289d850a2ffSArvind Sankar /* valid surrogate pair */
290d850a2ffSArvind Sankar ++(*s16);
291d850a2ffSArvind Sankar return (0x10000 - (0xd800 << 10) - 0xdc00) + (c0 << 10) + c1;
292d850a2ffSArvind Sankar }
293d850a2ffSArvind 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
vsnprintf(char * buf,size_t size,const char * fmt,va_list ap)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;
382d850a2ffSArvind Sankar if (qualifier == 'l') {
383d850a2ffSArvind Sankar ((u16 *)tmp)[0] = (u16)va_arg(args, unsigned int);
384d850a2ffSArvind Sankar ((u16 *)tmp)[1] = L'\0';
385d850a2ffSArvind Sankar precision = INT_MAX;
386d850a2ffSArvind Sankar goto wstring;
387d850a2ffSArvind Sankar } else {
388d850a2ffSArvind Sankar tmp[0] = (unsigned char)va_arg(args, int);
3896c4bcd8aSArvind Sankar precision = len = 1;
390d850a2ffSArvind 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;
397d850a2ffSArvind Sankar s = va_arg(args, void *);
398fb031937SArvind Sankar if (!s)
399fb031937SArvind Sankar s = precision < 6 ? "" : "(null)";
400d850a2ffSArvind Sankar else if (qualifier == 'l') {
401d850a2ffSArvind Sankar wstring:
402d850a2ffSArvind Sankar flags |= WIDE;
403d850a2ffSArvind Sankar precision = len = utf16s_utf8nlen((const u16 *)s, precision);
404d850a2ffSArvind Sankar goto output;
405d850a2ffSArvind 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 */
505d850a2ffSArvind Sankar if (flags & WIDE) {
506d850a2ffSArvind Sankar const u16 *ws = (const u16 *)s;
507d850a2ffSArvind Sankar
508d850a2ffSArvind Sankar while (len-- > 0) {
509d850a2ffSArvind Sankar u32 c32 = utf16_to_utf32(&ws);
510d850a2ffSArvind Sankar u8 *s8;
511d850a2ffSArvind Sankar size_t clen;
512d850a2ffSArvind Sankar
513d850a2ffSArvind Sankar if (c32 < 0x80) {
514d850a2ffSArvind Sankar PUTC(c32);
515d850a2ffSArvind Sankar continue;
516d850a2ffSArvind Sankar }
517d850a2ffSArvind Sankar
518d850a2ffSArvind Sankar /* Number of trailing octets */
519d850a2ffSArvind Sankar clen = 1 + (c32 >= 0x800) + (c32 >= 0x10000);
520d850a2ffSArvind Sankar
521d850a2ffSArvind Sankar len -= clen;
522d850a2ffSArvind Sankar s8 = (u8 *)&buf[pos];
523d850a2ffSArvind Sankar
524d850a2ffSArvind Sankar /* Avoid writing partial character */
525d850a2ffSArvind Sankar PUTC('\0');
526d850a2ffSArvind Sankar pos += clen;
527d850a2ffSArvind Sankar if (pos >= size)
528d850a2ffSArvind Sankar continue;
529d850a2ffSArvind Sankar
530d850a2ffSArvind Sankar /* Set high bits of leading octet */
531d850a2ffSArvind Sankar *s8 = (0xf00 >> 1) >> clen;
532d850a2ffSArvind Sankar /* Write trailing octets in reverse order */
533d850a2ffSArvind Sankar for (s8 += clen; clen; --clen, c32 >>= 6)
534d850a2ffSArvind Sankar *s8-- = 0x80 | (c32 & 0x3f);
535d850a2ffSArvind Sankar /* Set low bits of leading octet */
536d850a2ffSArvind Sankar *s8 |= c32;
537d850a2ffSArvind Sankar }
538d850a2ffSArvind Sankar } else {
5396c4bcd8aSArvind Sankar while (len-- > 0)
5408fb331e1SArvind Sankar PUTC(*s++);
541d850a2ffSArvind 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
snprintf(char * buf,size_t size,const char * fmt,...)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