blob: 38acccf535d4e70767f0fbd770fce699a69c782d [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
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -070034#define CHAR_TYPE wchar_t
Elliott Hughes94336d82014-04-29 17:39:29 -070035
Elliott Hughes94336d82014-04-29 17:39:29 -070036#include <sys/mman.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070037#include <sys/types.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070038
39#include <errno.h>
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -070040#include <float.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070041#include <langinfo.h>
42#include <limits.h>
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -070043#include <locale.h>
44#include <math.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070045#include <stdarg.h>
46#include <stddef.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070047#include <stdint.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070048#include <stdio.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070049#include <stdlib.h>
50#include <string.h>
51#include <unistd.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070052#include <wchar.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070053
Elliott Hughes94336d82014-04-29 17:39:29 -070054#include "fvwrite.h"
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -070055#include "gdtoa.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 Hughes618303c2017-11-02 16:58:44 -070086static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, size_t* argtablesiz);
Elliott Hughesc8f2c522017-10-31 13:07:51 -070087static int __grow_type_table(unsigned char** typetable, int* tablesize);
Elliott Hughes94336d82014-04-29 17:39:29 -070088
89/*
90 * Helper function for `fprintf to unbuffered unix file': creates a
91 * temporary buffer. We only work on write-only files; this avoids
92 * worries about ungetc buffers and so forth.
93 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070094static int __sbprintf(FILE* fp, const wchar_t* fmt, va_list ap) {
95 int ret;
96 FILE fake;
97 struct __sfileext fakeext;
98 unsigned char buf[BUFSIZ];
Elliott Hughes94336d82014-04-29 17:39:29 -070099
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700100 _FILEEXT_SETUP(&fake, &fakeext);
101 /* copy the important variables */
102 fake._flags = fp->_flags & ~__SNBF;
103 fake._file = fp->_file;
104 fake._cookie = fp->_cookie;
105 fake._write = fp->_write;
Elliott Hughes94336d82014-04-29 17:39:29 -0700106
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700107 /* set up the buffer */
108 fake._bf._base = fake._p = buf;
109 fake._bf._size = fake._w = sizeof(buf);
110 fake._lbfsize = 0; /* not actually used, but Just In Case */
Elliott Hughes94336d82014-04-29 17:39:29 -0700111
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700112 /* do the work, then copy any error status */
113 ret = __vfwprintf(&fake, fmt, ap);
114 if (ret >= 0 && __sflush(&fake)) ret = EOF;
115 if (fake._flags & __SERR) fp->_flags |= __SERR;
116 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -0700117}
118
119/*
120 * Like __fputwc_unlock, but handles fake string (__SSTR) files properly.
121 * File must already be locked.
122 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700123static wint_t __xfputwc(wchar_t wc, FILE* fp) {
124 mbstate_t mbs;
125 char buf[MB_LEN_MAX];
126 struct __suio uio;
127 struct __siov iov;
128 size_t len;
Elliott Hughes94336d82014-04-29 17:39:29 -0700129
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700130 if ((fp->_flags & __SSTR) == 0) return (__fputwc_unlock(wc, fp));
Elliott Hughes94336d82014-04-29 17:39:29 -0700131
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700132 bzero(&mbs, sizeof(mbs));
133 len = wcrtomb(buf, wc, &mbs);
134 if (len == (size_t)-1) {
135 fp->_flags |= __SERR;
136 errno = EILSEQ;
137 return (WEOF);
138 }
139 uio.uio_iov = &iov;
140 uio.uio_resid = len;
141 uio.uio_iovcnt = 1;
142 iov.iov_base = buf;
143 iov.iov_len = len;
144 return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF);
Elliott Hughes94336d82014-04-29 17:39:29 -0700145}
146
147/*
148 * Convert a multibyte character string argument for the %s format to a wide
149 * string representation. ``prec'' specifies the maximum number of bytes
150 * to output. If ``prec'' is greater than or equal to zero, we can't assume
151 * that the multibyte character string ends in a null character.
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700152 *
Elliott Hughes94336d82014-04-29 17:39:29 -0700153 * Returns NULL on failure.
154 * To find out what happened check errno for ENOMEM, EILSEQ and EINVAL.
155 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700156static wchar_t* __mbsconv(char* mbsarg, int prec) {
157 mbstate_t mbs;
158 wchar_t *convbuf, *wcp;
159 const char* p;
160 size_t insize, nchars, nconv;
Elliott Hughes94336d82014-04-29 17:39:29 -0700161
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700162 if (mbsarg == NULL) return (NULL);
Elliott Hughes94336d82014-04-29 17:39:29 -0700163
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700164 /*
165 * Supplied argument is a multibyte string; convert it to wide
166 * characters first.
167 */
168 if (prec >= 0) {
169 /*
170 * String is not guaranteed to be NUL-terminated. Find the
171 * number of characters to print.
172 */
173 p = mbsarg;
174 insize = nchars = nconv = 0;
175 bzero(&mbs, sizeof(mbs));
176 while (nchars != (size_t)prec) {
177 nconv = mbrlen(p, MB_CUR_MAX, &mbs);
178 if (nconv == (size_t)0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
179 p += nconv;
180 nchars++;
181 insize += nconv;
182 }
183 if (nconv == (size_t)-1 || nconv == (size_t)-2) return (NULL);
184 } else
185 insize = strlen(mbsarg);
Elliott Hughes94336d82014-04-29 17:39:29 -0700186
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700187 /*
188 * Allocate buffer for the result and perform the conversion,
189 * converting at most `size' bytes of the input multibyte string to
190 * wide characters for printing.
191 */
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700192 convbuf = static_cast<wchar_t*>(calloc(insize + 1, sizeof(*convbuf)));
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700193 if (convbuf == NULL) return (NULL);
194 wcp = convbuf;
195 p = mbsarg;
196 bzero(&mbs, sizeof(mbs));
197 nconv = 0;
198 while (insize != 0) {
199 nconv = mbrtowc(wcp, p, insize, &mbs);
200 if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2) break;
201 wcp++;
202 p += nconv;
203 insize -= nconv;
204 }
205 if (nconv == (size_t)-1 || nconv == (size_t)-2) {
206 free(convbuf);
207 return (NULL);
208 }
209 *wcp = '\0';
Elliott Hughes94336d82014-04-29 17:39:29 -0700210
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700211 return (convbuf);
Elliott Hughes94336d82014-04-29 17:39:29 -0700212}
213
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700214#define DEFPREC 6
Elliott Hughes94336d82014-04-29 17:39:29 -0700215
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700216#define to_digit(c) ((c) - '0')
217#define is_digit(c) ((unsigned)to_digit(c) <= 9)
218#define to_char(n) ((CHAR_TYPE)((n) + '0'))
219
220template <typename CharT>
221static int exponent(CharT* p0, int exp, int fmtch) {
222 CharT* p = p0;
223 *p++ = fmtch;
224 if (exp < 0) {
225 exp = -exp;
226 *p++ = '-';
227 } else {
228 *p++ = '+';
229 }
230
231 CharT expbuf[MAXEXPDIG];
232 CharT* t = expbuf + MAXEXPDIG;
233 if (exp > 9) {
234 do {
235 *--t = to_char(exp % 10);
236 } while ((exp /= 10) > 9);
237 *--t = to_char(exp);
238 for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
239 } else {
240 /*
241 * Exponents for decimal floating point conversions
242 * (%[eEgG]) must be at least two characters long,
243 * whereas exponents for hexadecimal conversions can
244 * be only one character long.
245 */
246 if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
247 *p++ = to_char(exp);
248 }
249 return (p - p0);
250}
Elliott Hughes94336d82014-04-29 17:39:29 -0700251
252/*
253 * The size of the buffer we use as scratch space for integer
254 * conversions, among other things. Technically, we would need the
255 * most space for base 10 conversions with thousands' grouping
256 * characters between each pair of digits. 100 bytes is a
257 * conservative overestimate even for a 128-bit uintmax_t.
258 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700259#define BUF 100
Elliott Hughes94336d82014-04-29 17:39:29 -0700260
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700261#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
Elliott Hughes94336d82014-04-29 17:39:29 -0700262
263/*
Elliott Hughes94336d82014-04-29 17:39:29 -0700264 * Flags used during conversion.
265 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700266#define ALT 0x0001 /* alternate form */
267#define LADJUST 0x0004 /* left adjustment */
268#define LONGDBL 0x0008 /* long double */
269#define LONGINT 0x0010 /* long integer */
270#define LLONGINT 0x0020 /* long long integer */
271#define SHORTINT 0x0040 /* short integer */
272#define ZEROPAD 0x0080 /* zero (as opposed to blank) pad */
273#define FPT 0x0100 /* Floating point number */
274#define PTRINT 0x0200 /* (unsigned) ptrdiff_t */
275#define SIZEINT 0x0400 /* (signed) size_t */
276#define CHARINT 0x0800 /* 8 bit integer */
277#define MAXINT 0x1000 /* largest integer size (intmax_t) */
Elliott Hughes94336d82014-04-29 17:39:29 -0700278
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700279int __vfwprintf(FILE* __restrict fp, const wchar_t* __restrict fmt0, __va_list ap) {
280 wchar_t* fmt; /* format string */
281 wchar_t ch; /* character from fmt */
282 int n, n2, n3; /* handy integers (short term usage) */
283 wchar_t* cp; /* handy char pointer (short term usage) */
284 int flags; /* flags as above */
285 int ret; /* return value accumulator */
286 int width; /* width from format (%8d), or 0 */
287 int prec; /* precision from format; <0 for N/A */
288 wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */
289 /*
290 * We can decompose the printed representation of floating
291 * point numbers into several parts, some of which may be empty:
292 *
293 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
294 * A B ---C--- D E F
295 *
296 * A: 'sign' holds this value if present; '\0' otherwise
297 * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
298 * C: cp points to the string MMMNNN. Leading and trailing
299 * zeros are not in the string and must be added.
300 * D: expchar holds this character; '\0' if no exponent, e.g. %f
301 * F: at least two digits for decimal, at least one digit for hex
302 */
303 char* decimal_point = NULL;
304 int signflag; /* true if float is negative */
305 union { /* floating point arguments %[aAeEfFgG] */
306 double dbl;
307 long double ldbl;
308 } fparg;
309 int expt; /* integer value of exponent */
310 char expchar; /* exponent character: [eEpP\0] */
311 char* dtoaend; /* pointer to end of converted digits */
312 int expsize; /* character count for expstr */
313 int lead; /* sig figs before decimal or group sep */
314 int ndig; /* actual number of digits returned by dtoa */
315 wchar_t expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
316 char* dtoaresult = NULL;
Elliott Hughes94336d82014-04-29 17:39:29 -0700317
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700318 uintmax_t _umax; /* integer arguments %[diouxX] */
319 enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
320 int dprec; /* a copy of prec if %[diouxX], 0 otherwise */
321 int realsz; /* field size expanded by dprec */
322 int size; /* size of converted field or string */
323 const char* xdigs; /* digits for %[xX] conversion */
324 wchar_t buf[BUF]; /* buffer with space for digits of uintmax_t */
325 wchar_t ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */
326 union arg* argtable; /* args, built due to positional arg */
327 union arg statargtable[STATIC_ARG_TBL_SIZE];
328 size_t argtablesiz;
329 int nextarg; /* 1-based argument index */
330 va_list orgap; /* original argument pointer */
331 wchar_t* convbuf; /* buffer for multibyte to wide conversion */
Elliott Hughes94336d82014-04-29 17:39:29 -0700332
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700333 /*
334 * Choose PADSIZE to trade efficiency vs. size. If larger printf
335 * fields occur frequently, increase PADSIZE and make the initialisers
336 * below longer.
337 */
338#define PADSIZE 16 /* pad chunk size */
339 static wchar_t blanks[PADSIZE] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
340 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
341 static wchar_t zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0',
342 '0', '0', '0', '0', '0', '0', '0', '0' };
Elliott Hughes94336d82014-04-29 17:39:29 -0700343
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700344 static const char xdigs_lower[] = "0123456789abcdef";
345 static const char xdigs_upper[] = "0123456789ABCDEF";
Elliott Hughes94336d82014-04-29 17:39:29 -0700346
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700347 /*
348 * BEWARE, these `goto error' on error, PRINT uses 'n3',
349 * PAD uses `n' and 'n3', and PRINTANDPAD uses 'n', 'n2', and 'n3'.
350 */
351#define PRINT(ptr, len) \
352 do { \
353 for (n3 = 0; n3 < (len); n3++) { \
354 if ((__xfputwc((ptr)[n3], fp)) == WEOF) goto error; \
355 } \
356 } while (0)
357#define PAD(howmany, with) \
358 do { \
359 if ((n = (howmany)) > 0) { \
360 while (n > PADSIZE) { \
361 PRINT(with, PADSIZE); \
362 n -= PADSIZE; \
363 } \
364 PRINT(with, n); \
365 } \
366 } while (0)
367#define PRINTANDPAD(p, ep, len, with) \
368 do { \
369 n2 = (ep) - (p); \
370 if (n2 > (len)) n2 = (len); \
371 if (n2 > 0) PRINT((p), n2); \
372 PAD((len) - (n2 > 0 ? n2 : 0), (with)); \
373 } while (0)
Elliott Hughes94336d82014-04-29 17:39:29 -0700374
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700375 /*
376 * To extend shorts properly, we need both signed and unsigned
377 * argument extraction methods.
378 */
379#define SARG() \
380 ((intmax_t)(flags & MAXINT \
381 ? GETARG(intmax_t) \
382 : flags & LLONGINT \
383 ? GETARG(long long) \
384 : flags & LONGINT \
385 ? GETARG(long) \
386 : flags & PTRINT \
387 ? GETARG(ptrdiff_t) \
388 : flags & SIZEINT \
389 ? GETARG(ssize_t) \
390 : flags & SHORTINT \
391 ? (short)GETARG(int) \
392 : flags & CHARINT ? (signed char)GETARG(int) \
393 : GETARG(int)))
394#define UARG() \
395 ((uintmax_t)(flags & MAXINT \
396 ? GETARG(uintmax_t) \
397 : flags & LLONGINT \
398 ? GETARG(unsigned long long) \
399 : flags & LONGINT \
400 ? GETARG(unsigned long) \
401 : flags & PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \
402 flags & SIZEINT \
403 ? GETARG(size_t) \
404 : flags & SHORTINT \
405 ? (unsigned short)GETARG(int) \
406 : flags & CHARINT ? (unsigned char)GETARG(int) \
407 : GETARG(unsigned int)))
Elliott Hughes94336d82014-04-29 17:39:29 -0700408
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700409 /*
410 * Append a digit to a value and check for overflow.
411 */
412#define APPEND_DIGIT(val, dig) \
413 do { \
414 if ((val) > INT_MAX / 10) goto overflow; \
415 (val) *= 10; \
416 if ((val) > INT_MAX - to_digit((dig))) goto overflow; \
417 (val) += to_digit((dig)); \
418 } while (0)
Elliott Hughes94336d82014-04-29 17:39:29 -0700419
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700420 /*
421 * Get * arguments, including the form *nn$. Preserve the nextarg
422 * that the argument can be gotten once the type is determined.
423 */
424#define GETASTER(val) \
425 n2 = 0; \
426 cp = fmt; \
427 while (is_digit(*cp)) { \
428 APPEND_DIGIT(n2, *cp); \
429 cp++; \
430 } \
431 if (*cp == '$') { \
432 int hold = nextarg; \
433 if (argtable == NULL) { \
434 argtable = statargtable; \
435 __find_arguments(fmt0, orgap, &argtable, &argtablesiz); \
436 } \
437 nextarg = n2; \
438 val = GETARG(int); \
439 nextarg = hold; \
440 fmt = ++cp; \
441 } else { \
442 val = GETARG(int); \
443 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700444
445/*
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700446 * Get the argument indexed by nextarg. If the argument table is
447 * built, use it to get the argument. If its not, get the next
448 * argument (and arguments must be gotten sequentially).
449 */
Elliott Hughes94336d82014-04-29 17:39:29 -0700450#define GETARG(type) \
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700451 ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type)))
Elliott Hughes94336d82014-04-29 17:39:29 -0700452
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700453 _SET_ORIENTATION(fp, 1);
454 /* sorry, fwprintf(read_only_file, "") returns EOF, not 0 */
455 if (cantwrite(fp)) {
456 errno = EBADF;
457 return (EOF);
458 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700459
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700460 /* optimise fwprintf(stderr) (and other unbuffered Unix files) */
461 if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0)
462 return (__sbprintf(fp, fmt0, ap));
Elliott Hughes94336d82014-04-29 17:39:29 -0700463
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700464 fmt = (wchar_t*)fmt0;
465 argtable = NULL;
466 nextarg = 1;
467 va_copy(orgap, ap);
468 ret = 0;
469 convbuf = NULL;
Elliott Hughes94336d82014-04-29 17:39:29 -0700470
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700471 /*
472 * Scan the format for conversions (`%' character).
473 */
474 for (;;) {
475 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
476 if (fmt != cp) {
477 ptrdiff_t m = fmt - cp;
478 if (m < 0 || m > INT_MAX - ret) goto overflow;
479 PRINT(cp, m);
480 ret += m;
481 }
482 if (ch == '\0') goto done;
483 fmt++; /* skip over '%' */
Elliott Hughes94336d82014-04-29 17:39:29 -0700484
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700485 flags = 0;
486 dprec = 0;
487 width = 0;
488 prec = -1;
489 sign = '\0';
490 ox[1] = '\0';
Elliott Hughes94336d82014-04-29 17:39:29 -0700491
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700492 rflag:
493 ch = *fmt++;
494 reswitch:
495 switch (ch) {
496 case ' ':
497 /*
498 * ``If the space and + flags both appear, the space
499 * flag will be ignored.''
500 * -- ANSI X3J11
501 */
502 if (!sign) sign = ' ';
503 goto rflag;
504 case '#':
505 flags |= ALT;
506 goto rflag;
507 case '\'':
508 /* grouping not implemented */
509 goto rflag;
510 case '*':
511 /*
512 * ``A negative field width argument is taken as a
513 * - flag followed by a positive field width.''
514 * -- ANSI X3J11
515 * They don't exclude field widths read from args.
516 */
517 GETASTER(width);
518 if (width >= 0) goto rflag;
519 if (width == INT_MIN) goto overflow;
520 width = -width;
521 /* FALLTHROUGH */
522 case '-':
523 flags |= LADJUST;
524 goto rflag;
525 case '+':
526 sign = '+';
527 goto rflag;
528 case '.':
529 if ((ch = *fmt++) == '*') {
530 GETASTER(n);
531 prec = n < 0 ? -1 : n;
532 goto rflag;
533 }
534 n = 0;
535 while (is_digit(ch)) {
536 APPEND_DIGIT(n, ch);
537 ch = *fmt++;
538 }
539 if (ch == '$') {
540 nextarg = n;
541 if (argtable == NULL) {
542 argtable = statargtable;
543 __find_arguments(fmt0, orgap, &argtable, &argtablesiz);
544 }
545 goto rflag;
546 }
547 prec = n;
548 goto reswitch;
549 case '0':
550 /*
551 * ``Note that 0 is taken as a flag, not as the
552 * beginning of a field width.''
553 * -- ANSI X3J11
554 */
555 flags |= ZEROPAD;
556 goto rflag;
557 case '1':
558 case '2':
559 case '3':
560 case '4':
561 case '5':
562 case '6':
563 case '7':
564 case '8':
565 case '9':
566 n = 0;
567 do {
568 APPEND_DIGIT(n, ch);
569 ch = *fmt++;
570 } while (is_digit(ch));
571 if (ch == '$') {
572 nextarg = n;
573 if (argtable == NULL) {
574 argtable = statargtable;
575 __find_arguments(fmt0, orgap, &argtable, &argtablesiz);
576 }
577 goto rflag;
578 }
579 width = n;
580 goto reswitch;
581 case 'L':
582 flags |= LONGDBL;
583 goto rflag;
584 case 'h':
585 if (*fmt == 'h') {
586 fmt++;
587 flags |= CHARINT;
588 } else {
589 flags |= SHORTINT;
590 }
591 goto rflag;
592 case 'j':
593 flags |= MAXINT;
594 goto rflag;
595 case 'l':
596 if (*fmt == 'l') {
597 fmt++;
598 flags |= LLONGINT;
599 } else {
600 flags |= LONGINT;
601 }
602 goto rflag;
603 case 'q':
604 flags |= LLONGINT;
605 goto rflag;
606 case 't':
607 flags |= PTRINT;
608 goto rflag;
609 case 'z':
610 flags |= SIZEINT;
611 goto rflag;
612 case 'C':
613 flags |= LONGINT;
614 /*FALLTHROUGH*/
615 case 'c':
616 if (flags & LONGINT)
617 *(cp = buf) = (wchar_t)GETARG(wint_t);
618 else
619 *(cp = buf) = (wchar_t)btowc(GETARG(int));
620 size = 1;
621 sign = '\0';
622 break;
623 case 'D':
624 flags |= LONGINT;
625 /*FALLTHROUGH*/
626 case 'd':
627 case 'i':
628 _umax = SARG();
629 if ((intmax_t)_umax < 0) {
630 _umax = -_umax;
631 sign = '-';
632 }
633 base = DEC;
634 goto number;
635 case 'a':
636 case 'A':
637 if (ch == 'a') {
638 ox[1] = 'x';
639 xdigs = xdigs_lower;
640 expchar = 'p';
641 } else {
642 ox[1] = 'X';
643 xdigs = xdigs_upper;
644 expchar = 'P';
645 }
646 if (prec >= 0) prec++;
647 if (dtoaresult) __freedtoa(dtoaresult);
648 if (flags & LONGDBL) {
649 fparg.ldbl = GETARG(long double);
650 dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
651 if (dtoaresult == NULL) {
652 errno = ENOMEM;
653 goto error;
654 }
655 } else {
656 fparg.dbl = GETARG(double);
657 dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
658 if (dtoaresult == NULL) {
659 errno = ENOMEM;
660 goto error;
661 }
662 }
663 if (prec < 0) prec = dtoaend - dtoaresult;
664 if (expt == INT_MAX) ox[1] = '\0';
665 free(convbuf);
666 cp = convbuf = __mbsconv(dtoaresult, -1);
667 if (cp == NULL) goto error;
668 ndig = dtoaend - dtoaresult;
669 goto fp_common;
670 case 'e':
671 case 'E':
672 expchar = ch;
673 if (prec < 0) /* account for digit before decpt */
674 prec = DEFPREC + 1;
675 else
676 prec++;
677 goto fp_begin;
678 case 'f':
679 case 'F':
680 expchar = '\0';
681 goto fp_begin;
682 case 'g':
683 case 'G':
684 expchar = ch - ('g' - 'e');
685 if (prec == 0) prec = 1;
686 fp_begin:
687 if (prec < 0) prec = DEFPREC;
688 if (dtoaresult) __freedtoa(dtoaresult);
689 if (flags & LONGDBL) {
690 fparg.ldbl = GETARG(long double);
691 dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
692 if (dtoaresult == NULL) {
693 errno = ENOMEM;
694 goto error;
695 }
696 } else {
697 fparg.dbl = GETARG(double);
698 dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
699 if (dtoaresult == NULL) {
700 errno = ENOMEM;
701 goto error;
702 }
703 if (expt == 9999) expt = INT_MAX;
704 }
705 free(convbuf);
706 cp = convbuf = __mbsconv(dtoaresult, -1);
707 if (cp == NULL) goto error;
708 ndig = dtoaend - dtoaresult;
709 fp_common:
710 if (signflag) sign = '-';
711 if (expt == INT_MAX) { /* inf or nan */
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700712 if (*cp == 'N') {
713 cp = const_cast<wchar_t*>((ch >= 'a') ? L"nan" : L"NAN");
714 } else {
715 cp = const_cast<wchar_t*>((ch >= 'a') ? L"inf" : L"INF");
716 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700717 size = 3;
718 flags &= ~ZEROPAD;
719 break;
720 }
721 flags |= FPT;
722 if (ch == 'g' || ch == 'G') {
723 if (expt > -4 && expt <= prec) {
724 /* Make %[gG] smell like %[fF] */
725 expchar = '\0';
726 if (flags & ALT)
727 prec -= expt;
728 else
729 prec = ndig - expt;
730 if (prec < 0) prec = 0;
731 } else {
732 /*
733 * Make %[gG] smell like %[eE], but
734 * trim trailing zeroes if no # flag.
735 */
736 if (!(flags & ALT)) prec = ndig;
737 }
738 }
739 if (expchar) {
740 expsize = exponent(expstr, expt - 1, expchar);
741 size = expsize + prec;
742 if (prec > 1 || flags & ALT) ++size;
743 } else {
744 /* space for digits before decimal point */
745 if (expt > 0)
746 size = expt;
747 else /* "0" */
748 size = 1;
749 /* space for decimal pt and following digits */
750 if (prec || flags & ALT) size += prec + 1;
751 lead = expt;
752 }
753 break;
Elliott Hughese2341d02014-05-02 18:16:32 -0700754#ifndef NO_PRINTF_PERCENT_N
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700755 case 'n':
756 if (flags & LLONGINT)
757 *GETARG(long long*) = ret;
758 else if (flags & LONGINT)
759 *GETARG(long*) = ret;
760 else if (flags & SHORTINT)
761 *GETARG(short*) = ret;
762 else if (flags & CHARINT)
763 *GETARG(signed char*) = ret;
764 else if (flags & PTRINT)
765 *GETARG(ptrdiff_t*) = ret;
766 else if (flags & SIZEINT)
767 *GETARG(ssize_t*) = ret;
768 else if (flags & MAXINT)
769 *GETARG(intmax_t*) = ret;
770 else
771 *GETARG(int*) = ret;
772 continue; /* no output */
773#endif /* NO_PRINTF_PERCENT_N */
774 case 'O':
775 flags |= LONGINT;
776 /*FALLTHROUGH*/
777 case 'o':
778 _umax = UARG();
779 base = OCT;
780 goto nosign;
781 case 'p':
782 /*
783 * ``The argument shall be a pointer to void. The
784 * value of the pointer is converted to a sequence
785 * of printable characters, in an implementation-
786 * defined manner.''
787 * -- ANSI X3J11
788 */
789 _umax = (u_long)GETARG(void*);
790 base = HEX;
791 xdigs = xdigs_lower;
792 ox[1] = 'x';
793 goto nosign;
794 case 'S':
795 flags |= LONGINT;
796 /*FALLTHROUGH*/
797 case 's':
798 if (flags & LONGINT) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700799 if ((cp = GETARG(wchar_t*)) == NULL) cp = const_cast<wchar_t*>(L"(null)");
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700800 } else {
801 char* mbsarg;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700802 if ((mbsarg = GETARG(char*)) == NULL) mbsarg = const_cast<char*>("(null)");
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700803 free(convbuf);
804 convbuf = __mbsconv(mbsarg, prec);
805 if (convbuf == NULL) {
806 fp->_flags |= __SERR;
807 goto error;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700808 } else {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700809 cp = convbuf;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700810 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700811 }
812 if (prec >= 0) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700813 size = wcsnlen(cp, prec);
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700814 } else {
815 size_t len;
Elliott Hughes94336d82014-04-29 17:39:29 -0700816
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700817 if ((len = wcslen(cp)) > INT_MAX) goto overflow;
818 size = (int)len;
819 }
820 sign = '\0';
821 break;
822 case 'U':
823 flags |= LONGINT;
824 /*FALLTHROUGH*/
825 case 'u':
826 _umax = UARG();
827 base = DEC;
828 goto nosign;
829 case 'X':
830 xdigs = xdigs_upper;
831 goto hex;
832 case 'x':
833 xdigs = xdigs_lower;
834 hex:
835 _umax = UARG();
836 base = HEX;
837 /* leading 0x/X only if non-zero */
838 if (flags & ALT && _umax != 0) ox[1] = ch;
Elliott Hughes94336d82014-04-29 17:39:29 -0700839
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700840 /* unsigned conversions */
841 nosign:
842 sign = '\0';
843 /*
844 * ``... diouXx conversions ... if a precision is
845 * specified, the 0 flag will be ignored.''
846 * -- ANSI X3J11
847 */
848 number:
849 if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
Elliott Hughes94336d82014-04-29 17:39:29 -0700850
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700851 /*
852 * ``The result of converting a zero value with an
853 * explicit precision of zero is no characters.''
854 * -- ANSI X3J11
855 */
856 cp = buf + BUF;
857 if (_umax != 0 || prec != 0) {
858 /*
859 * Unsigned mod is hard, and unsigned mod
860 * by a constant is easier than that by
861 * a variable; hence this switch.
862 */
863 switch (base) {
864 case OCT:
865 do {
866 *--cp = to_char(_umax & 7);
867 _umax >>= 3;
868 } while (_umax);
869 /* handle octal leading 0 */
870 if (flags & ALT && *cp != '0') *--cp = '0';
871 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700872
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700873 case DEC:
874 /* many numbers are 1 digit */
875 while (_umax >= 10) {
876 *--cp = to_char(_umax % 10);
877 _umax /= 10;
878 }
879 *--cp = to_char(_umax);
880 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700881
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700882 case HEX:
883 do {
884 *--cp = xdigs[_umax & 15];
885 _umax >>= 4;
886 } while (_umax);
887 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700888
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700889 default:
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700890 abort();
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700891 }
892 }
893 size = buf + BUF - cp;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700894 if (size > BUF) abort(); /* should never happen */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700895 break;
896 default: /* "%?" prints ?, unless ? is NUL */
897 if (ch == '\0') goto done;
898 /* pretend it was %c with argument ch */
899 cp = buf;
900 *cp = ch;
901 size = 1;
902 sign = '\0';
903 break;
904 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700905
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700906 /*
907 * All reasonable formats wind up here. At this point, `cp'
908 * points to a string which (if not flags&LADJUST) should be
909 * padded out to `width' places. If flags&ZEROPAD, it should
910 * first be prefixed by any sign or other prefix; otherwise,
911 * it should be blank padded before the prefix is emitted.
912 * After any left-hand padding and prefixing, emit zeroes
913 * required by a decimal %[diouxX] precision, then print the
914 * string proper, then emit zeroes required by any leftover
915 * floating precision; finally, if LADJUST, pad with blanks.
916 *
917 * Compute actual size, so we know how much to pad.
918 * size excludes decimal prec; realsz includes it.
919 */
920 realsz = dprec > size ? dprec : size;
921 if (sign) realsz++;
922 if (ox[1]) realsz += 2;
Elliott Hughes94336d82014-04-29 17:39:29 -0700923
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700924 /* right-adjusting blank padding */
925 if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700926
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700927 /* prefix */
928 if (sign) PRINT(&sign, 1);
929 if (ox[1]) { /* ox[1] is either x, X, or \0 */
930 ox[0] = '0';
931 PRINT(ox, 2);
932 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700933
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700934 /* right-adjusting zero padding */
935 if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700936
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700937 /* leading zeroes from decimal precision */
938 PAD(dprec - size, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700939
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700940 /* the string or number proper */
941 if ((flags & FPT) == 0) {
942 PRINT(cp, size);
943 } else { /* glue together f_p fragments */
944 if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR);
945 if (!expchar) { /* %[fF] or sufficiently short %[gG] */
946 if (expt <= 0) {
947 PRINT(zeroes, 1);
948 if (prec || flags & ALT) PRINT(decimal_point, 1);
949 PAD(-expt, zeroes);
950 /* already handled initial 0's */
951 prec += expt;
952 } else {
953 PRINTANDPAD(cp, convbuf + ndig, lead, zeroes);
954 cp += lead;
955 if (prec || flags & ALT) PRINT(decimal_point, 1);
956 }
957 PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
958 } else { /* %[eE] or sufficiently long %[gG] */
959 if (prec > 1 || flags & ALT) {
960 buf[0] = *cp++;
961 buf[1] = *decimal_point;
962 PRINT(buf, 2);
963 PRINT(cp, ndig - 1);
964 PAD(prec - ndig, zeroes);
965 } else { /* XeYYY */
966 PRINT(cp, 1);
967 }
968 PRINT(expstr, expsize);
969 }
970 }
971 /* left-adjusting padding (always blank) */
972 if (flags & LADJUST) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700973
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700974 /* finally, adjust ret */
975 if (width < realsz) width = realsz;
976 if (width > INT_MAX - ret) goto overflow;
977 ret += width;
978 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700979done:
980error:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700981 va_end(orgap);
982 if (__sferror(fp)) ret = -1;
983 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -0700984
985overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700986 errno = ENOMEM;
987 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -0700988
989finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700990 free(convbuf);
991 if (dtoaresult) __freedtoa(dtoaresult);
992 if (argtable != NULL && argtable != statargtable) {
993 munmap(argtable, argtablesiz);
994 argtable = NULL;
995 }
996 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -0700997}
998
Elliott Hughes94336d82014-04-29 17:39:29 -0700999/*
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;
Elliott Hughes618303c2017-11-02 16:58:44 -07001183 case 'j':
1184 flags |= MAXINT;
1185 goto rflag;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001186 case 'l':
1187 if (*fmt == 'l') {
1188 fmt++;
1189 flags |= LLONGINT;
1190 } else {
1191 flags |= LONGINT;
1192 }
1193 goto rflag;
1194 case 'q':
1195 flags |= LLONGINT;
1196 goto rflag;
1197 case 't':
1198 flags |= PTRINT;
1199 goto rflag;
1200 case 'z':
1201 flags |= SIZEINT;
1202 goto rflag;
1203 case 'C':
1204 flags |= LONGINT;
1205 /*FALLTHROUGH*/
1206 case 'c':
1207 if (flags & LONGINT)
1208 ADDTYPE(T_WINT);
1209 else
1210 ADDTYPE(T_INT);
1211 break;
1212 case 'D':
1213 flags |= LONGINT;
1214 /*FALLTHROUGH*/
1215 case 'd':
1216 case 'i':
1217 ADDSARG();
1218 break;
1219 case 'a':
1220 case 'A':
1221 case 'e':
1222 case 'E':
1223 case 'f':
1224 case 'F':
1225 case 'g':
1226 case 'G':
1227 if (flags & LONGDBL)
1228 ADDTYPE(T_LONG_DOUBLE);
1229 else
1230 ADDTYPE(T_DOUBLE);
1231 break;
Elliott Hughese2341d02014-05-02 18:16:32 -07001232#ifndef NO_PRINTF_PERCENT_N
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001233 case 'n':
1234 if (flags & LLONGINT)
1235 ADDTYPE(TP_LLONG);
1236 else if (flags & LONGINT)
1237 ADDTYPE(TP_LONG);
1238 else if (flags & SHORTINT)
1239 ADDTYPE(TP_SHORT);
1240 else if (flags & PTRINT)
1241 ADDTYPE(TP_PTRINT);
1242 else if (flags & SIZEINT)
1243 ADDTYPE(TP_SSIZEINT);
1244 else if (flags & MAXINT)
1245 ADDTYPE(TP_MAXINT);
1246 else
1247 ADDTYPE(TP_INT);
1248 continue; /* no output */
1249#endif /* NO_PRINTF_PERCENT_N */
1250 case 'O':
1251 flags |= LONGINT;
1252 /*FALLTHROUGH*/
1253 case 'o':
1254 ADDUARG();
1255 break;
1256 case 'p':
1257 ADDTYPE(TP_VOID);
1258 break;
1259 case 'S':
1260 flags |= LONGINT;
1261 /*FALLTHROUGH*/
1262 case 's':
1263 if (flags & LONGINT)
1264 ADDTYPE(TP_CHAR);
1265 else
1266 ADDTYPE(TP_WCHAR);
1267 break;
1268 case 'U':
1269 flags |= LONGINT;
1270 /*FALLTHROUGH*/
1271 case 'u':
1272 case 'X':
1273 case 'x':
1274 ADDUARG();
1275 break;
1276 default: /* "%?" prints ?, unless ? is NUL */
1277 if (ch == '\0') goto done;
1278 break;
1279 }
1280 }
Elliott Hughes94336d82014-04-29 17:39:29 -07001281done:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001282 /*
1283 * Build the argument table.
1284 */
1285 if (tablemax >= STATIC_ARG_TBL_SIZE) {
1286 *argtablesiz = sizeof(union arg) * (tablemax + 1);
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001287 *argtable = static_cast<arg*>(mmap(NULL, *argtablesiz,
1288 PROT_WRITE | PROT_READ,
1289 MAP_ANON | MAP_PRIVATE, -1, 0));
1290 if (*argtable == MAP_FAILED) return -1;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001291 }
Elliott Hughes94336d82014-04-29 17:39:29 -07001292
1293#if 0
1294 /* XXX is this required? */
1295 (*argtable)[0].intarg = 0;
1296#endif
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001297 for (n = 1; n <= tablemax; n++) {
1298 switch (typetable[n]) {
1299 case T_UNUSED:
1300 case T_CHAR:
1301 case T_U_CHAR:
1302 case T_SHORT:
1303 case T_U_SHORT:
1304 case T_INT:
1305 (*argtable)[n].intarg = va_arg(ap, int);
1306 break;
1307 case TP_SHORT:
1308 (*argtable)[n].pshortarg = va_arg(ap, short*);
1309 break;
1310 case T_U_INT:
1311 (*argtable)[n].uintarg = va_arg(ap, unsigned int);
1312 break;
1313 case TP_INT:
1314 (*argtable)[n].pintarg = va_arg(ap, int*);
1315 break;
1316 case T_LONG:
1317 (*argtable)[n].longarg = va_arg(ap, long);
1318 break;
1319 case T_U_LONG:
1320 (*argtable)[n].ulongarg = va_arg(ap, unsigned long);
1321 break;
1322 case TP_LONG:
1323 (*argtable)[n].plongarg = va_arg(ap, long*);
1324 break;
1325 case T_LLONG:
1326 (*argtable)[n].longlongarg = va_arg(ap, long long);
1327 break;
1328 case T_U_LLONG:
1329 (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
1330 break;
1331 case TP_LLONG:
1332 (*argtable)[n].plonglongarg = va_arg(ap, long long*);
1333 break;
1334 case T_DOUBLE:
1335 (*argtable)[n].doublearg = va_arg(ap, double);
1336 break;
1337 case T_LONG_DOUBLE:
1338 (*argtable)[n].longdoublearg = va_arg(ap, long double);
1339 break;
1340 case TP_CHAR:
1341 (*argtable)[n].pchararg = va_arg(ap, char*);
1342 break;
1343 case TP_VOID:
1344 (*argtable)[n].pvoidarg = va_arg(ap, void*);
1345 break;
1346 case T_PTRINT:
1347 (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
1348 break;
1349 case TP_PTRINT:
1350 (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*);
1351 break;
1352 case T_SIZEINT:
1353 (*argtable)[n].sizearg = va_arg(ap, size_t);
1354 break;
1355 case T_SSIZEINT:
1356 (*argtable)[n].ssizearg = va_arg(ap, ssize_t);
1357 break;
1358 case TP_SSIZEINT:
1359 (*argtable)[n].pssizearg = va_arg(ap, ssize_t*);
1360 break;
Elliott Hughes618303c2017-11-02 16:58:44 -07001361 case T_MAXINT:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001362 (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
1363 break;
Elliott Hughes618303c2017-11-02 16:58:44 -07001364 case T_MAXUINT:
1365 (*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t);
1366 break;
1367 case TP_MAXINT:
1368 (*argtable)[n].pintmaxarg = va_arg(ap, intmax_t*);
1369 break;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001370 case T_WINT:
1371 (*argtable)[n].wintarg = va_arg(ap, wint_t);
1372 break;
1373 case TP_WCHAR:
1374 (*argtable)[n].pwchararg = va_arg(ap, wchar_t*);
1375 break;
1376 }
1377 }
1378 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -07001379
1380overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001381 errno = ENOMEM;
1382 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -07001383
1384finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001385 if (typetable != NULL && typetable != stattypetable) {
1386 munmap(typetable, *argtablesiz);
1387 typetable = NULL;
1388 }
1389 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -07001390}
1391
1392/*
1393 * Increase the size of the type table.
1394 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001395static int __grow_type_table(unsigned char** typetable, int* tablesize) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001396 unsigned char* old_table = *typetable;
1397 int new_size = *tablesize * 2;
Elliott Hughes94336d82014-04-29 17:39:29 -07001398
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001399 if (new_size < getpagesize()) new_size = getpagesize();
Elliott Hughes94336d82014-04-29 17:39:29 -07001400
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001401 if (*tablesize == STATIC_ARG_TBL_SIZE) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001402 *typetable = static_cast<unsigned char*>(mmap(NULL, new_size,
1403 PROT_WRITE | PROT_READ,
1404 MAP_ANON | MAP_PRIVATE, -1, 0));
1405 if (*typetable == MAP_FAILED) return -1;
1406 bcopy(old_table, *typetable, *tablesize);
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001407 } else {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001408 unsigned char* new_table = static_cast<unsigned char*>(mmap(NULL, new_size,
1409 PROT_WRITE | PROT_READ,
1410 MAP_ANON | MAP_PRIVATE, -1, 0));
1411 if (new_table == MAP_FAILED) return -1;
1412 memmove(new_table, *typetable, *tablesize);
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001413 munmap(*typetable, *tablesize);
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001414 *typetable = new_table;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001415 }
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001416 memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize));
Elliott Hughes94336d82014-04-29 17:39:29 -07001417
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001418 *tablesize = new_size;
1419 return 0;
Elliott Hughes94336d82014-04-29 17:39:29 -07001420}