blob: 80eb78a670cced18f7d98c296a760d9ff388d390 [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 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 */
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700193 convbuf = static_cast<wchar_t*>(calloc(insize + 1, sizeof(*convbuf)));
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700194 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 Hughesc8f2c522017-10-31 13:07:51 -0700215#define DEFPREC 6
Elliott Hughes94336d82014-04-29 17:39:29 -0700216
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700217#define to_digit(c) ((c) - '0')
218#define is_digit(c) ((unsigned)to_digit(c) <= 9)
219#define to_char(n) ((CHAR_TYPE)((n) + '0'))
220
221template <typename CharT>
222static int exponent(CharT* p0, int exp, int fmtch) {
223 CharT* p = p0;
224 *p++ = fmtch;
225 if (exp < 0) {
226 exp = -exp;
227 *p++ = '-';
228 } else {
229 *p++ = '+';
230 }
231
232 CharT expbuf[MAXEXPDIG];
233 CharT* t = expbuf + MAXEXPDIG;
234 if (exp > 9) {
235 do {
236 *--t = to_char(exp % 10);
237 } while ((exp /= 10) > 9);
238 *--t = to_char(exp);
239 for (; t < expbuf + MAXEXPDIG; *p++ = *t++) /* nothing */;
240 } else {
241 /*
242 * Exponents for decimal floating point conversions
243 * (%[eEgG]) must be at least two characters long,
244 * whereas exponents for hexadecimal conversions can
245 * be only one character long.
246 */
247 if (fmtch == 'e' || fmtch == 'E') *p++ = '0';
248 *p++ = to_char(exp);
249 }
250 return (p - p0);
251}
Elliott Hughes94336d82014-04-29 17:39:29 -0700252
253/*
254 * The size of the buffer we use as scratch space for integer
255 * conversions, among other things. Technically, we would need the
256 * most space for base 10 conversions with thousands' grouping
257 * characters between each pair of digits. 100 bytes is a
258 * conservative overestimate even for a 128-bit uintmax_t.
259 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700260#define BUF 100
Elliott Hughes94336d82014-04-29 17:39:29 -0700261
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700262#define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
Elliott Hughes94336d82014-04-29 17:39:29 -0700263
264/*
Elliott Hughes94336d82014-04-29 17:39:29 -0700265 * Flags used during conversion.
266 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700267#define ALT 0x0001 /* alternate form */
268#define LADJUST 0x0004 /* left adjustment */
269#define LONGDBL 0x0008 /* long double */
270#define LONGINT 0x0010 /* long integer */
271#define LLONGINT 0x0020 /* long long integer */
272#define SHORTINT 0x0040 /* short integer */
273#define ZEROPAD 0x0080 /* zero (as opposed to blank) pad */
274#define FPT 0x0100 /* Floating point number */
275#define PTRINT 0x0200 /* (unsigned) ptrdiff_t */
276#define SIZEINT 0x0400 /* (signed) size_t */
277#define CHARINT 0x0800 /* 8 bit integer */
278#define MAXINT 0x1000 /* largest integer size (intmax_t) */
Elliott Hughes94336d82014-04-29 17:39:29 -0700279
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700280int __vfwprintf(FILE* __restrict fp, const wchar_t* __restrict fmt0, __va_list ap) {
281 wchar_t* fmt; /* format string */
282 wchar_t ch; /* character from fmt */
283 int n, n2, n3; /* handy integers (short term usage) */
284 wchar_t* cp; /* handy char pointer (short term usage) */
285 int flags; /* flags as above */
286 int ret; /* return value accumulator */
287 int width; /* width from format (%8d), or 0 */
288 int prec; /* precision from format; <0 for N/A */
289 wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */
290 /*
291 * We can decompose the printed representation of floating
292 * point numbers into several parts, some of which may be empty:
293 *
294 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
295 * A B ---C--- D E F
296 *
297 * A: 'sign' holds this value if present; '\0' otherwise
298 * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
299 * C: cp points to the string MMMNNN. Leading and trailing
300 * zeros are not in the string and must be added.
301 * D: expchar holds this character; '\0' if no exponent, e.g. %f
302 * F: at least two digits for decimal, at least one digit for hex
303 */
304 char* decimal_point = NULL;
305 int signflag; /* true if float is negative */
306 union { /* floating point arguments %[aAeEfFgG] */
307 double dbl;
308 long double ldbl;
309 } fparg;
310 int expt; /* integer value of exponent */
311 char expchar; /* exponent character: [eEpP\0] */
312 char* dtoaend; /* pointer to end of converted digits */
313 int expsize; /* character count for expstr */
314 int lead; /* sig figs before decimal or group sep */
315 int ndig; /* actual number of digits returned by dtoa */
316 wchar_t expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
317 char* dtoaresult = NULL;
Elliott Hughes94336d82014-04-29 17:39:29 -0700318
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700319 uintmax_t _umax; /* integer arguments %[diouxX] */
320 enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
321 int dprec; /* a copy of prec if %[diouxX], 0 otherwise */
322 int realsz; /* field size expanded by dprec */
323 int size; /* size of converted field or string */
324 const char* xdigs; /* digits for %[xX] conversion */
325 wchar_t buf[BUF]; /* buffer with space for digits of uintmax_t */
326 wchar_t ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */
327 union arg* argtable; /* args, built due to positional arg */
328 union arg statargtable[STATIC_ARG_TBL_SIZE];
329 size_t argtablesiz;
330 int nextarg; /* 1-based argument index */
331 va_list orgap; /* original argument pointer */
332 wchar_t* convbuf; /* buffer for multibyte to wide conversion */
Elliott Hughes94336d82014-04-29 17:39:29 -0700333
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700334 /*
335 * Choose PADSIZE to trade efficiency vs. size. If larger printf
336 * fields occur frequently, increase PADSIZE and make the initialisers
337 * below longer.
338 */
339#define PADSIZE 16 /* pad chunk size */
340 static wchar_t blanks[PADSIZE] = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
341 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
342 static wchar_t zeroes[PADSIZE] = { '0', '0', '0', '0', '0', '0', '0', '0',
343 '0', '0', '0', '0', '0', '0', '0', '0' };
Elliott Hughes94336d82014-04-29 17:39:29 -0700344
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700345 static const char xdigs_lower[] = "0123456789abcdef";
346 static const char xdigs_upper[] = "0123456789ABCDEF";
Elliott Hughes94336d82014-04-29 17:39:29 -0700347
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700348 /*
349 * BEWARE, these `goto error' on error, PRINT uses 'n3',
350 * PAD uses `n' and 'n3', and PRINTANDPAD uses 'n', 'n2', and 'n3'.
351 */
352#define PRINT(ptr, len) \
353 do { \
354 for (n3 = 0; n3 < (len); n3++) { \
355 if ((__xfputwc((ptr)[n3], fp)) == WEOF) goto error; \
356 } \
357 } while (0)
358#define PAD(howmany, with) \
359 do { \
360 if ((n = (howmany)) > 0) { \
361 while (n > PADSIZE) { \
362 PRINT(with, PADSIZE); \
363 n -= PADSIZE; \
364 } \
365 PRINT(with, n); \
366 } \
367 } while (0)
368#define PRINTANDPAD(p, ep, len, with) \
369 do { \
370 n2 = (ep) - (p); \
371 if (n2 > (len)) n2 = (len); \
372 if (n2 > 0) PRINT((p), n2); \
373 PAD((len) - (n2 > 0 ? n2 : 0), (with)); \
374 } while (0)
Elliott Hughes94336d82014-04-29 17:39:29 -0700375
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700376 /*
377 * To extend shorts properly, we need both signed and unsigned
378 * argument extraction methods.
379 */
380#define SARG() \
381 ((intmax_t)(flags & MAXINT \
382 ? GETARG(intmax_t) \
383 : flags & LLONGINT \
384 ? GETARG(long long) \
385 : flags & LONGINT \
386 ? GETARG(long) \
387 : flags & PTRINT \
388 ? GETARG(ptrdiff_t) \
389 : flags & SIZEINT \
390 ? GETARG(ssize_t) \
391 : flags & SHORTINT \
392 ? (short)GETARG(int) \
393 : flags & CHARINT ? (signed char)GETARG(int) \
394 : GETARG(int)))
395#define UARG() \
396 ((uintmax_t)(flags & MAXINT \
397 ? GETARG(uintmax_t) \
398 : flags & LLONGINT \
399 ? GETARG(unsigned long long) \
400 : flags & LONGINT \
401 ? GETARG(unsigned long) \
402 : flags & PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \
403 flags & SIZEINT \
404 ? GETARG(size_t) \
405 : flags & SHORTINT \
406 ? (unsigned short)GETARG(int) \
407 : flags & CHARINT ? (unsigned char)GETARG(int) \
408 : GETARG(unsigned int)))
Elliott Hughes94336d82014-04-29 17:39:29 -0700409
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700410 /*
411 * Append a digit to a value and check for overflow.
412 */
413#define APPEND_DIGIT(val, dig) \
414 do { \
415 if ((val) > INT_MAX / 10) goto overflow; \
416 (val) *= 10; \
417 if ((val) > INT_MAX - to_digit((dig))) goto overflow; \
418 (val) += to_digit((dig)); \
419 } while (0)
Elliott Hughes94336d82014-04-29 17:39:29 -0700420
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700421 /*
422 * Get * arguments, including the form *nn$. Preserve the nextarg
423 * that the argument can be gotten once the type is determined.
424 */
425#define GETASTER(val) \
426 n2 = 0; \
427 cp = fmt; \
428 while (is_digit(*cp)) { \
429 APPEND_DIGIT(n2, *cp); \
430 cp++; \
431 } \
432 if (*cp == '$') { \
433 int hold = nextarg; \
434 if (argtable == NULL) { \
435 argtable = statargtable; \
436 __find_arguments(fmt0, orgap, &argtable, &argtablesiz); \
437 } \
438 nextarg = n2; \
439 val = GETARG(int); \
440 nextarg = hold; \
441 fmt = ++cp; \
442 } else { \
443 val = GETARG(int); \
444 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700445
446/*
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700447 * Get the argument indexed by nextarg. If the argument table is
448 * built, use it to get the argument. If its not, get the next
449 * argument (and arguments must be gotten sequentially).
450 */
Elliott Hughes94336d82014-04-29 17:39:29 -0700451#define GETARG(type) \
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700452 ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type)))
Elliott Hughes94336d82014-04-29 17:39:29 -0700453
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700454 _SET_ORIENTATION(fp, 1);
455 /* sorry, fwprintf(read_only_file, "") returns EOF, not 0 */
456 if (cantwrite(fp)) {
457 errno = EBADF;
458 return (EOF);
459 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700460
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700461 /* optimise fwprintf(stderr) (and other unbuffered Unix files) */
462 if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0)
463 return (__sbprintf(fp, fmt0, ap));
Elliott Hughes94336d82014-04-29 17:39:29 -0700464
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700465 fmt = (wchar_t*)fmt0;
466 argtable = NULL;
467 nextarg = 1;
468 va_copy(orgap, ap);
469 ret = 0;
470 convbuf = NULL;
Elliott Hughes94336d82014-04-29 17:39:29 -0700471
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700472 /*
473 * Scan the format for conversions (`%' character).
474 */
475 for (;;) {
476 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
477 if (fmt != cp) {
478 ptrdiff_t m = fmt - cp;
479 if (m < 0 || m > INT_MAX - ret) goto overflow;
480 PRINT(cp, m);
481 ret += m;
482 }
483 if (ch == '\0') goto done;
484 fmt++; /* skip over '%' */
Elliott Hughes94336d82014-04-29 17:39:29 -0700485
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700486 flags = 0;
487 dprec = 0;
488 width = 0;
489 prec = -1;
490 sign = '\0';
491 ox[1] = '\0';
Elliott Hughes94336d82014-04-29 17:39:29 -0700492
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700493 rflag:
494 ch = *fmt++;
495 reswitch:
496 switch (ch) {
497 case ' ':
498 /*
499 * ``If the space and + flags both appear, the space
500 * flag will be ignored.''
501 * -- ANSI X3J11
502 */
503 if (!sign) sign = ' ';
504 goto rflag;
505 case '#':
506 flags |= ALT;
507 goto rflag;
508 case '\'':
509 /* grouping not implemented */
510 goto rflag;
511 case '*':
512 /*
513 * ``A negative field width argument is taken as a
514 * - flag followed by a positive field width.''
515 * -- ANSI X3J11
516 * They don't exclude field widths read from args.
517 */
518 GETASTER(width);
519 if (width >= 0) goto rflag;
520 if (width == INT_MIN) goto overflow;
521 width = -width;
522 /* FALLTHROUGH */
523 case '-':
524 flags |= LADJUST;
525 goto rflag;
526 case '+':
527 sign = '+';
528 goto rflag;
529 case '.':
530 if ((ch = *fmt++) == '*') {
531 GETASTER(n);
532 prec = n < 0 ? -1 : n;
533 goto rflag;
534 }
535 n = 0;
536 while (is_digit(ch)) {
537 APPEND_DIGIT(n, ch);
538 ch = *fmt++;
539 }
540 if (ch == '$') {
541 nextarg = n;
542 if (argtable == NULL) {
543 argtable = statargtable;
544 __find_arguments(fmt0, orgap, &argtable, &argtablesiz);
545 }
546 goto rflag;
547 }
548 prec = n;
549 goto reswitch;
550 case '0':
551 /*
552 * ``Note that 0 is taken as a flag, not as the
553 * beginning of a field width.''
554 * -- ANSI X3J11
555 */
556 flags |= ZEROPAD;
557 goto rflag;
558 case '1':
559 case '2':
560 case '3':
561 case '4':
562 case '5':
563 case '6':
564 case '7':
565 case '8':
566 case '9':
567 n = 0;
568 do {
569 APPEND_DIGIT(n, ch);
570 ch = *fmt++;
571 } while (is_digit(ch));
572 if (ch == '$') {
573 nextarg = n;
574 if (argtable == NULL) {
575 argtable = statargtable;
576 __find_arguments(fmt0, orgap, &argtable, &argtablesiz);
577 }
578 goto rflag;
579 }
580 width = n;
581 goto reswitch;
582 case 'L':
583 flags |= LONGDBL;
584 goto rflag;
585 case 'h':
586 if (*fmt == 'h') {
587 fmt++;
588 flags |= CHARINT;
589 } else {
590 flags |= SHORTINT;
591 }
592 goto rflag;
593 case 'j':
594 flags |= MAXINT;
595 goto rflag;
596 case 'l':
597 if (*fmt == 'l') {
598 fmt++;
599 flags |= LLONGINT;
600 } else {
601 flags |= LONGINT;
602 }
603 goto rflag;
604 case 'q':
605 flags |= LLONGINT;
606 goto rflag;
607 case 't':
608 flags |= PTRINT;
609 goto rflag;
610 case 'z':
611 flags |= SIZEINT;
612 goto rflag;
613 case 'C':
614 flags |= LONGINT;
615 /*FALLTHROUGH*/
616 case 'c':
617 if (flags & LONGINT)
618 *(cp = buf) = (wchar_t)GETARG(wint_t);
619 else
620 *(cp = buf) = (wchar_t)btowc(GETARG(int));
621 size = 1;
622 sign = '\0';
623 break;
624 case 'D':
625 flags |= LONGINT;
626 /*FALLTHROUGH*/
627 case 'd':
628 case 'i':
629 _umax = SARG();
630 if ((intmax_t)_umax < 0) {
631 _umax = -_umax;
632 sign = '-';
633 }
634 base = DEC;
635 goto number;
636 case 'a':
637 case 'A':
638 if (ch == 'a') {
639 ox[1] = 'x';
640 xdigs = xdigs_lower;
641 expchar = 'p';
642 } else {
643 ox[1] = 'X';
644 xdigs = xdigs_upper;
645 expchar = 'P';
646 }
647 if (prec >= 0) prec++;
648 if (dtoaresult) __freedtoa(dtoaresult);
649 if (flags & LONGDBL) {
650 fparg.ldbl = GETARG(long double);
651 dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
652 if (dtoaresult == NULL) {
653 errno = ENOMEM;
654 goto error;
655 }
656 } else {
657 fparg.dbl = GETARG(double);
658 dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
659 if (dtoaresult == NULL) {
660 errno = ENOMEM;
661 goto error;
662 }
663 }
664 if (prec < 0) prec = dtoaend - dtoaresult;
665 if (expt == INT_MAX) ox[1] = '\0';
666 free(convbuf);
667 cp = convbuf = __mbsconv(dtoaresult, -1);
668 if (cp == NULL) goto error;
669 ndig = dtoaend - dtoaresult;
670 goto fp_common;
671 case 'e':
672 case 'E':
673 expchar = ch;
674 if (prec < 0) /* account for digit before decpt */
675 prec = DEFPREC + 1;
676 else
677 prec++;
678 goto fp_begin;
679 case 'f':
680 case 'F':
681 expchar = '\0';
682 goto fp_begin;
683 case 'g':
684 case 'G':
685 expchar = ch - ('g' - 'e');
686 if (prec == 0) prec = 1;
687 fp_begin:
688 if (prec < 0) prec = DEFPREC;
689 if (dtoaresult) __freedtoa(dtoaresult);
690 if (flags & LONGDBL) {
691 fparg.ldbl = GETARG(long double);
692 dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
693 if (dtoaresult == NULL) {
694 errno = ENOMEM;
695 goto error;
696 }
697 } else {
698 fparg.dbl = GETARG(double);
699 dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
700 if (dtoaresult == NULL) {
701 errno = ENOMEM;
702 goto error;
703 }
704 if (expt == 9999) expt = INT_MAX;
705 }
706 free(convbuf);
707 cp = convbuf = __mbsconv(dtoaresult, -1);
708 if (cp == NULL) goto error;
709 ndig = dtoaend - dtoaresult;
710 fp_common:
711 if (signflag) sign = '-';
712 if (expt == INT_MAX) { /* inf or nan */
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700713 if (*cp == 'N') {
714 cp = const_cast<wchar_t*>((ch >= 'a') ? L"nan" : L"NAN");
715 } else {
716 cp = const_cast<wchar_t*>((ch >= 'a') ? L"inf" : L"INF");
717 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700718 size = 3;
719 flags &= ~ZEROPAD;
720 break;
721 }
722 flags |= FPT;
723 if (ch == 'g' || ch == 'G') {
724 if (expt > -4 && expt <= prec) {
725 /* Make %[gG] smell like %[fF] */
726 expchar = '\0';
727 if (flags & ALT)
728 prec -= expt;
729 else
730 prec = ndig - expt;
731 if (prec < 0) prec = 0;
732 } else {
733 /*
734 * Make %[gG] smell like %[eE], but
735 * trim trailing zeroes if no # flag.
736 */
737 if (!(flags & ALT)) prec = ndig;
738 }
739 }
740 if (expchar) {
741 expsize = exponent(expstr, expt - 1, expchar);
742 size = expsize + prec;
743 if (prec > 1 || flags & ALT) ++size;
744 } else {
745 /* space for digits before decimal point */
746 if (expt > 0)
747 size = expt;
748 else /* "0" */
749 size = 1;
750 /* space for decimal pt and following digits */
751 if (prec || flags & ALT) size += prec + 1;
752 lead = expt;
753 }
754 break;
Elliott Hughese2341d02014-05-02 18:16:32 -0700755#ifndef NO_PRINTF_PERCENT_N
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700756 case 'n':
757 if (flags & LLONGINT)
758 *GETARG(long long*) = ret;
759 else if (flags & LONGINT)
760 *GETARG(long*) = ret;
761 else if (flags & SHORTINT)
762 *GETARG(short*) = ret;
763 else if (flags & CHARINT)
764 *GETARG(signed char*) = ret;
765 else if (flags & PTRINT)
766 *GETARG(ptrdiff_t*) = ret;
767 else if (flags & SIZEINT)
768 *GETARG(ssize_t*) = ret;
769 else if (flags & MAXINT)
770 *GETARG(intmax_t*) = ret;
771 else
772 *GETARG(int*) = ret;
773 continue; /* no output */
774#endif /* NO_PRINTF_PERCENT_N */
775 case 'O':
776 flags |= LONGINT;
777 /*FALLTHROUGH*/
778 case 'o':
779 _umax = UARG();
780 base = OCT;
781 goto nosign;
782 case 'p':
783 /*
784 * ``The argument shall be a pointer to void. The
785 * value of the pointer is converted to a sequence
786 * of printable characters, in an implementation-
787 * defined manner.''
788 * -- ANSI X3J11
789 */
790 _umax = (u_long)GETARG(void*);
791 base = HEX;
792 xdigs = xdigs_lower;
793 ox[1] = 'x';
794 goto nosign;
795 case 'S':
796 flags |= LONGINT;
797 /*FALLTHROUGH*/
798 case 's':
799 if (flags & LONGINT) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700800 if ((cp = GETARG(wchar_t*)) == NULL) cp = const_cast<wchar_t*>(L"(null)");
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700801 } else {
802 char* mbsarg;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700803 if ((mbsarg = GETARG(char*)) == NULL) mbsarg = const_cast<char*>("(null)");
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700804 free(convbuf);
805 convbuf = __mbsconv(mbsarg, prec);
806 if (convbuf == NULL) {
807 fp->_flags |= __SERR;
808 goto error;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700809 } else {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700810 cp = convbuf;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700811 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700812 }
813 if (prec >= 0) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700814 size = wcsnlen(cp, prec);
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700815 } else {
816 size_t len;
Elliott Hughes94336d82014-04-29 17:39:29 -0700817
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700818 if ((len = wcslen(cp)) > INT_MAX) goto overflow;
819 size = (int)len;
820 }
821 sign = '\0';
822 break;
823 case 'U':
824 flags |= LONGINT;
825 /*FALLTHROUGH*/
826 case 'u':
827 _umax = UARG();
828 base = DEC;
829 goto nosign;
830 case 'X':
831 xdigs = xdigs_upper;
832 goto hex;
833 case 'x':
834 xdigs = xdigs_lower;
835 hex:
836 _umax = UARG();
837 base = HEX;
838 /* leading 0x/X only if non-zero */
839 if (flags & ALT && _umax != 0) ox[1] = ch;
Elliott Hughes94336d82014-04-29 17:39:29 -0700840
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700841 /* unsigned conversions */
842 nosign:
843 sign = '\0';
844 /*
845 * ``... diouXx conversions ... if a precision is
846 * specified, the 0 flag will be ignored.''
847 * -- ANSI X3J11
848 */
849 number:
850 if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
Elliott Hughes94336d82014-04-29 17:39:29 -0700851
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700852 /*
853 * ``The result of converting a zero value with an
854 * explicit precision of zero is no characters.''
855 * -- ANSI X3J11
856 */
857 cp = buf + BUF;
858 if (_umax != 0 || prec != 0) {
859 /*
860 * Unsigned mod is hard, and unsigned mod
861 * by a constant is easier than that by
862 * a variable; hence this switch.
863 */
864 switch (base) {
865 case OCT:
866 do {
867 *--cp = to_char(_umax & 7);
868 _umax >>= 3;
869 } while (_umax);
870 /* handle octal leading 0 */
871 if (flags & ALT && *cp != '0') *--cp = '0';
872 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700873
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700874 case DEC:
875 /* many numbers are 1 digit */
876 while (_umax >= 10) {
877 *--cp = to_char(_umax % 10);
878 _umax /= 10;
879 }
880 *--cp = to_char(_umax);
881 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700882
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700883 case HEX:
884 do {
885 *--cp = xdigs[_umax & 15];
886 _umax >>= 4;
887 } while (_umax);
888 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700889
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700890 default:
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700891 abort();
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700892 }
893 }
894 size = buf + BUF - cp;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700895 if (size > BUF) abort(); /* should never happen */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700896 break;
897 default: /* "%?" prints ?, unless ? is NUL */
898 if (ch == '\0') goto done;
899 /* pretend it was %c with argument ch */
900 cp = buf;
901 *cp = ch;
902 size = 1;
903 sign = '\0';
904 break;
905 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700906
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700907 /*
908 * All reasonable formats wind up here. At this point, `cp'
909 * points to a string which (if not flags&LADJUST) should be
910 * padded out to `width' places. If flags&ZEROPAD, it should
911 * first be prefixed by any sign or other prefix; otherwise,
912 * it should be blank padded before the prefix is emitted.
913 * After any left-hand padding and prefixing, emit zeroes
914 * required by a decimal %[diouxX] precision, then print the
915 * string proper, then emit zeroes required by any leftover
916 * floating precision; finally, if LADJUST, pad with blanks.
917 *
918 * Compute actual size, so we know how much to pad.
919 * size excludes decimal prec; realsz includes it.
920 */
921 realsz = dprec > size ? dprec : size;
922 if (sign) realsz++;
923 if (ox[1]) realsz += 2;
Elliott Hughes94336d82014-04-29 17:39:29 -0700924
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700925 /* right-adjusting blank padding */
926 if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700927
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700928 /* prefix */
929 if (sign) PRINT(&sign, 1);
930 if (ox[1]) { /* ox[1] is either x, X, or \0 */
931 ox[0] = '0';
932 PRINT(ox, 2);
933 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700934
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700935 /* right-adjusting zero padding */
936 if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700937
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700938 /* leading zeroes from decimal precision */
939 PAD(dprec - size, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700940
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700941 /* the string or number proper */
942 if ((flags & FPT) == 0) {
943 PRINT(cp, size);
944 } else { /* glue together f_p fragments */
945 if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR);
946 if (!expchar) { /* %[fF] or sufficiently short %[gG] */
947 if (expt <= 0) {
948 PRINT(zeroes, 1);
949 if (prec || flags & ALT) PRINT(decimal_point, 1);
950 PAD(-expt, zeroes);
951 /* already handled initial 0's */
952 prec += expt;
953 } else {
954 PRINTANDPAD(cp, convbuf + ndig, lead, zeroes);
955 cp += lead;
956 if (prec || flags & ALT) PRINT(decimal_point, 1);
957 }
958 PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
959 } else { /* %[eE] or sufficiently long %[gG] */
960 if (prec > 1 || flags & ALT) {
961 buf[0] = *cp++;
962 buf[1] = *decimal_point;
963 PRINT(buf, 2);
964 PRINT(cp, ndig - 1);
965 PAD(prec - ndig, zeroes);
966 } else { /* XeYYY */
967 PRINT(cp, 1);
968 }
969 PRINT(expstr, expsize);
970 }
971 }
972 /* left-adjusting padding (always blank) */
973 if (flags & LADJUST) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700974
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700975 /* finally, adjust ret */
976 if (width < realsz) width = realsz;
977 if (width > INT_MAX - ret) goto overflow;
978 ret += width;
979 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700980done:
981error:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700982 va_end(orgap);
983 if (__sferror(fp)) ret = -1;
984 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -0700985
986overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700987 errno = ENOMEM;
988 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -0700989
990finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700991 free(convbuf);
992 if (dtoaresult) __freedtoa(dtoaresult);
993 if (argtable != NULL && argtable != statargtable) {
994 munmap(argtable, argtablesiz);
995 argtable = NULL;
996 }
997 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -0700998}
999
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001000int vfwprintf(FILE* __restrict fp, const wchar_t* __restrict fmt0, __va_list ap) {
1001 int r;
Elliott Hughes94336d82014-04-29 17:39:29 -07001002
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001003 FLOCKFILE(fp);
1004 r = __vfwprintf(fp, fmt0, ap);
1005 FUNLOCKFILE(fp);
Elliott Hughes94336d82014-04-29 17:39:29 -07001006
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001007 return (r);
Elliott Hughes94336d82014-04-29 17:39:29 -07001008}
Elliott Hughes506c6de2016-01-15 16:30:18 -08001009DEF_STRONG(vfwprintf);
Elliott Hughes94336d82014-04-29 17:39:29 -07001010
1011/*
1012 * Type ids for argument type table.
1013 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001014#define T_UNUSED 0
1015#define T_SHORT 1
1016#define T_U_SHORT 2
1017#define TP_SHORT 3
1018#define T_INT 4
1019#define T_U_INT 5
1020#define TP_INT 6
1021#define T_LONG 7
1022#define T_U_LONG 8
1023#define TP_LONG 9
1024#define T_LLONG 10
1025#define T_U_LLONG 11
1026#define TP_LLONG 12
1027#define T_DOUBLE 13
1028#define T_LONG_DOUBLE 14
1029#define TP_CHAR 15
1030#define TP_VOID 16
1031#define T_PTRINT 17
1032#define TP_PTRINT 18
1033#define T_SIZEINT 19
1034#define T_SSIZEINT 20
1035#define TP_SSIZEINT 21
1036#define T_MAXINT 22
1037#define T_MAXUINT 23
1038#define TP_MAXINT 24
1039#define T_CHAR 25
1040#define T_U_CHAR 26
1041#define T_WINT 27
1042#define TP_WCHAR 28
Elliott Hughes94336d82014-04-29 17:39:29 -07001043
1044/*
1045 * Find all arguments when a positional parameter is encountered. Returns a
1046 * table, indexed by argument number, of pointers to each arguments. The
1047 * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
1048 * It will be replaced with a mmap-ed one if it overflows (malloc cannot be
1049 * used since we are attempting to make snprintf thread safe, and alloca is
1050 * problematic since we have nested functions..)
1051 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001052static int __find_arguments(const wchar_t* fmt0, va_list ap, union arg** argtable,
1053 size_t* argtablesiz) {
1054 wchar_t* fmt; /* format string */
1055 int ch; /* character from fmt */
1056 int n, n2; /* handy integer (short term usage) */
1057 wchar_t* cp; /* handy char pointer (short term usage) */
1058 int flags; /* flags as above */
1059 unsigned char* typetable; /* table of types */
1060 unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
1061 int tablesize; /* current size of type table */
1062 int tablemax; /* largest used index in table */
1063 int nextarg; /* 1-based argument index */
1064 int ret = 0; /* return value */
Elliott Hughes94336d82014-04-29 17:39:29 -07001065
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001066 /*
1067 * Add an argument type to the table, expanding if necessary.
1068 */
1069#define ADDTYPE(type) \
1070 ((nextarg >= tablesize) ? __grow_type_table(&typetable, &tablesize) : 0, \
1071 (nextarg > tablemax) ? tablemax = nextarg : 0, typetable[nextarg++] = type)
Elliott Hughes94336d82014-04-29 17:39:29 -07001072
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001073#define ADDSARG() \
1074 ((flags & MAXINT) \
1075 ? ADDTYPE(T_MAXINT) \
1076 : ((flags & PTRINT) ? ADDTYPE(T_PTRINT) \
1077 : ((flags & SIZEINT) \
1078 ? ADDTYPE(T_SSIZEINT) \
1079 : ((flags & LLONGINT) \
1080 ? ADDTYPE(T_LLONG) \
1081 : ((flags & LONGINT) \
1082 ? ADDTYPE(T_LONG) \
1083 : ((flags & SHORTINT) \
1084 ? ADDTYPE(T_SHORT) \
1085 : ((flags & CHARINT) ? ADDTYPE(T_CHAR) \
1086 : ADDTYPE(T_INT))))))))
Elliott Hughes94336d82014-04-29 17:39:29 -07001087
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001088#define ADDUARG() \
1089 ((flags & MAXINT) \
1090 ? ADDTYPE(T_MAXUINT) \
1091 : ((flags & PTRINT) \
1092 ? ADDTYPE(T_PTRINT) \
1093 : ((flags & SIZEINT) \
1094 ? ADDTYPE(T_SIZEINT) \
1095 : ((flags & LLONGINT) \
1096 ? ADDTYPE(T_U_LLONG) \
1097 : ((flags & LONGINT) \
1098 ? ADDTYPE(T_U_LONG) \
1099 : ((flags & SHORTINT) \
1100 ? ADDTYPE(T_U_SHORT) \
1101 : ((flags & CHARINT) ? ADDTYPE(T_U_CHAR) \
1102 : ADDTYPE(T_U_INT))))))))
Elliott Hughes94336d82014-04-29 17:39:29 -07001103
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001104 /*
1105 * Add * arguments to the type array.
1106 */
1107#define ADDASTER() \
1108 n2 = 0; \
1109 cp = fmt; \
1110 while (is_digit(*cp)) { \
1111 APPEND_DIGIT(n2, *cp); \
1112 cp++; \
1113 } \
1114 if (*cp == '$') { \
1115 int hold = nextarg; \
1116 nextarg = n2; \
1117 ADDTYPE(T_INT); \
1118 nextarg = hold; \
1119 fmt = ++cp; \
1120 } else { \
1121 ADDTYPE(T_INT); \
1122 }
1123 fmt = (wchar_t*)fmt0;
1124 typetable = stattypetable;
1125 tablesize = STATIC_ARG_TBL_SIZE;
1126 tablemax = 0;
1127 nextarg = 1;
1128 memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
Elliott Hughes94336d82014-04-29 17:39:29 -07001129
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001130 /*
1131 * Scan the format for conversions (`%' character).
1132 */
1133 for (;;) {
1134 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
1135 if (ch == '\0') goto done;
1136 fmt++; /* skip over '%' */
Elliott Hughes94336d82014-04-29 17:39:29 -07001137
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001138 flags = 0;
Elliott Hughes94336d82014-04-29 17:39:29 -07001139
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001140 rflag:
1141 ch = *fmt++;
1142 reswitch:
1143 switch (ch) {
1144 case ' ':
1145 case '#':
1146 case '\'':
1147 goto rflag;
1148 case '*':
1149 ADDASTER();
1150 goto rflag;
1151 case '-':
1152 case '+':
1153 goto rflag;
1154 case '.':
1155 if ((ch = *fmt++) == '*') {
1156 ADDASTER();
1157 goto rflag;
1158 }
1159 while (is_digit(ch)) {
1160 ch = *fmt++;
1161 }
1162 goto reswitch;
1163 case '0':
1164 goto rflag;
1165 case '1':
1166 case '2':
1167 case '3':
1168 case '4':
1169 case '5':
1170 case '6':
1171 case '7':
1172 case '8':
1173 case '9':
1174 n = 0;
1175 do {
1176 APPEND_DIGIT(n, ch);
1177 ch = *fmt++;
1178 } while (is_digit(ch));
1179 if (ch == '$') {
1180 nextarg = n;
1181 goto rflag;
1182 }
1183 goto reswitch;
1184 case 'L':
1185 flags |= LONGDBL;
1186 goto rflag;
1187 case 'h':
1188 if (*fmt == 'h') {
1189 fmt++;
1190 flags |= CHARINT;
1191 } else {
1192 flags |= SHORTINT;
1193 }
1194 goto rflag;
1195 case 'l':
1196 if (*fmt == 'l') {
1197 fmt++;
1198 flags |= LLONGINT;
1199 } else {
1200 flags |= LONGINT;
1201 }
1202 goto rflag;
1203 case 'q':
1204 flags |= LLONGINT;
1205 goto rflag;
1206 case 't':
1207 flags |= PTRINT;
1208 goto rflag;
1209 case 'z':
1210 flags |= SIZEINT;
1211 goto rflag;
1212 case 'C':
1213 flags |= LONGINT;
1214 /*FALLTHROUGH*/
1215 case 'c':
1216 if (flags & LONGINT)
1217 ADDTYPE(T_WINT);
1218 else
1219 ADDTYPE(T_INT);
1220 break;
1221 case 'D':
1222 flags |= LONGINT;
1223 /*FALLTHROUGH*/
1224 case 'd':
1225 case 'i':
1226 ADDSARG();
1227 break;
1228 case 'a':
1229 case 'A':
1230 case 'e':
1231 case 'E':
1232 case 'f':
1233 case 'F':
1234 case 'g':
1235 case 'G':
1236 if (flags & LONGDBL)
1237 ADDTYPE(T_LONG_DOUBLE);
1238 else
1239 ADDTYPE(T_DOUBLE);
1240 break;
Elliott Hughese2341d02014-05-02 18:16:32 -07001241#ifndef NO_PRINTF_PERCENT_N
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001242 case 'n':
1243 if (flags & LLONGINT)
1244 ADDTYPE(TP_LLONG);
1245 else if (flags & LONGINT)
1246 ADDTYPE(TP_LONG);
1247 else if (flags & SHORTINT)
1248 ADDTYPE(TP_SHORT);
1249 else if (flags & PTRINT)
1250 ADDTYPE(TP_PTRINT);
1251 else if (flags & SIZEINT)
1252 ADDTYPE(TP_SSIZEINT);
1253 else if (flags & MAXINT)
1254 ADDTYPE(TP_MAXINT);
1255 else
1256 ADDTYPE(TP_INT);
1257 continue; /* no output */
1258#endif /* NO_PRINTF_PERCENT_N */
1259 case 'O':
1260 flags |= LONGINT;
1261 /*FALLTHROUGH*/
1262 case 'o':
1263 ADDUARG();
1264 break;
1265 case 'p':
1266 ADDTYPE(TP_VOID);
1267 break;
1268 case 'S':
1269 flags |= LONGINT;
1270 /*FALLTHROUGH*/
1271 case 's':
1272 if (flags & LONGINT)
1273 ADDTYPE(TP_CHAR);
1274 else
1275 ADDTYPE(TP_WCHAR);
1276 break;
1277 case 'U':
1278 flags |= LONGINT;
1279 /*FALLTHROUGH*/
1280 case 'u':
1281 case 'X':
1282 case 'x':
1283 ADDUARG();
1284 break;
1285 default: /* "%?" prints ?, unless ? is NUL */
1286 if (ch == '\0') goto done;
1287 break;
1288 }
1289 }
Elliott Hughes94336d82014-04-29 17:39:29 -07001290done:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001291 /*
1292 * Build the argument table.
1293 */
1294 if (tablemax >= STATIC_ARG_TBL_SIZE) {
1295 *argtablesiz = sizeof(union arg) * (tablemax + 1);
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001296 *argtable = static_cast<arg*>(mmap(NULL, *argtablesiz,
1297 PROT_WRITE | PROT_READ,
1298 MAP_ANON | MAP_PRIVATE, -1, 0));
1299 if (*argtable == MAP_FAILED) return -1;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001300 }
Elliott Hughes94336d82014-04-29 17:39:29 -07001301
1302#if 0
1303 /* XXX is this required? */
1304 (*argtable)[0].intarg = 0;
1305#endif
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001306 for (n = 1; n <= tablemax; n++) {
1307 switch (typetable[n]) {
1308 case T_UNUSED:
1309 case T_CHAR:
1310 case T_U_CHAR:
1311 case T_SHORT:
1312 case T_U_SHORT:
1313 case T_INT:
1314 (*argtable)[n].intarg = va_arg(ap, int);
1315 break;
1316 case TP_SHORT:
1317 (*argtable)[n].pshortarg = va_arg(ap, short*);
1318 break;
1319 case T_U_INT:
1320 (*argtable)[n].uintarg = va_arg(ap, unsigned int);
1321 break;
1322 case TP_INT:
1323 (*argtable)[n].pintarg = va_arg(ap, int*);
1324 break;
1325 case T_LONG:
1326 (*argtable)[n].longarg = va_arg(ap, long);
1327 break;
1328 case T_U_LONG:
1329 (*argtable)[n].ulongarg = va_arg(ap, unsigned long);
1330 break;
1331 case TP_LONG:
1332 (*argtable)[n].plongarg = va_arg(ap, long*);
1333 break;
1334 case T_LLONG:
1335 (*argtable)[n].longlongarg = va_arg(ap, long long);
1336 break;
1337 case T_U_LLONG:
1338 (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
1339 break;
1340 case TP_LLONG:
1341 (*argtable)[n].plonglongarg = va_arg(ap, long long*);
1342 break;
1343 case T_DOUBLE:
1344 (*argtable)[n].doublearg = va_arg(ap, double);
1345 break;
1346 case T_LONG_DOUBLE:
1347 (*argtable)[n].longdoublearg = va_arg(ap, long double);
1348 break;
1349 case TP_CHAR:
1350 (*argtable)[n].pchararg = va_arg(ap, char*);
1351 break;
1352 case TP_VOID:
1353 (*argtable)[n].pvoidarg = va_arg(ap, void*);
1354 break;
1355 case T_PTRINT:
1356 (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
1357 break;
1358 case TP_PTRINT:
1359 (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*);
1360 break;
1361 case T_SIZEINT:
1362 (*argtable)[n].sizearg = va_arg(ap, size_t);
1363 break;
1364 case T_SSIZEINT:
1365 (*argtable)[n].ssizearg = va_arg(ap, ssize_t);
1366 break;
1367 case TP_SSIZEINT:
1368 (*argtable)[n].pssizearg = va_arg(ap, ssize_t*);
1369 break;
1370 case TP_MAXINT:
1371 (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
1372 break;
1373 case T_WINT:
1374 (*argtable)[n].wintarg = va_arg(ap, wint_t);
1375 break;
1376 case TP_WCHAR:
1377 (*argtable)[n].pwchararg = va_arg(ap, wchar_t*);
1378 break;
1379 }
1380 }
1381 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -07001382
1383overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001384 errno = ENOMEM;
1385 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -07001386
1387finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001388 if (typetable != NULL && typetable != stattypetable) {
1389 munmap(typetable, *argtablesiz);
1390 typetable = NULL;
1391 }
1392 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -07001393}
1394
1395/*
1396 * Increase the size of the type table.
1397 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001398static int __grow_type_table(unsigned char** typetable, int* tablesize) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001399 unsigned char* old_table = *typetable;
1400 int new_size = *tablesize * 2;
Elliott Hughes94336d82014-04-29 17:39:29 -07001401
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001402 if (new_size < getpagesize()) new_size = getpagesize();
Elliott Hughes94336d82014-04-29 17:39:29 -07001403
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001404 if (*tablesize == STATIC_ARG_TBL_SIZE) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001405 *typetable = static_cast<unsigned char*>(mmap(NULL, new_size,
1406 PROT_WRITE | PROT_READ,
1407 MAP_ANON | MAP_PRIVATE, -1, 0));
1408 if (*typetable == MAP_FAILED) return -1;
1409 bcopy(old_table, *typetable, *tablesize);
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001410 } else {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001411 unsigned char* new_table = static_cast<unsigned char*>(mmap(NULL, new_size,
1412 PROT_WRITE | PROT_READ,
1413 MAP_ANON | MAP_PRIVATE, -1, 0));
1414 if (new_table == MAP_FAILED) return -1;
1415 memmove(new_table, *typetable, *tablesize);
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001416 munmap(*typetable, *tablesize);
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001417 *typetable = new_table;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001418 }
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001419 memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize));
Elliott Hughes94336d82014-04-29 17:39:29 -07001420
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001421 *tablesize = new_size;
1422 return 0;
Elliott Hughes94336d82014-04-29 17:39:29 -07001423}