/src/frr/lib/printf/vfprintf.c
Line | Count | Source |
1 | | /*- |
2 | | * SPDX-License-Identifier: BSD-3-Clause |
3 | | * |
4 | | * Copyright (c) 1990, 1993 |
5 | | * The Regents of the University of California. All rights reserved. |
6 | | * |
7 | | * This code is derived from software contributed to Berkeley by |
8 | | * Chris Torek. |
9 | | * |
10 | | * Copyright (c) 2011 The FreeBSD Foundation |
11 | | * All rights reserved. |
12 | | * Portions of this software were developed by David Chisnall |
13 | | * under sponsorship from the FreeBSD Foundation. |
14 | | * |
15 | | * Redistribution and use in source and binary forms, with or without |
16 | | * modification, are permitted provided that the following conditions |
17 | | * are met: |
18 | | * 1. Redistributions of source code must retain the above copyright |
19 | | * notice, this list of conditions and the following disclaimer. |
20 | | * 2. Redistributions in binary form must reproduce the above copyright |
21 | | * notice, this list of conditions and the following disclaimer in the |
22 | | * documentation and/or other materials provided with the distribution. |
23 | | * 3. Neither the name of the University nor the names of its contributors |
24 | | * may be used to endorse or promote products derived from this software |
25 | | * without specific prior written permission. |
26 | | * |
27 | | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
28 | | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
29 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
30 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
31 | | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
32 | | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
33 | | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
34 | | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
35 | | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
36 | | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
37 | | * SUCH DAMAGE. |
38 | | */ |
39 | | |
40 | | #ifdef HAVE_CONFIG_H |
41 | | #include "config.h" |
42 | | #endif |
43 | | |
44 | | #ifdef HAVE_SYS_CDEFS_H |
45 | | #include <sys/cdefs.h> |
46 | | #endif |
47 | | |
48 | | /* |
49 | | * Actual printf innards. |
50 | | * |
51 | | * This code is large and complicated... |
52 | | */ |
53 | | |
54 | | #include <sys/types.h> |
55 | | #include <sys/uio.h> |
56 | | |
57 | | #include <ctype.h> |
58 | | #include <errno.h> |
59 | | #include <limits.h> |
60 | | #include <stddef.h> |
61 | | #include <stdint.h> |
62 | | #include <stdio.h> |
63 | | #include <stdlib.h> |
64 | | #include <string.h> |
65 | | #include <wchar.h> |
66 | | |
67 | | #include <stdarg.h> |
68 | | |
69 | | #include "printflocal.h" |
70 | | |
71 | 7.13k | #define CHAR char |
72 | | #include "printfcommon.h" |
73 | | |
74 | | #ifdef WCHAR_SUPPORT |
75 | | /* |
76 | | * Convert a wide character string argument for the %ls format to a multibyte |
77 | | * string representation. If not -1, prec specifies the maximum number of |
78 | | * bytes to output, and also means that we can't assume that the wide char. |
79 | | * string ends is null-terminated. |
80 | | */ |
81 | | static char * |
82 | | __wcsconv(wchar_t *wcsarg, int prec) |
83 | | { |
84 | | static const mbstate_t initial; |
85 | | mbstate_t mbs; |
86 | | char buf[MB_LEN_MAX]; |
87 | | wchar_t *p; |
88 | | char *convbuf; |
89 | | size_t clen, nbytes; |
90 | | |
91 | | /* Allocate space for the maximum number of bytes we could output. */ |
92 | | if (prec < 0) { |
93 | | p = wcsarg; |
94 | | mbs = initial; |
95 | | nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs); |
96 | | if (nbytes == (size_t)-1) |
97 | | return NULL; |
98 | | } else { |
99 | | /* |
100 | | * Optimisation: if the output precision is small enough, |
101 | | * just allocate enough memory for the maximum instead of |
102 | | * scanning the string. |
103 | | */ |
104 | | if (prec < 128) |
105 | | nbytes = prec; |
106 | | else { |
107 | | nbytes = 0; |
108 | | p = wcsarg; |
109 | | mbs = initial; |
110 | | for (;;) { |
111 | | clen = wcrtomb(buf, *p++, &mbs); |
112 | | if (clen == 0 || clen == (size_t)-1 || |
113 | | nbytes + clen > (size_t)prec) |
114 | | break; |
115 | | nbytes += clen; |
116 | | } |
117 | | } |
118 | | } |
119 | | if ((convbuf = malloc(nbytes + 1)) == NULL) |
120 | | return NULL; |
121 | | |
122 | | /* Fill the output buffer. */ |
123 | | p = wcsarg; |
124 | | mbs = initial; |
125 | | if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p, |
126 | | nbytes, &mbs)) == (size_t)-1) { |
127 | | free(convbuf); |
128 | | return NULL; |
129 | | } |
130 | | convbuf[nbytes] = '\0'; |
131 | | return (convbuf); |
132 | | } |
133 | | #endif /* WCHAR_SUPPORT */ |
134 | | |
135 | | /* |
136 | | * The size of the buffer we use as scratch space for integer |
137 | | * conversions, among other things. We need enough space to |
138 | | * write a uintmax_t in octal (plus one byte). |
139 | | */ |
140 | | #if UINTMAX_MAX <= UINT64_MAX |
141 | 28.5k | #define BUF 80 |
142 | | #else |
143 | | #error "BUF must be large enough to format a uintmax_t" |
144 | | #endif |
145 | | |
146 | | /* |
147 | | * Non-MT-safe version |
148 | | */ |
149 | | ssize_t vbprintfrr(struct fbuf *cb_in, const char *fmt0, va_list ap) |
150 | | __attribute__((no_sanitize("unsigned-integer-overflow"))) |
151 | 417k | { |
152 | 417k | const char *fmt; /* format string */ |
153 | 417k | int ch; /* character from fmt */ |
154 | 417k | int n, n2; /* handy integer (short term usage) */ |
155 | 417k | const char *cp; /* handy char pointer (short term usage) */ |
156 | 417k | int flags; /* flags as above */ |
157 | 417k | int ret; /* return value accumulator */ |
158 | 417k | int width; /* width from format (%8d), or 0 */ |
159 | 417k | int prec; /* precision from format; <0 for N/A */ |
160 | 417k | int saved_errno; |
161 | 417k | char sign; /* sign prefix (' ', '+', '-', or \0) */ |
162 | | |
163 | 417k | u_long ulval = 0; /* integer arguments %[diouxX] */ |
164 | 417k | uintmax_t ujval = 0; /* %j, %ll, %q, %t, %z integers */ |
165 | 417k | void *ptrval; /* %p */ |
166 | 417k | int base; /* base for [diouxX] conversion */ |
167 | 417k | int dprec; /* a copy of prec if [diouxX], 0 otherwise */ |
168 | 417k | int realsz; /* field size expanded by dprec, sign, etc */ |
169 | 417k | int size; /* size of converted field or string */ |
170 | 417k | int prsize; /* max size of printed field */ |
171 | 417k | const char *xdigs; /* digits for %[xX] conversion */ |
172 | 417k | struct io_state io; /* I/O buffering state */ |
173 | 417k | char buf[BUF]; /* buffer with space for digits of uintmax_t */ |
174 | 417k | char ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ |
175 | 417k | union arg *argtable; /* args, built due to positional arg */ |
176 | 417k | union arg statargtable [STATIC_ARG_TBL_SIZE]; |
177 | 417k | int nextarg; /* 1-based argument index */ |
178 | 417k | va_list orgap; /* original argument pointer */ |
179 | 417k | char *convbuf; /* wide to multibyte conversion result */ |
180 | 417k | char *extstart = NULL; /* where printfrr_ext* started printing */ |
181 | 417k | struct fbuf cb_copy, *cb; |
182 | 417k | struct fmt_outpos *opos; |
183 | | |
184 | 417k | static const char xdigs_lower[16] = "0123456789abcdef"; |
185 | 417k | static const char xdigs_upper[16] = "0123456789ABCDEF"; |
186 | | |
187 | | /* BEWARE, these `goto error' on error. */ |
188 | 417k | #define PRINT(ptr, len) { \ |
189 | 400k | if (io_print(&io, (ptr), (len))) \ |
190 | 400k | goto error; \ |
191 | 400k | } |
192 | 417k | #define PAD(howmany, with) { \ |
193 | 78.4k | if (io_pad(&io, (howmany), (with))) \ |
194 | 78.4k | goto error; \ |
195 | 78.4k | } |
196 | 417k | #define PRINTANDPAD(p, ep, len, with) { \ |
197 | 417k | if (io_printandpad(&io, (p), (ep), (len), (with))) \ |
198 | 417k | goto error; \ |
199 | 417k | } |
200 | 956k | #define FLUSH() do { } while (0) |
201 | | |
202 | | /* |
203 | | * Get the argument indexed by nextarg. If the argument table is |
204 | | * built, use it to get the argument. If its not, get the next |
205 | | * argument (and arguments must be gotten sequentially). |
206 | | */ |
207 | 417k | #define GETARG(type) \ |
208 | 538k | ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ |
209 | 538k | (nextarg++, va_arg(ap, type))) |
210 | | |
211 | | /* |
212 | | * To extend shorts properly, we need both signed and unsigned |
213 | | * argument extraction methods. |
214 | | */ |
215 | 417k | #define SARG() \ |
216 | 417k | (flags&LONGINT ? GETARG(long) : \ |
217 | 5.28k | flags&SHORTINT ? (long)(short)GETARG(int) : \ |
218 | 5.27k | flags&CHARINT ? (long)(signed char)GETARG(int) : \ |
219 | 5.27k | (long)GETARG(int)) |
220 | 417k | #define UARG() \ |
221 | 417k | (flags&LONGINT ? GETARG(u_long) : \ |
222 | 1.84k | flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ |
223 | 86 | flags&CHARINT ? (u_long)(u_char)GETARG(int) : \ |
224 | 86 | (u_long)GETARG(u_int)) |
225 | 417k | #define INTMAX_SIZE (INTMAXT|SIZET|PTRDIFFT|LLONGINT|LONGDBL) |
226 | 417k | #define SJARG() \ |
227 | 417k | (flags&LONGDBL ? GETARG(int64_t) : \ |
228 | 4 | flags&INTMAXT ? GETARG(intmax_t) : \ |
229 | 4 | flags&SIZET ? (intmax_t)GETARG(ssize_t) : \ |
230 | 0 | flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \ |
231 | 0 | (intmax_t)GETARG(long long)) |
232 | 417k | #define UJARG() \ |
233 | 417k | (flags&LONGDBL ? GETARG(uint64_t) : \ |
234 | 0 | flags&INTMAXT ? GETARG(uintmax_t) : \ |
235 | 0 | flags&SIZET ? (uintmax_t)GETARG(size_t) : \ |
236 | 0 | flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \ |
237 | 0 | (uintmax_t)GETARG(unsigned long long)) |
238 | | |
239 | | /* |
240 | | * Get * arguments, including the form *nn$. Preserve the nextarg |
241 | | * that the argument can be gotten once the type is determined. |
242 | | */ |
243 | 417k | #define GETASTER(val) \ |
244 | 417k | n2 = 0; \ |
245 | 0 | cp = fmt; \ |
246 | 0 | while (is_digit(*cp)) { \ |
247 | 0 | n2 = 10 * n2 + to_digit(*cp); \ |
248 | 0 | cp++; \ |
249 | 0 | } \ |
250 | 0 | if (*cp == '$') { \ |
251 | 0 | int hold = nextarg; \ |
252 | 0 | if (argtable == NULL) { \ |
253 | 0 | argtable = statargtable; \ |
254 | 0 | if (_frr_find_arguments (fmt0, orgap, &argtable)) { \ |
255 | 0 | ret = EOF; \ |
256 | 0 | goto error; \ |
257 | 0 | } \ |
258 | 0 | } \ |
259 | 0 | nextarg = n2; \ |
260 | 0 | val = GETARG (int); \ |
261 | 0 | nextarg = hold; \ |
262 | 0 | fmt = ++cp; \ |
263 | 0 | } else { \ |
264 | 0 | val = GETARG (int); \ |
265 | 0 | } |
266 | | |
267 | 417k | xdigs = xdigs_lower; |
268 | 417k | saved_errno = errno; |
269 | 417k | convbuf = NULL; |
270 | 417k | fmt = (char *)fmt0; |
271 | 417k | argtable = NULL; |
272 | 417k | nextarg = 1; |
273 | 417k | va_copy(orgap, ap); |
274 | | |
275 | 417k | if (cb_in) { |
276 | | /* prevent printfrr exts from polluting cb->outpos */ |
277 | 417k | cb_copy = *cb_in; |
278 | 417k | cb_copy.outpos = NULL; |
279 | 417k | cb_copy.outpos_n = cb_copy.outpos_i = 0; |
280 | 417k | cb = &cb_copy; |
281 | 417k | } else |
282 | 0 | cb = NULL; |
283 | | |
284 | 417k | io_init(&io, cb); |
285 | 417k | ret = 0; |
286 | | |
287 | | /* |
288 | | * Scan the format for conversions (`%' character). |
289 | | */ |
290 | 956k | for (;;) { |
291 | 1.84M | for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) |
292 | 886k | /* void */; |
293 | 956k | if ((n = fmt - cp) != 0) { |
294 | 359k | if ((unsigned)ret + n > INT_MAX) { |
295 | 0 | ret = EOF; |
296 | 0 | errno = EOVERFLOW; |
297 | 0 | goto error; |
298 | 0 | } |
299 | 359k | PRINT(cp, n); |
300 | 359k | ret += n; |
301 | 359k | } |
302 | 956k | if (ch == '\0') |
303 | 417k | goto done; |
304 | 538k | fmt++; /* skip over '%' */ |
305 | | |
306 | 538k | flags = 0; |
307 | 538k | dprec = 0; |
308 | 538k | width = -1; |
309 | 538k | prec = -1; |
310 | 538k | sign = '\0'; |
311 | 538k | ox[1] = '\0'; |
312 | | |
313 | 538k | if (cb_in && cb_in->outpos_i < cb_in->outpos_n) |
314 | 3.37k | opos = &cb_in->outpos[cb_in->outpos_i]; |
315 | 534k | else |
316 | 534k | opos = NULL; |
317 | | |
318 | 547k | rflag: ch = *fmt++; |
319 | 552k | reswitch: switch (ch) { |
320 | 0 | case ' ': |
321 | | /*- |
322 | | * ``If the space and + flags both appear, the space |
323 | | * flag will be ignored.'' |
324 | | * -- ANSI X3J11 |
325 | | */ |
326 | 0 | if (!sign) |
327 | 0 | sign = ' '; |
328 | 0 | goto rflag; |
329 | 0 | case '#': |
330 | 0 | flags |= ALT; |
331 | 0 | goto rflag; |
332 | 0 | case '*': |
333 | | /*- |
334 | | * ``A negative field width argument is taken as a |
335 | | * - flag followed by a positive field width.'' |
336 | | * -- ANSI X3J11 |
337 | | * They don't exclude field widths read from args. |
338 | | */ |
339 | 0 | GETASTER (width); |
340 | 0 | if (width >= 0) |
341 | 0 | goto rflag; |
342 | 0 | width = -width; |
343 | | /* FALLTHROUGH */ |
344 | 0 | case '-': |
345 | 0 | flags |= LADJUST; |
346 | 0 | goto rflag; |
347 | 1.75k | case '+': |
348 | 1.75k | sign = '+'; |
349 | 1.75k | goto rflag; |
350 | 0 | case '\'': |
351 | 0 | flags |= GROUPING; |
352 | 0 | goto rflag; |
353 | 0 | case '.': |
354 | 0 | if ((ch = *fmt++) == '*') { |
355 | 0 | GETASTER (prec); |
356 | 0 | goto rflag; |
357 | 0 | } |
358 | 0 | prec = 0; |
359 | 0 | while (is_digit(ch)) { |
360 | 0 | prec = 10 * prec + to_digit(ch); |
361 | 0 | ch = *fmt++; |
362 | 0 | } |
363 | 0 | goto reswitch; |
364 | 5.27k | case '0': |
365 | | /*- |
366 | | * ``Note that 0 is taken as a flag, not as the |
367 | | * beginning of a field width.'' |
368 | | * -- ANSI X3J11 |
369 | | */ |
370 | 5.27k | flags |= ZEROPAD; |
371 | 5.27k | goto rflag; |
372 | 3.51k | case '1': case '2': case '3': case '4': |
373 | 5.27k | case '5': case '6': case '7': case '8': case '9': |
374 | 5.27k | n = 0; |
375 | 5.27k | do { |
376 | 5.27k | n = 10 * n + to_digit(ch); |
377 | 5.27k | ch = *fmt++; |
378 | 5.27k | } while (is_digit(ch)); |
379 | 5.27k | if (ch == '$') { |
380 | 0 | nextarg = n; |
381 | 0 | if (argtable == NULL) { |
382 | 0 | argtable = statargtable; |
383 | 0 | if (_frr_find_arguments (fmt0, orgap, |
384 | 0 | &argtable)) { |
385 | 0 | ret = EOF; |
386 | 0 | goto error; |
387 | 0 | } |
388 | 0 | } |
389 | 0 | goto rflag; |
390 | 0 | } |
391 | 5.27k | width = n; |
392 | 5.27k | goto reswitch; |
393 | 0 | case 'L': |
394 | 0 | flags |= LONGDBL; |
395 | 0 | goto rflag; |
396 | 0 | case 'h': |
397 | 0 | if (flags & SHORTINT) { |
398 | 0 | flags &= ~SHORTINT; |
399 | 0 | flags |= CHARINT; |
400 | 0 | } else |
401 | 0 | flags |= SHORTINT; |
402 | 0 | goto rflag; |
403 | 4 | case 'j': |
404 | 4 | flags |= INTMAXT; |
405 | 4 | goto rflag; |
406 | 1.76k | case 'l': |
407 | 1.76k | if (flags & LONGINT) { |
408 | 0 | flags &= ~LONGINT; |
409 | 0 | flags |= LLONGINT; |
410 | 0 | } else |
411 | 1.76k | flags |= LONGINT; |
412 | 1.76k | goto rflag; |
413 | 0 | case 'q': |
414 | 0 | flags |= LLONGINT; /* not necessarily */ |
415 | 0 | goto rflag; |
416 | 0 | case 't': |
417 | 0 | flags |= PTRDIFFT; |
418 | 0 | goto rflag; |
419 | 0 | case 'z': |
420 | 0 | flags |= SIZET; |
421 | 0 | goto rflag; |
422 | 0 | case 'C': |
423 | 0 | flags |= LONGINT; |
424 | | /*FALLTHROUGH*/ |
425 | 0 | case 'c': |
426 | | #ifdef WCHAR_SUPPORT |
427 | | if (flags & LONGINT) { |
428 | | static const mbstate_t initial; |
429 | | mbstate_t mbs; |
430 | | size_t mbseqlen; |
431 | | |
432 | | mbs = initial; |
433 | | mbseqlen = wcrtomb(cp = buf, |
434 | | (wchar_t)GETARG(wint_t), &mbs); |
435 | | if (mbseqlen == (size_t)-1) { |
436 | | goto error; |
437 | | } |
438 | | size = (int)mbseqlen; |
439 | | } else |
440 | | #endif /* WCHAR_SUPPORT */ |
441 | 0 | { |
442 | 0 | buf[0] = GETARG(int); |
443 | 0 | cp = buf; |
444 | 0 | size = 1; |
445 | 0 | } |
446 | 0 | sign = '\0'; |
447 | 0 | break; |
448 | 0 | case 'D': |
449 | 0 | flags |= LONGINT; |
450 | | /*FALLTHROUGH*/ |
451 | 5.28k | case 'd': |
452 | 5.28k | case 'i': |
453 | 5.28k | if (flags & INTMAX_SIZE) |
454 | 4 | ujval = SJARG(); |
455 | 5.28k | else |
456 | 5.28k | ulval = (u_long)SARG(); |
457 | | |
458 | 5.28k | if (printfrr_ext_char(fmt[0])) { |
459 | 0 | struct printfrr_eargs ea = { |
460 | 0 | .fmt = fmt, |
461 | 0 | .precision = prec, |
462 | 0 | .width = width, |
463 | 0 | .alt_repr = !!(flags & ALT), |
464 | 0 | .leftadj = !!(flags & LADJUST), |
465 | 0 | }; |
466 | |
|
467 | 0 | if (cb) |
468 | 0 | extstart = cb->pos; |
469 | |
|
470 | 0 | size = printfrr_exti(cb, &ea, |
471 | 0 | (flags & INTMAX_SIZE) ? ujval |
472 | 0 | : (uintmax_t)ulval); |
473 | 0 | if (size >= 0) { |
474 | 0 | fmt = ea.fmt; |
475 | 0 | width = ea.width; |
476 | 0 | goto ext_printed; |
477 | 0 | } |
478 | 0 | } |
479 | 5.28k | if (flags & INTMAX_SIZE) { |
480 | 4 | if ((intmax_t)ujval < 0) { |
481 | 0 | ujval = -ujval; |
482 | 0 | sign = '-'; |
483 | 0 | } |
484 | 5.28k | } else { |
485 | 5.28k | if ((long)ulval < 0) { |
486 | 0 | ulval = (~ulval) + 1; |
487 | 0 | sign = '-'; |
488 | 0 | } |
489 | 5.28k | } |
490 | 5.28k | base = 10; |
491 | 5.28k | goto number; |
492 | 0 | #ifndef NO_FLOATING_POINT |
493 | 0 | case 'a': |
494 | 0 | case 'A': |
495 | 0 | case 'e': |
496 | 0 | case 'E': |
497 | 0 | case 'f': |
498 | 0 | case 'F': |
499 | 0 | case 'g': |
500 | 0 | case 'G': |
501 | 0 | if (flags & LONGDBL) { |
502 | 0 | long double arg = GETARG(long double); |
503 | 0 | char fmt[6] = "%.*L"; |
504 | 0 | fmt[4] = ch; |
505 | 0 | fmt[5] = '\0'; |
506 | |
|
507 | 0 | #pragma GCC diagnostic push |
508 | 0 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" |
509 | 0 | snprintf(buf, sizeof(buf), fmt, prec, arg); |
510 | 0 | #pragma GCC diagnostic pop |
511 | 0 | } else { |
512 | 0 | double arg = GETARG(double); |
513 | 0 | char fmt[5] = "%.*"; |
514 | 0 | fmt[3] = ch; |
515 | 0 | fmt[4] = '\0'; |
516 | |
|
517 | 0 | #pragma GCC diagnostic push |
518 | 0 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" |
519 | 0 | snprintf(buf, sizeof(buf), fmt, prec, arg); |
520 | 0 | #pragma GCC diagnostic pop |
521 | 0 | } |
522 | 0 | cp = buf; |
523 | | /* for proper padding */ |
524 | 0 | if (*cp == '-') { |
525 | 0 | cp++; |
526 | 0 | sign = '-'; |
527 | 0 | } |
528 | | /* "inf" */ |
529 | 0 | if (!is_digit(*cp) && *cp != '.') |
530 | 0 | flags &= ~ZEROPAD; |
531 | 0 | size = strlen(buf); |
532 | 0 | break; |
533 | 0 | #endif |
534 | 0 | case 'm': |
535 | 0 | cp = strerror(saved_errno); |
536 | 0 | size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); |
537 | 0 | sign = '\0'; |
538 | 0 | break; |
539 | 0 | case 'O': |
540 | 0 | flags |= LONGINT; |
541 | | /*FALLTHROUGH*/ |
542 | 0 | case 'o': |
543 | 0 | if (flags & INTMAX_SIZE) |
544 | 0 | ujval = UJARG(); |
545 | 0 | else |
546 | 0 | ulval = UARG(); |
547 | 0 | base = 8; |
548 | 0 | goto nosign; |
549 | 499k | case 'p': |
550 | | /*- |
551 | | * ``The argument shall be a pointer to void. The |
552 | | * value of the pointer is converted to a sequence |
553 | | * of printable characters, in an implementation- |
554 | | * defined manner.'' |
555 | | * -- ANSI X3J11 |
556 | | */ |
557 | 499k | ptrval = GETARG(void *); |
558 | 499k | if (printfrr_ext_char(fmt[0])) { |
559 | 499k | struct printfrr_eargs ea = { |
560 | 499k | .fmt = fmt, |
561 | 499k | .precision = prec, |
562 | 499k | .width = width, |
563 | 499k | .alt_repr = !!(flags & ALT), |
564 | 499k | .leftadj = !!(flags & LADJUST), |
565 | 499k | }; |
566 | | |
567 | 499k | if (cb) |
568 | 499k | extstart = cb->pos; |
569 | | |
570 | 499k | size = printfrr_extp(cb, &ea, ptrval); |
571 | 499k | if (size >= 0) { |
572 | 499k | fmt = ea.fmt; |
573 | 499k | width = ea.width; |
574 | 499k | goto ext_printed; |
575 | 499k | } |
576 | 499k | } |
577 | 0 | ujval = (uintmax_t)(uintptr_t)ptrval; |
578 | 0 | base = 16; |
579 | 0 | xdigs = xdigs_lower; |
580 | 0 | flags = flags | INTMAXT; |
581 | 0 | ox[1] = 'x'; |
582 | 0 | goto nosign; |
583 | 0 | case 'S': |
584 | 0 | flags |= LONGINT; |
585 | | /*FALLTHROUGH*/ |
586 | 32.1k | case 's': |
587 | | #ifdef WCHAR_SUPPORT |
588 | | if (flags & LONGINT) { |
589 | | wchar_t *wcp; |
590 | | |
591 | | if (convbuf != NULL) |
592 | | free(convbuf); |
593 | | if ((wcp = GETARG(wchar_t *)) == NULL) |
594 | | cp = "(null)"; |
595 | | else { |
596 | | convbuf = __wcsconv(wcp, prec); |
597 | | if (convbuf == NULL) { |
598 | | goto error; |
599 | | } |
600 | | cp = convbuf; |
601 | | } |
602 | | } else |
603 | | #endif |
604 | 32.1k | if ((cp = GETARG(char *)) == NULL) |
605 | 0 | cp = "(null)"; |
606 | 32.1k | size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp); |
607 | 32.1k | sign = '\0'; |
608 | 32.1k | break; |
609 | 0 | case 'U': |
610 | 0 | flags |= LONGINT; |
611 | | /*FALLTHROUGH*/ |
612 | 1.84k | case 'u': |
613 | 1.84k | if (flags & INTMAX_SIZE) |
614 | 0 | ujval = UJARG(); |
615 | 1.84k | else |
616 | 1.84k | ulval = UARG(); |
617 | 1.84k | base = 10; |
618 | 1.84k | goto nosign; |
619 | 0 | case 'X': |
620 | 0 | xdigs = xdigs_upper; |
621 | 0 | goto hex; |
622 | 0 | case 'x': |
623 | 0 | xdigs = xdigs_lower; |
624 | 0 | hex: |
625 | 0 | if (flags & INTMAX_SIZE) |
626 | 0 | ujval = UJARG(); |
627 | 0 | else |
628 | 0 | ulval = UARG(); |
629 | 0 | base = 16; |
630 | | /* leading 0x/X only if non-zero */ |
631 | 0 | if (flags & ALT && |
632 | 0 | (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0)) |
633 | 0 | ox[1] = ch; |
634 | |
|
635 | 0 | flags &= ~GROUPING; |
636 | | /* unsigned conversions */ |
637 | 1.84k | nosign: sign = '\0'; |
638 | | /*- |
639 | | * ``... diouXx conversions ... if a precision is |
640 | | * specified, the 0 flag will be ignored.'' |
641 | | * -- ANSI X3J11 |
642 | | */ |
643 | 7.12k | number: if ((dprec = prec) >= 0) |
644 | 0 | flags &= ~ZEROPAD; |
645 | | |
646 | | /*- |
647 | | * ``The result of converting a zero value with an |
648 | | * explicit precision of zero is no characters.'' |
649 | | * -- ANSI X3J11 |
650 | | * |
651 | | * ``The C Standard is clear enough as is. The call |
652 | | * printf("%#.0o", 0) should print 0.'' |
653 | | * -- Defect Report #151 |
654 | | */ |
655 | 7.12k | cp = buf + BUF; |
656 | 7.12k | if (flags & INTMAX_SIZE) { |
657 | 4 | if (ujval != 0 || prec != 0 || |
658 | 0 | (flags & ALT && base == 8)) |
659 | 4 | cp = __ujtoa(ujval, buf + BUF, base, |
660 | 4 | flags & ALT, xdigs); |
661 | 7.12k | } else { |
662 | 7.12k | if (ulval != 0 || prec != 0 || |
663 | 0 | (flags & ALT && base == 8)) |
664 | 7.12k | cp = __ultoa(ulval, buf + BUF, base, |
665 | 7.12k | flags & ALT, xdigs); |
666 | 7.12k | } |
667 | 7.12k | size = buf + BUF - cp; |
668 | 7.12k | if (size > BUF) /* should never happen */ |
669 | 0 | abort(); |
670 | 7.12k | break; |
671 | 7.12k | default: /* "%?" prints ?, unless ? is NUL */ |
672 | 0 | if (ch == '\0') |
673 | 0 | goto done; |
674 | | /* pretend it was %c with argument ch */ |
675 | 0 | buf[0] = ch; |
676 | 0 | cp = buf; |
677 | 0 | size = 1; |
678 | 0 | sign = '\0'; |
679 | 0 | opos = NULL; |
680 | 0 | break; |
681 | 552k | } |
682 | | |
683 | | /* |
684 | | * All reasonable formats wind up here. At this point, `cp' |
685 | | * points to a string which (if not flags&LADJUST) should be |
686 | | * padded out to `width' places. If flags&ZEROPAD, it should |
687 | | * first be prefixed by any sign or other prefix; otherwise, |
688 | | * it should be blank padded before the prefix is emitted. |
689 | | * After any left-hand padding and prefixing, emit zeroes |
690 | | * required by a decimal [diouxX] precision, then print the |
691 | | * string proper, then emit zeroes required by any leftover |
692 | | * floating precision; finally, if LADJUST, pad with blanks. |
693 | | * |
694 | | * Compute actual size, so we know how much to pad. |
695 | | * size excludes decimal prec; realsz includes it. |
696 | | */ |
697 | 39.2k | if (width < 0) |
698 | 33.9k | width = 0; |
699 | | |
700 | 39.2k | realsz = dprec > size ? dprec : size; |
701 | 39.2k | if (sign) |
702 | 1.75k | realsz++; |
703 | 39.2k | if (ox[1]) |
704 | 0 | realsz += 2; |
705 | | |
706 | 39.2k | prsize = width > realsz ? width : realsz; |
707 | 39.2k | if ((unsigned int)ret + prsize > INT_MAX) { |
708 | 0 | ret = EOF; |
709 | 0 | errno = EOVERFLOW; |
710 | 0 | goto error; |
711 | 0 | } |
712 | | |
713 | | /* right-adjusting blank padding */ |
714 | 39.2k | if ((flags & (LADJUST|ZEROPAD)) == 0) |
715 | 39.2k | PAD(width - realsz, blanks); |
716 | | |
717 | 39.2k | if (opos) |
718 | 3.37k | opos->off_start = cb->pos - cb->buf; |
719 | | |
720 | | /* prefix */ |
721 | 39.2k | if (sign) |
722 | 39.2k | PRINT(&sign, 1); |
723 | | |
724 | 39.2k | if (ox[1]) { /* ox[1] is either x, X, or \0 */ |
725 | 0 | ox[0] = '0'; |
726 | 0 | PRINT(ox, 2); |
727 | 0 | } |
728 | | |
729 | | /* right-adjusting zero padding */ |
730 | 39.2k | if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) |
731 | 39.2k | PAD(width - realsz, zeroes); |
732 | | |
733 | | /* the string or number proper */ |
734 | | /* leading zeroes from decimal precision */ |
735 | 39.2k | PAD(dprec - size, zeroes); |
736 | 39.2k | PRINT(cp, size); |
737 | | |
738 | 39.2k | if (opos) { |
739 | 3.37k | opos->off_end = cb->pos - cb->buf; |
740 | 3.37k | cb_in->outpos_i++; |
741 | 3.37k | } |
742 | | |
743 | | /* left-adjusting padding (always blank) */ |
744 | 39.2k | if (flags & LADJUST) |
745 | 39.2k | PAD(width - realsz, blanks); |
746 | | |
747 | | /* finally, adjust ret */ |
748 | 39.2k | ret += prsize; |
749 | | |
750 | 39.2k | FLUSH(); /* copy out the I/O vectors */ |
751 | 39.2k | continue; |
752 | | |
753 | 499k | ext_printed: |
754 | | /* when we arrive here, a printfrr extension has written to cb |
755 | | * (if non-NULL), but we still need to handle padding. The |
756 | | * original cb->pos is in extstart; the return value from the |
757 | | * ext is in size. |
758 | | * |
759 | | * Keep analogous to code above please. |
760 | | */ |
761 | | |
762 | 499k | if (width < 0) |
763 | 499k | width = 0; |
764 | | |
765 | 499k | realsz = size; |
766 | 499k | prsize = width > realsz ? width : realsz; |
767 | 499k | if ((unsigned int)ret + prsize > INT_MAX) { |
768 | 0 | ret = EOF; |
769 | 0 | errno = EOVERFLOW; |
770 | 0 | goto error; |
771 | 0 | } |
772 | | |
773 | | /* right-adjusting blank padding - need to move the chars |
774 | | * that the extension has already written. Should be very |
775 | | * rare. |
776 | | */ |
777 | 499k | if (cb && width > size && (flags & (LADJUST|ZEROPAD)) == 0) { |
778 | 0 | size_t nwritten = cb->pos - extstart; |
779 | 0 | size_t navail = cb->buf + cb->len - extstart; |
780 | 0 | size_t npad = width - realsz; |
781 | 0 | size_t nmove; |
782 | |
|
783 | 0 | if (navail < npad) |
784 | 0 | navail = 0; |
785 | 0 | else |
786 | 0 | navail -= npad; |
787 | 0 | nmove = MIN(nwritten, navail); |
788 | |
|
789 | 0 | memmove(extstart + npad, extstart, nmove); |
790 | |
|
791 | 0 | cb->pos = extstart; |
792 | 0 | PAD(npad, blanks); |
793 | 0 | cb->pos += nmove; |
794 | 0 | extstart += npad; |
795 | 0 | } |
796 | | |
797 | 499k | io.avail = cb ? cb->len - (cb->pos - cb->buf) : 0; |
798 | | |
799 | 499k | if (opos && extstart <= cb->pos) { |
800 | 0 | opos->off_start = extstart - cb->buf; |
801 | 0 | opos->off_end = cb->pos - cb->buf; |
802 | 0 | cb_in->outpos_i++; |
803 | 0 | } |
804 | | |
805 | | /* left-adjusting padding (always blank) */ |
806 | 499k | if (flags & LADJUST) |
807 | 499k | PAD(width - realsz, blanks); |
808 | | |
809 | | /* finally, adjust ret */ |
810 | 499k | ret += prsize; |
811 | | |
812 | 499k | FLUSH(); /* copy out the I/O vectors */ |
813 | 499k | } |
814 | 417k | done: |
815 | 417k | FLUSH(); |
816 | 417k | error: |
817 | 417k | va_end(orgap); |
818 | 417k | if (convbuf != NULL) |
819 | 0 | free(convbuf); |
820 | 417k | if ((argtable != NULL) && (argtable != statargtable)) |
821 | 0 | free (argtable); |
822 | 417k | if (cb_in) |
823 | 417k | cb_in->pos = cb->pos; |
824 | 417k | return (ret); |
825 | | /* NOTREACHED */ |
826 | 417k | } |
827 | | |