blob: e8f10568c90a408a9f1f623e99a88d58b1b1ebfb [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 Hughes93a1f8b2017-11-07 22:52:29 -080035#define FUNCTION_NAME __vfwprintf
Elliott Hughes94336d82014-04-29 17:39:29 -070036
Elliott Hughes94336d82014-04-29 17:39:29 -070037#include <sys/mman.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070038#include <sys/types.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070039
40#include <errno.h>
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -070041#include <float.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070042#include <langinfo.h>
43#include <limits.h>
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -070044#include <locale.h>
45#include <math.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070046#include <stdarg.h>
47#include <stddef.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070048#include <stdint.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070049#include <stdio.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070050#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
Elliott Hughesc8f2c522017-10-31 13:07:51 -070053#include <wchar.h>
Elliott Hughes94336d82014-04-29 17:39:29 -070054
Elliott Hughes94336d82014-04-29 17:39:29 -070055#include "fvwrite.h"
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -070056#include "gdtoa.h"
Elliott Hughesc8f2c522017-10-31 13:07:51 -070057#include "local.h"
Elliott Hughes94336d82014-04-29 17:39:29 -070058
Elliott Hughes94336d82014-04-29 17:39:29 -070059union arg {
Elliott Hughesc8f2c522017-10-31 13:07:51 -070060 int intarg;
61 unsigned int uintarg;
62 long longarg;
63 unsigned long ulongarg;
64 long long longlongarg;
65 unsigned long long ulonglongarg;
66 ptrdiff_t ptrdiffarg;
67 size_t sizearg;
68 ssize_t ssizearg;
69 intmax_t intmaxarg;
70 uintmax_t uintmaxarg;
71 void* pvoidarg;
72 char* pchararg;
73 signed char* pschararg;
74 short* pshortarg;
75 int* pintarg;
76 long* plongarg;
77 long long* plonglongarg;
78 ptrdiff_t* pptrdiffarg;
79 ssize_t* pssizearg;
80 intmax_t* pintmaxarg;
81 double doublearg;
82 long double longdoublearg;
83 wint_t wintarg;
84 wchar_t* pwchararg;
Elliott Hughes94336d82014-04-29 17:39:29 -070085};
86
Elliott Hughes618303c2017-11-02 16:58:44 -070087static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable, size_t* argtablesiz);
Elliott Hughesc8f2c522017-10-31 13:07:51 -070088static 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 Hughes93a1f8b2017-11-07 22:52:29 -080095static int __sbprintf(FILE* fp, const CHAR_TYPE* fmt, va_list ap) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -070096 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 */
Elliott Hughes93a1f8b2017-11-07 22:52:29 -0800113 int ret = FUNCTION_NAME(&fake, fmt, ap);
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700114 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 Hughes93a1f8b2017-11-07 22:52:29 -0800279int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, __va_list ap) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700280 wchar_t ch; /* character from fmt */
281 int n, n2, n3; /* handy integers (short term usage) */
Elliott Hughes93a1f8b2017-11-07 22:52:29 -0800282 CHAR_TYPE* cp; /* handy char pointer (short term usage) */
283 CHAR_TYPE sign; /* sign prefix (' ', '+', '-', or \0) */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700284 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 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700288 /*
289 * We can decompose the printed representation of floating
290 * point numbers into several parts, some of which may be empty:
291 *
292 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
293 * A B ---C--- D E F
294 *
295 * A: 'sign' holds this value if present; '\0' otherwise
296 * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
297 * C: cp points to the string MMMNNN. Leading and trailing
298 * zeros are not in the string and must be added.
299 * D: expchar holds this character; '\0' if no exponent, e.g. %f
300 * F: at least two digits for decimal, at least one digit for hex
301 */
302 char* decimal_point = NULL;
303 int signflag; /* true if float is negative */
304 union { /* floating point arguments %[aAeEfFgG] */
305 double dbl;
306 long double ldbl;
307 } fparg;
308 int expt; /* integer value of exponent */
309 char expchar; /* exponent character: [eEpP\0] */
310 char* dtoaend; /* pointer to end of converted digits */
311 int expsize; /* character count for expstr */
312 int lead; /* sig figs before decimal or group sep */
313 int ndig; /* actual number of digits returned by dtoa */
Elliott Hughes93a1f8b2017-11-07 22:52:29 -0800314 CHAR_TYPE expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700315 char* dtoaresult = NULL;
Elliott Hughes94336d82014-04-29 17:39:29 -0700316
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700317 uintmax_t _umax; /* integer arguments %[diouxX] */
318 enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
319 int dprec; /* a copy of prec if %[diouxX], 0 otherwise */
320 int realsz; /* field size expanded by dprec */
321 int size; /* size of converted field or string */
322 const char* xdigs; /* digits for %[xX] conversion */
Elliott Hughes93a1f8b2017-11-07 22:52:29 -0800323 CHAR_TYPE buf[BUF]; /* buffer with space for digits of uintmax_t */
324 CHAR_TYPE ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700325 union arg* argtable; /* args, built due to positional arg */
326 union arg statargtable[STATIC_ARG_TBL_SIZE];
327 size_t argtablesiz;
328 int nextarg; /* 1-based argument index */
329 va_list orgap; /* original argument pointer */
Elliott Hughes93a1f8b2017-11-07 22:52:29 -0800330 CHAR_TYPE* convbuf; /* buffer for wide/multibyte conversion */
Elliott Hughes94336d82014-04-29 17:39:29 -0700331
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700332 /*
333 * Choose PADSIZE to trade efficiency vs. size. If larger printf
334 * fields occur frequently, increase PADSIZE and make the initialisers
335 * below longer.
336 */
337#define PADSIZE 16 /* pad chunk size */
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700338 static CHAR_TYPE blanks[PADSIZE] = {
339 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '
340 };
341 static CHAR_TYPE zeroes[PADSIZE] = {
342 '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'
343 };
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 */
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700425#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 if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { \
437 ret = -1; \
438 goto error; \
439 } \
440 } \
441 nextarg = n2; \
442 val = GETARG(int); \
443 nextarg = hold; \
444 fmt = ++cp; \
445 } else { \
446 val = GETARG(int); \
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700447 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700448
449/*
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700450 * Get the argument indexed by nextarg. If the argument table is
451 * built, use it to get the argument. If its not, get the next
452 * argument (and arguments must be gotten sequentially).
453 */
Elliott Hughes94336d82014-04-29 17:39:29 -0700454#define GETARG(type) \
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700455 ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : (nextarg++, va_arg(ap, type)))
Elliott Hughes94336d82014-04-29 17:39:29 -0700456
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700457 _SET_ORIENTATION(fp, 1);
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700458
459 // Writing "" to a read only file returns EOF, not 0.
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700460 if (cantwrite(fp)) {
461 errno = EBADF;
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700462 return EOF;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700463 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700464
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700465 // Optimize writes to stderr and other unbuffered files).
466 if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700467 return (__sbprintf(fp, fmt0, ap));
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700468 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700469
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700470 CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0);
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700471 argtable = NULL;
472 nextarg = 1;
473 va_copy(orgap, ap);
474 ret = 0;
475 convbuf = NULL;
Elliott Hughes94336d82014-04-29 17:39:29 -0700476
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700477 /*
478 * Scan the format for conversions (`%' character).
479 */
480 for (;;) {
481 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
482 if (fmt != cp) {
483 ptrdiff_t m = fmt - cp;
484 if (m < 0 || m > INT_MAX - ret) goto overflow;
485 PRINT(cp, m);
486 ret += m;
487 }
488 if (ch == '\0') goto done;
489 fmt++; /* skip over '%' */
Elliott Hughes94336d82014-04-29 17:39:29 -0700490
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700491 flags = 0;
492 dprec = 0;
493 width = 0;
494 prec = -1;
495 sign = '\0';
496 ox[1] = '\0';
Elliott Hughes94336d82014-04-29 17:39:29 -0700497
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700498 rflag:
499 ch = *fmt++;
500 reswitch:
501 switch (ch) {
502 case ' ':
503 /*
504 * ``If the space and + flags both appear, the space
505 * flag will be ignored.''
506 * -- ANSI X3J11
507 */
508 if (!sign) sign = ' ';
509 goto rflag;
510 case '#':
511 flags |= ALT;
512 goto rflag;
513 case '\'':
514 /* grouping not implemented */
515 goto rflag;
516 case '*':
517 /*
518 * ``A negative field width argument is taken as a
519 * - flag followed by a positive field width.''
520 * -- ANSI X3J11
521 * They don't exclude field widths read from args.
522 */
523 GETASTER(width);
524 if (width >= 0) goto rflag;
525 if (width == INT_MIN) goto overflow;
526 width = -width;
527 /* FALLTHROUGH */
528 case '-':
529 flags |= LADJUST;
530 goto rflag;
531 case '+':
532 sign = '+';
533 goto rflag;
534 case '.':
535 if ((ch = *fmt++) == '*') {
536 GETASTER(n);
537 prec = n < 0 ? -1 : n;
538 goto rflag;
539 }
540 n = 0;
541 while (is_digit(ch)) {
542 APPEND_DIGIT(n, ch);
543 ch = *fmt++;
544 }
545 if (ch == '$') {
546 nextarg = n;
547 if (argtable == NULL) {
548 argtable = statargtable;
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700549 if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
550 ret = -1;
551 goto error;
552 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700553 }
554 goto rflag;
555 }
556 prec = n;
557 goto reswitch;
558 case '0':
559 /*
560 * ``Note that 0 is taken as a flag, not as the
561 * beginning of a field width.''
562 * -- ANSI X3J11
563 */
564 flags |= ZEROPAD;
565 goto rflag;
566 case '1':
567 case '2':
568 case '3':
569 case '4':
570 case '5':
571 case '6':
572 case '7':
573 case '8':
574 case '9':
575 n = 0;
576 do {
577 APPEND_DIGIT(n, ch);
578 ch = *fmt++;
579 } while (is_digit(ch));
580 if (ch == '$') {
581 nextarg = n;
582 if (argtable == NULL) {
583 argtable = statargtable;
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700584 if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) {
585 ret = -1;
586 goto error;
587 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700588 }
589 goto rflag;
590 }
591 width = n;
592 goto reswitch;
593 case 'L':
594 flags |= LONGDBL;
595 goto rflag;
596 case 'h':
597 if (*fmt == 'h') {
598 fmt++;
599 flags |= CHARINT;
600 } else {
601 flags |= SHORTINT;
602 }
603 goto rflag;
604 case 'j':
605 flags |= MAXINT;
606 goto rflag;
607 case 'l':
608 if (*fmt == 'l') {
609 fmt++;
610 flags |= LLONGINT;
611 } else {
612 flags |= LONGINT;
613 }
614 goto rflag;
615 case 'q':
616 flags |= LLONGINT;
617 goto rflag;
618 case 't':
619 flags |= PTRINT;
620 goto rflag;
621 case 'z':
622 flags |= SIZEINT;
623 goto rflag;
624 case 'C':
625 flags |= LONGINT;
626 /*FALLTHROUGH*/
627 case 'c':
628 if (flags & LONGINT)
629 *(cp = buf) = (wchar_t)GETARG(wint_t);
630 else
631 *(cp = buf) = (wchar_t)btowc(GETARG(int));
632 size = 1;
633 sign = '\0';
634 break;
635 case 'D':
636 flags |= LONGINT;
637 /*FALLTHROUGH*/
638 case 'd':
639 case 'i':
640 _umax = SARG();
641 if ((intmax_t)_umax < 0) {
642 _umax = -_umax;
643 sign = '-';
644 }
645 base = DEC;
646 goto number;
647 case 'a':
648 case 'A':
649 if (ch == 'a') {
650 ox[1] = 'x';
651 xdigs = xdigs_lower;
652 expchar = 'p';
653 } else {
654 ox[1] = 'X';
655 xdigs = xdigs_upper;
656 expchar = 'P';
657 }
658 if (prec >= 0) prec++;
659 if (dtoaresult) __freedtoa(dtoaresult);
660 if (flags & LONGDBL) {
661 fparg.ldbl = GETARG(long double);
662 dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend);
663 if (dtoaresult == NULL) {
664 errno = ENOMEM;
665 goto error;
666 }
667 } else {
668 fparg.dbl = GETARG(double);
669 dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend);
670 if (dtoaresult == NULL) {
671 errno = ENOMEM;
672 goto error;
673 }
674 }
675 if (prec < 0) prec = dtoaend - dtoaresult;
676 if (expt == INT_MAX) ox[1] = '\0';
677 free(convbuf);
678 cp = convbuf = __mbsconv(dtoaresult, -1);
679 if (cp == NULL) goto error;
680 ndig = dtoaend - dtoaresult;
681 goto fp_common;
682 case 'e':
683 case 'E':
684 expchar = ch;
685 if (prec < 0) /* account for digit before decpt */
686 prec = DEFPREC + 1;
687 else
688 prec++;
689 goto fp_begin;
690 case 'f':
691 case 'F':
692 expchar = '\0';
693 goto fp_begin;
694 case 'g':
695 case 'G':
696 expchar = ch - ('g' - 'e');
697 if (prec == 0) prec = 1;
698 fp_begin:
699 if (prec < 0) prec = DEFPREC;
700 if (dtoaresult) __freedtoa(dtoaresult);
701 if (flags & LONGDBL) {
702 fparg.ldbl = GETARG(long double);
703 dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
704 if (dtoaresult == NULL) {
705 errno = ENOMEM;
706 goto error;
707 }
708 } else {
709 fparg.dbl = GETARG(double);
710 dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend);
711 if (dtoaresult == NULL) {
712 errno = ENOMEM;
713 goto error;
714 }
715 if (expt == 9999) expt = INT_MAX;
716 }
717 free(convbuf);
718 cp = convbuf = __mbsconv(dtoaresult, -1);
719 if (cp == NULL) goto error;
720 ndig = dtoaend - dtoaresult;
721 fp_common:
722 if (signflag) sign = '-';
723 if (expt == INT_MAX) { /* inf or nan */
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700724 if (*cp == 'N') {
725 cp = const_cast<wchar_t*>((ch >= 'a') ? L"nan" : L"NAN");
726 } else {
727 cp = const_cast<wchar_t*>((ch >= 'a') ? L"inf" : L"INF");
728 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700729 size = 3;
730 flags &= ~ZEROPAD;
731 break;
732 }
733 flags |= FPT;
734 if (ch == 'g' || ch == 'G') {
735 if (expt > -4 && expt <= prec) {
736 /* Make %[gG] smell like %[fF] */
737 expchar = '\0';
738 if (flags & ALT)
739 prec -= expt;
740 else
741 prec = ndig - expt;
742 if (prec < 0) prec = 0;
743 } else {
744 /*
745 * Make %[gG] smell like %[eE], but
746 * trim trailing zeroes if no # flag.
747 */
748 if (!(flags & ALT)) prec = ndig;
749 }
750 }
751 if (expchar) {
752 expsize = exponent(expstr, expt - 1, expchar);
753 size = expsize + prec;
754 if (prec > 1 || flags & ALT) ++size;
755 } else {
756 /* space for digits before decimal point */
757 if (expt > 0)
758 size = expt;
759 else /* "0" */
760 size = 1;
761 /* space for decimal pt and following digits */
762 if (prec || flags & ALT) size += prec + 1;
763 lead = expt;
764 }
765 break;
Elliott Hughese2341d02014-05-02 18:16:32 -0700766#ifndef NO_PRINTF_PERCENT_N
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700767 case 'n':
768 if (flags & LLONGINT)
769 *GETARG(long long*) = ret;
770 else if (flags & LONGINT)
771 *GETARG(long*) = ret;
772 else if (flags & SHORTINT)
773 *GETARG(short*) = ret;
774 else if (flags & CHARINT)
775 *GETARG(signed char*) = ret;
776 else if (flags & PTRINT)
777 *GETARG(ptrdiff_t*) = ret;
778 else if (flags & SIZEINT)
779 *GETARG(ssize_t*) = ret;
780 else if (flags & MAXINT)
781 *GETARG(intmax_t*) = ret;
782 else
783 *GETARG(int*) = ret;
784 continue; /* no output */
785#endif /* NO_PRINTF_PERCENT_N */
786 case 'O':
787 flags |= LONGINT;
788 /*FALLTHROUGH*/
789 case 'o':
790 _umax = UARG();
791 base = OCT;
792 goto nosign;
793 case 'p':
794 /*
795 * ``The argument shall be a pointer to void. The
796 * value of the pointer is converted to a sequence
797 * of printable characters, in an implementation-
798 * defined manner.''
799 * -- ANSI X3J11
800 */
801 _umax = (u_long)GETARG(void*);
802 base = HEX;
803 xdigs = xdigs_lower;
804 ox[1] = 'x';
805 goto nosign;
806 case 'S':
807 flags |= LONGINT;
808 /*FALLTHROUGH*/
809 case 's':
810 if (flags & LONGINT) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700811 if ((cp = GETARG(wchar_t*)) == NULL) cp = const_cast<wchar_t*>(L"(null)");
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700812 } else {
813 char* mbsarg;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700814 if ((mbsarg = GETARG(char*)) == NULL) mbsarg = const_cast<char*>("(null)");
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700815 free(convbuf);
816 convbuf = __mbsconv(mbsarg, prec);
817 if (convbuf == NULL) {
818 fp->_flags |= __SERR;
819 goto error;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700820 } else {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700821 cp = convbuf;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700822 }
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700823 }
824 if (prec >= 0) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700825 size = wcsnlen(cp, prec);
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700826 } else {
827 size_t len;
Elliott Hughes94336d82014-04-29 17:39:29 -0700828
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700829 if ((len = wcslen(cp)) > INT_MAX) goto overflow;
830 size = (int)len;
831 }
832 sign = '\0';
833 break;
834 case 'U':
835 flags |= LONGINT;
836 /*FALLTHROUGH*/
837 case 'u':
838 _umax = UARG();
839 base = DEC;
840 goto nosign;
841 case 'X':
842 xdigs = xdigs_upper;
843 goto hex;
844 case 'x':
845 xdigs = xdigs_lower;
846 hex:
847 _umax = UARG();
848 base = HEX;
849 /* leading 0x/X only if non-zero */
850 if (flags & ALT && _umax != 0) ox[1] = ch;
Elliott Hughes94336d82014-04-29 17:39:29 -0700851
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700852 /* unsigned conversions */
853 nosign:
854 sign = '\0';
855 /*
856 * ``... diouXx conversions ... if a precision is
857 * specified, the 0 flag will be ignored.''
858 * -- ANSI X3J11
859 */
860 number:
861 if ((dprec = prec) >= 0) flags &= ~ZEROPAD;
Elliott Hughes94336d82014-04-29 17:39:29 -0700862
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700863 /*
864 * ``The result of converting a zero value with an
865 * explicit precision of zero is no characters.''
866 * -- ANSI X3J11
867 */
868 cp = buf + BUF;
869 if (_umax != 0 || prec != 0) {
870 /*
871 * Unsigned mod is hard, and unsigned mod
872 * by a constant is easier than that by
873 * a variable; hence this switch.
874 */
875 switch (base) {
876 case OCT:
877 do {
878 *--cp = to_char(_umax & 7);
879 _umax >>= 3;
880 } while (_umax);
881 /* handle octal leading 0 */
882 if (flags & ALT && *cp != '0') *--cp = '0';
883 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700884
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700885 case DEC:
886 /* many numbers are 1 digit */
887 while (_umax >= 10) {
888 *--cp = to_char(_umax % 10);
889 _umax /= 10;
890 }
891 *--cp = to_char(_umax);
892 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700893
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700894 case HEX:
895 do {
896 *--cp = xdigs[_umax & 15];
897 _umax >>= 4;
898 } while (_umax);
899 break;
Elliott Hughes94336d82014-04-29 17:39:29 -0700900
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700901 default:
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700902 abort();
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700903 }
904 }
905 size = buf + BUF - cp;
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -0700906 if (size > BUF) abort(); /* should never happen */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700907 break;
908 default: /* "%?" prints ?, unless ? is NUL */
909 if (ch == '\0') goto done;
910 /* pretend it was %c with argument ch */
911 cp = buf;
912 *cp = ch;
913 size = 1;
914 sign = '\0';
915 break;
916 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700917
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700918 /*
919 * All reasonable formats wind up here. At this point, `cp'
920 * points to a string which (if not flags&LADJUST) should be
921 * padded out to `width' places. If flags&ZEROPAD, it should
922 * first be prefixed by any sign or other prefix; otherwise,
923 * it should be blank padded before the prefix is emitted.
924 * After any left-hand padding and prefixing, emit zeroes
925 * required by a decimal %[diouxX] precision, then print the
926 * string proper, then emit zeroes required by any leftover
927 * floating precision; finally, if LADJUST, pad with blanks.
928 *
929 * Compute actual size, so we know how much to pad.
930 * size excludes decimal prec; realsz includes it.
931 */
932 realsz = dprec > size ? dprec : size;
933 if (sign) realsz++;
934 if (ox[1]) realsz += 2;
Elliott Hughes94336d82014-04-29 17:39:29 -0700935
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700936 /* right-adjusting blank padding */
937 if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700938
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700939 /* prefix */
940 if (sign) PRINT(&sign, 1);
941 if (ox[1]) { /* ox[1] is either x, X, or \0 */
942 ox[0] = '0';
943 PRINT(ox, 2);
944 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700945
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700946 /* right-adjusting zero padding */
947 if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700948
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700949 /* leading zeroes from decimal precision */
950 PAD(dprec - size, zeroes);
Elliott Hughes94336d82014-04-29 17:39:29 -0700951
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700952 /* the string or number proper */
953 if ((flags & FPT) == 0) {
954 PRINT(cp, size);
955 } else { /* glue together f_p fragments */
956 if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR);
957 if (!expchar) { /* %[fF] or sufficiently short %[gG] */
958 if (expt <= 0) {
959 PRINT(zeroes, 1);
960 if (prec || flags & ALT) PRINT(decimal_point, 1);
961 PAD(-expt, zeroes);
962 /* already handled initial 0's */
963 prec += expt;
964 } else {
965 PRINTANDPAD(cp, convbuf + ndig, lead, zeroes);
966 cp += lead;
967 if (prec || flags & ALT) PRINT(decimal_point, 1);
968 }
969 PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
970 } else { /* %[eE] or sufficiently long %[gG] */
971 if (prec > 1 || flags & ALT) {
972 buf[0] = *cp++;
973 buf[1] = *decimal_point;
974 PRINT(buf, 2);
975 PRINT(cp, ndig - 1);
976 PAD(prec - ndig, zeroes);
977 } else { /* XeYYY */
978 PRINT(cp, 1);
979 }
980 PRINT(expstr, expsize);
981 }
982 }
983 /* left-adjusting padding (always blank) */
984 if (flags & LADJUST) PAD(width - realsz, blanks);
Elliott Hughes94336d82014-04-29 17:39:29 -0700985
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700986 /* finally, adjust ret */
987 if (width < realsz) width = realsz;
988 if (width > INT_MAX - ret) goto overflow;
989 ret += width;
990 }
Elliott Hughes94336d82014-04-29 17:39:29 -0700991done:
992error:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700993 va_end(orgap);
994 if (__sferror(fp)) ret = -1;
995 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -0700996
997overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700998 errno = ENOMEM;
999 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -07001000
1001finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001002 free(convbuf);
1003 if (dtoaresult) __freedtoa(dtoaresult);
1004 if (argtable != NULL && argtable != statargtable) {
1005 munmap(argtable, argtablesiz);
1006 argtable = NULL;
1007 }
1008 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -07001009}
1010
Elliott Hughes94336d82014-04-29 17:39:29 -07001011/*
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 Hughes5305a4d2017-11-03 14:00:37 -07001052static int __find_arguments(const CHAR_TYPE* fmt0, va_list ap, union arg** argtable,
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001053 size_t* argtablesiz) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001054 int ch; /* character from fmt */
1055 int n, n2; /* handy integer (short term usage) */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001056 int flags; /* flags as above */
1057 unsigned char* typetable; /* table of types */
1058 unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
1059 int tablesize; /* current size of type table */
1060 int tablemax; /* largest used index in table */
1061 int nextarg; /* 1-based argument index */
1062 int ret = 0; /* return value */
Elliott Hughes94336d82014-04-29 17:39:29 -07001063
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001064 /*
1065 * Add an argument type to the table, expanding if necessary.
1066 */
1067#define ADDTYPE(type) \
1068 ((nextarg >= tablesize) ? __grow_type_table(&typetable, &tablesize) : 0, \
1069 (nextarg > tablemax) ? tablemax = nextarg : 0, typetable[nextarg++] = type)
Elliott Hughes94336d82014-04-29 17:39:29 -07001070
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001071#define ADDSARG() \
1072 ((flags & MAXINT) \
1073 ? ADDTYPE(T_MAXINT) \
1074 : ((flags & PTRINT) ? ADDTYPE(T_PTRINT) \
1075 : ((flags & SIZEINT) \
1076 ? ADDTYPE(T_SSIZEINT) \
1077 : ((flags & LLONGINT) \
1078 ? ADDTYPE(T_LLONG) \
1079 : ((flags & LONGINT) \
1080 ? ADDTYPE(T_LONG) \
1081 : ((flags & SHORTINT) \
1082 ? ADDTYPE(T_SHORT) \
1083 : ((flags & CHARINT) ? ADDTYPE(T_CHAR) \
1084 : ADDTYPE(T_INT))))))))
Elliott Hughes94336d82014-04-29 17:39:29 -07001085
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001086#define ADDUARG() \
1087 ((flags & MAXINT) \
1088 ? ADDTYPE(T_MAXUINT) \
1089 : ((flags & PTRINT) \
1090 ? ADDTYPE(T_PTRINT) \
1091 : ((flags & SIZEINT) \
1092 ? ADDTYPE(T_SIZEINT) \
1093 : ((flags & LLONGINT) \
1094 ? ADDTYPE(T_U_LLONG) \
1095 : ((flags & LONGINT) \
1096 ? ADDTYPE(T_U_LONG) \
1097 : ((flags & SHORTINT) \
1098 ? ADDTYPE(T_U_SHORT) \
1099 : ((flags & CHARINT) ? ADDTYPE(T_U_CHAR) \
1100 : ADDTYPE(T_U_INT))))))))
Elliott Hughes94336d82014-04-29 17:39:29 -07001101
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001102 /*
1103 * Add * arguments to the type array.
1104 */
1105#define ADDASTER() \
1106 n2 = 0; \
1107 cp = fmt; \
1108 while (is_digit(*cp)) { \
1109 APPEND_DIGIT(n2, *cp); \
1110 cp++; \
1111 } \
1112 if (*cp == '$') { \
1113 int hold = nextarg; \
1114 nextarg = n2; \
1115 ADDTYPE(T_INT); \
1116 nextarg = hold; \
1117 fmt = ++cp; \
1118 } else { \
1119 ADDTYPE(T_INT); \
1120 }
Elliott Hughes5305a4d2017-11-03 14:00:37 -07001121 CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0);
1122 CHAR_TYPE* cp;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001123 typetable = stattypetable;
1124 tablesize = STATIC_ARG_TBL_SIZE;
1125 tablemax = 0;
1126 nextarg = 1;
1127 memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
Elliott Hughes94336d82014-04-29 17:39:29 -07001128
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001129 /*
1130 * Scan the format for conversions (`%' character).
1131 */
1132 for (;;) {
1133 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue;
1134 if (ch == '\0') goto done;
1135 fmt++; /* skip over '%' */
Elliott Hughes94336d82014-04-29 17:39:29 -07001136
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001137 flags = 0;
Elliott Hughes94336d82014-04-29 17:39:29 -07001138
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001139 rflag:
1140 ch = *fmt++;
1141 reswitch:
1142 switch (ch) {
1143 case ' ':
1144 case '#':
1145 case '\'':
1146 goto rflag;
1147 case '*':
1148 ADDASTER();
1149 goto rflag;
1150 case '-':
1151 case '+':
1152 goto rflag;
1153 case '.':
1154 if ((ch = *fmt++) == '*') {
1155 ADDASTER();
1156 goto rflag;
1157 }
1158 while (is_digit(ch)) {
1159 ch = *fmt++;
1160 }
1161 goto reswitch;
1162 case '0':
1163 goto rflag;
1164 case '1':
1165 case '2':
1166 case '3':
1167 case '4':
1168 case '5':
1169 case '6':
1170 case '7':
1171 case '8':
1172 case '9':
1173 n = 0;
1174 do {
1175 APPEND_DIGIT(n, ch);
1176 ch = *fmt++;
1177 } while (is_digit(ch));
1178 if (ch == '$') {
1179 nextarg = n;
1180 goto rflag;
1181 }
1182 goto reswitch;
1183 case 'L':
1184 flags |= LONGDBL;
1185 goto rflag;
1186 case 'h':
1187 if (*fmt == 'h') {
1188 fmt++;
1189 flags |= CHARINT;
1190 } else {
1191 flags |= SHORTINT;
1192 }
1193 goto rflag;
Elliott Hughes618303c2017-11-02 16:58:44 -07001194 case 'j':
1195 flags |= MAXINT;
1196 goto rflag;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001197 case 'l':
1198 if (*fmt == 'l') {
1199 fmt++;
1200 flags |= LLONGINT;
1201 } else {
1202 flags |= LONGINT;
1203 }
1204 goto rflag;
1205 case 'q':
1206 flags |= LLONGINT;
1207 goto rflag;
1208 case 't':
1209 flags |= PTRINT;
1210 goto rflag;
1211 case 'z':
1212 flags |= SIZEINT;
1213 goto rflag;
1214 case 'C':
1215 flags |= LONGINT;
1216 /*FALLTHROUGH*/
1217 case 'c':
1218 if (flags & LONGINT)
1219 ADDTYPE(T_WINT);
1220 else
1221 ADDTYPE(T_INT);
1222 break;
1223 case 'D':
1224 flags |= LONGINT;
1225 /*FALLTHROUGH*/
1226 case 'd':
1227 case 'i':
1228 ADDSARG();
1229 break;
1230 case 'a':
1231 case 'A':
1232 case 'e':
1233 case 'E':
1234 case 'f':
1235 case 'F':
1236 case 'g':
1237 case 'G':
1238 if (flags & LONGDBL)
1239 ADDTYPE(T_LONG_DOUBLE);
1240 else
1241 ADDTYPE(T_DOUBLE);
1242 break;
Elliott Hughese2341d02014-05-02 18:16:32 -07001243#ifndef NO_PRINTF_PERCENT_N
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001244 case 'n':
1245 if (flags & LLONGINT)
1246 ADDTYPE(TP_LLONG);
1247 else if (flags & LONGINT)
1248 ADDTYPE(TP_LONG);
1249 else if (flags & SHORTINT)
1250 ADDTYPE(TP_SHORT);
1251 else if (flags & PTRINT)
1252 ADDTYPE(TP_PTRINT);
1253 else if (flags & SIZEINT)
1254 ADDTYPE(TP_SSIZEINT);
1255 else if (flags & MAXINT)
1256 ADDTYPE(TP_MAXINT);
1257 else
1258 ADDTYPE(TP_INT);
1259 continue; /* no output */
1260#endif /* NO_PRINTF_PERCENT_N */
1261 case 'O':
1262 flags |= LONGINT;
1263 /*FALLTHROUGH*/
1264 case 'o':
1265 ADDUARG();
1266 break;
1267 case 'p':
1268 ADDTYPE(TP_VOID);
1269 break;
1270 case 'S':
1271 flags |= LONGINT;
1272 /*FALLTHROUGH*/
1273 case 's':
Elliott Hughes93a1f8b2017-11-07 22:52:29 -08001274 ADDTYPE((flags & LONGINT) ? TP_WCHAR : TP_CHAR);
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001275 break;
1276 case 'U':
1277 flags |= LONGINT;
1278 /*FALLTHROUGH*/
1279 case 'u':
1280 case 'X':
1281 case 'x':
1282 ADDUARG();
1283 break;
1284 default: /* "%?" prints ?, unless ? is NUL */
1285 if (ch == '\0') goto done;
1286 break;
1287 }
1288 }
Elliott Hughes94336d82014-04-29 17:39:29 -07001289done:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001290 /*
1291 * Build the argument table.
1292 */
1293 if (tablemax >= STATIC_ARG_TBL_SIZE) {
1294 *argtablesiz = sizeof(union arg) * (tablemax + 1);
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001295 *argtable = static_cast<arg*>(mmap(NULL, *argtablesiz,
1296 PROT_WRITE | PROT_READ,
1297 MAP_ANON | MAP_PRIVATE, -1, 0));
1298 if (*argtable == MAP_FAILED) return -1;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001299 }
Elliott Hughes94336d82014-04-29 17:39:29 -07001300
1301#if 0
1302 /* XXX is this required? */
1303 (*argtable)[0].intarg = 0;
1304#endif
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001305 for (n = 1; n <= tablemax; n++) {
1306 switch (typetable[n]) {
1307 case T_UNUSED:
1308 case T_CHAR:
1309 case T_U_CHAR:
1310 case T_SHORT:
1311 case T_U_SHORT:
1312 case T_INT:
1313 (*argtable)[n].intarg = va_arg(ap, int);
1314 break;
1315 case TP_SHORT:
1316 (*argtable)[n].pshortarg = va_arg(ap, short*);
1317 break;
1318 case T_U_INT:
1319 (*argtable)[n].uintarg = va_arg(ap, unsigned int);
1320 break;
1321 case TP_INT:
1322 (*argtable)[n].pintarg = va_arg(ap, int*);
1323 break;
1324 case T_LONG:
1325 (*argtable)[n].longarg = va_arg(ap, long);
1326 break;
1327 case T_U_LONG:
1328 (*argtable)[n].ulongarg = va_arg(ap, unsigned long);
1329 break;
1330 case TP_LONG:
1331 (*argtable)[n].plongarg = va_arg(ap, long*);
1332 break;
1333 case T_LLONG:
1334 (*argtable)[n].longlongarg = va_arg(ap, long long);
1335 break;
1336 case T_U_LLONG:
1337 (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
1338 break;
1339 case TP_LLONG:
1340 (*argtable)[n].plonglongarg = va_arg(ap, long long*);
1341 break;
1342 case T_DOUBLE:
1343 (*argtable)[n].doublearg = va_arg(ap, double);
1344 break;
1345 case T_LONG_DOUBLE:
1346 (*argtable)[n].longdoublearg = va_arg(ap, long double);
1347 break;
1348 case TP_CHAR:
1349 (*argtable)[n].pchararg = va_arg(ap, char*);
1350 break;
1351 case TP_VOID:
1352 (*argtable)[n].pvoidarg = va_arg(ap, void*);
1353 break;
1354 case T_PTRINT:
1355 (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
1356 break;
1357 case TP_PTRINT:
1358 (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t*);
1359 break;
1360 case T_SIZEINT:
1361 (*argtable)[n].sizearg = va_arg(ap, size_t);
1362 break;
1363 case T_SSIZEINT:
1364 (*argtable)[n].ssizearg = va_arg(ap, ssize_t);
1365 break;
1366 case TP_SSIZEINT:
1367 (*argtable)[n].pssizearg = va_arg(ap, ssize_t*);
1368 break;
Elliott Hughes618303c2017-11-02 16:58:44 -07001369 case T_MAXINT:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001370 (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
1371 break;
Elliott Hughes618303c2017-11-02 16:58:44 -07001372 case T_MAXUINT:
1373 (*argtable)[n].uintmaxarg = va_arg(ap, uintmax_t);
1374 break;
1375 case TP_MAXINT:
1376 (*argtable)[n].pintmaxarg = va_arg(ap, intmax_t*);
1377 break;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001378 case T_WINT:
1379 (*argtable)[n].wintarg = va_arg(ap, wint_t);
1380 break;
1381 case TP_WCHAR:
1382 (*argtable)[n].pwchararg = va_arg(ap, wchar_t*);
1383 break;
1384 }
1385 }
1386 goto finish;
Elliott Hughes94336d82014-04-29 17:39:29 -07001387
1388overflow:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001389 errno = ENOMEM;
1390 ret = -1;
Elliott Hughes94336d82014-04-29 17:39:29 -07001391
1392finish:
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001393 if (typetable != NULL && typetable != stattypetable) {
1394 munmap(typetable, *argtablesiz);
1395 typetable = NULL;
1396 }
1397 return (ret);
Elliott Hughes94336d82014-04-29 17:39:29 -07001398}
1399
1400/*
1401 * Increase the size of the type table.
1402 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001403static int __grow_type_table(unsigned char** typetable, int* tablesize) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001404 unsigned char* old_table = *typetable;
1405 int new_size = *tablesize * 2;
Elliott Hughes94336d82014-04-29 17:39:29 -07001406
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001407 if (new_size < getpagesize()) new_size = getpagesize();
Elliott Hughes94336d82014-04-29 17:39:29 -07001408
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001409 if (*tablesize == STATIC_ARG_TBL_SIZE) {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001410 *typetable = static_cast<unsigned char*>(mmap(NULL, new_size,
1411 PROT_WRITE | PROT_READ,
1412 MAP_ANON | MAP_PRIVATE, -1, 0));
1413 if (*typetable == MAP_FAILED) return -1;
1414 bcopy(old_table, *typetable, *tablesize);
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001415 } else {
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001416 unsigned char* new_table = static_cast<unsigned char*>(mmap(NULL, new_size,
1417 PROT_WRITE | PROT_READ,
1418 MAP_ANON | MAP_PRIVATE, -1, 0));
1419 if (new_table == MAP_FAILED) return -1;
1420 memmove(new_table, *typetable, *tablesize);
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001421 munmap(*typetable, *tablesize);
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001422 *typetable = new_table;
Elliott Hughesc8f2c522017-10-31 13:07:51 -07001423 }
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001424 memset(*typetable + *tablesize, T_UNUSED, (new_size - *tablesize));
Elliott Hughes94336d82014-04-29 17:39:29 -07001425
Elliott Hughes2f9c8ce2017-11-01 13:54:47 -07001426 *tablesize = new_size;
1427 return 0;
Elliott Hughes94336d82014-04-29 17:39:29 -07001428}