blob: 1d1d2a2fdfefc69d174f132045b6056843fc7150 [file] [log] [blame]
Greg Hartman76d05dc2016-11-23 15:51:27 -08001/*
2 * vsnprintf.c
3 *
4 * vsnprintf(), from which the rest of the printf()
5 * family is built
6 */
7
8#include <stdarg.h>
9#include <stddef.h>
10#include <inttypes.h>
11#include <string.h>
12#include <limits.h>
13#include <stdio.h>
14
15enum flags {
16 FL_ZERO = 0x01, /* Zero modifier */
17 FL_MINUS = 0x02, /* Minus modifier */
18 FL_PLUS = 0x04, /* Plus modifier */
19 FL_TICK = 0x08, /* ' modifier */
20 FL_SPACE = 0x10, /* Space modifier */
21 FL_HASH = 0x20, /* # modifier */
22 FL_SIGNED = 0x40, /* Number is signed */
23 FL_UPPER = 0x80 /* Upper case digits */
24};
25
26/* These may have to be adjusted on certain implementations */
27enum ranks {
28 rank_char = -2,
29 rank_short = -1,
30 rank_int = 0,
31 rank_long = 1,
32 rank_longlong = 2
33};
34
35#define MIN_RANK rank_char
36#define MAX_RANK rank_longlong
37
38#define INTMAX_RANK rank_longlong
39#define SIZE_T_RANK rank_long
40#define PTRDIFF_T_RANK rank_long
41
42#define EMIT(x) ({ if (o<n){*q++ = (x);} o++; })
43
44static size_t
45format_int(char *q, size_t n, uintmax_t val, enum flags flags,
46 int base, int width, int prec)
47{
48 char *qq;
49 size_t o = 0, oo;
50 static const char lcdigits[] = "0123456789abcdef";
51 static const char ucdigits[] = "0123456789ABCDEF";
52 const char *digits;
53 uintmax_t tmpval;
54 int minus = 0;
55 int ndigits = 0, nchars;
56 int tickskip, b4tick;
57
58 /* Select type of digits */
59 digits = (flags & FL_UPPER) ? ucdigits : lcdigits;
60
61 /* If signed, separate out the minus */
62 if (flags & FL_SIGNED && (intmax_t) val < 0) {
63 minus = 1;
64 val = (uintmax_t) (-(intmax_t) val);
65 }
66
67 /* Count the number of digits needed. This returns zero for 0. */
68 tmpval = val;
69 while (tmpval) {
70 tmpval /= base;
71 ndigits++;
72 }
73
74 /* Adjust ndigits for size of output */
75
76 if (flags & FL_HASH && base == 8) {
77 if (prec < ndigits + 1)
78 prec = ndigits + 1;
79 }
80
81 if (ndigits < prec) {
82 ndigits = prec; /* Mandatory number padding */
83 } else if (val == 0) {
84 ndigits = 1; /* Zero still requires space */
85 }
86
87 /* For ', figure out what the skip should be */
88 if (flags & FL_TICK) {
89 tickskip = (base == 16) ? 4 : 3;
90 } else {
91 tickskip = ndigits; /* No tick marks */
92 }
93
94 /* Tick marks aren't digits, but generated by the number converter */
95 ndigits += (ndigits - 1) / tickskip;
96
97 /* Now compute the number of nondigits */
98 nchars = ndigits;
99
100 if (minus || (flags & (FL_PLUS | FL_SPACE)))
101 nchars++; /* Need space for sign */
102 if ((flags & FL_HASH) && base == 16) {
103 nchars += 2; /* Add 0x for hex */
104 }
105
106 /* Emit early space padding */
107 if (!(flags & (FL_MINUS | FL_ZERO)) && width > nchars) {
108 while (width > nchars) {
109 EMIT(' ');
110 width--;
111 }
112 }
113
114 /* Emit nondigits */
115 if (minus)
116 EMIT('-');
117 else if (flags & FL_PLUS)
118 EMIT('+');
119 else if (flags & FL_SPACE)
120 EMIT(' ');
121
122 if ((flags & FL_HASH) && base == 16) {
123 EMIT('0');
124 EMIT((flags & FL_UPPER) ? 'X' : 'x');
125 }
126
127 /* Emit zero padding */
128 if ((flags & (FL_MINUS | FL_ZERO)) == FL_ZERO && width > ndigits) {
129 while (width > nchars) {
130 EMIT('0');
131 width--;
132 }
133 }
134
135 /* Generate the number. This is done from right to left. */
136 q += ndigits; /* Advance the pointer to end of number */
137 o += ndigits;
138 qq = q;
139 oo = o; /* Temporary values */
140
141 b4tick = tickskip;
142 while (ndigits > 0) {
143 if (!b4tick--) {
144 qq--;
145 oo--;
146 ndigits--;
147 if (oo < n)
148 *qq = '_';
149 b4tick = tickskip - 1;
150 }
151 qq--;
152 oo--;
153 ndigits--;
154 if (oo < n)
155 *qq = digits[val % base];
156 val /= base;
157 }
158
159 /* Emit late space padding */
160 while ((flags & FL_MINUS) && width > nchars) {
161 EMIT(' ');
162 width--;
163 }
164
165 return o;
166}
167
168int vsnprintf(char *buffer, size_t n, const char *format, va_list ap)
169{
170 const char *p = format;
171 char ch;
172 char *q = buffer;
173 size_t o = 0; /* Number of characters output */
174 uintmax_t val = 0;
175 int rank = rank_int; /* Default rank */
176 int width = 0;
177 int prec = -1;
178 int base;
179 size_t sz;
180 enum flags flags = 0;
181 enum {
182 st_normal, /* Ground state */
183 st_flags, /* Special flags */
184 st_width, /* Field width */
185 st_prec, /* Field precision */
186 st_modifiers /* Length or conversion modifiers */
187 } state = st_normal;
188 const char *sarg; /* %s string argument */
189 char carg; /* %c char argument */
190 int slen; /* String length */
191
192 while ((ch = *p++)) {
193 switch (state) {
194 case st_normal:
195 if (ch == '%') {
196 state = st_flags;
197 flags = 0;
198 rank = rank_int;
199 width = 0;
200 prec = -1;
201 } else {
202 EMIT(ch);
203 }
204 break;
205
206 case st_flags:
207 switch (ch) {
208 case '-':
209 flags |= FL_MINUS;
210 break;
211 case '+':
212 flags |= FL_PLUS;
213 break;
214 case '\'':
215 flags |= FL_TICK;
216 break;
217 case ' ':
218 flags |= FL_SPACE;
219 break;
220 case '#':
221 flags |= FL_HASH;
222 break;
223 case '0':
224 flags |= FL_ZERO;
225 break;
226 default:
227 state = st_width;
228 p--; /* Process this character again */
229 break;
230 }
231 break;
232
233 case st_width:
234 if (ch >= '0' && ch <= '9') {
235 width = width * 10 + (ch - '0');
236 } else if (ch == '*') {
237 width = va_arg(ap, int);
238 if (width < 0) {
239 width = -width;
240 flags |= FL_MINUS;
241 }
242 } else if (ch == '.') {
243 prec = 0; /* Precision given */
244 state = st_prec;
245 } else {
246 state = st_modifiers;
247 p--; /* Process this character again */
248 }
249 break;
250
251 case st_prec:
252 if (ch >= '0' && ch <= '9') {
253 prec = prec * 10 + (ch - '0');
254 } else if (ch == '*') {
255 prec = va_arg(ap, int);
256 if (prec < 0)
257 prec = -1;
258 } else {
259 state = st_modifiers;
260 p--; /* Process this character again */
261 }
262 break;
263
264 case st_modifiers:
265 switch (ch) {
266 /* Length modifiers - nonterminal sequences */
267 case 'h':
268 rank--; /* Shorter rank */
269 break;
270 case 'l':
271 rank++; /* Longer rank */
272 break;
273 case 'j':
274 rank = INTMAX_RANK;
275 break;
276 case 'z':
277 rank = SIZE_T_RANK;
278 break;
279 case 't':
280 rank = PTRDIFF_T_RANK;
281 break;
282 case 'L':
283 case 'q':
284 rank += 2;
285 break;
286 default:
287 /* Output modifiers - terminal sequences */
288 state = st_normal; /* Next state will be normal */
289 if (rank < MIN_RANK) /* Canonicalize rank */
290 rank = MIN_RANK;
291 else if (rank > MAX_RANK)
292 rank = MAX_RANK;
293
294 switch (ch) {
295 case 'P': /* Upper case pointer */
296 flags |= FL_UPPER;
297 /* fall through */
298 case 'p': /* Pointer */
299 base = 16;
300 prec = (CHAR_BIT * sizeof(void *) + 3) / 4;
301 flags |= FL_HASH;
302 val = (uintmax_t) (uintptr_t) va_arg(ap, void *);
303 goto is_integer;
304
305 case 'd': /* Signed decimal output */
306 case 'i':
307 base = 10;
308 flags |= FL_SIGNED;
309 switch (rank) {
310 case rank_char:
311 /* Yes, all these casts are needed... */
312 val =
313 (uintmax_t) (intmax_t) (signed char)va_arg(ap,
314 signed
315 int);
316 break;
317 case rank_short:
318 val =
319 (uintmax_t) (intmax_t) (signed short)va_arg(ap,
320 signed
321 int);
322 break;
323 case rank_int:
324 val = (uintmax_t) (intmax_t) va_arg(ap, signed int);
325 break;
326 case rank_long:
327 val = (uintmax_t) (intmax_t) va_arg(ap, signed long);
328 break;
329 case rank_longlong:
330 val =
331 (uintmax_t) (intmax_t) va_arg(ap, signed long long);
332 break;
333 }
334 goto is_integer;
335 case 'o': /* Octal */
336 base = 8;
337 goto is_unsigned;
338 case 'u': /* Unsigned decimal */
339 base = 10;
340 goto is_unsigned;
341 case 'X': /* Upper case hexadecimal */
342 flags |= FL_UPPER;
343 /* fall through */
344 case 'x': /* Hexadecimal */
345 base = 16;
346 goto is_unsigned;
347
348is_unsigned:
349 switch (rank) {
350 case rank_char:
351 val =
352 (uintmax_t) (unsigned char)va_arg(ap, unsigned int);
353 break;
354 case rank_short:
355 val =
356 (uintmax_t) (unsigned short)va_arg(ap,
357 unsigned int);
358 break;
359 case rank_int:
360 val = (uintmax_t) va_arg(ap, unsigned int);
361 break;
362 case rank_long:
363 val = (uintmax_t) va_arg(ap, unsigned long);
364 break;
365 case rank_longlong:
366 val = (uintmax_t) va_arg(ap, unsigned long long);
367 break;
368 }
369 /* fall through */
370
371is_integer:
372 sz = format_int(q, (o < n) ? n - o : 0, val, flags, base,
373 width, prec);
374 q += sz;
375 o += sz;
376 break;
377
378 case 'c': /* Character */
379 carg = (char)va_arg(ap, int);
380 sarg = &carg;
381 slen = 1;
382 goto is_string;
383 case 's': /* String */
384 sarg = va_arg(ap, const char *);
385 sarg = sarg ? sarg : "(null)";
386 slen = strlen(sarg);
387 goto is_string;
388
389is_string:
390 {
391 char sch;
392 int i;
393
394 if (prec != -1 && slen > prec)
395 slen = prec;
396
397 if (width > slen && !(flags & FL_MINUS)) {
398 char pad = (flags & FL_ZERO) ? '0' : ' ';
399 while (width > slen) {
400 EMIT(pad);
401 width--;
402 }
403 }
404 for (i = slen; i; i--) {
405 sch = *sarg++;
406 EMIT(sch);
407 }
408 if (width > slen && (flags & FL_MINUS)) {
409 while (width > slen) {
410 EMIT(' ');
411 width--;
412 }
413 }
414 }
415 break;
416
417 case 'n': /* Output the number of characters written */
418 {
419 switch (rank) {
420 case rank_char:
421 *va_arg(ap, signed char *) = o;
422 break;
423 case rank_short:
424 *va_arg(ap, signed short *) = o;
425 break;
426 case rank_int:
427 *va_arg(ap, signed int *) = o;
428 break;
429 case rank_long:
430 *va_arg(ap, signed long *) = o;
431 break;
432 case rank_longlong:
433 *va_arg(ap, signed long long *) = o;
434 break;
435 }
436 }
437 break;
438
439 default: /* Anything else, including % */
440 EMIT(ch);
441 break;
442 }
443 }
444 }
445 }
446
447 /* Null-terminate the string */
448 if (o < n)
449 *q = '\0'; /* No overflow */
450 else if (n > 0)
451 buffer[n - 1] = '\0'; /* Overflow - terminate at end of buffer */
452
453 return o;
454}