blob: 647b59af1f86bf5abea6d199e1b9706b2ffa763a [file] [log] [blame]
Elliott Hughes506c6de2016-01-15 16:30:18 -08001/* $OpenBSD: vfwprintf.c,v 1.15 2015/12/28 22:08:18 mmcc Exp $ */
Elliott Hughes94336d82014-04-29 17:39:29 -07002/*-
3 * Copyright (c) 1990 The Regents of the University of California.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Chris Torek.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34/*
35 * Actual wprintf innards.
36 *
37 * This code is large and complicated...
38 */
39
Elliott Hughes94336d82014-04-29 17:39:29 -070040#include <sys/mman.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070041#include <sys/types.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070042
43#include <errno.h>
44#include <langinfo.h>
45#include <limits.h>
46#include <stdarg.h>
47#include <stddef.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070048#include <stdint.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070049#include <stdio.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070050#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070053#include <wchar.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070054
Elliott Hughes94336d82014-04-29 17:39:29 -070055#include "fvwrite.h"
Elliott Hughesc8f2c522017-10-31 13:07:51 -070056#include "local.h"
Elliott Hughes94336d82014-04-29 17:39:29 -070057
Elliott Hughes94336d82014-04-29 17:39:29 -070058union arg {
Elliott Hughesc8f2c522017-10-31 13:07:51 -070059 int intarg;
60 unsigned int uintarg;
61 long longarg;
62 unsigned long ulongarg;
63 long long longlongarg;
64 unsigned long long ulonglongarg;
65 ptrdiff_t ptrdiffarg;
66 size_t sizearg;
67 ssize_t ssizearg;
68 intmax_t intmaxarg;
69 uintmax_t uintmaxarg;
70 void* pvoidarg;
71 char* pchararg;
72 signed char* pschararg;
73 short* pshortarg;
74 int* pintarg;
75 long* plongarg;
76 long long* plonglongarg;
77 ptrdiff_t* pptrdiffarg;
78 ssize_t* pssizearg;
79 intmax_t* pintmaxarg;
80 double doublearg;
81 long double longdoublearg;
82 wint_t wintarg;
83 wchar_t* pwchararg;
Elliott Hughes94336d82014-04-29 17:39:29 -070084};
85
Elliott Hughesc8f2c522017-10-31 13:07:51 -070086static int __find_arguments(const wchar_t* fmt0, va_list ap, union arg** argtable,
87 size_t* argtablesiz);
88static int __grow_type_table(unsigned char** typetable, int* tablesize);
Elliott Hughes94336d82014-04-29 17:39:29 -070089
90/*
91 * Helper function for `fprintf to unbuffered unix file': creates a
92 * temporary buffer. We only work on write-only files; this avoids
93 * worries about ungetc buffers and so forth.
94 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070095static int __sbprintf(FILE* fp, const wchar_t* fmt, va_list ap) {
96 int ret;
97 FILE fake;
98 struct __sfileext fakeext;
99 unsigned char buf[BUFSIZ];
Elliott Hughes94336d82014-04-29 17:39:29 -0700100
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700101 _FILEEXT_SETUP(&fake, &fakeext);
102 /* copy the important variables */
103 fake._flags = fp->_flags & ~__SNBF;
104 fake._file = fp->_file;
105 fake._cookie = fp->_cookie;
106 fake._write = fp->_write;
Elliott Hughes94336d82014-04-29 17:39:29 -0700107
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700108 /* set up the buffer */
109 fake._bf._base = fake._p = buf;
110 fake._bf._size = fake._w = sizeof(buf);
111 fake._lbfsize = 0; /* not actually used, but Just In Case */
Elliott Hughes94336d82014-04-29 17:39:29 -0700112
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700113 /* do the work, then copy any error status */
114 ret = __vfwprintf(&fake, fmt, ap);
115 if (ret >= 0 && __sflush(&fake)) ret = EOF;
116 if (fake._flags & __SERR) fp->_flags |= __SERR;
117 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -0700118}
119
120/*
121 * Like __fputwc_unlock, but handles fake string (__SSTR) files properly.
122 * File must already be locked.
123 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700124static wint_t __xfputwc(wchar_t wc, FILE* fp) {
125 mbstate_t mbs;
126 char buf[MB_LEN_MAX];
127 struct __suio uio;
128 struct __siov iov;
129 size_t len;
Elliott Hughes94336d82014-04-29 17:39:29 -0700130
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700131 if ((fp->_flags & __SSTR) == 0) return (__fputwc_unlock(wc, fp));
Elliott Hughes94336d82014-04-29 17:39:29 -0700132
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700133 bzero(&mbs, sizeof(mbs));
134 len = wcrtomb(buf, wc, &mbs);
135 if (len == (size_t)-1) {
136 fp->_flags |= __SERR;
137 errno = EILSEQ;
138 return (WEOF);
139 }
140 uio.uio_iov = &iov;
141 uio.uio_resid = len;
142 uio.uio_iovcnt = 1;
143 iov.iov_base = buf;
144 iov.iov_len = len;
145 return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF);
Elliott Hughes94336d82014-04-29 17:39:29 -0700146}
147
148/*
149 * Convert a multibyte character string argument for the %s format to a wide
150 * string representation. ``prec'' specifies the maximum number of bytes
151 * to output. If ``prec'' is greater than or equal to zero, we can't assume
152 * that the multibyte character string ends in a null character.
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700153 *
Elliott Hughes94336d82014-04-29 17:39:29 -0700154 * Returns NULL on failure.
155 * To find out what happened check errno for ENOMEM, EILSEQ and EINVAL.
156 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700157static wchar_t* __mbsconv(char* mbsarg, int prec) {
158 mbstate_t mbs;
159 wchar_t *convbuf, *wcp;
160 const char* p;
161 size_t insize, nchars, nconv;
Elliott Hughes94336d82014-04-29 17:39:29 -0700162
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700163 if (mbsarg == NULL) return (NULL);
Elliott Hughes94336d82014-04-29 17:39:29 -0700164
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700165 /*
166 * Supplied argument is a multibyte string; convert it to wide
167 * characters first.
168 */
169 if (prec >= 0) {
170 /*
171 * String is not guaranteed to be NUL-terminated. Find the
172 * number of characters to print.
173 */
174 p = mbsarg;
175 insize = nchars = nconv = 0;
176 bzero(&mbs, sizeof(mbs));
177 while (nchars != (size_t)prec) {
178 nconv = mbrlen(p, MB_CUR_MAX, &mbs);
179 if (nconv == (size_t)0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
180 p += nconv;
181 nchars++;
182 insize += nconv;
183 }
184 if (nconv == (size_t)-1 || nconv == (size_t)-2) return (NULL);
185 } else
186 insize = strlen(mbsarg);
Elliott Hughes94336d82014-04-29 17:39:29 -0700187
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700188 /*
189 * Allocate buffer for the result and perform the conversion,
190 * converting at most `size' bytes of the input multibyte string to
191 * wide characters for printing.
192 */
193 convbuf = calloc(insize + 1, sizeof(*convbuf));
194 if (convbuf == NULL) return (NULL);
195 wcp = convbuf;
196 p = mbsarg;
197 bzero(&mbs, sizeof(mbs));
198 nconv = 0;
199 while (insize != 0) {
200 nconv = mbrtowc(wcp, p, insize, &mbs);
201 if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
202 wcp++;
203 p += nconv;
204 insize -= nconv;
205 }
206 if (nconv == (size_t)-1 || nconv == (size_t)-2) {
207 free(convbuf);
208 return (NULL);
209 }
210 *wcp = '\0';
Elliott Hughes94336d82014-04-29 17:39:29 -0700211
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700212 return (convbuf);
Elliott Hughes94336d82014-04-29 17:39:29 -0700213}
214
Elliott Hughes94336d82014-04-29 17:39:29 -0700215#include <float.h>
216#include <locale.h>
217#include <math.h>
218#include "floatio.h"
Elliott Hughesf1ada792014-05-02 17:56:56 -0700219#include "gdtoa.h"
Elliott Hughes94336d82014-04-29 17:39:29 -0700220
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700221#define DEFPREC 6
Elliott Hughes94336d82014-04-29 17:39:29 -0700222
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700223static int exponent(wchar_t*, int, int);
Elliott Hughes94336d82014-04-29 17:39:29 -0700224
225/*
226 * The size of the buffer we use as scratch space for integer
227 * conversions, among other things. Technically, we would need the
228 * most space for base 10 conversions with thousands' grouping
229 * characters between each pair of digits. 100 bytes is a
230 * conservative overestimate even for a 128-bit uintmax_t.
231 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700232#define BUF 100
Elliott Hughes94336d82014-04-29 17:39:29 -0700233
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700234#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
Elliott Hughes94336d82014-04-29 17:39:29 -0700235
236/*
237 * Macros for converting digits to letters and vice versa
238 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700239#define to_digit(c) ((c) - '0')
240#define is_digit(c) ((unsigned)to_digit(c) <= 9)
241#define to_char(n) ((wchar_t)((n) + '0'))
Elliott Hughes94336d82014-04-29 17:39:29 -0700242
243/*
244 * Flags used during conversion.
245 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700246#define ALT 0x0001 /* alternate form */
247#define LADJUST 0x0004 /* left adjustment */
248#define LONGDBL 0x0008 /* long double */
249#define LONGINT 0x0010 /* long integer */
250#define LLONGINT 0x0020 /* long long integer */
251#define SHORTINT 0x0040 /* short integer */
252#define ZEROPAD 0x0080 /* zero (as opposed to blank) pad */
253#define FPT 0x0100 /* Floating point number */
254#define PTRINT 0x0200 /* (unsigned) ptrdiff_t */
255#define SIZEINT 0x0400 /* (signed) size_t */
256#define CHARINT 0x0800 /* 8 bit integer */
257#define MAXINT 0x1000 /* largest integer size (intmax_t) */
Elliott Hughes94336d82014-04-29 17:39:29 -0700258
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700259int __vfwprintf(FILE* __restrict fp, const wchar_t* __restrict fmt0, __va_list ap) {
260 wchar_t* fmt; /* format string */
261 wchar_t ch; /* character from fmt */
262 int n, n2, n3; /* handy integers (short term usage) */
263 wchar_t* cp; /* handy char pointer (short term usage) */
264 int flags; /* flags as above */
265 int ret; /* return value accumulator */
266 int width; /* width from format (%8d), or 0 */
267 int prec; /* precision from format; <0 for N/A */
268 wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */
269 /*
270 * We can decompose the printed representation of floating
271 * point numbers into several parts, some of which may be empty:
272 *
273 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
274 * A B ---C--- D E F
275 *
276 * A: 'sign' holds this value if present; '\0' otherwise
277 * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
278 * C: cp points to the string MMMNNN. Leading and trailing
279 * zeros are not in the string and must be added.
280 * D: expchar holds this character; '\0' if no exponent, e.g. %f
281 * F: at least two digits for decimal, at least one digit for hex
282 */
283 char* decimal_point = NULL;
284 int signflag; /* true if float is negative */
285 union { /* floating point arguments %[aAeEfFgG] */
286 double dbl;
287 long double ldbl;
288 } fparg;
289 int expt; /* integer value of exponent */
290 char expchar; /* exponent character: [eEpP\0] */
291 char* dtoaend; /* pointer to end of converted digits */
292 int expsize; /* character count for expstr */
293 int lead; /* sig figs before decimal or group sep */
294 int ndig; /* actual number of digits returned by dtoa */
295 wchar_t expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
296 char* dtoaresult = NULL;
Elliott Hughes94336d82014-04-29 17:39:29 -0700297
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700298 uintmax_t _umax; /* integer arguments %[diouxX] */
299 enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
300 int dprec; /* a copy of prec if %[diouxX], 0 otherwise */
301 int realsz; /* field size expanded by dprec */
302 int size; /* size of converted field or string */
303 const char* xdigs; /* digits for %[xX] conversion */
304 wchar_t buf[BUF]; /* buffer with space for digits of uintmax_t */
305 wchar_t ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */
306 union arg* argtable; /* args, built due to positional arg */
307 union arg statargtable[STATIC_ARG_TBL_SIZE];
308 size_t argtablesiz;
309 int nextarg; /* 1-based argument index */
310 va_list orgap; /* original argument pointer */
311 wchar_t* convbuf; /* buffer for multibyte to wide conversion */
Elliott Hughes94336d82014-04-29 17:39:29 -0700312
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700313 /*
314 * Choose PADSIZE to trade efficiency vs. size. If larger printf
315 * fields occur frequently, increase PADSIZE and make the initialisers
316 * below longer.
317 */
318#define PADSIZE 16 /* pad chunk size */
319 static wchar_t blanks[PADSIZE] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
320 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
321 static wchar_t zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0',
322 '0', '0', '0', '0', '0', '0', '0', '0' };
Elliott Hughes94336d82014-04-29 17:39:29 -0700323
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700324 static const char xdigs_lower[16] = "0123456789abcdef";
325 static const char xdigs_upper[16] = "0123456789ABCDEF";
Elliott Hughes94336d82014-04-29 17:39:29 -0700326
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700327 /*
328 * BEWARE, these `goto error' on error, PRINT uses 'n3',
329 * PAD uses `n' and 'n3', and PRINTANDPAD uses 'n', 'n2', and 'n3'.
330 */
331#define PRINT(ptr, len) \
332 do { \
333 for (n3 = 0; n3 < (len); n3++) { \
334 if ((__xfputwc((ptr)[n3], fp)) == WEOF) goto error; \
335 } \
336 } while (0)
337#define PAD(howmany, with) \
338 do { \
339 if ((n = (howmany)) > 0) { \
340 while (n > PADSIZE) { \
341 PRINT(with, PADSIZE); \
342 n -= PADSIZE; \
343 } \
344 PRINT(with, n); \
345 } \
346 } while (0)
347#define PRINTANDPAD(p, ep, len, with) \
348 do { \
349 n2 = (ep) - (p); \
350 if (n2 > (len)) n2 = (len); \
351 if (n2 > 0) PRINT((p), n2); \
352 PAD((len) - (n2 > 0 ? n2 : 0), (with)); \
353 } while (0)
Elliott Hughes94336d82014-04-29 17:39:29 -0700354
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700355 /*
356 * To extend shorts properly, we need both signed and unsigned
357 * argument extraction methods.
358 */
359#define SARG() \
360 ((intmax_t)(flags & MAXINT \
361 ? GETARG(intmax_t) \
362 : flags & LLONGINT \
363 ? GETARG(long long) \
364 : flags & LONGINT \
365 ? GETARG(long) \
366 : flags & PTRINT \
367 ? GETARG(ptrdiff_t) \
368 : flags & SIZEINT \
369 ? GETARG(ssize_t) \
370 : flags & SHORTINT \
371 ? (short)GETARG(int) \
372 : flags & CHARINT ? (signed char)GETARG(int) \
373 : GETARG(int)))
374#define UARG() \
375 ((uintmax_t)(flags & MAXINT \
376 ? GETARG(uintmax_t) \
377 : flags & LLONGINT \
378 ? GETARG(unsigned long long) \
379 : flags & LONGINT \
380 ? GETARG(unsigned long) \
381 : flags & PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \
382 flags & SIZEINT \
383 ? GETARG(size_t) \
384 : flags & SHORTINT \
385 ? (unsigned short)GETARG(int) \
386 : flags & CHARINT ? (unsigned char)GETARG(int) \
387 : GETARG(unsigned int)))
Elliott Hughes94336d82014-04-29 17:39:29 -0700388
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700389 /*
390 * Append a digit to a value and check for overflow.
391 */
392#define APPEND_DIGIT(val, dig) \
393 do { \
394 if ((val) > INT_MAX / 10) goto overflow; \
395 (val) *= 10; \
396 if ((val) > INT_MAX - to_digit((dig))) goto overflow; \
397 (val) += to_digit((dig)); \
398 } while (0)
Elliott Hughes94336d82014-04-29 17:39:29 -0700399
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700400 /*
401 * Get * arguments, including the form *nn$. Preserve the nextarg
402 * that the argument can be gotten once the type is determined.
403 */
404#define GETASTER(val) \
405 n2 = 0; \
406 cp = fmt; \
407 while (is_digit(*cp)) { \
408 APPEND_DIGIT(n2, *cp); \
409 cp++; \
410 } \
411 if (*cp == '$') { \
412 int hold = nextarg; \
413 if (argtable == NULL) { \
414 argtable = statargtable; \
415 __find_arguments(fmt0, orgap, &argtable, &argtablesiz); \
416 } \
417 nextarg = n2; \
418 val = GETARG(int); \
419 nextarg = hold; \
420 fmt = ++cp; \
421 } else { \
422 val = GETARG(int); \
423 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700424
425/*
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700426 * Get the argument indexed by nextarg. If the argument table is
427 * built, use it to get the argument. If its not, get the next
428 * argument (and arguments must be gotten sequentially).
429 */
Elliott Hughes94336d82014-04-29 17:39:29 -0700430#define GETARG(type) \
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700431 ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type)))
Elliott Hughes94336d82014-04-29 17:39:29 -0700432
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700433 _SET_ORIENTATION(fp, 1);
434 /* sorry, fwprintf(read_only_file, "") returns EOF, not 0 */
435 if (cantwrite(fp)) {
436 errno = EBADF;
437 return (EOF);
438 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700439
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700440 /* optimise fwprintf(stderr) (and other unbuffered Unix files) */
441 if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0)
442 return (__sbprintf(fp, fmt0, ap));
Elliott Hughes94336d82014-04-29 17:39:29 -0700443
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700444 fmt = (wchar_t*)fmt0;
445 argtable = NULL;
446 nextarg = 1;
447 va_copy(orgap, ap);
448 ret = 0;
449 convbuf = NULL;
Elliott Hughes94336d82014-04-29 17:39:29 -0700450
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700451 /*
452 * Scan the format for conversions (`%' character).
453 */
454 for (;;) {
455 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
456 if (fmt != cp) {
457 ptrdiff_t m = fmt - cp;
458 if (m < 0 || m > INT_MAX - ret) goto overflow;
459 PRINT(cp, m);
460 ret += m;
461 }
462 if (ch == '\0') goto done;
463 fmt++; /* skip over '%' */
Elliott Hughes94336d82014-04-29 17:39:29 -0700464
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700465 flags = 0;
466 dprec = 0;
467 width = 0;
468 prec = -1;
469 sign = '\0';
470 ox[1] = '\0';
Elliott Hughes94336d82014-04-29 17:39:29 -0700471
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700472 rflag:
473 ch = *fmt++;
474 reswitch:
475 switch (ch) {
476 case ' ':
477 /*
478 * ``If the space and + flags both appear, the space
479 * flag will be ignored.''
480 * -- ANSI X3J11
481 */
482 if (!sign) sign = ' ';
483 goto rflag;
484 case '#':
485 flags |= ALT;
486 goto rflag;
487 case '\'':
488 /* grouping not implemented */
489 goto rflag;
490 case '*':
491 /*
492 * ``A negative field width argument is taken as a
493 * - flag followed by a positive field width.''
494 * -- ANSI X3J11
495 * They don't exclude field widths read from args.
496 */
497 GETASTER(width);
498 if (width >= 0) goto rflag;
499 if (width == INT_MIN) goto overflow;
500 width = -width;
501 /* FALLTHROUGH */
502 case '-':
503 flags |= LADJUST;
504 goto rflag;
505 case '+':
506 sign = '+';
507 goto rflag;
508 case '.':
509 if ((ch = *fmt++) == '*') {
510 GETASTER(n);
511 prec = n < 0 ? -1 : n;
512 goto rflag;
513 }
514 n = 0;
515 while (is_digit(ch)) {
516 APPEND_DIGIT(n, ch);
517 ch = *fmt++;
518 }
519 if (ch == '$') {
520 nextarg = n;
521 if (argtable == NULL) {
522 argtable = statargtable;
523 __find_arguments(fmt0, orgap, &argtable, &argtablesiz);
524 }
525 goto rflag;
526 }
527 prec = n;
528 goto reswitch;
529 case '0':
530 /*
531 * ``Note that 0 is taken as a flag, not as the
532 * beginning of a field width.''
533 * -- ANSI X3J11
534 */
535 flags |= ZEROPAD;
536 goto rflag;
537 case '1':
538 case '2':
539 case '3':
540 case '4':
541 case '5':
542 case '6':
543 case '7':
544 case '8':
545 case '9':
546 n = 0;
547 do {
548 APPEND_DIGIT(n, ch);
549 ch = *fmt++;
550 } while (is_digit(ch));
551 if (ch == '$') {
552 nextarg = n;
553 if (argtable == NULL) {
554 argtable = statargtable;
555 __find_arguments(fmt0, orgap, &argtable, &argtablesiz);
556 }
557 goto rflag;
558 }
559 width = n;
560 goto reswitch;
561 case 'L':
562 flags |= LONGDBL;
563 goto rflag;
564 case 'h':
565 if (*fmt == 'h') {
566 fmt++;
567 flags |= CHARINT;
568 } else {
569 flags |= SHORTINT;
570 }
571 goto rflag;
572 case 'j':
573 flags |= MAXINT;
574 goto rflag;
575 case 'l':
576 if (*fmt == 'l') {
577 fmt++;
578 flags |= LLONGINT;
579 } else {
580 flags |= LONGINT;
581 }
582 goto rflag;
583 case 'q':
584 flags |= LLONGINT;
585 goto rflag;
586 case 't':
587 flags |= PTRINT;
588 goto rflag;
589 case 'z':
590 flags |= SIZEINT;
591 goto rflag;
592 case 'C':
593 flags |= LONGINT;
594 /*FALLTHROUGH*/
595 case 'c':
596 if (flags & LONGINT)
597 *(cp = buf) = (wchar_t)GETARG(wint_t);
598 else
599 *(cp = buf) = (wchar_t)btowc(GETARG(int));
600 size = 1;
601 sign = '\0';
602 break;
603 case 'D':
604 flags |= LONGINT;
605 /*FALLTHROUGH*/
606 case 'd':
607 case 'i':
608 _umax = SARG();
609 if ((intmax_t)_umax < 0) {
610 _umax = -_umax;
611 sign = '-';
612 }
613 base = DEC;
614 goto number;
615 case 'a':
616 case 'A':
617 if (ch == 'a') {
618 ox[1] = 'x';
619 xdigs = xdigs_lower;
620 expchar = 'p';
621 } else {
622 ox[1] = 'X';
623 xdigs = xdigs_upper;
624 expchar = 'P';
625 }
626 if (prec >= 0) prec++;
627 if (dtoaresult) __freedtoa(dtoaresult);
628 if (flags & LONGDBL) {
629 fparg.ldbl = GETARG(long double);
630 dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
631 if (dtoaresult == NULL) {
632 errno = ENOMEM;
633 goto error;
634 }
635 } else {
636 fparg.dbl = GETARG(double);
637 dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
638 if (dtoaresult == NULL) {
639 errno = ENOMEM;
640 goto error;
641 }
642 }
643 if (prec < 0) prec = dtoaend - dtoaresult;
644 if (expt == INT_MAX) ox[1] = '\0';
645 free(convbuf);
646 cp = convbuf = __mbsconv(dtoaresult, -1);
647 if (cp == NULL) goto error;
648 ndig = dtoaend - dtoaresult;
649 goto fp_common;
650 case 'e':
651 case 'E':
652 expchar = ch;
653 if (prec < 0) /* account for digit before decpt */
654 prec = DEFPREC + 1;
655 else
656 prec++;
657 goto fp_begin;
658 case 'f':
659 case 'F':
660 expchar = '\0';
661 goto fp_begin;
662 case 'g':
663 case 'G':
664 expchar = ch - ('g' - 'e');
665 if (prec == 0) prec = 1;
666 fp_begin:
667 if (prec < 0) prec = DEFPREC;
668 if (dtoaresult) __freedtoa(dtoaresult);
669 if (flags & LONGDBL) {
670 fparg.ldbl = GETARG(long double);
671 dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
672 if (dtoaresult == NULL) {
673 errno = ENOMEM;
674 goto error;
675 }
676 } else {
677 fparg.dbl = GETARG(double);
678 dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
679 if (dtoaresult == NULL) {
680 errno = ENOMEM;
681 goto error;
682 }
683 if (expt == 9999) expt = INT_MAX;
684 }
685 free(convbuf);
686 cp = convbuf = __mbsconv(dtoaresult, -1);
687 if (cp == NULL) goto error;
688 ndig = dtoaend - dtoaresult;
689 fp_common:
690 if (signflag) sign = '-';
691 if (expt == INT_MAX) { /* inf or nan */
692 if (*cp == 'N')
693 cp = (ch >= 'a') ? L"nan" : L"NAN";
694 else
695 cp = (ch >= 'a') ? L"inf" : L"INF";
696 size = 3;
697 flags &= ~ZEROPAD;
698 break;
699 }
700 flags |= FPT;
701 if (ch == 'g' || ch == 'G') {
702 if (expt > -4 && expt <= prec) {
703 /* Make %[gG] smell like %[fF] */
704 expchar = '\0';
705 if (flags & ALT)
706 prec -= expt;
707 else
708 prec = ndig - expt;
709 if (prec < 0) prec = 0;
710 } else {
711 /*
712 * Make %[gG] smell like %[eE], but
713 * trim trailing zeroes if no # flag.
714 */
715 if (!(flags & ALT)) prec = ndig;
716 }
717 }
718 if (expchar) {
719 expsize = exponent(expstr, expt - 1, expchar);
720 size = expsize + prec;
721 if (prec > 1 || flags & ALT) ++size;
722 } else {
723 /* space for digits before decimal point */
724 if (expt > 0)
725 size = expt;
726 else /* "0" */
727 size = 1;
728 /* space for decimal pt and following digits */
729 if (prec || flags & ALT) size += prec + 1;
730 lead = expt;
731 }
732 break;
Elliott Hughese2341d02014-05-02 18:16:32 -0700733#ifndef NO_PRINTF_PERCENT_N
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700734 case 'n':
735 if (flags & LLONGINT)
736 *GETARG(long long*) = ret;
737 else if (flags & LONGINT)
738 *GETARG(long*) = ret;
739 else if (flags & SHORTINT)
740 *GETARG(short*) = ret;
741 else if (flags & CHARINT)
742 *GETARG(signed char*) = ret;
743 else if (flags & PTRINT)
744 *GETARG(ptrdiff_t*) = ret;
745 else if (flags & SIZEINT)
746 *GETARG(ssize_t*) = ret;
747 else if (flags & MAXINT)
748 *GETARG(intmax_t*) = ret;
749 else
750 *GETARG(int*) = ret;
751 continue; /* no output */
752#endif /* NO_PRINTF_PERCENT_N */
753 case 'O':
754 flags |= LONGINT;
755 /*FALLTHROUGH*/
756 case 'o':
757 _umax = UARG();
758 base = OCT;
759 goto nosign;
760 case 'p':
761 /*
762 * ``The argument shall be a pointer to void. The
763 * value of the pointer is converted to a sequence
764 * of printable characters, in an implementation-
765 * defined manner.''
766 * -- ANSI X3J11
767 */
768 _umax = (u_long)GETARG(void*);
769 base = HEX;
770 xdigs = xdigs_lower;
771 ox[1] = 'x';
772 goto nosign;
773 case 'S':
774 flags |= LONGINT;
775 /*FALLTHROUGH*/
776 case 's':
777 if (flags & LONGINT) {
778 if ((cp = GETARG(wchar_t*)) == NULL) cp = L"(null)";
779 } else {
780 char* mbsarg;
781 if ((mbsarg = GETARG(char*)) == NULL) mbsarg = "(null)";
782 free(convbuf);
783 convbuf = __mbsconv(mbsarg, prec);
784 if (convbuf == NULL) {
785 fp->_flags |= __SERR;
786 goto error;
787 } else
788 cp = convbuf;
789 }
790 if (prec >= 0) {
791 /*
792 * can't use wcslen; can only look for the
793 * NUL in the first `prec' characters, and
794 * wcslen() will go further.
795 */
796 wchar_t* p = wmemchr(cp, 0, prec);
Elliott Hughes94336d82014-04-29 17:39:29 -0700797
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700798 size = p ? (p - cp) : prec;
799 } else {
800 size_t len;
Elliott Hughes94336d82014-04-29 17:39:29 -0700801
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700802 if ((len = wcslen(cp)) > INT_MAX) goto overflow;
803 size = (int)len;
804 }
805 sign = '\0';
806 break;
807 case 'U':
808 flags |= LONGINT;
809 /*FALLTHROUGH*/
810 case 'u':
811 _umax = UARG();
812 base = DEC;
813 goto nosign;
814 case 'X':
815 xdigs = xdigs_upper;
816 goto hex;
817 case 'x':
818 xdigs = xdigs_lower;
819 hex:
820 _umax = UARG();
821 base = HEX;
822 /* leading 0x/X only if non-zero */
823 if (flags & ALT && _umax != 0) ox[1] = ch;
Elliott Hughes94336d82014-04-29 17:39:29 -0700824
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700825 /* unsigned conversions */
826 nosign:
827 sign = '\0';
828 /*
829 * ``... diouXx conversions ... if a precision is
830 * specified, the 0 flag will be ignored.''
831 * -- ANSI X3J11
832 */
833 number:
834 if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
Elliott Hughes94336d82014-04-29 17:39:29 -0700835
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700836 /*
837 * ``The result of converting a zero value with an
838 * explicit precision of zero is no characters.''
839 * -- ANSI X3J11
840 */
841 cp = buf + BUF;
842 if (_umax != 0 || prec != 0) {
843 /*
844 * Unsigned mod is hard, and unsigned mod
845 * by a constant is easier than that by
846 * a variable; hence this switch.
847 */
848 switch (base) {
849 case OCT:
850 do {
851 *--cp = to_char(_umax & 7);
852 _umax >>= 3;
853 } while (_umax);
854 /* handle octal leading 0 */
855 if (flags & ALT && *cp != '0') *--cp = '0';
856 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700857
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700858 case DEC:
859 /* many numbers are 1 digit */
860 while (_umax >= 10) {
861 *--cp = to_char(_umax % 10);
862 _umax /= 10;
863 }
864 *--cp = to_char(_umax);
865 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700866
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700867 case HEX:
868 do {
869 *--cp = xdigs[_umax & 15];
870 _umax >>= 4;
871 } while (_umax);
872 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700873
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700874 default:
875 cp = L"bug in vfwprintf: bad base";
876 size = wcslen(cp);
877 goto skipsize;
878 }
879 }
880 size = buf + BUF - cp;
881 if (size > BUF) /* should never happen */
882 abort();
883 skipsize:
884 break;
885 default: /* "%?" prints ?, unless ? is NUL */
886 if (ch == '\0') goto done;
887 /* pretend it was %c with argument ch */
888 cp = buf;
889 *cp = ch;
890 size = 1;
891 sign = '\0';
892 break;
893 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700894
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700895 /*
896 * All reasonable formats wind up here. At this point, `cp'
897 * points to a string which (if not flags&LADJUST) should be
898 * padded out to `width' places. If flags&ZEROPAD, it should
899 * first be prefixed by any sign or other prefix; otherwise,
900 * it should be blank padded before the prefix is emitted.
901 * After any left-hand padding and prefixing, emit zeroes
902 * required by a decimal %[diouxX] precision, then print the
903 * string proper, then emit zeroes required by any leftover
904 * floating precision; finally, if LADJUST, pad with blanks.
905 *
906 * Compute actual size, so we know how much to pad.
907 * size excludes decimal prec; realsz includes it.
908 */
909 realsz = dprec > size ? dprec : size;
910 if (sign) realsz++;
911 if (ox[1]) realsz += 2;
Elliott Hughes94336d82014-04-29 17:39:29 -0700912
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700913 /* right-adjusting blank padding */
914 if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700915
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700916 /* prefix */
917 if (sign) PRINT(&sign, 1);
918 if (ox[1]) { /* ox[1] is either x, X, or \0 */
919 ox[0] = '0';
920 PRINT(ox, 2);
921 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700922
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700923 /* right-adjusting zero padding */
924 if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700925
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700926 /* leading zeroes from decimal precision */
927 PAD(dprec - size, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700928
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700929 /* the string or number proper */
930 if ((flags & FPT) == 0) {
931 PRINT(cp, size);
932 } else { /* glue together f_p fragments */
933 if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR);
934 if (!expchar) { /* %[fF] or sufficiently short %[gG] */
935 if (expt <= 0) {
936 PRINT(zeroes, 1);
937 if (prec || flags & ALT) PRINT(decimal_point, 1);
938 PAD(-expt, zeroes);
939 /* already handled initial 0's */
940 prec += expt;
941 } else {
942 PRINTANDPAD(cp, convbuf + ndig, lead, zeroes);
943 cp += lead;
944 if (prec || flags & ALT) PRINT(decimal_point, 1);
945 }
946 PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
947 } else { /* %[eE] or sufficiently long %[gG] */
948 if (prec > 1 || flags & ALT) {
949 buf[0] = *cp++;
950 buf[1] = *decimal_point;
951 PRINT(buf, 2);
952 PRINT(cp, ndig - 1);
953 PAD(prec - ndig, zeroes);
954 } else { /* XeYYY */
955 PRINT(cp, 1);
956 }
957 PRINT(expstr, expsize);
958 }
959 }
960 /* left-adjusting padding (always blank) */
961 if (flags & LADJUST) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700962
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700963 /* finally, adjust ret */
964 if (width < realsz) width = realsz;
965 if (width > INT_MAX - ret) goto overflow;
966 ret += width;
967 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700968done:
969error:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700970 va_end(orgap);
971 if (__sferror(fp)) ret = -1;
972 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -0700973
974overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700975 errno = ENOMEM;
976 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -0700977
978finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700979 free(convbuf);
980 if (dtoaresult) __freedtoa(dtoaresult);
981 if (argtable != NULL && argtable != statargtable) {
982 munmap(argtable, argtablesiz);
983 argtable = NULL;
984 }
985 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -0700986}
987
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700988int vfwprintf(FILE* __restrict fp, const wchar_t* __restrict fmt0, __va_list ap) {
989 int r;
Elliott Hughes94336d82014-04-29 17:39:29 -0700990
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700991 FLOCKFILE(fp);
992 r = __vfwprintf(fp, fmt0, ap);
993 FUNLOCKFILE(fp);
Elliott Hughes94336d82014-04-29 17:39:29 -0700994
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700995 return (r);
Elliott Hughes94336d82014-04-29 17:39:29 -0700996}
Elliott Hughes506c6de2016-01-15 16:30:18 -0800997DEF_STRONG(vfwprintf);
Elliott Hughes94336d82014-04-29 17:39:29 -0700998
999/*
1000 * Type ids for argument type table.
1001 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001002#define T_UNUSED 0
1003#define T_SHORT 1
1004#define T_U_SHORT 2
1005#define TP_SHORT 3
1006#define T_INT 4
1007#define T_U_INT 5
1008#define TP_INT 6
1009#define T_LONG 7
1010#define T_U_LONG 8
1011#define TP_LONG 9
1012#define T_LLONG 10
1013#define T_U_LLONG 11
1014#define TP_LLONG 12
1015#define T_DOUBLE 13
1016#define T_LONG_DOUBLE 14
1017#define TP_CHAR 15
1018#define TP_VOID 16
1019#define T_PTRINT 17
1020#define TP_PTRINT 18
1021#define T_SIZEINT 19
1022#define T_SSIZEINT 20
1023#define TP_SSIZEINT 21
1024#define T_MAXINT 22
1025#define T_MAXUINT 23
1026#define TP_MAXINT 24
1027#define T_CHAR 25
1028#define T_U_CHAR 26
1029#define T_WINT 27
1030#define TP_WCHAR 28
Elliott Hughes94336d82014-04-29 17:39:29 -07001031
1032/*
1033 * Find all arguments when a positional parameter is encountered. Returns a
1034 * table, indexed by argument number, of pointers to each arguments. The
1035 * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
1036 * It will be replaced with a mmap-ed one if it overflows (malloc cannot be
1037 * used since we are attempting to make snprintf thread safe, and alloca is
1038 * problematic since we have nested functions..)
1039 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001040static int __find_arguments(const wchar_t* fmt0, va_list ap, union arg** argtable,
1041 size_t* argtablesiz) {
1042 wchar_t* fmt; /* format string */
1043 int ch; /* character from fmt */
1044 int n, n2; /* handy integer (short term usage) */
1045 wchar_t* cp; /* handy char pointer (short term usage) */
1046 int flags; /* flags as above */
1047 unsigned char* typetable; /* table of types */
1048 unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
1049 int tablesize; /* current size of type table */
1050 int tablemax; /* largest used index in table */
1051 int nextarg; /* 1-based argument index */
1052 int ret = 0; /* return value */
Elliott Hughes94336d82014-04-29 17:39:29 -07001053
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001054 /*
1055 * Add an argument type to the table, expanding if necessary.
1056 */
1057#define ADDTYPE(type) \
1058 ((nextarg >= tablesize) ? __grow_type_table(&typetable, &tablesize) : 0, \
1059 (nextarg > tablemax) ? tablemax = nextarg : 0, typetable[nextarg++] = type)
Elliott Hughes94336d82014-04-29 17:39:29 -07001060
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001061#define ADDSARG() \
1062 ((flags & MAXINT) \
1063 ? ADDTYPE(T_MAXINT) \
1064 : ((flags & PTRINT) ? ADDTYPE(T_PTRINT) \
1065 : ((flags & SIZEINT) \
1066 ? ADDTYPE(T_SSIZEINT) \
1067 : ((flags & LLONGINT) \
1068 ? ADDTYPE(T_LLONG) \
1069 : ((flags & LONGINT) \
1070 ? ADDTYPE(T_LONG) \
1071 : ((flags & SHORTINT) \
1072 ? ADDTYPE(T_SHORT) \
1073 : ((flags & CHARINT) ? ADDTYPE(T_CHAR) \
1074 : ADDTYPE(T_INT))))))))
Elliott Hughes94336d82014-04-29 17:39:29 -07001075
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001076#define ADDUARG() \
1077 ((flags & MAXINT) \
1078 ? ADDTYPE(T_MAXUINT) \
1079 : ((flags & PTRINT) \
1080 ? ADDTYPE(T_PTRINT) \
1081 : ((flags & SIZEINT) \
1082 ? ADDTYPE(T_SIZEINT) \
1083 : ((flags & LLONGINT) \
1084 ? ADDTYPE(T_U_LLONG) \
1085 : ((flags & LONGINT) \
1086 ? ADDTYPE(T_U_LONG) \
1087 : ((flags & SHORTINT) \
1088 ? ADDTYPE(T_U_SHORT) \
1089 : ((flags & CHARINT) ? ADDTYPE(T_U_CHAR) \
1090 : ADDTYPE(T_U_INT))))))))
Elliott Hughes94336d82014-04-29 17:39:29 -07001091
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001092 /*
1093 * Add * arguments to the type array.
1094 */
1095#define ADDASTER() \
1096 n2 = 0; \
1097 cp = fmt; \
1098 while (is_digit(*cp)) { \
1099 APPEND_DIGIT(n2, *cp); \
1100 cp++; \
1101 } \
1102 if (*cp == '$') { \
1103 int hold = nextarg; \
1104 nextarg = n2; \
1105 ADDTYPE(T_INT); \
1106 nextarg = hold; \
1107 fmt = ++cp; \
1108 } else { \
1109 ADDTYPE(T_INT); \
1110 }
1111 fmt = (wchar_t*)fmt0;
1112 typetable = stattypetable;
1113 tablesize = STATIC_ARG_TBL_SIZE;
1114 tablemax = 0;
1115 nextarg = 1;
1116 memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
Elliott Hughes94336d82014-04-29 17:39:29 -07001117
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001118 /*
1119 * Scan the format for conversions (`%' character).
1120 */
1121 for (;;) {
1122 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
1123 if (ch == '\0') goto done;
1124 fmt++; /* skip over '%' */
Elliott Hughes94336d82014-04-29 17:39:29 -07001125
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001126 flags = 0;
Elliott Hughes94336d82014-04-29 17:39:29 -07001127
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001128 rflag:
1129 ch = *fmt++;
1130 reswitch:
1131 switch (ch) {
1132 case ' ':
1133 case '#':
1134 case '\'':
1135 goto rflag;
1136 case '*':
1137 ADDASTER();
1138 goto rflag;
1139 case '-':
1140 case '+':
1141 goto rflag;
1142 case '.':
1143 if ((ch = *fmt++) == '*') {
1144 ADDASTER();
1145 goto rflag;
1146 }
1147 while (is_digit(ch)) {
1148 ch = *fmt++;
1149 }
1150 goto reswitch;
1151 case '0':
1152 goto rflag;
1153 case '1':
1154 case '2':
1155 case '3':
1156 case '4':
1157 case '5':
1158 case '6':
1159 case '7':
1160 case '8':
1161 case '9':
1162 n = 0;
1163 do {
1164 APPEND_DIGIT(n, ch);
1165 ch = *fmt++;
1166 } while (is_digit(ch));
1167 if (ch == '$') {
1168 nextarg = n;
1169 goto rflag;
1170 }
1171 goto reswitch;
1172 case 'L':
1173 flags |= LONGDBL;
1174 goto rflag;
1175 case 'h':
1176 if (*fmt == 'h') {
1177 fmt++;
1178 flags |= CHARINT;
1179 } else {
1180 flags |= SHORTINT;
1181 }
1182 goto rflag;
1183 case 'l':
1184 if (*fmt == 'l') {
1185 fmt++;
1186 flags |= LLONGINT;
1187 } else {
1188 flags |= LONGINT;
1189 }
1190 goto rflag;
1191 case 'q':
1192 flags |= LLONGINT;
1193 goto rflag;
1194 case 't':
1195 flags |= PTRINT;
1196 goto rflag;
1197 case 'z':
1198 flags |= SIZEINT;
1199 goto rflag;
1200 case 'C':
1201 flags |= LONGINT;
1202 /*FALLTHROUGH*/
1203 case 'c':
1204 if (flags & LONGINT)
1205 ADDTYPE(T_WINT);
1206 else
1207 ADDTYPE(T_INT);
1208 break;
1209 case 'D':
1210 flags |= LONGINT;
1211 /*FALLTHROUGH*/
1212 case 'd':
1213 case 'i':
1214 ADDSARG();
1215 break;
1216 case 'a':
1217 case 'A':
1218 case 'e':
1219 case 'E':
1220 case 'f':
1221 case 'F':
1222 case 'g':
1223 case 'G':
1224 if (flags & LONGDBL)
1225 ADDTYPE(T_LONG_DOUBLE);
1226 else
1227 ADDTYPE(T_DOUBLE);
1228 break;
Elliott Hughese2341d02014-05-02 18:16:32 -07001229#ifndef NO_PRINTF_PERCENT_N
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001230 case 'n':
1231 if (flags & LLONGINT)
1232 ADDTYPE(TP_LLONG);
1233 else if (flags & LONGINT)
1234 ADDTYPE(TP_LONG);
1235 else if (flags & SHORTINT)
1236 ADDTYPE(TP_SHORT);
1237 else if (flags & PTRINT)
1238 ADDTYPE(TP_PTRINT);
1239 else if (flags & SIZEINT)
1240 ADDTYPE(TP_SSIZEINT);
1241 else if (flags & MAXINT)
1242 ADDTYPE(TP_MAXINT);
1243 else
1244 ADDTYPE(TP_INT);
1245 continue; /* no output */
1246#endif /* NO_PRINTF_PERCENT_N */
1247 case 'O':
1248 flags |= LONGINT;
1249 /*FALLTHROUGH*/
1250 case 'o':
1251 ADDUARG();
1252 break;
1253 case 'p':
1254 ADDTYPE(TP_VOID);
1255 break;
1256 case 'S':
1257 flags |= LONGINT;
1258 /*FALLTHROUGH*/
1259 case 's':
1260 if (flags & LONGINT)
1261 ADDTYPE(TP_CHAR);
1262 else
1263 ADDTYPE(TP_WCHAR);
1264 break;
1265 case 'U':
1266 flags |= LONGINT;
1267 /*FALLTHROUGH*/
1268 case 'u':
1269 case 'X':
1270 case 'x':
1271 ADDUARG();
1272 break;
1273 default: /* "%?" prints ?, unless ? is NUL */
1274 if (ch == '\0') goto done;
1275 break;
1276 }
1277 }
Elliott Hughes94336d82014-04-29 17:39:29 -07001278done:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001279 /*
1280 * Build the argument table.
1281 */
1282 if (tablemax >= STATIC_ARG_TBL_SIZE) {
1283 *argtablesiz = sizeof(union arg) * (tablemax + 1);
1284 *argtable = mmap(NULL, *argtablesiz, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
1285 if (*argtable == MAP_FAILED) return (-1);
1286 }
Elliott Hughes94336d82014-04-29 17:39:29 -07001287
1288#if 0
1289 /* XXX is this required? */
1290 (*argtable)[0].intarg = 0;
1291#endif
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001292 for (n = 1; n <= tablemax; n++) {
1293 switch (typetable[n]) {
1294 case T_UNUSED:
1295 case T_CHAR:
1296 case T_U_CHAR:
1297 case T_SHORT:
1298 case T_U_SHORT:
1299 case T_INT:
1300 (*argtable)[n].intarg = va_arg(ap, int);
1301 break;
1302 case TP_SHORT:
1303 (*argtable)[n].pshortarg = va_arg(ap, short*);
1304 break;
1305 case T_U_INT:
1306 (*argtable)[n].uintarg = va_arg(ap, unsigned int);
1307 break;
1308 case TP_INT:
1309 (*argtable)[n].pintarg = va_arg(ap, int*);
1310 break;
1311 case T_LONG:
1312 (*argtable)[n].longarg = va_arg(ap, long);
1313 break;
1314 case T_U_LONG:
1315 (*argtable)[n].ulongarg = va_arg(ap, unsigned long);
1316 break;
1317 case TP_LONG:
1318 (*argtable)[n].plongarg = va_arg(ap, long*);
1319 break;
1320 case T_LLONG:
1321 (*argtable)[n].longlongarg = va_arg(ap, long long);
1322 break;
1323 case T_U_LLONG:
1324 (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
1325 break;
1326 case TP_LLONG:
1327 (*argtable)[n].plonglongarg = va_arg(ap, long long*);
1328 break;
1329 case T_DOUBLE:
1330 (*argtable)[n].doublearg = va_arg(ap, double);
1331 break;
1332 case T_LONG_DOUBLE:
1333 (*argtable)[n].longdoublearg = va_arg(ap, long double);
1334 break;
1335 case TP_CHAR:
1336 (*argtable)[n].pchararg = va_arg(ap, char*);
1337 break;
1338 case TP_VOID:
1339 (*argtable)[n].pvoidarg = va_arg(ap, void*);
1340 break;
1341 case T_PTRINT:
1342 (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
1343 break;
1344 case TP_PTRINT:
1345 (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*);
1346 break;
1347 case T_SIZEINT:
1348 (*argtable)[n].sizearg = va_arg(ap, size_t);
1349 break;
1350 case T_SSIZEINT:
1351 (*argtable)[n].ssizearg = va_arg(ap, ssize_t);
1352 break;
1353 case TP_SSIZEINT:
1354 (*argtable)[n].pssizearg = va_arg(ap, ssize_t*);
1355 break;
1356 case TP_MAXINT:
1357 (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
1358 break;
1359 case T_WINT:
1360 (*argtable)[n].wintarg = va_arg(ap, wint_t);
1361 break;
1362 case TP_WCHAR:
1363 (*argtable)[n].pwchararg = va_arg(ap, wchar_t*);
1364 break;
1365 }
1366 }
1367 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -07001368
1369overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001370 errno = ENOMEM;
1371 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -07001372
1373finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001374 if (typetable != NULL && typetable != stattypetable) {
1375 munmap(typetable, *argtablesiz);
1376 typetable = NULL;
1377 }
1378 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -07001379}
1380
1381/*
1382 * Increase the size of the type table.
1383 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001384static int __grow_type_table(unsigned char** typetable, int* tablesize) {
1385 unsigned char* oldtable = *typetable;
1386 int newsize = *tablesize * 2;
Elliott Hughes94336d82014-04-29 17:39:29 -07001387
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001388 if (newsize < getpagesize()) newsize = getpagesize();
Elliott Hughes94336d82014-04-29 17:39:29 -07001389
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001390 if (*tablesize == STATIC_ARG_TBL_SIZE) {
1391 *typetable = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
1392 if (*typetable == MAP_FAILED) return (-1);
1393 bcopy(oldtable, *typetable, *tablesize);
1394 } else {
1395 unsigned char* new = mmap(NULL, newsize, PROT_WRITE | PROT_READ, MAP_ANON | MAP_PRIVATE, -1, 0);
1396 if (new == MAP_FAILED) return (-1);
1397 memmove(new, *typetable, *tablesize);
1398 munmap(*typetable, *tablesize);
1399 *typetable = new;
1400 }
1401 memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
Elliott Hughes94336d82014-04-29 17:39:29 -07001402
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001403 *tablesize = newsize;
1404 return (0);
Elliott Hughes94336d82014-04-29 17:39:29 -07001405}
1406
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001407static int exponent(wchar_t* p0, int exp, int fmtch) {
1408 wchar_t *p, *t;
1409 wchar_t expbuf[MAXEXPDIG];
Elliott Hughes94336d82014-04-29 17:39:29 -07001410
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001411 p = p0;
1412 *p++ = fmtch;
1413 if (exp < 0) {
1414 exp = -exp;
1415 *p++ = '-';
1416 } else
1417 *p++ = '+';
1418 t = expbuf + MAXEXPDIG;
1419 if (exp > 9) {
1420 do {
1421 *--t = to_char(exp % 10);
1422 } while ((exp /= 10) > 9);
1423 *--t = to_char(exp);
1424 for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
1425 } else {
1426 /*
1427 * Exponents for decimal floating point conversions
1428 * (%[eEgG]) must be at least two characters long,
1429 * whereas exponents for hexadecimal conversions can
1430 * be only one character long.
1431 */
1432 if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
1433 *p++ = to_char(exp);
1434 }
1435 return (p - p0);
Elliott Hughes94336d82014-04-29 17:39:29 -07001436}