1f412fd2aSLoGin
2f412fd2aSLoGin #include <dragonstub/dragonstub.h>
3f412fd2aSLoGin #include <dragonstub/linux/stdarg.h>
4f412fd2aSLoGin #include <dragonstub/bug.h>
5f412fd2aSLoGin #include <dragonstub/minmax.h>
6f412fd2aSLoGin #include <dragonstub/limits.h>
7f412fd2aSLoGin #include <dragonstub/compiler_types.h>
8f412fd2aSLoGin #include <dragonstub/linux/err.h>
9f412fd2aSLoGin #include <dragonstub/linux/byteorder.h>
10f412fd2aSLoGin #include <dragonstub/linux/div64.h>
11f412fd2aSLoGin
12f412fd2aSLoGin struct printf_spec {
13f412fd2aSLoGin unsigned int type : 8; /* format_type enum */
14f412fd2aSLoGin signed int field_width : 24; /* width of output field */
15f412fd2aSLoGin unsigned int flags : 8; /* flags to number() */
16f412fd2aSLoGin unsigned int base : 8; /* number base, 8, 10 or 16 only */
17f412fd2aSLoGin signed int precision : 16; /* # of digits/chars */
18f412fd2aSLoGin } __packed;
19f412fd2aSLoGin
20f412fd2aSLoGin #define SIGN 1 /* unsigned/signed, must be 1 */
21f412fd2aSLoGin #define LEFT 2 /* left justified */
22f412fd2aSLoGin #define PLUS 4 /* show plus */
23f412fd2aSLoGin #define SPACE 8 /* space if plus */
24f412fd2aSLoGin #define ZEROPAD 16 /* pad with zero, must be 16 == '0' - ' ' */
25f412fd2aSLoGin #define SMALL 32 /* use lowercase in hex (must be 32 == 0x20) */
26f412fd2aSLoGin #define SPECIAL 64 /* prefix hex with "0x", octal with "0" */
27f412fd2aSLoGin
28f412fd2aSLoGin static_assert(SIGN == 1);
29f412fd2aSLoGin static_assert(ZEROPAD == ('0' - ' '));
30f412fd2aSLoGin static_assert(SMALL == ('a' ^ 'A'));
31f412fd2aSLoGin
32f412fd2aSLoGin enum format_type {
33f412fd2aSLoGin FORMAT_TYPE_NONE, /* Just a string part */
34f412fd2aSLoGin FORMAT_TYPE_WIDTH,
35f412fd2aSLoGin FORMAT_TYPE_PRECISION,
36f412fd2aSLoGin FORMAT_TYPE_CHAR,
37f412fd2aSLoGin FORMAT_TYPE_STR,
38f412fd2aSLoGin FORMAT_TYPE_PTR,
39f412fd2aSLoGin FORMAT_TYPE_PERCENT_CHAR,
40f412fd2aSLoGin FORMAT_TYPE_INVALID,
41f412fd2aSLoGin FORMAT_TYPE_LONG_LONG,
42f412fd2aSLoGin FORMAT_TYPE_ULONG,
43f412fd2aSLoGin FORMAT_TYPE_LONG,
44f412fd2aSLoGin FORMAT_TYPE_UBYTE,
45f412fd2aSLoGin FORMAT_TYPE_BYTE,
46f412fd2aSLoGin FORMAT_TYPE_USHORT,
47f412fd2aSLoGin FORMAT_TYPE_SHORT,
48f412fd2aSLoGin FORMAT_TYPE_UINT,
49f412fd2aSLoGin FORMAT_TYPE_INT,
50f412fd2aSLoGin FORMAT_TYPE_SIZE_T,
51f412fd2aSLoGin FORMAT_TYPE_PTRDIFF
52f412fd2aSLoGin };
53f412fd2aSLoGin
skip_atoi(const char ** s)54f412fd2aSLoGin static noinline_for_stack int skip_atoi(const char **s)
55f412fd2aSLoGin {
56f412fd2aSLoGin int i = 0;
57f412fd2aSLoGin
58f412fd2aSLoGin do {
59f412fd2aSLoGin i = i * 10 + *((*s)++) - '0';
60f412fd2aSLoGin } while (isdigit(**s));
61f412fd2aSLoGin
62f412fd2aSLoGin return i;
63f412fd2aSLoGin }
64f412fd2aSLoGin
65f412fd2aSLoGin /*
66f412fd2aSLoGin * Decimal conversion is by far the most typical, and is used for
67f412fd2aSLoGin * /proc and /sys data. This directly impacts e.g. top performance
68f412fd2aSLoGin * with many processes running. We optimize it for speed by emitting
69f412fd2aSLoGin * two characters at a time, using a 200 byte lookup table. This
70f412fd2aSLoGin * roughly halves the number of multiplications compared to computing
71f412fd2aSLoGin * the digits one at a time. Implementation strongly inspired by the
72f412fd2aSLoGin * previous version, which in turn used ideas described at
73f412fd2aSLoGin * <http://www.cs.uiowa.edu/~jones/bcd/divide.html> (with permission
74f412fd2aSLoGin * from the author, Douglas W. Jones).
75f412fd2aSLoGin *
76f412fd2aSLoGin * It turns out there is precisely one 26 bit fixed-point
77f412fd2aSLoGin * approximation a of 64/100 for which x/100 == (x * (u64)a) >> 32
78f412fd2aSLoGin * holds for all x in [0, 10^8-1], namely a = 0x28f5c29. The actual
79f412fd2aSLoGin * range happens to be somewhat larger (x <= 1073741898), but that's
80f412fd2aSLoGin * irrelevant for our purpose.
81f412fd2aSLoGin *
82f412fd2aSLoGin * For dividing a number in the range [10^4, 10^6-1] by 100, we still
83f412fd2aSLoGin * need a 32x32->64 bit multiply, so we simply use the same constant.
84f412fd2aSLoGin *
85f412fd2aSLoGin * For dividing a number in the range [100, 10^4-1] by 100, there are
86f412fd2aSLoGin * several options. The simplest is (x * 0x147b) >> 19, which is valid
87f412fd2aSLoGin * for all x <= 43698.
88f412fd2aSLoGin */
89f412fd2aSLoGin
90f412fd2aSLoGin static const u16 decpair[100] = {
91f412fd2aSLoGin #define _(x) (__force u16) cpu_to_le16(((x % 10) | ((x / 10) << 8)) + 0x3030)
92f412fd2aSLoGin _(0), _(1), _(2), _(3), _(4), _(5), _(6), _(7), _(8), _(9),
93f412fd2aSLoGin _(10), _(11), _(12), _(13), _(14), _(15), _(16), _(17), _(18), _(19),
94f412fd2aSLoGin _(20), _(21), _(22), _(23), _(24), _(25), _(26), _(27), _(28), _(29),
95f412fd2aSLoGin _(30), _(31), _(32), _(33), _(34), _(35), _(36), _(37), _(38), _(39),
96f412fd2aSLoGin _(40), _(41), _(42), _(43), _(44), _(45), _(46), _(47), _(48), _(49),
97f412fd2aSLoGin _(50), _(51), _(52), _(53), _(54), _(55), _(56), _(57), _(58), _(59),
98f412fd2aSLoGin _(60), _(61), _(62), _(63), _(64), _(65), _(66), _(67), _(68), _(69),
99f412fd2aSLoGin _(70), _(71), _(72), _(73), _(74), _(75), _(76), _(77), _(78), _(79),
100f412fd2aSLoGin _(80), _(81), _(82), _(83), _(84), _(85), _(86), _(87), _(88), _(89),
101f412fd2aSLoGin _(90), _(91), _(92), _(93), _(94), _(95), _(96), _(97), _(98), _(99),
102f412fd2aSLoGin #undef _
103f412fd2aSLoGin };
104f412fd2aSLoGin
105f412fd2aSLoGin /*
106f412fd2aSLoGin * This will print a single '0' even if r == 0, since we would
107f412fd2aSLoGin * immediately jump to out_r where two 0s would be written but only
108f412fd2aSLoGin * one of them accounted for in buf. This is needed by ip4_string
109f412fd2aSLoGin * below. All other callers pass a non-zero value of r.
110f412fd2aSLoGin */
put_dec_trunc8(char * buf,unsigned r)111f412fd2aSLoGin static noinline_for_stack char *put_dec_trunc8(char *buf, unsigned r)
112f412fd2aSLoGin {
113f412fd2aSLoGin unsigned q;
114f412fd2aSLoGin
115f412fd2aSLoGin /* 1 <= r < 10^8 */
116f412fd2aSLoGin if (r < 100)
117f412fd2aSLoGin goto out_r;
118f412fd2aSLoGin
119f412fd2aSLoGin /* 100 <= r < 10^8 */
120f412fd2aSLoGin q = (r * (u64)0x28f5c29) >> 32;
121f412fd2aSLoGin *((u16 *)buf) = decpair[r - 100 * q];
122f412fd2aSLoGin buf += 2;
123f412fd2aSLoGin
124f412fd2aSLoGin /* 1 <= q < 10^6 */
125f412fd2aSLoGin if (q < 100)
126f412fd2aSLoGin goto out_q;
127f412fd2aSLoGin
128f412fd2aSLoGin /* 100 <= q < 10^6 */
129f412fd2aSLoGin r = (q * (u64)0x28f5c29) >> 32;
130f412fd2aSLoGin *((u16 *)buf) = decpair[q - 100 * r];
131f412fd2aSLoGin buf += 2;
132f412fd2aSLoGin
133f412fd2aSLoGin /* 1 <= r < 10^4 */
134f412fd2aSLoGin if (r < 100)
135f412fd2aSLoGin goto out_r;
136f412fd2aSLoGin
137f412fd2aSLoGin /* 100 <= r < 10^4 */
138f412fd2aSLoGin q = (r * 0x147b) >> 19;
139f412fd2aSLoGin *((u16 *)buf) = decpair[r - 100 * q];
140f412fd2aSLoGin buf += 2;
141f412fd2aSLoGin out_q:
142f412fd2aSLoGin /* 1 <= q < 100 */
143f412fd2aSLoGin r = q;
144f412fd2aSLoGin out_r:
145f412fd2aSLoGin /* 1 <= r < 100 */
146f412fd2aSLoGin *((u16 *)buf) = decpair[r];
147f412fd2aSLoGin buf += r < 10 ? 1 : 2;
148f412fd2aSLoGin return buf;
149f412fd2aSLoGin }
150f412fd2aSLoGin
151f412fd2aSLoGin #if BITS_PER_LONG == 64 && BITS_PER_LONG_LONG == 64
put_dec_full8(char * buf,unsigned r)152f412fd2aSLoGin static noinline_for_stack char *put_dec_full8(char *buf, unsigned r)
153f412fd2aSLoGin {
154f412fd2aSLoGin unsigned q;
155f412fd2aSLoGin
156f412fd2aSLoGin /* 0 <= r < 10^8 */
157f412fd2aSLoGin q = (r * (u64)0x28f5c29) >> 32;
158f412fd2aSLoGin *((u16 *)buf) = decpair[r - 100 * q];
159f412fd2aSLoGin buf += 2;
160f412fd2aSLoGin
161f412fd2aSLoGin /* 0 <= q < 10^6 */
162f412fd2aSLoGin r = (q * (u64)0x28f5c29) >> 32;
163f412fd2aSLoGin *((u16 *)buf) = decpair[q - 100 * r];
164f412fd2aSLoGin buf += 2;
165f412fd2aSLoGin
166f412fd2aSLoGin /* 0 <= r < 10^4 */
167f412fd2aSLoGin q = (r * 0x147b) >> 19;
168f412fd2aSLoGin *((u16 *)buf) = decpair[r - 100 * q];
169f412fd2aSLoGin buf += 2;
170f412fd2aSLoGin
171f412fd2aSLoGin /* 0 <= q < 100 */
172f412fd2aSLoGin *((u16 *)buf) = decpair[q];
173f412fd2aSLoGin buf += 2;
174f412fd2aSLoGin return buf;
175f412fd2aSLoGin }
176f412fd2aSLoGin
put_dec(char * buf,unsigned long long n)177f412fd2aSLoGin static noinline_for_stack char *put_dec(char *buf, unsigned long long n)
178f412fd2aSLoGin {
179f412fd2aSLoGin if (n >= 100 * 1000 * 1000)
180f412fd2aSLoGin buf = put_dec_full8(buf, do_div(n, 100 * 1000 * 1000));
181f412fd2aSLoGin /* 1 <= n <= 1.6e11 */
182f412fd2aSLoGin if (n >= 100 * 1000 * 1000)
183f412fd2aSLoGin buf = put_dec_full8(buf, do_div(n, 100 * 1000 * 1000));
184f412fd2aSLoGin /* 1 <= n < 1e8 */
185f412fd2aSLoGin return put_dec_trunc8(buf, n);
186f412fd2aSLoGin }
187f412fd2aSLoGin
188f412fd2aSLoGin #elif BITS_PER_LONG == 32 && BITS_PER_LONG_LONG == 64
189f412fd2aSLoGin
put_dec_full4(char * buf,unsigned r)190*78b790faSLoGin static void put_dec_full4(char *buf, unsigned r)
191f412fd2aSLoGin {
192f412fd2aSLoGin unsigned q;
193f412fd2aSLoGin
194f412fd2aSLoGin /* 0 <= r < 10^4 */
195f412fd2aSLoGin q = (r * 0x147b) >> 19;
196f412fd2aSLoGin *((u16 *)buf) = decpair[r - 100 * q];
197f412fd2aSLoGin buf += 2;
198f412fd2aSLoGin /* 0 <= q < 100 */
199f412fd2aSLoGin *((u16 *)buf) = decpair[q];
200f412fd2aSLoGin }
201f412fd2aSLoGin
202f412fd2aSLoGin /*
203f412fd2aSLoGin * Call put_dec_full4 on x % 10000, return x / 10000.
204f412fd2aSLoGin * The approximation x/10000 == (x * 0x346DC5D7) >> 43
205f412fd2aSLoGin * holds for all x < 1,128,869,999. The largest value this
206f412fd2aSLoGin * helper will ever be asked to convert is 1,125,520,955.
207f412fd2aSLoGin * (second call in the put_dec code, assuming n is all-ones).
208f412fd2aSLoGin */
put_dec_helper4(char * buf,unsigned x)209*78b790faSLoGin static noinline_for_stack unsigned put_dec_helper4(char *buf, unsigned x)
210f412fd2aSLoGin {
211f412fd2aSLoGin uint32_t q = (x * (uint64_t)0x346DC5D7) >> 43;
212f412fd2aSLoGin
213f412fd2aSLoGin put_dec_full4(buf, x - q * 10000);
214f412fd2aSLoGin return q;
215f412fd2aSLoGin }
216f412fd2aSLoGin
217f412fd2aSLoGin /* Based on code by Douglas W. Jones found at
218f412fd2aSLoGin * <http://www.cs.uiowa.edu/~jones/bcd/decimal.html#sixtyfour>
219f412fd2aSLoGin * (with permission from the author).
220f412fd2aSLoGin * Performs no 64-bit division and hence should be fast on 32-bit machines.
221f412fd2aSLoGin */
put_dec(char * buf,unsigned long long n)222*78b790faSLoGin static char *put_dec(char *buf, unsigned long long n)
223f412fd2aSLoGin {
224f412fd2aSLoGin uint32_t d3, d2, d1, q, h;
225f412fd2aSLoGin
226f412fd2aSLoGin if (n < 100 * 1000 * 1000)
227f412fd2aSLoGin return put_dec_trunc8(buf, n);
228f412fd2aSLoGin
229f412fd2aSLoGin d1 = ((uint32_t)n >> 16); /* implicit "& 0xffff" */
230f412fd2aSLoGin h = (n >> 32);
231f412fd2aSLoGin d2 = (h)&0xffff;
232f412fd2aSLoGin d3 = (h >> 16); /* implicit "& 0xffff" */
233f412fd2aSLoGin
234f412fd2aSLoGin /* n = 2^48 d3 + 2^32 d2 + 2^16 d1 + d0
235f412fd2aSLoGin = 281_4749_7671_0656 d3 + 42_9496_7296 d2 + 6_5536 d1 + d0 */
236f412fd2aSLoGin q = 656 * d3 + 7296 * d2 + 5536 * d1 + ((uint32_t)n & 0xffff);
237f412fd2aSLoGin q = put_dec_helper4(buf, q);
238f412fd2aSLoGin
239f412fd2aSLoGin q += 7671 * d3 + 9496 * d2 + 6 * d1;
240f412fd2aSLoGin q = put_dec_helper4(buf + 4, q);
241f412fd2aSLoGin
242f412fd2aSLoGin q += 4749 * d3 + 42 * d2;
243f412fd2aSLoGin q = put_dec_helper4(buf + 8, q);
244f412fd2aSLoGin
245f412fd2aSLoGin q += 281 * d3;
246f412fd2aSLoGin buf += 12;
247f412fd2aSLoGin if (q)
248f412fd2aSLoGin buf = put_dec_trunc8(buf, q);
249*78b790faSLoGin else
250*78b790faSLoGin while (buf[-1] == '0')
251f412fd2aSLoGin --buf;
252f412fd2aSLoGin
253f412fd2aSLoGin return buf;
254f412fd2aSLoGin }
255f412fd2aSLoGin
256f412fd2aSLoGin #endif
257f412fd2aSLoGin
258f412fd2aSLoGin #define FIELD_WIDTH_MAX ((1 << 23) - 1)
259f412fd2aSLoGin #define PRECISION_MAX ((1 << 15) - 1)
260f412fd2aSLoGin
set_field_width(struct printf_spec * spec,int width)261f412fd2aSLoGin static void set_field_width(struct printf_spec *spec, int width)
262f412fd2aSLoGin {
263f412fd2aSLoGin spec->field_width = width;
264f412fd2aSLoGin if (WARN_ONCE(spec->field_width != width, "field width %d too large",
265f412fd2aSLoGin width)) {
266f412fd2aSLoGin spec->field_width =
267f412fd2aSLoGin clamp(width, -FIELD_WIDTH_MAX, FIELD_WIDTH_MAX);
268f412fd2aSLoGin }
269f412fd2aSLoGin }
270f412fd2aSLoGin
set_precision(struct printf_spec * spec,int prec)271f412fd2aSLoGin static void set_precision(struct printf_spec *spec, int prec)
272f412fd2aSLoGin {
273f412fd2aSLoGin spec->precision = prec;
274f412fd2aSLoGin if (WARN_ONCE(spec->precision != prec, "precision %d too large",
275f412fd2aSLoGin prec)) {
276f412fd2aSLoGin spec->precision = clamp(prec, 0, PRECISION_MAX);
277f412fd2aSLoGin }
278f412fd2aSLoGin }
279f412fd2aSLoGin
280f412fd2aSLoGin static noinline_for_stack char *
number(char * buf,char * end,unsigned long long num,struct printf_spec spec)281f412fd2aSLoGin number(char *buf, char *end, unsigned long long num, struct printf_spec spec)
282f412fd2aSLoGin {
283f412fd2aSLoGin /* put_dec requires 2-byte alignment of the buffer. */
284f412fd2aSLoGin char tmp[3 * sizeof(num)] __aligned(2);
285f412fd2aSLoGin char sign;
286f412fd2aSLoGin char locase;
287f412fd2aSLoGin int need_pfx = ((spec.flags & SPECIAL) && spec.base != 10);
288f412fd2aSLoGin int i;
289f412fd2aSLoGin bool is_zero = num == 0LL;
290f412fd2aSLoGin int field_width = spec.field_width;
291f412fd2aSLoGin int precision = spec.precision;
292f412fd2aSLoGin
293f412fd2aSLoGin /* locase = 0 or 0x20. ORing digits or letters with 'locase'
294f412fd2aSLoGin * produces same digits or (maybe lowercased) letters */
295f412fd2aSLoGin locase = (spec.flags & SMALL);
296f412fd2aSLoGin if (spec.flags & LEFT)
297f412fd2aSLoGin spec.flags &= ~ZEROPAD;
298f412fd2aSLoGin sign = 0;
299f412fd2aSLoGin if (spec.flags & SIGN) {
300f412fd2aSLoGin if ((signed long long)num < 0) {
301f412fd2aSLoGin sign = '-';
302f412fd2aSLoGin num = -(signed long long)num;
303f412fd2aSLoGin field_width--;
304f412fd2aSLoGin } else if (spec.flags & PLUS) {
305f412fd2aSLoGin sign = '+';
306f412fd2aSLoGin field_width--;
307f412fd2aSLoGin } else if (spec.flags & SPACE) {
308f412fd2aSLoGin sign = ' ';
309f412fd2aSLoGin field_width--;
310f412fd2aSLoGin }
311f412fd2aSLoGin }
312f412fd2aSLoGin if (need_pfx) {
313f412fd2aSLoGin if (spec.base == 16)
314f412fd2aSLoGin field_width -= 2;
315f412fd2aSLoGin else if (!is_zero)
316f412fd2aSLoGin field_width--;
317f412fd2aSLoGin }
318f412fd2aSLoGin
319f412fd2aSLoGin /* generate full string in tmp[], in reverse order */
320f412fd2aSLoGin i = 0;
321f412fd2aSLoGin if (num < spec.base)
322f412fd2aSLoGin tmp[i++] = hex_asc_upper[num] | locase;
323f412fd2aSLoGin else if (spec.base != 10) { /* 8 or 16 */
324f412fd2aSLoGin int mask = spec.base - 1;
325f412fd2aSLoGin int shift = 3;
326f412fd2aSLoGin
327f412fd2aSLoGin if (spec.base == 16)
328f412fd2aSLoGin shift = 4;
329f412fd2aSLoGin do {
330f412fd2aSLoGin tmp[i++] = (hex_asc_upper[((unsigned char)num) & mask] |
331f412fd2aSLoGin locase);
332f412fd2aSLoGin num >>= shift;
333f412fd2aSLoGin } while (num);
334f412fd2aSLoGin } else { /* base 10 */
335f412fd2aSLoGin i = put_dec(tmp, num) - tmp;
336f412fd2aSLoGin }
337f412fd2aSLoGin
338f412fd2aSLoGin /* printing 100 using %2d gives "100", not "00" */
339f412fd2aSLoGin if (i > precision)
340f412fd2aSLoGin precision = i;
341f412fd2aSLoGin /* leading space padding */
342f412fd2aSLoGin field_width -= precision;
343f412fd2aSLoGin if (!(spec.flags & (ZEROPAD | LEFT))) {
344f412fd2aSLoGin while (--field_width >= 0) {
345f412fd2aSLoGin if (buf < end)
346f412fd2aSLoGin *buf = ' ';
347f412fd2aSLoGin ++buf;
348f412fd2aSLoGin }
349f412fd2aSLoGin }
350f412fd2aSLoGin /* sign */
351f412fd2aSLoGin if (sign) {
352f412fd2aSLoGin if (buf < end)
353f412fd2aSLoGin *buf = sign;
354f412fd2aSLoGin ++buf;
355f412fd2aSLoGin }
356f412fd2aSLoGin /* "0x" / "0" prefix */
357f412fd2aSLoGin if (need_pfx) {
358f412fd2aSLoGin if (spec.base == 16 || !is_zero) {
359f412fd2aSLoGin if (buf < end)
360f412fd2aSLoGin *buf = '0';
361f412fd2aSLoGin ++buf;
362f412fd2aSLoGin }
363f412fd2aSLoGin if (spec.base == 16) {
364f412fd2aSLoGin if (buf < end)
365f412fd2aSLoGin *buf = ('X' | locase);
366f412fd2aSLoGin ++buf;
367f412fd2aSLoGin }
368f412fd2aSLoGin }
369f412fd2aSLoGin /* zero or space padding */
370f412fd2aSLoGin if (!(spec.flags & LEFT)) {
371f412fd2aSLoGin char c = ' ' + (spec.flags & ZEROPAD);
372f412fd2aSLoGin
373f412fd2aSLoGin while (--field_width >= 0) {
374f412fd2aSLoGin if (buf < end)
375f412fd2aSLoGin *buf = c;
376f412fd2aSLoGin ++buf;
377f412fd2aSLoGin }
378f412fd2aSLoGin }
379f412fd2aSLoGin /* hmm even more zero padding? */
380f412fd2aSLoGin while (i <= --precision) {
381f412fd2aSLoGin if (buf < end)
382f412fd2aSLoGin *buf = '0';
383f412fd2aSLoGin ++buf;
384f412fd2aSLoGin }
385f412fd2aSLoGin /* actual digits of result */
386f412fd2aSLoGin while (--i >= 0) {
387f412fd2aSLoGin if (buf < end)
388f412fd2aSLoGin *buf = tmp[i];
389f412fd2aSLoGin ++buf;
390f412fd2aSLoGin }
391f412fd2aSLoGin /* trailing space padding */
392f412fd2aSLoGin while (--field_width >= 0) {
393f412fd2aSLoGin if (buf < end)
394f412fd2aSLoGin *buf = ' ';
395f412fd2aSLoGin ++buf;
396f412fd2aSLoGin }
397f412fd2aSLoGin
398f412fd2aSLoGin return buf;
399f412fd2aSLoGin }
400f412fd2aSLoGin
401f412fd2aSLoGin static noinline_for_stack char *
special_hex_number(char * buf,char * end,unsigned long long num,int size)402f412fd2aSLoGin special_hex_number(char *buf, char *end, unsigned long long num, int size)
403f412fd2aSLoGin {
404f412fd2aSLoGin struct printf_spec spec;
405f412fd2aSLoGin
406f412fd2aSLoGin spec.type = FORMAT_TYPE_PTR;
407f412fd2aSLoGin spec.field_width = 2 + 2 * size; /* 0x + hex */
408f412fd2aSLoGin spec.flags = SPECIAL | SMALL | ZEROPAD;
409f412fd2aSLoGin spec.base = 16;
410f412fd2aSLoGin spec.precision = -1;
411f412fd2aSLoGin
412f412fd2aSLoGin return number(buf, end, num, spec);
413f412fd2aSLoGin }
414f412fd2aSLoGin
move_right(char * buf,char * end,unsigned len,unsigned spaces)415f412fd2aSLoGin static void move_right(char *buf, char *end, unsigned len, unsigned spaces)
416f412fd2aSLoGin {
417f412fd2aSLoGin size_t size;
418f412fd2aSLoGin if (buf >= end) /* nowhere to put anything */
419f412fd2aSLoGin return;
420f412fd2aSLoGin size = end - buf;
421f412fd2aSLoGin if (size <= spaces) {
422f412fd2aSLoGin memset(buf, ' ', size);
423f412fd2aSLoGin return;
424f412fd2aSLoGin }
425f412fd2aSLoGin if (len) {
426f412fd2aSLoGin if (len > size - spaces)
427f412fd2aSLoGin len = size - spaces;
428f412fd2aSLoGin memmove(buf + spaces, buf, len);
429f412fd2aSLoGin }
430f412fd2aSLoGin memset(buf, ' ', spaces);
431f412fd2aSLoGin }
432f412fd2aSLoGin
433f412fd2aSLoGin /*
434f412fd2aSLoGin * Handle field width padding for a string.
435f412fd2aSLoGin * @buf: current buffer position
436f412fd2aSLoGin * @n: length of string
437f412fd2aSLoGin * @end: end of output buffer
438f412fd2aSLoGin * @spec: for field width and flags
439f412fd2aSLoGin * Returns: new buffer position after padding.
440f412fd2aSLoGin */
widen_string(char * buf,int n,char * end,struct printf_spec spec)441f412fd2aSLoGin static noinline_for_stack char *widen_string(char *buf, int n, char *end,
442f412fd2aSLoGin struct printf_spec spec)
443f412fd2aSLoGin {
444f412fd2aSLoGin unsigned spaces;
445f412fd2aSLoGin
446f412fd2aSLoGin if (likely(n >= spec.field_width))
447f412fd2aSLoGin return buf;
448f412fd2aSLoGin /* we want to pad the sucker */
449f412fd2aSLoGin spaces = spec.field_width - n;
450f412fd2aSLoGin if (!(spec.flags & LEFT)) {
451f412fd2aSLoGin move_right(buf - n, end, n, spaces);
452f412fd2aSLoGin return buf + spaces;
453f412fd2aSLoGin }
454f412fd2aSLoGin while (spaces--) {
455f412fd2aSLoGin if (buf < end)
456f412fd2aSLoGin *buf = ' ';
457f412fd2aSLoGin ++buf;
458f412fd2aSLoGin }
459f412fd2aSLoGin return buf;
460f412fd2aSLoGin }
461f412fd2aSLoGin
462f412fd2aSLoGin /* Handle string from a well known address. */
string_nocheck(char * buf,char * end,const char * s,struct printf_spec spec)463f412fd2aSLoGin static char *string_nocheck(char *buf, char *end, const char *s,
464f412fd2aSLoGin struct printf_spec spec)
465f412fd2aSLoGin {
466f412fd2aSLoGin int len = 0;
467f412fd2aSLoGin int lim = spec.precision;
468f412fd2aSLoGin
469f412fd2aSLoGin while (lim--) {
470f412fd2aSLoGin char c = *s++;
471f412fd2aSLoGin if (!c)
472f412fd2aSLoGin break;
473f412fd2aSLoGin if (buf < end)
474f412fd2aSLoGin *buf = c;
475f412fd2aSLoGin ++buf;
476f412fd2aSLoGin ++len;
477f412fd2aSLoGin }
478f412fd2aSLoGin return widen_string(buf, len, end, spec);
479f412fd2aSLoGin }
480f412fd2aSLoGin
481f412fd2aSLoGin /* Be careful: error messages must fit into the given buffer. */
error_string(char * buf,char * end,const char * s,struct printf_spec spec)482f412fd2aSLoGin static char *error_string(char *buf, char *end, const char *s,
483f412fd2aSLoGin struct printf_spec spec)
484f412fd2aSLoGin {
485f412fd2aSLoGin /*
486f412fd2aSLoGin * Hard limit to avoid a completely insane messages. It actually
487f412fd2aSLoGin * works pretty well because most error messages are in
488f412fd2aSLoGin * the many pointer format modifiers.
489f412fd2aSLoGin */
490f412fd2aSLoGin if (spec.precision == -1)
491f412fd2aSLoGin spec.precision = 2 * sizeof(void *);
492f412fd2aSLoGin
493f412fd2aSLoGin return string_nocheck(buf, end, s, spec);
494f412fd2aSLoGin }
495f412fd2aSLoGin
496f412fd2aSLoGin /*
497f412fd2aSLoGin * Do not call any complex external code here. Nested printk()/vsprintf()
498f412fd2aSLoGin * might cause infinite loops. Failures might break printk() and would
499f412fd2aSLoGin * be hard to debug.
500f412fd2aSLoGin */
check_pointer_msg(const void * ptr)501f412fd2aSLoGin static const char *check_pointer_msg(const void *ptr)
502f412fd2aSLoGin {
503f412fd2aSLoGin if (!ptr)
504f412fd2aSLoGin return "(null)";
505f412fd2aSLoGin
506f412fd2aSLoGin if ((unsigned long)ptr < PAGE_SIZE || IS_ERR_VALUE(ptr))
507f412fd2aSLoGin return "(efault)";
508f412fd2aSLoGin
509f412fd2aSLoGin return NULL;
510f412fd2aSLoGin }
511f412fd2aSLoGin
check_pointer(char ** buf,char * end,const void * ptr,struct printf_spec spec)512f412fd2aSLoGin static int check_pointer(char **buf, char *end, const void *ptr,
513f412fd2aSLoGin struct printf_spec spec)
514f412fd2aSLoGin {
515f412fd2aSLoGin const char *err_msg;
516f412fd2aSLoGin
517f412fd2aSLoGin err_msg = check_pointer_msg(ptr);
518f412fd2aSLoGin if (err_msg) {
519f412fd2aSLoGin *buf = error_string(*buf, end, err_msg, spec);
520f412fd2aSLoGin return -EFAULT;
521f412fd2aSLoGin }
522f412fd2aSLoGin
523f412fd2aSLoGin return 0;
524f412fd2aSLoGin }
525f412fd2aSLoGin
string(char * buf,char * end,const char * s,struct printf_spec spec)526f412fd2aSLoGin static noinline_for_stack char *string(char *buf, char *end, const char *s,
527f412fd2aSLoGin struct printf_spec spec)
528f412fd2aSLoGin {
529f412fd2aSLoGin if (check_pointer(&buf, end, s, spec))
530f412fd2aSLoGin return buf;
531f412fd2aSLoGin
532f412fd2aSLoGin return string_nocheck(buf, end, s, spec);
533f412fd2aSLoGin }
534f412fd2aSLoGin
pointer_string(char * buf,char * end,const void * ptr,struct printf_spec spec)535f412fd2aSLoGin static char *pointer_string(char *buf, char *end, const void *ptr,
536f412fd2aSLoGin struct printf_spec spec)
537f412fd2aSLoGin {
538f412fd2aSLoGin spec.base = 16;
539f412fd2aSLoGin spec.flags |= SMALL;
540f412fd2aSLoGin if (spec.field_width == -1) {
541f412fd2aSLoGin spec.field_width = 2 * sizeof(ptr);
542f412fd2aSLoGin spec.flags |= ZEROPAD;
543f412fd2aSLoGin }
544f412fd2aSLoGin
545f412fd2aSLoGin return number(buf, end, (unsigned long int)ptr, spec);
546f412fd2aSLoGin }
547f412fd2aSLoGin
hex_string(char * buf,char * end,u8 * addr,struct printf_spec spec,const char * fmt)548*78b790faSLoGin static noinline_for_stack char *hex_string(char *buf, char *end, u8 *addr,
549*78b790faSLoGin struct printf_spec spec,
550f412fd2aSLoGin const char *fmt)
551f412fd2aSLoGin {
552f412fd2aSLoGin int i, len = 1; /* if we pass '%ph[CDN]', field width remains
553f412fd2aSLoGin negative value, fallback to the default */
554f412fd2aSLoGin char separator;
555f412fd2aSLoGin
556f412fd2aSLoGin if (spec.field_width == 0)
557f412fd2aSLoGin /* nothing to print */
558f412fd2aSLoGin return buf;
559f412fd2aSLoGin
560f412fd2aSLoGin if (check_pointer(&buf, end, addr, spec))
561f412fd2aSLoGin return buf;
562f412fd2aSLoGin
563f412fd2aSLoGin switch (fmt[1]) {
564f412fd2aSLoGin case 'C':
565f412fd2aSLoGin separator = ':';
566f412fd2aSLoGin break;
567f412fd2aSLoGin case 'D':
568f412fd2aSLoGin separator = '-';
569f412fd2aSLoGin break;
570f412fd2aSLoGin case 'N':
571f412fd2aSLoGin separator = 0;
572f412fd2aSLoGin break;
573f412fd2aSLoGin default:
574f412fd2aSLoGin separator = ' ';
575f412fd2aSLoGin break;
576f412fd2aSLoGin }
577f412fd2aSLoGin
578f412fd2aSLoGin if (spec.field_width > 0)
579f412fd2aSLoGin len = min_t(int, spec.field_width, 64);
580f412fd2aSLoGin
581f412fd2aSLoGin for (i = 0; i < len; ++i) {
582f412fd2aSLoGin if (buf < end)
583f412fd2aSLoGin *buf = hex_asc_hi(addr[i]);
584f412fd2aSLoGin ++buf;
585f412fd2aSLoGin if (buf < end)
586f412fd2aSLoGin *buf = hex_asc_lo(addr[i]);
587f412fd2aSLoGin ++buf;
588f412fd2aSLoGin
589f412fd2aSLoGin if (separator && i != len - 1) {
590f412fd2aSLoGin if (buf < end)
591f412fd2aSLoGin *buf = separator;
592f412fd2aSLoGin ++buf;
593f412fd2aSLoGin }
594f412fd2aSLoGin }
595f412fd2aSLoGin
596f412fd2aSLoGin return buf;
597f412fd2aSLoGin }
598f412fd2aSLoGin
mac_address_string(char * buf,char * end,u8 * addr,struct printf_spec spec,const char * fmt)599*78b790faSLoGin static noinline_for_stack char *mac_address_string(char *buf, char *end,
600*78b790faSLoGin u8 *addr,
601*78b790faSLoGin struct printf_spec spec,
602*78b790faSLoGin const char *fmt)
603f412fd2aSLoGin {
604f412fd2aSLoGin char mac_addr[sizeof("xx:xx:xx:xx:xx:xx")];
605f412fd2aSLoGin char *p = mac_addr;
606f412fd2aSLoGin int i;
607f412fd2aSLoGin char separator;
608f412fd2aSLoGin bool reversed = false;
609f412fd2aSLoGin
610f412fd2aSLoGin if (check_pointer(&buf, end, addr, spec))
611f412fd2aSLoGin return buf;
612f412fd2aSLoGin
613f412fd2aSLoGin switch (fmt[1]) {
614f412fd2aSLoGin case 'F':
615f412fd2aSLoGin separator = '-';
616f412fd2aSLoGin break;
617f412fd2aSLoGin
618f412fd2aSLoGin case 'R':
619f412fd2aSLoGin reversed = true;
620f412fd2aSLoGin fallthrough;
621f412fd2aSLoGin
622f412fd2aSLoGin default:
623f412fd2aSLoGin separator = ':';
624f412fd2aSLoGin break;
625f412fd2aSLoGin }
626f412fd2aSLoGin
627f412fd2aSLoGin for (i = 0; i < 6; i++) {
628f412fd2aSLoGin if (reversed)
629f412fd2aSLoGin p = hex_byte_pack(p, addr[5 - i]);
630f412fd2aSLoGin else
631f412fd2aSLoGin p = hex_byte_pack(p, addr[i]);
632f412fd2aSLoGin
633f412fd2aSLoGin if (fmt[0] == 'M' && i != 5)
634f412fd2aSLoGin *p++ = separator;
635f412fd2aSLoGin }
636f412fd2aSLoGin *p = '\0';
637f412fd2aSLoGin
638f412fd2aSLoGin return string_nocheck(buf, end, mac_addr, spec);
639f412fd2aSLoGin }
640f412fd2aSLoGin
default_pointer(char * buf,char * end,const void * ptr,struct printf_spec spec)641f412fd2aSLoGin static char *default_pointer(char *buf, char *end, const void *ptr,
642f412fd2aSLoGin struct printf_spec spec)
643f412fd2aSLoGin {
644f412fd2aSLoGin return pointer_string(buf, end, ptr, spec);
645f412fd2aSLoGin }
646f412fd2aSLoGin
err_ptr(char * buf,char * end,void * ptr,struct printf_spec spec)647*78b790faSLoGin static char *err_ptr(char *buf, char *end, void *ptr, struct printf_spec spec)
648f412fd2aSLoGin {
649f412fd2aSLoGin int err = PTR_ERR(ptr);
650f412fd2aSLoGin // const char *sym = errname(err);
651f412fd2aSLoGin
652f412fd2aSLoGin // if (sym)
653f412fd2aSLoGin // return string_nocheck(buf, end, sym, spec);
654f412fd2aSLoGin
655f412fd2aSLoGin /*
656f412fd2aSLoGin * Somebody passed ERR_PTR(-1234) or some other non-existing
657f412fd2aSLoGin * Efoo - or perhaps CONFIG_SYMBOLIC_ERRNAME=n. Fall back to
658f412fd2aSLoGin * printing it as its decimal representation.
659f412fd2aSLoGin */
660f412fd2aSLoGin spec.flags |= SIGN;
661f412fd2aSLoGin spec.base = 10;
662f412fd2aSLoGin return number(buf, end, err, spec);
663f412fd2aSLoGin }
664f412fd2aSLoGin
665f412fd2aSLoGin /*
666f412fd2aSLoGin * Show a '%p' thing. A kernel extension is that the '%p' is followed
667f412fd2aSLoGin * by an extra set of alphanumeric characters that are extended format
668f412fd2aSLoGin * specifiers.
669f412fd2aSLoGin *
670f412fd2aSLoGin * Please update scripts/checkpatch.pl when adding/removing conversion
671f412fd2aSLoGin * characters. (Search for "check for vsprintf extension").
672f412fd2aSLoGin *
673f412fd2aSLoGin * Right now we handle:
674f412fd2aSLoGin *
675f412fd2aSLoGin * - 'S' For symbolic direct pointers (or function descriptors) with offset
676f412fd2aSLoGin * - 's' For symbolic direct pointers (or function descriptors) without offset
677f412fd2aSLoGin * - '[Ss]R' as above with __builtin_extract_return_addr() translation
678f412fd2aSLoGin * - 'S[R]b' as above with module build ID (for use in backtraces)
679f412fd2aSLoGin * - '[Ff]' %pf and %pF were obsoleted and later removed in favor of
680f412fd2aSLoGin * %ps and %pS. Be careful when re-using these specifiers.
681f412fd2aSLoGin * - 'B' For backtraced symbolic direct pointers with offset
682f412fd2aSLoGin * - 'Bb' as above with module build ID (for use in backtraces)
683f412fd2aSLoGin * - 'R' For decoded struct resource, e.g., [mem 0x0-0x1f 64bit pref]
684f412fd2aSLoGin * - 'r' For raw struct resource, e.g., [mem 0x0-0x1f flags 0x201]
685f412fd2aSLoGin * - 'b[l]' For a bitmap, the number of bits is determined by the field
686f412fd2aSLoGin * width which must be explicitly specified either as part of the
687f412fd2aSLoGin * format string '%32b[l]' or through '%*b[l]', [l] selects
688f412fd2aSLoGin * range-list format instead of hex format
689f412fd2aSLoGin * - 'M' For a 6-byte MAC address, it prints the address in the
690f412fd2aSLoGin * usual colon-separated hex notation
691f412fd2aSLoGin * - 'm' For a 6-byte MAC address, it prints the hex address without colons
692f412fd2aSLoGin * - 'MF' For a 6-byte MAC FDDI address, it prints the address
693f412fd2aSLoGin * with a dash-separated hex notation
694f412fd2aSLoGin * - '[mM]R' For a 6-byte MAC address, Reverse order (Bluetooth)
695f412fd2aSLoGin * - 'I' [46] for IPv4/IPv6 addresses printed in the usual way
696f412fd2aSLoGin * IPv4 uses dot-separated decimal without leading 0's (1.2.3.4)
697f412fd2aSLoGin * IPv6 uses colon separated network-order 16 bit hex with leading 0's
698f412fd2aSLoGin * [S][pfs]
699f412fd2aSLoGin * Generic IPv4/IPv6 address (struct sockaddr *) that falls back to
700f412fd2aSLoGin * [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
701f412fd2aSLoGin * - 'i' [46] for 'raw' IPv4/IPv6 addresses
702f412fd2aSLoGin * IPv6 omits the colons (01020304...0f)
703f412fd2aSLoGin * IPv4 uses dot-separated decimal with leading 0's (010.123.045.006)
704f412fd2aSLoGin * [S][pfs]
705f412fd2aSLoGin * Generic IPv4/IPv6 address (struct sockaddr *) that falls back to
706f412fd2aSLoGin * [4] or [6] and is able to print port [p], flowinfo [f], scope [s]
707f412fd2aSLoGin * - '[Ii][4S][hnbl]' IPv4 addresses in host, network, big or little endian order
708f412fd2aSLoGin * - 'I[6S]c' for IPv6 addresses printed as specified by
709f412fd2aSLoGin * https://tools.ietf.org/html/rfc5952
710f412fd2aSLoGin * - 'E[achnops]' For an escaped buffer, where rules are defined by combination
711f412fd2aSLoGin * of the following flags (see string_escape_mem() for the
712f412fd2aSLoGin * details):
713f412fd2aSLoGin * a - ESCAPE_ANY
714f412fd2aSLoGin * c - ESCAPE_SPECIAL
715f412fd2aSLoGin * h - ESCAPE_HEX
716f412fd2aSLoGin * n - ESCAPE_NULL
717f412fd2aSLoGin * o - ESCAPE_OCTAL
718f412fd2aSLoGin * p - ESCAPE_NP
719f412fd2aSLoGin * s - ESCAPE_SPACE
720f412fd2aSLoGin * By default ESCAPE_ANY_NP is used.
721f412fd2aSLoGin * - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form
722f412fd2aSLoGin * "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
723f412fd2aSLoGin * Options for %pU are:
724f412fd2aSLoGin * b big endian lower case hex (default)
725f412fd2aSLoGin * B big endian UPPER case hex
726f412fd2aSLoGin * l little endian lower case hex
727f412fd2aSLoGin * L little endian UPPER case hex
728f412fd2aSLoGin * big endian output byte order is:
729f412fd2aSLoGin * [0][1][2][3]-[4][5]-[6][7]-[8][9]-[10][11][12][13][14][15]
730f412fd2aSLoGin * little endian output byte order is:
731f412fd2aSLoGin * [3][2][1][0]-[5][4]-[7][6]-[8][9]-[10][11][12][13][14][15]
732f412fd2aSLoGin * - 'V' For a struct va_format which contains a format string * and va_list *,
733f412fd2aSLoGin * call vsnprintf(->format, *->va_list).
734f412fd2aSLoGin * Implements a "recursive vsnprintf".
735f412fd2aSLoGin * Do not use this feature without some mechanism to verify the
736f412fd2aSLoGin * correctness of the format string and va_list arguments.
737f412fd2aSLoGin * - 'K' For a kernel pointer that should be hidden from unprivileged users.
738f412fd2aSLoGin * Use only for procfs, sysfs and similar files, not printk(); please
739f412fd2aSLoGin * read the documentation (path below) first.
740f412fd2aSLoGin * - 'NF' For a netdev_features_t
741f412fd2aSLoGin * - '4cc' V4L2 or DRM FourCC code, with endianness and raw numerical value.
742f412fd2aSLoGin * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with
743f412fd2aSLoGin * a certain separator (' ' by default):
744f412fd2aSLoGin * C colon
745f412fd2aSLoGin * D dash
746f412fd2aSLoGin * N no separator
747f412fd2aSLoGin * The maximum supported length is 64 bytes of the input. Consider
748f412fd2aSLoGin * to use print_hex_dump() for the larger input.
749f412fd2aSLoGin * - 'a[pd]' For address types [p] phys_addr_t, [d] dma_addr_t and derivatives
750f412fd2aSLoGin * (default assumed to be phys_addr_t, passed by reference)
751f412fd2aSLoGin * - 'd[234]' For a dentry name (optionally 2-4 last components)
752f412fd2aSLoGin * - 'D[234]' Same as 'd' but for a struct file
753f412fd2aSLoGin * - 'g' For block_device name (gendisk + partition number)
754f412fd2aSLoGin * - 't[RT][dt][r][s]' For time and date as represented by:
755f412fd2aSLoGin * R struct rtc_time
756f412fd2aSLoGin * T time64_t
757f412fd2aSLoGin * - 'C' For a clock, it prints the name (Common Clock Framework) or address
758f412fd2aSLoGin * (legacy clock framework) of the clock
759f412fd2aSLoGin * - 'Cn' For a clock, it prints the name (Common Clock Framework) or address
760f412fd2aSLoGin * (legacy clock framework) of the clock
761f412fd2aSLoGin * - 'G' For flags to be printed as a collection of symbolic strings that would
762f412fd2aSLoGin * construct the specific value. Supported flags given by option:
763f412fd2aSLoGin * p page flags (see struct page) given as pointer to unsigned long
764f412fd2aSLoGin * g gfp flags (GFP_* and __GFP_*) given as pointer to gfp_t
765f412fd2aSLoGin * v vma flags (VM_*) given as pointer to unsigned long
766f412fd2aSLoGin * - 'OF[fnpPcCF]' For a device tree object
767f412fd2aSLoGin * Without any optional arguments prints the full_name
768f412fd2aSLoGin * f device node full_name
769f412fd2aSLoGin * n device node name
770f412fd2aSLoGin * p device node phandle
771f412fd2aSLoGin * P device node path spec (name + @unit)
772f412fd2aSLoGin * F device node flags
773f412fd2aSLoGin * c major compatible string
774f412fd2aSLoGin * C full compatible string
775f412fd2aSLoGin * - 'fw[fP]' For a firmware node (struct fwnode_handle) pointer
776f412fd2aSLoGin * Without an option prints the full name of the node
777f412fd2aSLoGin * f full name
778f412fd2aSLoGin * P node name, including a possible unit address
779f412fd2aSLoGin * - 'x' For printing the address unmodified. Equivalent to "%lx".
780f412fd2aSLoGin * Please read the documentation (path below) before using!
781f412fd2aSLoGin * - '[ku]s' For a BPF/tracing related format specifier, e.g. used out of
782f412fd2aSLoGin * bpf_trace_printk() where [ku] prefix specifies either kernel (k)
783f412fd2aSLoGin * or user (u) memory to probe, and:
784f412fd2aSLoGin * s a string, equivalent to "%s" on direct vsnprintf() use
785f412fd2aSLoGin *
786f412fd2aSLoGin * ** When making changes please also update:
787f412fd2aSLoGin * Documentation/core-api/printk-formats.rst
788f412fd2aSLoGin *
789f412fd2aSLoGin * Note: The default behaviour (unadorned %p) is to hash the address,
790f412fd2aSLoGin * rendering it useful as a unique identifier.
791f412fd2aSLoGin *
792f412fd2aSLoGin * There is also a '%pA' format specifier, but it is only intended to be used
793f412fd2aSLoGin * from Rust code to format core::fmt::Arguments. Do *not* use it from C.
794f412fd2aSLoGin * See rust/kernel/print.rs for details.
795f412fd2aSLoGin */
pointer(const char * fmt,char * buf,char * end,void * ptr,struct printf_spec spec)796f412fd2aSLoGin static noinline_for_stack char *pointer(const char *fmt, char *buf, char *end,
797f412fd2aSLoGin void *ptr, struct printf_spec spec)
798f412fd2aSLoGin {
799f412fd2aSLoGin switch (*fmt) {
800f412fd2aSLoGin // case 'S':
801f412fd2aSLoGin // case 's':
802f412fd2aSLoGin // ptr = dereference_symbol_descriptor(ptr);
803f412fd2aSLoGin // fallthrough;
804f412fd2aSLoGin // case 'B':
805f412fd2aSLoGin // return symbol_string(buf, end, ptr, spec, fmt);
806f412fd2aSLoGin // case 'R':
807f412fd2aSLoGin // case 'r':
808f412fd2aSLoGin // return resource_string(buf, end, ptr, spec, fmt);
809f412fd2aSLoGin case 'h':
810f412fd2aSLoGin return hex_string(buf, end, ptr, spec, fmt);
811f412fd2aSLoGin // case 'b':
812f412fd2aSLoGin // switch (fmt[1]) {
813f412fd2aSLoGin // case 'l':
814f412fd2aSLoGin // return bitmap_list_string(buf, end, ptr, spec, fmt);
815f412fd2aSLoGin // default:
816f412fd2aSLoGin // return bitmap_string(buf, end, ptr, spec, fmt);
817f412fd2aSLoGin // }
818f412fd2aSLoGin case 'M': /* Colon separated: 00:01:02:03:04:05 */
819f412fd2aSLoGin case 'm': /* Contiguous: 000102030405 */
820f412fd2aSLoGin /* [mM]F (FDDI) */
821f412fd2aSLoGin /* [mM]R (Reverse order; Bluetooth) */
822f412fd2aSLoGin return mac_address_string(buf, end, ptr, spec, fmt);
823f412fd2aSLoGin // case 'I': /* Formatted IP supported
824f412fd2aSLoGin // * 4: 1.2.3.4
825f412fd2aSLoGin // * 6: 0001:0203:...:0708
826f412fd2aSLoGin // * 6c: 1::708 or 1::1.2.3.4
827f412fd2aSLoGin // */
828f412fd2aSLoGin // case 'i': /* Contiguous:
829f412fd2aSLoGin // * 4: 001.002.003.004
830f412fd2aSLoGin // * 6: 000102...0f
831f412fd2aSLoGin // */
832f412fd2aSLoGin // return ip_addr_string(buf, end, ptr, spec, fmt);
833f412fd2aSLoGin // case 'E':
834f412fd2aSLoGin // return escaped_string(buf, end, ptr, spec, fmt);
835f412fd2aSLoGin // case 'U':
836f412fd2aSLoGin // return uuid_string(buf, end, ptr, spec, fmt);
837f412fd2aSLoGin // case 'V':
838f412fd2aSLoGin // return va_format(buf, end, ptr, spec, fmt);
839f412fd2aSLoGin // case 'K':
840f412fd2aSLoGin // return restricted_pointer(buf, end, ptr, spec);
841f412fd2aSLoGin // case 'N':
842f412fd2aSLoGin // return netdev_bits(buf, end, ptr, spec, fmt);
843f412fd2aSLoGin // case '4':
844f412fd2aSLoGin // return fourcc_string(buf, end, ptr, spec, fmt);
845f412fd2aSLoGin // case 'a':
846f412fd2aSLoGin // return address_val(buf, end, ptr, spec, fmt);
847f412fd2aSLoGin // case 'd':
848f412fd2aSLoGin // return dentry_name(buf, end, ptr, spec, fmt);
849f412fd2aSLoGin // case 't':
850f412fd2aSLoGin // return time_and_date(buf, end, ptr, spec, fmt);
851f412fd2aSLoGin // case 'C':
852f412fd2aSLoGin // return clock(buf, end, ptr, spec, fmt);
853f412fd2aSLoGin // case 'D':
854f412fd2aSLoGin // return file_dentry_name(buf, end, ptr, spec, fmt);
855f412fd2aSLoGin #ifdef CONFIG_BLOCK
856f412fd2aSLoGin case 'g':
857f412fd2aSLoGin return bdev_name(buf, end, ptr, spec, fmt);
858f412fd2aSLoGin #endif
859f412fd2aSLoGin
860f412fd2aSLoGin // case 'G':
861f412fd2aSLoGin // return flags_string(buf, end, ptr, spec, fmt);
862f412fd2aSLoGin // case 'O':
863f412fd2aSLoGin // return device_node_string(buf, end, ptr, spec, fmt + 1);
864f412fd2aSLoGin // case 'f':
865f412fd2aSLoGin // return fwnode_string(buf, end, ptr, spec, fmt + 1);
866f412fd2aSLoGin // case 'A':
867f412fd2aSLoGin // if (!IS_ENABLED(CONFIG_RUST)) {
868f412fd2aSLoGin // WARN_ONCE(1, "Please remove %%pA from non-Rust code\n");
869f412fd2aSLoGin // return error_string(buf, end, "(%pA?)", spec);
870f412fd2aSLoGin // }
871f412fd2aSLoGin // return rust_fmt_argument(buf, end, ptr);
872f412fd2aSLoGin case 'x':
873f412fd2aSLoGin return pointer_string(buf, end, ptr, spec);
874f412fd2aSLoGin case 'e':
875f412fd2aSLoGin /* %pe with a non-ERR_PTR gets treated as plain %p */
876f412fd2aSLoGin if (!IS_ERR(ptr))
877f412fd2aSLoGin return default_pointer(buf, end, ptr, spec);
878f412fd2aSLoGin return err_ptr(buf, end, ptr, spec);
879f412fd2aSLoGin case 'u':
880f412fd2aSLoGin case 'k':
881f412fd2aSLoGin switch (fmt[1]) {
882f412fd2aSLoGin case 's':
883f412fd2aSLoGin return string(buf, end, ptr, spec);
884f412fd2aSLoGin default:
885f412fd2aSLoGin return error_string(buf, end, "(einval)", spec);
886f412fd2aSLoGin }
887f412fd2aSLoGin default:
888f412fd2aSLoGin return default_pointer(buf, end, ptr, spec);
889f412fd2aSLoGin }
890f412fd2aSLoGin }
891f412fd2aSLoGin
892f412fd2aSLoGin /*
893f412fd2aSLoGin * Helper function to decode printf style format.
894f412fd2aSLoGin * Each call decode a token from the format and return the
895f412fd2aSLoGin * number of characters read (or likely the delta where it wants
896f412fd2aSLoGin * to go on the next call).
897f412fd2aSLoGin * The decoded token is returned through the parameters
898f412fd2aSLoGin *
899f412fd2aSLoGin * 'h', 'l', or 'L' for integer fields
900f412fd2aSLoGin * 'z' support added 23/7/1999 S.H.
901f412fd2aSLoGin * 'z' changed to 'Z' --davidm 1/25/99
902f412fd2aSLoGin * 'Z' changed to 'z' --adobriyan 2017-01-25
903f412fd2aSLoGin * 't' added for ptrdiff_t
904f412fd2aSLoGin *
905f412fd2aSLoGin * @fmt: the format string
906f412fd2aSLoGin * @type of the token returned
907f412fd2aSLoGin * @flags: various flags such as +, -, # tokens..
908f412fd2aSLoGin * @field_width: overwritten width
909f412fd2aSLoGin * @base: base of the number (octal, hex, ...)
910f412fd2aSLoGin * @precision: precision of a number
911f412fd2aSLoGin * @qualifier: qualifier of a number (long, size_t, ...)
912f412fd2aSLoGin */
format_decode(const char * fmt,struct printf_spec * spec)913f412fd2aSLoGin static noinline_for_stack int format_decode(const char *fmt,
914f412fd2aSLoGin struct printf_spec *spec)
915f412fd2aSLoGin {
916f412fd2aSLoGin const char *start = fmt;
917f412fd2aSLoGin char qualifier;
918f412fd2aSLoGin
919f412fd2aSLoGin /* we finished early by reading the field width */
920f412fd2aSLoGin if (spec->type == FORMAT_TYPE_WIDTH) {
921f412fd2aSLoGin if (spec->field_width < 0) {
922f412fd2aSLoGin spec->field_width = -spec->field_width;
923f412fd2aSLoGin spec->flags |= LEFT;
924f412fd2aSLoGin }
925f412fd2aSLoGin spec->type = FORMAT_TYPE_NONE;
926f412fd2aSLoGin goto precision;
927f412fd2aSLoGin }
928f412fd2aSLoGin
929f412fd2aSLoGin /* we finished early by reading the precision */
930f412fd2aSLoGin if (spec->type == FORMAT_TYPE_PRECISION) {
931f412fd2aSLoGin if (spec->precision < 0)
932f412fd2aSLoGin spec->precision = 0;
933f412fd2aSLoGin
934f412fd2aSLoGin spec->type = FORMAT_TYPE_NONE;
935f412fd2aSLoGin goto qualifier;
936f412fd2aSLoGin }
937f412fd2aSLoGin
938f412fd2aSLoGin /* By default */
939f412fd2aSLoGin spec->type = FORMAT_TYPE_NONE;
940f412fd2aSLoGin
941f412fd2aSLoGin for (; *fmt; ++fmt) {
942f412fd2aSLoGin if (*fmt == '%')
943f412fd2aSLoGin break;
944f412fd2aSLoGin }
945f412fd2aSLoGin
946f412fd2aSLoGin /* Return the current non-format string */
947f412fd2aSLoGin if (fmt != start || !*fmt)
948f412fd2aSLoGin return fmt - start;
949f412fd2aSLoGin
950f412fd2aSLoGin /* Process flags */
951f412fd2aSLoGin spec->flags = 0;
952f412fd2aSLoGin
953f412fd2aSLoGin while (1) { /* this also skips first '%' */
954f412fd2aSLoGin bool found = true;
955f412fd2aSLoGin
956f412fd2aSLoGin ++fmt;
957f412fd2aSLoGin
958f412fd2aSLoGin switch (*fmt) {
959f412fd2aSLoGin case '-':
960f412fd2aSLoGin spec->flags |= LEFT;
961f412fd2aSLoGin break;
962f412fd2aSLoGin case '+':
963f412fd2aSLoGin spec->flags |= PLUS;
964f412fd2aSLoGin break;
965f412fd2aSLoGin case ' ':
966f412fd2aSLoGin spec->flags |= SPACE;
967f412fd2aSLoGin break;
968f412fd2aSLoGin case '#':
969f412fd2aSLoGin spec->flags |= SPECIAL;
970f412fd2aSLoGin break;
971f412fd2aSLoGin case '0':
972f412fd2aSLoGin spec->flags |= ZEROPAD;
973f412fd2aSLoGin break;
974f412fd2aSLoGin default:
975f412fd2aSLoGin found = false;
976f412fd2aSLoGin }
977f412fd2aSLoGin
978f412fd2aSLoGin if (!found)
979f412fd2aSLoGin break;
980f412fd2aSLoGin }
981f412fd2aSLoGin
982f412fd2aSLoGin /* get field width */
983f412fd2aSLoGin spec->field_width = -1;
984f412fd2aSLoGin
985f412fd2aSLoGin if (isdigit(*fmt))
986f412fd2aSLoGin spec->field_width = skip_atoi(&fmt);
987f412fd2aSLoGin else if (*fmt == '*') {
988f412fd2aSLoGin /* it's the next argument */
989f412fd2aSLoGin spec->type = FORMAT_TYPE_WIDTH;
990f412fd2aSLoGin return ++fmt - start;
991f412fd2aSLoGin }
992f412fd2aSLoGin
993f412fd2aSLoGin precision:
994f412fd2aSLoGin /* get the precision */
995f412fd2aSLoGin spec->precision = -1;
996f412fd2aSLoGin if (*fmt == '.') {
997f412fd2aSLoGin ++fmt;
998f412fd2aSLoGin if (isdigit(*fmt)) {
999f412fd2aSLoGin spec->precision = skip_atoi(&fmt);
1000f412fd2aSLoGin if (spec->precision < 0)
1001f412fd2aSLoGin spec->precision = 0;
1002f412fd2aSLoGin } else if (*fmt == '*') {
1003f412fd2aSLoGin /* it's the next argument */
1004f412fd2aSLoGin spec->type = FORMAT_TYPE_PRECISION;
1005f412fd2aSLoGin return ++fmt - start;
1006f412fd2aSLoGin }
1007f412fd2aSLoGin }
1008f412fd2aSLoGin
1009f412fd2aSLoGin qualifier:
1010f412fd2aSLoGin /* get the conversion qualifier */
1011f412fd2aSLoGin qualifier = 0;
1012f412fd2aSLoGin if (*fmt == 'h' || _tolower(*fmt) == 'l' || *fmt == 'z' ||
1013f412fd2aSLoGin *fmt == 't') {
1014f412fd2aSLoGin qualifier = *fmt++;
1015f412fd2aSLoGin if (unlikely(qualifier == *fmt)) {
1016f412fd2aSLoGin if (qualifier == 'l') {
1017f412fd2aSLoGin qualifier = 'L';
1018f412fd2aSLoGin ++fmt;
1019f412fd2aSLoGin } else if (qualifier == 'h') {
1020f412fd2aSLoGin qualifier = 'H';
1021f412fd2aSLoGin ++fmt;
1022f412fd2aSLoGin }
1023f412fd2aSLoGin }
1024f412fd2aSLoGin }
1025f412fd2aSLoGin
1026f412fd2aSLoGin /* default base */
1027f412fd2aSLoGin spec->base = 10;
1028f412fd2aSLoGin switch (*fmt) {
1029f412fd2aSLoGin case 'c':
1030f412fd2aSLoGin spec->type = FORMAT_TYPE_CHAR;
1031f412fd2aSLoGin return ++fmt - start;
1032f412fd2aSLoGin
1033f412fd2aSLoGin case 's':
1034f412fd2aSLoGin spec->type = FORMAT_TYPE_STR;
1035f412fd2aSLoGin return ++fmt - start;
1036f412fd2aSLoGin
1037f412fd2aSLoGin case 'p':
1038f412fd2aSLoGin spec->type = FORMAT_TYPE_PTR;
1039f412fd2aSLoGin return ++fmt - start;
1040f412fd2aSLoGin
1041f412fd2aSLoGin case '%':
1042f412fd2aSLoGin spec->type = FORMAT_TYPE_PERCENT_CHAR;
1043f412fd2aSLoGin return ++fmt - start;
1044f412fd2aSLoGin
1045f412fd2aSLoGin /* integer number formats - set up the flags and "break" */
1046f412fd2aSLoGin case 'o':
1047f412fd2aSLoGin spec->base = 8;
1048f412fd2aSLoGin break;
1049f412fd2aSLoGin
1050f412fd2aSLoGin case 'x':
1051f412fd2aSLoGin spec->flags |= SMALL;
1052f412fd2aSLoGin fallthrough;
1053f412fd2aSLoGin
1054f412fd2aSLoGin case 'X':
1055f412fd2aSLoGin spec->base = 16;
1056f412fd2aSLoGin break;
1057f412fd2aSLoGin
1058f412fd2aSLoGin case 'd':
1059f412fd2aSLoGin case 'i':
1060f412fd2aSLoGin spec->flags |= SIGN;
1061f412fd2aSLoGin break;
1062f412fd2aSLoGin case 'u':
1063f412fd2aSLoGin break;
1064f412fd2aSLoGin
1065f412fd2aSLoGin case 'n':
1066f412fd2aSLoGin /*
1067f412fd2aSLoGin * Since %n poses a greater security risk than
1068f412fd2aSLoGin * utility, treat it as any other invalid or
1069f412fd2aSLoGin * unsupported format specifier.
1070f412fd2aSLoGin */
1071f412fd2aSLoGin fallthrough;
1072f412fd2aSLoGin
1073f412fd2aSLoGin default:
1074f412fd2aSLoGin WARN_ONCE(1,
1075f412fd2aSLoGin "Please remove unsupported %%%c in format string\n",
1076f412fd2aSLoGin *fmt);
1077f412fd2aSLoGin spec->type = FORMAT_TYPE_INVALID;
1078f412fd2aSLoGin return fmt - start;
1079f412fd2aSLoGin }
1080f412fd2aSLoGin
1081f412fd2aSLoGin if (qualifier == 'L')
1082f412fd2aSLoGin spec->type = FORMAT_TYPE_LONG_LONG;
1083f412fd2aSLoGin else if (qualifier == 'l') {
1084f412fd2aSLoGin BUILD_BUG_ON(FORMAT_TYPE_ULONG + SIGN != FORMAT_TYPE_LONG);
1085f412fd2aSLoGin spec->type = FORMAT_TYPE_ULONG + (spec->flags & SIGN);
1086f412fd2aSLoGin } else if (qualifier == 'z') {
1087f412fd2aSLoGin spec->type = FORMAT_TYPE_SIZE_T;
1088f412fd2aSLoGin } else if (qualifier == 't') {
1089f412fd2aSLoGin spec->type = FORMAT_TYPE_PTRDIFF;
1090f412fd2aSLoGin } else if (qualifier == 'H') {
1091f412fd2aSLoGin BUILD_BUG_ON(FORMAT_TYPE_UBYTE + SIGN != FORMAT_TYPE_BYTE);
1092f412fd2aSLoGin spec->type = FORMAT_TYPE_UBYTE + (spec->flags & SIGN);
1093f412fd2aSLoGin } else if (qualifier == 'h') {
1094f412fd2aSLoGin BUILD_BUG_ON(FORMAT_TYPE_USHORT + SIGN != FORMAT_TYPE_SHORT);
1095f412fd2aSLoGin spec->type = FORMAT_TYPE_USHORT + (spec->flags & SIGN);
1096f412fd2aSLoGin } else {
1097f412fd2aSLoGin BUILD_BUG_ON(FORMAT_TYPE_UINT + SIGN != FORMAT_TYPE_INT);
1098f412fd2aSLoGin spec->type = FORMAT_TYPE_UINT + (spec->flags & SIGN);
1099f412fd2aSLoGin }
1100f412fd2aSLoGin
1101f412fd2aSLoGin return ++fmt - start;
1102f412fd2aSLoGin }
1103f412fd2aSLoGin
1104f412fd2aSLoGin /**
1105f412fd2aSLoGin * vsnprintf - Format a string and place it in a buffer
1106f412fd2aSLoGin * @buf: The buffer to place the result into
1107f412fd2aSLoGin * @size: The size of the buffer, including the trailing null space
1108f412fd2aSLoGin * @fmt: The format string to use
1109f412fd2aSLoGin * @args: Arguments for the format string
1110f412fd2aSLoGin *
1111f412fd2aSLoGin * This function generally follows C99 vsnprintf, but has some
1112f412fd2aSLoGin * extensions and a few limitations:
1113f412fd2aSLoGin *
1114f412fd2aSLoGin * - ``%n`` is unsupported
1115f412fd2aSLoGin * - ``%p*`` is handled by pointer()
1116f412fd2aSLoGin *
1117f412fd2aSLoGin * See pointer() or Documentation/core-api/printk-formats.rst for more
1118f412fd2aSLoGin * extensive description.
1119f412fd2aSLoGin *
1120f412fd2aSLoGin * **Please update the documentation in both places when making changes**
1121f412fd2aSLoGin *
1122f412fd2aSLoGin * The return value is the number of characters which would
1123f412fd2aSLoGin * be generated for the given input, excluding the trailing
1124f412fd2aSLoGin * '\0', as per ISO C99. If you want to have the exact
1125f412fd2aSLoGin * number of characters written into @buf as return value
1126f412fd2aSLoGin * (not including the trailing '\0'), use vscnprintf(). If the
1127f412fd2aSLoGin * return is greater than or equal to @size, the resulting
1128f412fd2aSLoGin * string is truncated.
1129f412fd2aSLoGin *
1130f412fd2aSLoGin * If you're not already dealing with a va_list consider using snprintf().
1131f412fd2aSLoGin */
vsnprintf(char * buf,size_t size,const char * fmt,va_list args)1132f412fd2aSLoGin int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
1133f412fd2aSLoGin {
1134f412fd2aSLoGin unsigned long long num;
1135f412fd2aSLoGin char *str, *end;
1136f412fd2aSLoGin struct printf_spec spec = { 0 };
1137f412fd2aSLoGin
1138f412fd2aSLoGin /* Reject out-of-range values early. Large positive sizes are
1139f412fd2aSLoGin used for unknown buffer sizes. */
1140f412fd2aSLoGin if (WARN_ON_ONCE(size > INT_MAX))
1141f412fd2aSLoGin return 0;
1142f412fd2aSLoGin
1143f412fd2aSLoGin str = buf;
1144f412fd2aSLoGin end = buf + size;
1145f412fd2aSLoGin
1146f412fd2aSLoGin /* Make sure end is always >= buf */
1147f412fd2aSLoGin if (end < buf) {
1148f412fd2aSLoGin end = ((void *)-1);
1149f412fd2aSLoGin size = end - buf;
1150f412fd2aSLoGin }
1151f412fd2aSLoGin
1152f412fd2aSLoGin while (*fmt) {
1153f412fd2aSLoGin const char *old_fmt = fmt;
1154f412fd2aSLoGin int read = format_decode(fmt, &spec);
1155f412fd2aSLoGin
1156f412fd2aSLoGin fmt += read;
1157f412fd2aSLoGin
1158f412fd2aSLoGin switch (spec.type) {
1159f412fd2aSLoGin case FORMAT_TYPE_NONE: {
1160f412fd2aSLoGin int copy = read;
1161f412fd2aSLoGin if (str < end) {
1162f412fd2aSLoGin if (copy > end - str)
1163f412fd2aSLoGin copy = end - str;
1164f412fd2aSLoGin memcpy(str, old_fmt, copy);
1165f412fd2aSLoGin }
1166f412fd2aSLoGin str += read;
1167f412fd2aSLoGin break;
1168f412fd2aSLoGin }
1169f412fd2aSLoGin
1170f412fd2aSLoGin case FORMAT_TYPE_WIDTH:
1171f412fd2aSLoGin set_field_width(&spec, va_arg(args, int));
1172f412fd2aSLoGin break;
1173f412fd2aSLoGin
1174f412fd2aSLoGin case FORMAT_TYPE_PRECISION:
1175f412fd2aSLoGin set_precision(&spec, va_arg(args, int));
1176f412fd2aSLoGin break;
1177f412fd2aSLoGin
1178f412fd2aSLoGin case FORMAT_TYPE_CHAR: {
1179f412fd2aSLoGin char c;
1180f412fd2aSLoGin
1181f412fd2aSLoGin if (!(spec.flags & LEFT)) {
1182f412fd2aSLoGin while (--spec.field_width > 0) {
1183f412fd2aSLoGin if (str < end)
1184f412fd2aSLoGin *str = ' ';
1185f412fd2aSLoGin ++str;
1186f412fd2aSLoGin }
1187f412fd2aSLoGin }
1188f412fd2aSLoGin c = (unsigned char)va_arg(args, int);
1189f412fd2aSLoGin if (str < end)
1190f412fd2aSLoGin *str = c;
1191f412fd2aSLoGin ++str;
1192f412fd2aSLoGin while (--spec.field_width > 0) {
1193f412fd2aSLoGin if (str < end)
1194f412fd2aSLoGin *str = ' ';
1195f412fd2aSLoGin ++str;
1196f412fd2aSLoGin }
1197f412fd2aSLoGin break;
1198f412fd2aSLoGin }
1199f412fd2aSLoGin
1200f412fd2aSLoGin case FORMAT_TYPE_STR:
1201f412fd2aSLoGin str = string(str, end, va_arg(args, char *), spec);
1202f412fd2aSLoGin break;
1203f412fd2aSLoGin
1204f412fd2aSLoGin case FORMAT_TYPE_PTR:
1205f412fd2aSLoGin str = pointer(fmt, str, end, va_arg(args, void *),
1206f412fd2aSLoGin spec);
1207f412fd2aSLoGin while (isalnum(*fmt))
1208f412fd2aSLoGin fmt++;
1209f412fd2aSLoGin break;
1210f412fd2aSLoGin
1211f412fd2aSLoGin case FORMAT_TYPE_PERCENT_CHAR:
1212f412fd2aSLoGin if (str < end)
1213f412fd2aSLoGin *str = '%';
1214f412fd2aSLoGin ++str;
1215f412fd2aSLoGin break;
1216f412fd2aSLoGin
1217f412fd2aSLoGin case FORMAT_TYPE_INVALID:
1218f412fd2aSLoGin /*
1219f412fd2aSLoGin * Presumably the arguments passed gcc's type
1220f412fd2aSLoGin * checking, but there is no safe or sane way
1221f412fd2aSLoGin * for us to continue parsing the format and
1222f412fd2aSLoGin * fetching from the va_list; the remaining
1223f412fd2aSLoGin * specifiers and arguments would be out of
1224f412fd2aSLoGin * sync.
1225f412fd2aSLoGin */
1226f412fd2aSLoGin goto out;
1227f412fd2aSLoGin
1228f412fd2aSLoGin default:
1229f412fd2aSLoGin switch (spec.type) {
1230f412fd2aSLoGin case FORMAT_TYPE_LONG_LONG:
1231f412fd2aSLoGin num = va_arg(args, long long);
1232f412fd2aSLoGin break;
1233f412fd2aSLoGin case FORMAT_TYPE_ULONG:
1234f412fd2aSLoGin num = va_arg(args, unsigned long);
1235f412fd2aSLoGin break;
1236f412fd2aSLoGin case FORMAT_TYPE_LONG:
1237f412fd2aSLoGin num = va_arg(args, long);
1238f412fd2aSLoGin break;
1239f412fd2aSLoGin case FORMAT_TYPE_SIZE_T:
1240f412fd2aSLoGin if (spec.flags & SIGN)
1241f412fd2aSLoGin num = va_arg(args, ssize_t);
1242f412fd2aSLoGin else
1243f412fd2aSLoGin num = va_arg(args, size_t);
1244f412fd2aSLoGin break;
1245f412fd2aSLoGin case FORMAT_TYPE_PTRDIFF:
1246f412fd2aSLoGin num = va_arg(args, ptrdiff_t);
1247f412fd2aSLoGin break;
1248f412fd2aSLoGin case FORMAT_TYPE_UBYTE:
1249f412fd2aSLoGin num = (unsigned char)va_arg(args, int);
1250f412fd2aSLoGin break;
1251f412fd2aSLoGin case FORMAT_TYPE_BYTE:
1252f412fd2aSLoGin num = (signed char)va_arg(args, int);
1253f412fd2aSLoGin break;
1254f412fd2aSLoGin case FORMAT_TYPE_USHORT:
1255f412fd2aSLoGin num = (unsigned short)va_arg(args, int);
1256f412fd2aSLoGin break;
1257f412fd2aSLoGin case FORMAT_TYPE_SHORT:
1258f412fd2aSLoGin num = (short)va_arg(args, int);
1259f412fd2aSLoGin break;
1260f412fd2aSLoGin case FORMAT_TYPE_INT:
1261f412fd2aSLoGin num = (int)va_arg(args, int);
1262f412fd2aSLoGin break;
1263f412fd2aSLoGin default:
1264f412fd2aSLoGin num = va_arg(args, unsigned int);
1265f412fd2aSLoGin }
1266f412fd2aSLoGin
1267f412fd2aSLoGin str = number(str, end, num, spec);
1268f412fd2aSLoGin }
1269f412fd2aSLoGin }
1270f412fd2aSLoGin
1271f412fd2aSLoGin out:
1272f412fd2aSLoGin if (size > 0) {
1273f412fd2aSLoGin if (str < end)
1274f412fd2aSLoGin *str = '\0';
1275f412fd2aSLoGin else
1276f412fd2aSLoGin end[-1] = '\0';
1277f412fd2aSLoGin }
1278f412fd2aSLoGin
1279f412fd2aSLoGin /* the trailing null byte doesn't count towards the total */
1280f412fd2aSLoGin return str - buf;
1281f412fd2aSLoGin }
1282f412fd2aSLoGin
1283f412fd2aSLoGin /**
1284f412fd2aSLoGin * snprintf - Format a string and place it in a buffer
1285f412fd2aSLoGin * @buf: The buffer to place the result into
1286f412fd2aSLoGin * @size: The size of the buffer, including the trailing null space
1287f412fd2aSLoGin * @fmt: The format string to use
1288f412fd2aSLoGin * @...: Arguments for the format string
1289f412fd2aSLoGin *
1290f412fd2aSLoGin * The return value is the number of characters which would be
1291f412fd2aSLoGin * generated for the given input, excluding the trailing null,
1292f412fd2aSLoGin * as per ISO C99. If the return is greater than or equal to
1293f412fd2aSLoGin * @size, the resulting string is truncated.
1294f412fd2aSLoGin *
1295f412fd2aSLoGin * See the vsnprintf() documentation for format string extensions over C99.
1296f412fd2aSLoGin */
snprintf(char * buf,size_t size,const char * fmt,...)1297f412fd2aSLoGin int snprintf(char *buf, size_t size, const char *fmt, ...)
1298f412fd2aSLoGin {
1299f412fd2aSLoGin va_list args;
1300f412fd2aSLoGin int i;
1301f412fd2aSLoGin
1302f412fd2aSLoGin va_start(args, fmt);
1303f412fd2aSLoGin i = vsnprintf(buf, size, fmt, args);
1304f412fd2aSLoGin va_end(args);
1305f412fd2aSLoGin
1306f412fd2aSLoGin return i;
1307f412fd2aSLoGin }