1*fc99cf93Smillert /* $OpenBSD: vfwprintf.c,v 1.23 2023/10/06 16:41:02 millert Exp $ */
225963022Sstsp /*-
325963022Sstsp * Copyright (c) 1990 The Regents of the University of California.
425963022Sstsp * All rights reserved.
525963022Sstsp *
625963022Sstsp * This code is derived from software contributed to Berkeley by
725963022Sstsp * Chris Torek.
825963022Sstsp *
925963022Sstsp * Redistribution and use in source and binary forms, with or without
1025963022Sstsp * modification, are permitted provided that the following conditions
1125963022Sstsp * are met:
1225963022Sstsp * 1. Redistributions of source code must retain the above copyright
1325963022Sstsp * notice, this list of conditions and the following disclaimer.
1425963022Sstsp * 2. Redistributions in binary form must reproduce the above copyright
1525963022Sstsp * notice, this list of conditions and the following disclaimer in the
1625963022Sstsp * documentation and/or other materials provided with the distribution.
1725963022Sstsp * 3. Neither the name of the University nor the names of its contributors
1825963022Sstsp * may be used to endorse or promote products derived from this software
1925963022Sstsp * without specific prior written permission.
2025963022Sstsp *
2125963022Sstsp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2225963022Sstsp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2325963022Sstsp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2425963022Sstsp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2525963022Sstsp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2625963022Sstsp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2725963022Sstsp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2825963022Sstsp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2925963022Sstsp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3025963022Sstsp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3125963022Sstsp * SUCH DAMAGE.
3225963022Sstsp */
3325963022Sstsp
3425963022Sstsp /*
3525963022Sstsp * Actual wprintf innards.
3625963022Sstsp *
3725963022Sstsp * This code is large and complicated...
3825963022Sstsp */
3925963022Sstsp
4025963022Sstsp #include <sys/types.h>
4125963022Sstsp #include <sys/mman.h>
4225963022Sstsp
4325963022Sstsp #include <errno.h>
444ddf57ccSmatthew #include <langinfo.h>
4525963022Sstsp #include <limits.h>
4625963022Sstsp #include <stdarg.h>
4725963022Sstsp #include <stddef.h>
4825963022Sstsp #include <stdio.h>
4925963022Sstsp #include <stdint.h>
5025963022Sstsp #include <stdlib.h>
5125963022Sstsp #include <string.h>
52a469cbf7Sderaadt #include <syslog.h>
5325963022Sstsp #include <unistd.h>
5425963022Sstsp
5525963022Sstsp #include "local.h"
5625963022Sstsp #include "fvwrite.h"
5725963022Sstsp
5825963022Sstsp union arg {
5925963022Sstsp int intarg;
6025963022Sstsp unsigned int uintarg;
6125963022Sstsp long longarg;
6225963022Sstsp unsigned long ulongarg;
6325963022Sstsp long long longlongarg;
6425963022Sstsp unsigned long long ulonglongarg;
6525963022Sstsp ptrdiff_t ptrdiffarg;
6625963022Sstsp size_t sizearg;
6725963022Sstsp ssize_t ssizearg;
6825963022Sstsp intmax_t intmaxarg;
6925963022Sstsp uintmax_t uintmaxarg;
7025963022Sstsp void *pvoidarg;
7125963022Sstsp char *pchararg;
7225963022Sstsp signed char *pschararg;
7325963022Sstsp short *pshortarg;
7425963022Sstsp int *pintarg;
7525963022Sstsp long *plongarg;
7625963022Sstsp long long *plonglongarg;
7725963022Sstsp ptrdiff_t *pptrdiffarg;
7825963022Sstsp ssize_t *pssizearg;
7925963022Sstsp intmax_t *pintmaxarg;
8025963022Sstsp #ifdef FLOATING_POINT
8125963022Sstsp double doublearg;
8225963022Sstsp long double longdoublearg;
8325963022Sstsp #endif
8425963022Sstsp wint_t wintarg;
8525963022Sstsp wchar_t *pwchararg;
8625963022Sstsp };
8725963022Sstsp
8825963022Sstsp static int __find_arguments(const wchar_t *fmt0, va_list ap, union arg **argtable,
8925963022Sstsp size_t *argtablesiz);
9025963022Sstsp static int __grow_type_table(unsigned char **typetable, int *tablesize);
9125963022Sstsp
9225963022Sstsp /*
9325963022Sstsp * Helper function for `fprintf to unbuffered unix file': creates a
9425963022Sstsp * temporary buffer. We only work on write-only files; this avoids
9525963022Sstsp * worries about ungetc buffers and so forth.
9625963022Sstsp */
9725963022Sstsp static int
__sbprintf(FILE * fp,const wchar_t * fmt,va_list ap)9825963022Sstsp __sbprintf(FILE *fp, const wchar_t *fmt, va_list ap)
9925963022Sstsp {
10025963022Sstsp int ret;
10125963022Sstsp FILE fake;
10225963022Sstsp struct __sfileext fakeext;
10325963022Sstsp unsigned char buf[BUFSIZ];
10425963022Sstsp
10525963022Sstsp _FILEEXT_SETUP(&fake, &fakeext);
10625963022Sstsp /* copy the important variables */
10725963022Sstsp fake._flags = fp->_flags & ~__SNBF;
10825963022Sstsp fake._file = fp->_file;
10925963022Sstsp fake._cookie = fp->_cookie;
11025963022Sstsp fake._write = fp->_write;
11125963022Sstsp
11225963022Sstsp /* set up the buffer */
11325963022Sstsp fake._bf._base = fake._p = buf;
11425963022Sstsp fake._bf._size = fake._w = sizeof(buf);
11525963022Sstsp fake._lbfsize = 0; /* not actually used, but Just In Case */
11625963022Sstsp
11725963022Sstsp /* do the work, then copy any error status */
11825963022Sstsp ret = __vfwprintf(&fake, fmt, ap);
11925963022Sstsp if (ret >= 0 && __sflush(&fake))
12025963022Sstsp ret = EOF;
12125963022Sstsp if (fake._flags & __SERR)
12225963022Sstsp fp->_flags |= __SERR;
12325963022Sstsp return (ret);
12425963022Sstsp }
12525963022Sstsp
12625963022Sstsp /*
12725963022Sstsp * Like __fputwc_unlock, but handles fake string (__SSTR) files properly.
12825963022Sstsp * File must already be locked.
12925963022Sstsp */
13025963022Sstsp static wint_t
__xfputwc(wchar_t wc,FILE * fp)13125963022Sstsp __xfputwc(wchar_t wc, FILE *fp)
13225963022Sstsp {
13325963022Sstsp mbstate_t mbs;
13425963022Sstsp char buf[MB_LEN_MAX];
13525963022Sstsp struct __suio uio;
13625963022Sstsp struct __siov iov;
13725963022Sstsp size_t len;
13825963022Sstsp
13925963022Sstsp if ((fp->_flags & __SSTR) == 0)
14025963022Sstsp return (__fputwc_unlock(wc, fp));
14125963022Sstsp
14225963022Sstsp bzero(&mbs, sizeof(mbs));
14325963022Sstsp len = wcrtomb(buf, wc, &mbs);
14425963022Sstsp if (len == (size_t)-1) {
14525963022Sstsp fp->_flags |= __SERR;
14625963022Sstsp errno = EILSEQ;
14725963022Sstsp return (WEOF);
14825963022Sstsp }
14925963022Sstsp uio.uio_iov = &iov;
15025963022Sstsp uio.uio_resid = len;
15125963022Sstsp uio.uio_iovcnt = 1;
15225963022Sstsp iov.iov_base = buf;
15325963022Sstsp iov.iov_len = len;
15425963022Sstsp return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF);
15525963022Sstsp }
15625963022Sstsp
15725963022Sstsp /*
15825963022Sstsp * Convert a multibyte character string argument for the %s format to a wide
15925963022Sstsp * string representation. ``prec'' specifies the maximum number of bytes
16025963022Sstsp * to output. If ``prec'' is greater than or equal to zero, we can't assume
16125963022Sstsp * that the multibyte character string ends in a null character.
16225963022Sstsp *
16325963022Sstsp * Returns NULL on failure.
16425963022Sstsp * To find out what happened check errno for ENOMEM, EILSEQ and EINVAL.
16525963022Sstsp */
16625963022Sstsp static wchar_t *
__mbsconv(char * mbsarg,int prec)16725963022Sstsp __mbsconv(char *mbsarg, int prec)
16825963022Sstsp {
16925963022Sstsp mbstate_t mbs;
17025963022Sstsp wchar_t *convbuf, *wcp;
17125963022Sstsp const char *p;
17225963022Sstsp size_t insize, nchars, nconv;
17325963022Sstsp
17425963022Sstsp if (mbsarg == NULL)
17525963022Sstsp return (NULL);
17625963022Sstsp
17725963022Sstsp /*
17825963022Sstsp * Supplied argument is a multibyte string; convert it to wide
17925963022Sstsp * characters first.
18025963022Sstsp */
18125963022Sstsp if (prec >= 0) {
18225963022Sstsp /*
18325963022Sstsp * String is not guaranteed to be NUL-terminated. Find the
18425963022Sstsp * number of characters to print.
18525963022Sstsp */
18625963022Sstsp p = mbsarg;
18725963022Sstsp insize = nchars = nconv = 0;
18825963022Sstsp bzero(&mbs, sizeof(mbs));
18925963022Sstsp while (nchars != (size_t)prec) {
19025963022Sstsp nconv = mbrlen(p, MB_CUR_MAX, &mbs);
19125963022Sstsp if (nconv == (size_t)0 || nconv == (size_t)-1 ||
19225963022Sstsp nconv == (size_t)-2)
19325963022Sstsp break;
19425963022Sstsp p += nconv;
19525963022Sstsp nchars++;
19625963022Sstsp insize += nconv;
19725963022Sstsp }
19825963022Sstsp if (nconv == (size_t)-1 || nconv == (size_t)-2)
19925963022Sstsp return (NULL);
20025963022Sstsp } else
20125963022Sstsp insize = strlen(mbsarg);
20225963022Sstsp
20325963022Sstsp /*
20425963022Sstsp * Allocate buffer for the result and perform the conversion,
20525963022Sstsp * converting at most `size' bytes of the input multibyte string to
20625963022Sstsp * wide characters for printing.
20725963022Sstsp */
20825963022Sstsp convbuf = calloc(insize + 1, sizeof(*convbuf));
20925963022Sstsp if (convbuf == NULL)
21025963022Sstsp return (NULL);
21125963022Sstsp wcp = convbuf;
21225963022Sstsp p = mbsarg;
21325963022Sstsp bzero(&mbs, sizeof(mbs));
21425963022Sstsp nconv = 0;
21525963022Sstsp while (insize != 0) {
21625963022Sstsp nconv = mbrtowc(wcp, p, insize, &mbs);
21725963022Sstsp if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2)
21825963022Sstsp break;
21925963022Sstsp wcp++;
22025963022Sstsp p += nconv;
22125963022Sstsp insize -= nconv;
22225963022Sstsp }
22325963022Sstsp if (nconv == (size_t)-1 || nconv == (size_t)-2) {
22425963022Sstsp free(convbuf);
22525963022Sstsp return (NULL);
22625963022Sstsp }
22725963022Sstsp *wcp = '\0';
22825963022Sstsp
22925963022Sstsp return (convbuf);
23025963022Sstsp }
23125963022Sstsp
23225963022Sstsp #ifdef FLOATING_POINT
23325963022Sstsp #include <float.h>
23425963022Sstsp #include <locale.h>
23525963022Sstsp #include <math.h>
23625963022Sstsp #include "floatio.h"
237def3c8f6Sguenther #include "gdtoa.h"
23825963022Sstsp
23925963022Sstsp #define DEFPREC 6
24025963022Sstsp
24125963022Sstsp static int exponent(wchar_t *, int, int);
24225963022Sstsp #endif /* FLOATING_POINT */
24325963022Sstsp
24425963022Sstsp /*
24525963022Sstsp * The size of the buffer we use as scratch space for integer
24625963022Sstsp * conversions, among other things. Technically, we would need the
24725963022Sstsp * most space for base 10 conversions with thousands' grouping
24825963022Sstsp * characters between each pair of digits. 100 bytes is a
24925963022Sstsp * conservative overestimate even for a 128-bit uintmax_t.
25025963022Sstsp */
25125963022Sstsp #define BUF 100
25225963022Sstsp
25325963022Sstsp #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */
25425963022Sstsp
25525963022Sstsp
25625963022Sstsp /*
25725963022Sstsp * Macros for converting digits to letters and vice versa
25825963022Sstsp */
25925963022Sstsp #define to_digit(c) ((c) - '0')
26025963022Sstsp #define is_digit(c) ((unsigned)to_digit(c) <= 9)
26125963022Sstsp #define to_char(n) ((wchar_t)((n) + '0'))
26225963022Sstsp
26325963022Sstsp /*
26425963022Sstsp * Flags used during conversion.
26525963022Sstsp */
26625963022Sstsp #define ALT 0x0001 /* alternate form */
26725963022Sstsp #define LADJUST 0x0004 /* left adjustment */
26825963022Sstsp #define LONGDBL 0x0008 /* long double */
26925963022Sstsp #define LONGINT 0x0010 /* long integer */
27025963022Sstsp #define LLONGINT 0x0020 /* long long integer */
27125963022Sstsp #define SHORTINT 0x0040 /* short integer */
27225963022Sstsp #define ZEROPAD 0x0080 /* zero (as opposed to blank) pad */
27325963022Sstsp #define FPT 0x0100 /* Floating point number */
27425963022Sstsp #define PTRINT 0x0200 /* (unsigned) ptrdiff_t */
27525963022Sstsp #define SIZEINT 0x0400 /* (signed) size_t */
27625963022Sstsp #define CHARINT 0x0800 /* 8 bit integer */
27725963022Sstsp #define MAXINT 0x1000 /* largest integer size (intmax_t) */
27825963022Sstsp
27925963022Sstsp int
__vfwprintf(FILE * __restrict fp,const wchar_t * __restrict fmt0,__va_list ap)28025963022Sstsp __vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, __va_list ap)
28125963022Sstsp {
28225963022Sstsp wchar_t *fmt; /* format string */
28325963022Sstsp wchar_t ch; /* character from fmt */
28425963022Sstsp int n, n2, n3; /* handy integers (short term usage) */
28525963022Sstsp wchar_t *cp; /* handy char pointer (short term usage) */
28625963022Sstsp int flags; /* flags as above */
28725963022Sstsp int ret; /* return value accumulator */
28825963022Sstsp int width; /* width from format (%8d), or 0 */
28925963022Sstsp int prec; /* precision from format; <0 for N/A */
29025963022Sstsp wchar_t sign; /* sign prefix (' ', '+', '-', or \0) */
29125963022Sstsp #ifdef FLOATING_POINT
29225963022Sstsp /*
29325963022Sstsp * We can decompose the printed representation of floating
29425963022Sstsp * point numbers into several parts, some of which may be empty:
29525963022Sstsp *
29625963022Sstsp * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
29725963022Sstsp * A B ---C--- D E F
29825963022Sstsp *
29925963022Sstsp * A: 'sign' holds this value if present; '\0' otherwise
30025963022Sstsp * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
30125963022Sstsp * C: cp points to the string MMMNNN. Leading and trailing
30225963022Sstsp * zeros are not in the string and must be added.
30325963022Sstsp * D: expchar holds this character; '\0' if no exponent, e.g. %f
30425963022Sstsp * F: at least two digits for decimal, at least one digit for hex
30525963022Sstsp */
3064ddf57ccSmatthew char *decimal_point = NULL;
30725963022Sstsp int signflag; /* true if float is negative */
30825963022Sstsp union { /* floating point arguments %[aAeEfFgG] */
30925963022Sstsp double dbl;
31025963022Sstsp long double ldbl;
31125963022Sstsp } fparg;
31225963022Sstsp int expt; /* integer value of exponent */
31325963022Sstsp char expchar; /* exponent character: [eEpP\0] */
31425963022Sstsp char *dtoaend; /* pointer to end of converted digits */
31525963022Sstsp int expsize; /* character count for expstr */
31625963022Sstsp int lead; /* sig figs before decimal or group sep */
31725963022Sstsp int ndig; /* actual number of digits returned by dtoa */
31825963022Sstsp wchar_t expstr[MAXEXPDIG+2]; /* buffer for exponent string: e+ZZZ */
31925963022Sstsp char *dtoaresult = NULL;
32025963022Sstsp #endif
32125963022Sstsp
32225963022Sstsp uintmax_t _umax; /* integer arguments %[diouxX] */
32325963022Sstsp enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */
32425963022Sstsp int dprec; /* a copy of prec if %[diouxX], 0 otherwise */
32525963022Sstsp int realsz; /* field size expanded by dprec */
32625963022Sstsp int size; /* size of converted field or string */
32725963022Sstsp const char *xdigs; /* digits for %[xX] conversion */
32825963022Sstsp wchar_t buf[BUF]; /* buffer with space for digits of uintmax_t */
32925963022Sstsp wchar_t ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */
33025963022Sstsp union arg *argtable; /* args, built due to positional arg */
33125963022Sstsp union arg statargtable[STATIC_ARG_TBL_SIZE];
33225963022Sstsp size_t argtablesiz;
33325963022Sstsp int nextarg; /* 1-based argument index */
33425963022Sstsp va_list orgap; /* original argument pointer */
33525963022Sstsp wchar_t *convbuf; /* buffer for multibyte to wide conversion */
33625963022Sstsp
33725963022Sstsp /*
33825963022Sstsp * Choose PADSIZE to trade efficiency vs. size. If larger printf
33925963022Sstsp * fields occur frequently, increase PADSIZE and make the initialisers
34025963022Sstsp * below longer.
34125963022Sstsp */
34225963022Sstsp #define PADSIZE 16 /* pad chunk size */
34325963022Sstsp static wchar_t blanks[PADSIZE] =
34425963022Sstsp {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
34525963022Sstsp static wchar_t zeroes[PADSIZE] =
34625963022Sstsp {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
34725963022Sstsp
34825963022Sstsp static const char xdigs_lower[16] = "0123456789abcdef";
34925963022Sstsp static const char xdigs_upper[16] = "0123456789ABCDEF";
35025963022Sstsp
35125963022Sstsp /*
35225963022Sstsp * BEWARE, these `goto error' on error, PRINT uses 'n3',
35325963022Sstsp * PAD uses `n' and 'n3', and PRINTANDPAD uses 'n', 'n2', and 'n3'.
35425963022Sstsp */
35525963022Sstsp #define PRINT(ptr, len) do { \
35625963022Sstsp for (n3 = 0; n3 < (len); n3++) { \
35725963022Sstsp if ((__xfputwc((ptr)[n3], fp)) == WEOF) \
35825963022Sstsp goto error; \
35925963022Sstsp } \
36025963022Sstsp } while (0)
36125963022Sstsp #define PAD(howmany, with) do { \
36225963022Sstsp if ((n = (howmany)) > 0) { \
36325963022Sstsp while (n > PADSIZE) { \
36425963022Sstsp PRINT(with, PADSIZE); \
36525963022Sstsp n -= PADSIZE; \
36625963022Sstsp } \
36725963022Sstsp PRINT(with, n); \
36825963022Sstsp } \
36925963022Sstsp } while (0)
37025963022Sstsp #define PRINTANDPAD(p, ep, len, with) do { \
37125963022Sstsp n2 = (ep) - (p); \
37225963022Sstsp if (n2 > (len)) \
37325963022Sstsp n2 = (len); \
37425963022Sstsp if (n2 > 0) \
37525963022Sstsp PRINT((p), n2); \
37625963022Sstsp PAD((len) - (n2 > 0 ? n2 : 0), (with)); \
37725963022Sstsp } while(0)
37825963022Sstsp
37925963022Sstsp /*
38025963022Sstsp * To extend shorts properly, we need both signed and unsigned
38125963022Sstsp * argument extraction methods.
38225963022Sstsp */
38325963022Sstsp #define SARG() \
38425963022Sstsp ((intmax_t)(flags&MAXINT ? GETARG(intmax_t) : \
38525963022Sstsp flags&LLONGINT ? GETARG(long long) : \
38625963022Sstsp flags&LONGINT ? GETARG(long) : \
38725963022Sstsp flags&PTRINT ? GETARG(ptrdiff_t) : \
38825963022Sstsp flags&SIZEINT ? GETARG(ssize_t) : \
38925963022Sstsp flags&SHORTINT ? (short)GETARG(int) : \
3907eff07bfSguenther flags&CHARINT ? (signed char)GETARG(int) : \
39125963022Sstsp GETARG(int)))
39225963022Sstsp #define UARG() \
39325963022Sstsp ((uintmax_t)(flags&MAXINT ? GETARG(uintmax_t) : \
39425963022Sstsp flags&LLONGINT ? GETARG(unsigned long long) : \
39525963022Sstsp flags&LONGINT ? GETARG(unsigned long) : \
39625963022Sstsp flags&PTRINT ? (uintptr_t)GETARG(ptrdiff_t) : /* XXX */ \
39725963022Sstsp flags&SIZEINT ? GETARG(size_t) : \
39825963022Sstsp flags&SHORTINT ? (unsigned short)GETARG(int) : \
39925963022Sstsp flags&CHARINT ? (unsigned char)GETARG(int) : \
40025963022Sstsp GETARG(unsigned int)))
40125963022Sstsp
40225963022Sstsp /*
40325963022Sstsp * Append a digit to a value and check for overflow.
40425963022Sstsp */
40525963022Sstsp #define APPEND_DIGIT(val, dig) do { \
40625963022Sstsp if ((val) > INT_MAX / 10) \
40725963022Sstsp goto overflow; \
40825963022Sstsp (val) *= 10; \
40925963022Sstsp if ((val) > INT_MAX - to_digit((dig))) \
41025963022Sstsp goto overflow; \
41125963022Sstsp (val) += to_digit((dig)); \
41225963022Sstsp } while (0)
41325963022Sstsp
41425963022Sstsp /*
41525963022Sstsp * Get * arguments, including the form *nn$. Preserve the nextarg
41625963022Sstsp * that the argument can be gotten once the type is determined.
41725963022Sstsp */
41825963022Sstsp #define GETASTER(val) \
41925963022Sstsp n2 = 0; \
42025963022Sstsp cp = fmt; \
42125963022Sstsp while (is_digit(*cp)) { \
42225963022Sstsp APPEND_DIGIT(n2, *cp); \
42325963022Sstsp cp++; \
42425963022Sstsp } \
42525963022Sstsp if (*cp == '$') { \
42625963022Sstsp int hold = nextarg; \
42725963022Sstsp if (argtable == NULL) { \
42825963022Sstsp argtable = statargtable; \
42909abd1b4Stb if (__find_arguments(fmt0, orgap, &argtable, \
43009abd1b4Stb &argtablesiz) == -1) { \
43109abd1b4Stb ret = -1; \
43209abd1b4Stb goto error; \
43309abd1b4Stb } \
43425963022Sstsp } \
43525963022Sstsp nextarg = n2; \
43625963022Sstsp val = GETARG(int); \
43725963022Sstsp nextarg = hold; \
43825963022Sstsp fmt = ++cp; \
43925963022Sstsp } else { \
44025963022Sstsp val = GETARG(int); \
44125963022Sstsp }
44225963022Sstsp
44325963022Sstsp /*
44425963022Sstsp * Get the argument indexed by nextarg. If the argument table is
44525963022Sstsp * built, use it to get the argument. If its not, get the next
44625963022Sstsp * argument (and arguments must be gotten sequentially).
44725963022Sstsp */
44825963022Sstsp #define GETARG(type) \
44925963022Sstsp ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
45025963022Sstsp (nextarg++, va_arg(ap, type)))
45125963022Sstsp
45225963022Sstsp _SET_ORIENTATION(fp, 1);
45325963022Sstsp /* sorry, fwprintf(read_only_file, "") returns EOF, not 0 */
454*fc99cf93Smillert if (cantwrite(fp))
45525963022Sstsp return (EOF);
45625963022Sstsp
45725963022Sstsp /* optimise fwprintf(stderr) (and other unbuffered Unix files) */
45825963022Sstsp if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
45925963022Sstsp fp->_file >= 0)
46025963022Sstsp return (__sbprintf(fp, fmt0, ap));
46125963022Sstsp
46225963022Sstsp fmt = (wchar_t *)fmt0;
46325963022Sstsp argtable = NULL;
46425963022Sstsp nextarg = 1;
46525963022Sstsp va_copy(orgap, ap);
46625963022Sstsp ret = 0;
46725963022Sstsp convbuf = NULL;
46825963022Sstsp
46925963022Sstsp /*
47025963022Sstsp * Scan the format for conversions (`%' character).
47125963022Sstsp */
47225963022Sstsp for (;;) {
47325963022Sstsp for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
47425963022Sstsp continue;
47525963022Sstsp if (fmt != cp) {
47625963022Sstsp ptrdiff_t m = fmt - cp;
47725963022Sstsp if (m < 0 || m > INT_MAX - ret)
47825963022Sstsp goto overflow;
47925963022Sstsp PRINT(cp, m);
48025963022Sstsp ret += m;
48125963022Sstsp }
48225963022Sstsp if (ch == '\0')
48325963022Sstsp goto done;
48425963022Sstsp fmt++; /* skip over '%' */
48525963022Sstsp
48625963022Sstsp flags = 0;
48725963022Sstsp dprec = 0;
48825963022Sstsp width = 0;
48925963022Sstsp prec = -1;
49025963022Sstsp sign = '\0';
49125963022Sstsp ox[1] = '\0';
49225963022Sstsp
49325963022Sstsp rflag: ch = *fmt++;
49425963022Sstsp reswitch: switch (ch) {
49525963022Sstsp case ' ':
49625963022Sstsp /*
49725963022Sstsp * ``If the space and + flags both appear, the space
49825963022Sstsp * flag will be ignored.''
49925963022Sstsp * -- ANSI X3J11
50025963022Sstsp */
50125963022Sstsp if (!sign)
50225963022Sstsp sign = ' ';
50325963022Sstsp goto rflag;
50425963022Sstsp case '#':
50525963022Sstsp flags |= ALT;
50625963022Sstsp goto rflag;
50725963022Sstsp case '\'':
50825963022Sstsp /* grouping not implemented */
50925963022Sstsp goto rflag;
51025963022Sstsp case '*':
51125963022Sstsp /*
51225963022Sstsp * ``A negative field width argument is taken as a
51325963022Sstsp * - flag followed by a positive field width.''
51425963022Sstsp * -- ANSI X3J11
51525963022Sstsp * They don't exclude field widths read from args.
51625963022Sstsp */
51725963022Sstsp GETASTER(width);
51825963022Sstsp if (width >= 0)
51925963022Sstsp goto rflag;
52025963022Sstsp if (width == INT_MIN)
52125963022Sstsp goto overflow;
52225963022Sstsp width = -width;
52325963022Sstsp /* FALLTHROUGH */
52425963022Sstsp case '-':
52525963022Sstsp flags |= LADJUST;
52625963022Sstsp goto rflag;
52725963022Sstsp case '+':
52825963022Sstsp sign = '+';
52925963022Sstsp goto rflag;
53025963022Sstsp case '.':
53125963022Sstsp if ((ch = *fmt++) == '*') {
53225963022Sstsp GETASTER(n);
53325963022Sstsp prec = n < 0 ? -1 : n;
53425963022Sstsp goto rflag;
53525963022Sstsp }
53625963022Sstsp n = 0;
53725963022Sstsp while (is_digit(ch)) {
53825963022Sstsp APPEND_DIGIT(n, ch);
53925963022Sstsp ch = *fmt++;
54025963022Sstsp }
54125963022Sstsp if (ch == '$') {
54225963022Sstsp nextarg = n;
54325963022Sstsp if (argtable == NULL) {
54425963022Sstsp argtable = statargtable;
54509abd1b4Stb if (__find_arguments(fmt0, orgap,
54609abd1b4Stb &argtable, &argtablesiz) == -1) {
54709abd1b4Stb ret = -1;
54809abd1b4Stb goto error;
54909abd1b4Stb }
55025963022Sstsp }
55125963022Sstsp goto rflag;
55225963022Sstsp }
55325963022Sstsp prec = n;
55425963022Sstsp goto reswitch;
55525963022Sstsp case '0':
55625963022Sstsp /*
55725963022Sstsp * ``Note that 0 is taken as a flag, not as the
55825963022Sstsp * beginning of a field width.''
55925963022Sstsp * -- ANSI X3J11
56025963022Sstsp */
56125963022Sstsp flags |= ZEROPAD;
56225963022Sstsp goto rflag;
56325963022Sstsp case '1': case '2': case '3': case '4':
56425963022Sstsp case '5': case '6': case '7': case '8': case '9':
56525963022Sstsp n = 0;
56625963022Sstsp do {
56725963022Sstsp APPEND_DIGIT(n, ch);
56825963022Sstsp ch = *fmt++;
56925963022Sstsp } while (is_digit(ch));
57025963022Sstsp if (ch == '$') {
57125963022Sstsp nextarg = n;
57225963022Sstsp if (argtable == NULL) {
57325963022Sstsp argtable = statargtable;
57409abd1b4Stb if (__find_arguments(fmt0, orgap,
57509abd1b4Stb &argtable, &argtablesiz) == -1) {
57609abd1b4Stb ret = -1;
57709abd1b4Stb goto error;
57809abd1b4Stb }
57925963022Sstsp }
58025963022Sstsp goto rflag;
58125963022Sstsp }
58225963022Sstsp width = n;
58325963022Sstsp goto reswitch;
58425963022Sstsp #ifdef FLOATING_POINT
58525963022Sstsp case 'L':
58625963022Sstsp flags |= LONGDBL;
58725963022Sstsp goto rflag;
58825963022Sstsp #endif
58925963022Sstsp case 'h':
59025963022Sstsp if (*fmt == 'h') {
59125963022Sstsp fmt++;
59225963022Sstsp flags |= CHARINT;
59325963022Sstsp } else {
59425963022Sstsp flags |= SHORTINT;
59525963022Sstsp }
59625963022Sstsp goto rflag;
59725963022Sstsp case 'j':
59825963022Sstsp flags |= MAXINT;
59925963022Sstsp goto rflag;
60025963022Sstsp case 'l':
60125963022Sstsp if (*fmt == 'l') {
60225963022Sstsp fmt++;
60325963022Sstsp flags |= LLONGINT;
60425963022Sstsp } else {
60525963022Sstsp flags |= LONGINT;
60625963022Sstsp }
60725963022Sstsp goto rflag;
60825963022Sstsp case 'q':
60925963022Sstsp flags |= LLONGINT;
61025963022Sstsp goto rflag;
61125963022Sstsp case 't':
61225963022Sstsp flags |= PTRINT;
61325963022Sstsp goto rflag;
61425963022Sstsp case 'z':
61525963022Sstsp flags |= SIZEINT;
61625963022Sstsp goto rflag;
61725963022Sstsp case 'C':
61825963022Sstsp flags |= LONGINT;
61925963022Sstsp /*FALLTHROUGH*/
62025963022Sstsp case 'c':
62125963022Sstsp if (flags & LONGINT)
62225963022Sstsp *(cp = buf) = (wchar_t)GETARG(wint_t);
62325963022Sstsp else
62425963022Sstsp *(cp = buf) = (wchar_t)btowc(GETARG(int));
62525963022Sstsp size = 1;
62625963022Sstsp sign = '\0';
62725963022Sstsp break;
62825963022Sstsp case 'D':
62925963022Sstsp flags |= LONGINT;
63025963022Sstsp /*FALLTHROUGH*/
63125963022Sstsp case 'd':
63225963022Sstsp case 'i':
63325963022Sstsp _umax = SARG();
63425963022Sstsp if ((intmax_t)_umax < 0) {
63525963022Sstsp _umax = -_umax;
63625963022Sstsp sign = '-';
63725963022Sstsp }
63825963022Sstsp base = DEC;
63925963022Sstsp goto number;
64025963022Sstsp #ifdef FLOATING_POINT
64125963022Sstsp case 'a':
64225963022Sstsp case 'A':
64325963022Sstsp if (ch == 'a') {
64425963022Sstsp ox[1] = 'x';
64525963022Sstsp xdigs = xdigs_lower;
64625963022Sstsp expchar = 'p';
64725963022Sstsp } else {
64825963022Sstsp ox[1] = 'X';
64925963022Sstsp xdigs = xdigs_upper;
65025963022Sstsp expchar = 'P';
65125963022Sstsp }
65225963022Sstsp if (prec >= 0)
65325963022Sstsp prec++;
65425963022Sstsp if (dtoaresult)
65525963022Sstsp __freedtoa(dtoaresult);
65625963022Sstsp if (flags & LONGDBL) {
65725963022Sstsp fparg.ldbl = GETARG(long double);
65825963022Sstsp dtoaresult =
65925963022Sstsp __hldtoa(fparg.ldbl, xdigs, prec,
66025963022Sstsp &expt, &signflag, &dtoaend);
66125963022Sstsp if (dtoaresult == NULL) {
66225963022Sstsp errno = ENOMEM;
66325963022Sstsp goto error;
66425963022Sstsp }
66525963022Sstsp } else {
66625963022Sstsp fparg.dbl = GETARG(double);
66725963022Sstsp dtoaresult =
66825963022Sstsp __hdtoa(fparg.dbl, xdigs, prec,
66925963022Sstsp &expt, &signflag, &dtoaend);
67025963022Sstsp if (dtoaresult == NULL) {
67125963022Sstsp errno = ENOMEM;
67225963022Sstsp goto error;
67325963022Sstsp }
67425963022Sstsp }
67525963022Sstsp if (prec < 0)
67625963022Sstsp prec = dtoaend - dtoaresult;
67725963022Sstsp if (expt == INT_MAX)
67825963022Sstsp ox[1] = '\0';
67925963022Sstsp free(convbuf);
68025963022Sstsp cp = convbuf = __mbsconv(dtoaresult, -1);
68125963022Sstsp if (cp == NULL)
68225963022Sstsp goto error;
68325963022Sstsp ndig = dtoaend - dtoaresult;
68425963022Sstsp goto fp_common;
68525963022Sstsp case 'e':
68625963022Sstsp case 'E':
68725963022Sstsp expchar = ch;
68825963022Sstsp if (prec < 0) /* account for digit before decpt */
68925963022Sstsp prec = DEFPREC + 1;
69025963022Sstsp else
69125963022Sstsp prec++;
69225963022Sstsp goto fp_begin;
69325963022Sstsp case 'f':
69425963022Sstsp case 'F':
69525963022Sstsp expchar = '\0';
69625963022Sstsp goto fp_begin;
69725963022Sstsp case 'g':
69825963022Sstsp case 'G':
69925963022Sstsp expchar = ch - ('g' - 'e');
70025963022Sstsp if (prec == 0)
70125963022Sstsp prec = 1;
70225963022Sstsp fp_begin:
70325963022Sstsp if (prec < 0)
70425963022Sstsp prec = DEFPREC;
70525963022Sstsp if (dtoaresult)
70625963022Sstsp __freedtoa(dtoaresult);
70725963022Sstsp if (flags & LONGDBL) {
70825963022Sstsp fparg.ldbl = GETARG(long double);
70925963022Sstsp dtoaresult =
71025963022Sstsp __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
71125963022Sstsp &expt, &signflag, &dtoaend);
71225963022Sstsp if (dtoaresult == NULL) {
71325963022Sstsp errno = ENOMEM;
71425963022Sstsp goto error;
71525963022Sstsp }
71625963022Sstsp } else {
71725963022Sstsp fparg.dbl = GETARG(double);
71825963022Sstsp dtoaresult =
71925963022Sstsp __dtoa(fparg.dbl, expchar ? 2 : 3, prec,
72025963022Sstsp &expt, &signflag, &dtoaend);
72125963022Sstsp if (dtoaresult == NULL) {
72225963022Sstsp errno = ENOMEM;
72325963022Sstsp goto error;
72425963022Sstsp }
72525963022Sstsp if (expt == 9999)
72625963022Sstsp expt = INT_MAX;
72725963022Sstsp }
72825963022Sstsp free(convbuf);
72925963022Sstsp cp = convbuf = __mbsconv(dtoaresult, -1);
73025963022Sstsp if (cp == NULL)
73125963022Sstsp goto error;
73225963022Sstsp ndig = dtoaend - dtoaresult;
73325963022Sstsp fp_common:
73425963022Sstsp if (signflag)
73525963022Sstsp sign = '-';
73625963022Sstsp if (expt == INT_MAX) { /* inf or nan */
7378d454a83Sdaniel if (*cp == 'N')
73825963022Sstsp cp = (ch >= 'a') ? L"nan" : L"NAN";
7398d454a83Sdaniel else
74025963022Sstsp cp = (ch >= 'a') ? L"inf" : L"INF";
74125963022Sstsp size = 3;
74225963022Sstsp flags &= ~ZEROPAD;
74325963022Sstsp break;
74425963022Sstsp }
74525963022Sstsp flags |= FPT;
74625963022Sstsp if (ch == 'g' || ch == 'G') {
74725963022Sstsp if (expt > -4 && expt <= prec) {
74825963022Sstsp /* Make %[gG] smell like %[fF] */
74925963022Sstsp expchar = '\0';
75025963022Sstsp if (flags & ALT)
75125963022Sstsp prec -= expt;
75225963022Sstsp else
75325963022Sstsp prec = ndig - expt;
75425963022Sstsp if (prec < 0)
75525963022Sstsp prec = 0;
75625963022Sstsp } else {
75725963022Sstsp /*
75825963022Sstsp * Make %[gG] smell like %[eE], but
75925963022Sstsp * trim trailing zeroes if no # flag.
76025963022Sstsp */
76125963022Sstsp if (!(flags & ALT))
76225963022Sstsp prec = ndig;
76325963022Sstsp }
76425963022Sstsp }
76525963022Sstsp if (expchar) {
76625963022Sstsp expsize = exponent(expstr, expt - 1, expchar);
76725963022Sstsp size = expsize + prec;
76825963022Sstsp if (prec > 1 || flags & ALT)
76925963022Sstsp ++size;
77025963022Sstsp } else {
77125963022Sstsp /* space for digits before decimal point */
77225963022Sstsp if (expt > 0)
77325963022Sstsp size = expt;
77425963022Sstsp else /* "0" */
77525963022Sstsp size = 1;
77625963022Sstsp /* space for decimal pt and following digits */
77725963022Sstsp if (prec || flags & ALT)
77825963022Sstsp size += prec + 1;
77925963022Sstsp lead = expt;
78025963022Sstsp }
78125963022Sstsp break;
78225963022Sstsp #endif /* FLOATING_POINT */
783cd45c0a6Sderaadt case 'n': {
7843db6bc30Sderaadt static const char n_msg[] = ": *wprintf used %n, aborting";
785cd45c0a6Sderaadt char buf[1024], *p;
786cd45c0a6Sderaadt
787cd45c0a6Sderaadt /* <10> is LOG_CRIT */
788cd45c0a6Sderaadt strlcpy(buf, "<10>", sizeof buf);
789cd45c0a6Sderaadt
790cd45c0a6Sderaadt /* Make sure progname does not fill the whole buffer */
791cd45c0a6Sderaadt strlcat(buf, __progname, sizeof(buf) - sizeof n_msg);
792cd45c0a6Sderaadt strlcat(buf, n_msg, sizeof buf);
7933db6bc30Sderaadt /*
7943db6bc30Sderaadt * vfprintf sends fmt0 via syslog, but this is not
7953db6bc30Sderaadt * good behaviour for wide strings.
7963db6bc30Sderaadt */
797cd45c0a6Sderaadt if ((p = strchr(buf, '\n')))
798cd45c0a6Sderaadt *p = '\0';
799cd45c0a6Sderaadt sendsyslog(buf, strlen(buf), LOG_CONS);
800cd45c0a6Sderaadt abort();
8013db6bc30Sderaadt break;
802cd45c0a6Sderaadt }
80325963022Sstsp case 'O':
80425963022Sstsp flags |= LONGINT;
80525963022Sstsp /*FALLTHROUGH*/
80625963022Sstsp case 'o':
80725963022Sstsp _umax = UARG();
80825963022Sstsp base = OCT;
80925963022Sstsp goto nosign;
81025963022Sstsp case 'p':
81125963022Sstsp /*
81225963022Sstsp * ``The argument shall be a pointer to void. The
81325963022Sstsp * value of the pointer is converted to a sequence
81425963022Sstsp * of printable characters, in an implementation-
81525963022Sstsp * defined manner.''
81625963022Sstsp * -- ANSI X3J11
81725963022Sstsp */
81825963022Sstsp _umax = (u_long)GETARG(void *);
81925963022Sstsp base = HEX;
82025963022Sstsp xdigs = xdigs_lower;
82125963022Sstsp ox[1] = 'x';
82225963022Sstsp goto nosign;
82325963022Sstsp case 'S':
82425963022Sstsp flags |= LONGINT;
82525963022Sstsp /*FALLTHROUGH*/
82625963022Sstsp case 's':
82725963022Sstsp if (flags & LONGINT) {
828a469cbf7Sderaadt if ((cp = GETARG(wchar_t *)) == NULL) {
829a469cbf7Sderaadt struct syslog_data sdata = SYSLOG_DATA_INIT;
830a469cbf7Sderaadt int save_errno = errno;
831a469cbf7Sderaadt
832a469cbf7Sderaadt syslog_r(LOG_CRIT | LOG_CONS, &sdata,
833f9216cc1Sderaadt "vfwprintf %%ls NULL in \"%ls\"", fmt0);
834a469cbf7Sderaadt errno = save_errno;
835a469cbf7Sderaadt
83625963022Sstsp cp = L"(null)";
837a469cbf7Sderaadt }
83825963022Sstsp } else {
83925963022Sstsp char *mbsarg;
840a469cbf7Sderaadt if ((mbsarg = GETARG(char *)) == NULL) {
841a469cbf7Sderaadt struct syslog_data sdata = SYSLOG_DATA_INIT;
842a469cbf7Sderaadt int save_errno = errno;
843a469cbf7Sderaadt
844a469cbf7Sderaadt syslog_r(LOG_CRIT | LOG_CONS, &sdata,
845f9216cc1Sderaadt "vfwprintf %%s NULL in \"%ls\"", fmt0);
846a469cbf7Sderaadt errno = save_errno;
847a469cbf7Sderaadt
84825963022Sstsp mbsarg = "(null)";
849a469cbf7Sderaadt }
85025963022Sstsp free(convbuf);
85125963022Sstsp convbuf = __mbsconv(mbsarg, prec);
85225963022Sstsp if (convbuf == NULL) {
85325963022Sstsp fp->_flags |= __SERR;
85425963022Sstsp goto error;
85525963022Sstsp } else
85625963022Sstsp cp = convbuf;
85725963022Sstsp }
85825963022Sstsp if (prec >= 0) {
85925963022Sstsp /*
86025963022Sstsp * can't use wcslen; can only look for the
86125963022Sstsp * NUL in the first `prec' characters, and
86225963022Sstsp * wcslen() will go further.
86325963022Sstsp */
86425963022Sstsp wchar_t *p = wmemchr(cp, 0, prec);
86525963022Sstsp
86625963022Sstsp size = p ? (p - cp) : prec;
86725963022Sstsp } else {
86825963022Sstsp size_t len;
86925963022Sstsp
87025963022Sstsp if ((len = wcslen(cp)) > INT_MAX)
87125963022Sstsp goto overflow;
87225963022Sstsp size = (int)len;
87325963022Sstsp }
87425963022Sstsp sign = '\0';
87525963022Sstsp break;
87625963022Sstsp case 'U':
87725963022Sstsp flags |= LONGINT;
87825963022Sstsp /*FALLTHROUGH*/
87925963022Sstsp case 'u':
88025963022Sstsp _umax = UARG();
88125963022Sstsp base = DEC;
88225963022Sstsp goto nosign;
88325963022Sstsp case 'X':
88425963022Sstsp xdigs = xdigs_upper;
88525963022Sstsp goto hex;
88625963022Sstsp case 'x':
88725963022Sstsp xdigs = xdigs_lower;
88825963022Sstsp hex: _umax = UARG();
88925963022Sstsp base = HEX;
89025963022Sstsp /* leading 0x/X only if non-zero */
89125963022Sstsp if (flags & ALT && _umax != 0)
89225963022Sstsp ox[1] = ch;
89325963022Sstsp
89425963022Sstsp /* unsigned conversions */
89525963022Sstsp nosign: sign = '\0';
89625963022Sstsp /*
89725963022Sstsp * ``... diouXx conversions ... if a precision is
89825963022Sstsp * specified, the 0 flag will be ignored.''
89925963022Sstsp * -- ANSI X3J11
90025963022Sstsp */
90125963022Sstsp number: if ((dprec = prec) >= 0)
90225963022Sstsp flags &= ~ZEROPAD;
90325963022Sstsp
90425963022Sstsp /*
90525963022Sstsp * ``The result of converting a zero value with an
90625963022Sstsp * explicit precision of zero is no characters.''
90725963022Sstsp * -- ANSI X3J11
90825963022Sstsp */
90925963022Sstsp cp = buf + BUF;
91025963022Sstsp if (_umax != 0 || prec != 0) {
91125963022Sstsp /*
91225963022Sstsp * Unsigned mod is hard, and unsigned mod
91325963022Sstsp * by a constant is easier than that by
91425963022Sstsp * a variable; hence this switch.
91525963022Sstsp */
91625963022Sstsp switch (base) {
91725963022Sstsp case OCT:
91825963022Sstsp do {
91925963022Sstsp *--cp = to_char(_umax & 7);
92025963022Sstsp _umax >>= 3;
92125963022Sstsp } while (_umax);
92225963022Sstsp /* handle octal leading 0 */
92325963022Sstsp if (flags & ALT && *cp != '0')
92425963022Sstsp *--cp = '0';
92525963022Sstsp break;
92625963022Sstsp
92725963022Sstsp case DEC:
92825963022Sstsp /* many numbers are 1 digit */
92925963022Sstsp while (_umax >= 10) {
93025963022Sstsp *--cp = to_char(_umax % 10);
93125963022Sstsp _umax /= 10;
93225963022Sstsp }
93325963022Sstsp *--cp = to_char(_umax);
93425963022Sstsp break;
93525963022Sstsp
93625963022Sstsp case HEX:
93725963022Sstsp do {
93825963022Sstsp *--cp = xdigs[_umax & 15];
93925963022Sstsp _umax >>= 4;
94025963022Sstsp } while (_umax);
94125963022Sstsp break;
94225963022Sstsp
94325963022Sstsp default:
94425963022Sstsp cp = L"bug in vfwprintf: bad base";
94525963022Sstsp size = wcslen(cp);
94625963022Sstsp goto skipsize;
94725963022Sstsp }
94825963022Sstsp }
94925963022Sstsp size = buf + BUF - cp;
95025963022Sstsp if (size > BUF) /* should never happen */
95125963022Sstsp abort();
95225963022Sstsp skipsize:
95325963022Sstsp break;
95425963022Sstsp default: /* "%?" prints ?, unless ? is NUL */
95525963022Sstsp if (ch == '\0')
95625963022Sstsp goto done;
95725963022Sstsp /* pretend it was %c with argument ch */
95825963022Sstsp cp = buf;
95925963022Sstsp *cp = ch;
96025963022Sstsp size = 1;
96125963022Sstsp sign = '\0';
96225963022Sstsp break;
96325963022Sstsp }
96425963022Sstsp
96525963022Sstsp /*
96625963022Sstsp * All reasonable formats wind up here. At this point, `cp'
96725963022Sstsp * points to a string which (if not flags&LADJUST) should be
96825963022Sstsp * padded out to `width' places. If flags&ZEROPAD, it should
96925963022Sstsp * first be prefixed by any sign or other prefix; otherwise,
97025963022Sstsp * it should be blank padded before the prefix is emitted.
97125963022Sstsp * After any left-hand padding and prefixing, emit zeroes
97225963022Sstsp * required by a decimal %[diouxX] precision, then print the
97325963022Sstsp * string proper, then emit zeroes required by any leftover
97425963022Sstsp * floating precision; finally, if LADJUST, pad with blanks.
97525963022Sstsp *
97625963022Sstsp * Compute actual size, so we know how much to pad.
97725963022Sstsp * size excludes decimal prec; realsz includes it.
97825963022Sstsp */
97925963022Sstsp realsz = dprec > size ? dprec : size;
98025963022Sstsp if (sign)
98125963022Sstsp realsz++;
98225963022Sstsp if (ox[1])
98325963022Sstsp realsz+= 2;
98425963022Sstsp
98525963022Sstsp /* right-adjusting blank padding */
98625963022Sstsp if ((flags & (LADJUST|ZEROPAD)) == 0)
98725963022Sstsp PAD(width - realsz, blanks);
98825963022Sstsp
98925963022Sstsp /* prefix */
99025963022Sstsp if (sign)
99125963022Sstsp PRINT(&sign, 1);
99225963022Sstsp if (ox[1]) { /* ox[1] is either x, X, or \0 */
99325963022Sstsp ox[0] = '0';
99425963022Sstsp PRINT(ox, 2);
99525963022Sstsp }
99625963022Sstsp
99725963022Sstsp /* right-adjusting zero padding */
99825963022Sstsp if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
99925963022Sstsp PAD(width - realsz, zeroes);
100025963022Sstsp
100125963022Sstsp /* leading zeroes from decimal precision */
100225963022Sstsp PAD(dprec - size, zeroes);
100325963022Sstsp
100425963022Sstsp /* the string or number proper */
100525963022Sstsp #ifdef FLOATING_POINT
100625963022Sstsp if ((flags & FPT) == 0) {
100725963022Sstsp PRINT(cp, size);
100825963022Sstsp } else { /* glue together f_p fragments */
10094ddf57ccSmatthew if (decimal_point == NULL)
10104ddf57ccSmatthew decimal_point = nl_langinfo(RADIXCHAR);
101125963022Sstsp if (!expchar) { /* %[fF] or sufficiently short %[gG] */
101225963022Sstsp if (expt <= 0) {
101325963022Sstsp PRINT(zeroes, 1);
101425963022Sstsp if (prec || flags & ALT)
101525963022Sstsp PRINT(decimal_point, 1);
101625963022Sstsp PAD(-expt, zeroes);
101725963022Sstsp /* already handled initial 0's */
101825963022Sstsp prec += expt;
101925963022Sstsp } else {
102025963022Sstsp PRINTANDPAD(cp, convbuf + ndig,
102125963022Sstsp lead, zeroes);
102225963022Sstsp cp += lead;
102325963022Sstsp if (prec || flags & ALT)
102425963022Sstsp PRINT(decimal_point, 1);
102525963022Sstsp }
102625963022Sstsp PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
102725963022Sstsp } else { /* %[eE] or sufficiently long %[gG] */
102825963022Sstsp if (prec > 1 || flags & ALT) {
102925963022Sstsp buf[0] = *cp++;
103025963022Sstsp buf[1] = *decimal_point;
103125963022Sstsp PRINT(buf, 2);
103225963022Sstsp PRINT(cp, ndig-1);
103325963022Sstsp PAD(prec - ndig, zeroes);
103425963022Sstsp } else { /* XeYYY */
103525963022Sstsp PRINT(cp, 1);
103625963022Sstsp }
103725963022Sstsp PRINT(expstr, expsize);
103825963022Sstsp }
103925963022Sstsp }
104025963022Sstsp #else
104125963022Sstsp PRINT(cp, size);
104225963022Sstsp #endif
104325963022Sstsp /* left-adjusting padding (always blank) */
104425963022Sstsp if (flags & LADJUST)
104525963022Sstsp PAD(width - realsz, blanks);
104625963022Sstsp
104725963022Sstsp /* finally, adjust ret */
104825963022Sstsp if (width < realsz)
104925963022Sstsp width = realsz;
105025963022Sstsp if (width > INT_MAX - ret)
105125963022Sstsp goto overflow;
105225963022Sstsp ret += width;
105325963022Sstsp }
105425963022Sstsp done:
105525963022Sstsp error:
105625963022Sstsp va_end(orgap);
105725963022Sstsp if (__sferror(fp))
105825963022Sstsp ret = -1;
105925963022Sstsp goto finish;
106025963022Sstsp
106125963022Sstsp overflow:
106225963022Sstsp errno = ENOMEM;
106325963022Sstsp ret = -1;
106425963022Sstsp
106525963022Sstsp finish:
106680682283Sstsp free(convbuf);
106725963022Sstsp #ifdef FLOATING_POINT
106825963022Sstsp if (dtoaresult)
106925963022Sstsp __freedtoa(dtoaresult);
107025963022Sstsp #endif
107125963022Sstsp if (argtable != NULL && argtable != statargtable) {
107225963022Sstsp munmap(argtable, argtablesiz);
107325963022Sstsp argtable = NULL;
107425963022Sstsp }
107525963022Sstsp return (ret);
107625963022Sstsp }
107725963022Sstsp
107825963022Sstsp int
vfwprintf(FILE * __restrict fp,const wchar_t * __restrict fmt0,__va_list ap)107925963022Sstsp vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, __va_list ap)
108025963022Sstsp {
108125963022Sstsp int r;
108225963022Sstsp
108325963022Sstsp FLOCKFILE(fp);
108425963022Sstsp r = __vfwprintf(fp, fmt0, ap);
108525963022Sstsp FUNLOCKFILE(fp);
108625963022Sstsp
108725963022Sstsp return (r);
108825963022Sstsp }
10899b9d2a55Sguenther DEF_STRONG(vfwprintf);
109025963022Sstsp
109125963022Sstsp /*
109225963022Sstsp * Type ids for argument type table.
109325963022Sstsp */
109425963022Sstsp #define T_UNUSED 0
109525963022Sstsp #define T_SHORT 1
109625963022Sstsp #define T_U_SHORT 2
109725963022Sstsp #define TP_SHORT 3
109825963022Sstsp #define T_INT 4
109925963022Sstsp #define T_U_INT 5
110025963022Sstsp #define TP_INT 6
110125963022Sstsp #define T_LONG 7
110225963022Sstsp #define T_U_LONG 8
110325963022Sstsp #define TP_LONG 9
110425963022Sstsp #define T_LLONG 10
110525963022Sstsp #define T_U_LLONG 11
110625963022Sstsp #define TP_LLONG 12
110725963022Sstsp #define T_DOUBLE 13
110825963022Sstsp #define T_LONG_DOUBLE 14
110925963022Sstsp #define TP_CHAR 15
111025963022Sstsp #define TP_VOID 16
111125963022Sstsp #define T_PTRINT 17
111225963022Sstsp #define TP_PTRINT 18
111325963022Sstsp #define T_SIZEINT 19
111425963022Sstsp #define T_SSIZEINT 20
111525963022Sstsp #define TP_SSIZEINT 21
111625963022Sstsp #define T_MAXINT 22
111725963022Sstsp #define T_MAXUINT 23
111825963022Sstsp #define TP_MAXINT 24
111925963022Sstsp #define T_CHAR 25
112025963022Sstsp #define T_U_CHAR 26
112125963022Sstsp #define T_WINT 27
112225963022Sstsp #define TP_WCHAR 28
112325963022Sstsp
112425963022Sstsp /*
112525963022Sstsp * Find all arguments when a positional parameter is encountered. Returns a
112625963022Sstsp * table, indexed by argument number, of pointers to each arguments. The
112725963022Sstsp * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
112825963022Sstsp * It will be replaced with a mmap-ed one if it overflows (malloc cannot be
112925963022Sstsp * used since we are attempting to make snprintf thread safe, and alloca is
113025963022Sstsp * problematic since we have nested functions..)
113125963022Sstsp */
113225963022Sstsp static int
__find_arguments(const wchar_t * fmt0,va_list ap,union arg ** argtable,size_t * argtablesiz)113325963022Sstsp __find_arguments(const wchar_t *fmt0, va_list ap, union arg **argtable,
113425963022Sstsp size_t *argtablesiz)
113525963022Sstsp {
113625963022Sstsp wchar_t *fmt; /* format string */
113725963022Sstsp int ch; /* character from fmt */
113825963022Sstsp int n, n2; /* handy integer (short term usage) */
113925963022Sstsp wchar_t *cp; /* handy char pointer (short term usage) */
114025963022Sstsp int flags; /* flags as above */
114125963022Sstsp unsigned char *typetable; /* table of types */
114225963022Sstsp unsigned char stattypetable[STATIC_ARG_TBL_SIZE];
114325963022Sstsp int tablesize; /* current size of type table */
114425963022Sstsp int tablemax; /* largest used index in table */
114525963022Sstsp int nextarg; /* 1-based argument index */
114625963022Sstsp int ret = 0; /* return value */
114725963022Sstsp
114825963022Sstsp /*
114925963022Sstsp * Add an argument type to the table, expanding if necessary.
115025963022Sstsp */
115125963022Sstsp #define ADDTYPE(type) \
115225963022Sstsp ((nextarg >= tablesize) ? \
115325963022Sstsp __grow_type_table(&typetable, &tablesize) : 0, \
115425963022Sstsp (nextarg > tablemax) ? tablemax = nextarg : 0, \
115525963022Sstsp typetable[nextarg++] = type)
115625963022Sstsp
115725963022Sstsp #define ADDSARG() \
115825963022Sstsp ((flags&MAXINT) ? ADDTYPE(T_MAXINT) : \
115925963022Sstsp ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
116025963022Sstsp ((flags&SIZEINT) ? ADDTYPE(T_SSIZEINT) : \
116125963022Sstsp ((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \
116225963022Sstsp ((flags&LONGINT) ? ADDTYPE(T_LONG) : \
116325963022Sstsp ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : \
116425963022Sstsp ((flags&CHARINT) ? ADDTYPE(T_CHAR) : ADDTYPE(T_INT))))))))
116525963022Sstsp
116625963022Sstsp #define ADDUARG() \
116725963022Sstsp ((flags&MAXINT) ? ADDTYPE(T_MAXUINT) : \
116825963022Sstsp ((flags&PTRINT) ? ADDTYPE(T_PTRINT) : \
116925963022Sstsp ((flags&SIZEINT) ? ADDTYPE(T_SIZEINT) : \
117025963022Sstsp ((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \
117125963022Sstsp ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
117225963022Sstsp ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : \
117325963022Sstsp ((flags&CHARINT) ? ADDTYPE(T_U_CHAR) : ADDTYPE(T_U_INT))))))))
117425963022Sstsp
117525963022Sstsp /*
117625963022Sstsp * Add * arguments to the type array.
117725963022Sstsp */
117825963022Sstsp #define ADDASTER() \
117925963022Sstsp n2 = 0; \
118025963022Sstsp cp = fmt; \
118125963022Sstsp while (is_digit(*cp)) { \
118225963022Sstsp APPEND_DIGIT(n2, *cp); \
118325963022Sstsp cp++; \
118425963022Sstsp } \
118525963022Sstsp if (*cp == '$') { \
118625963022Sstsp int hold = nextarg; \
118725963022Sstsp nextarg = n2; \
118825963022Sstsp ADDTYPE(T_INT); \
118925963022Sstsp nextarg = hold; \
119025963022Sstsp fmt = ++cp; \
119125963022Sstsp } else { \
119225963022Sstsp ADDTYPE(T_INT); \
119325963022Sstsp }
119425963022Sstsp fmt = (wchar_t *)fmt0;
119525963022Sstsp typetable = stattypetable;
119625963022Sstsp tablesize = STATIC_ARG_TBL_SIZE;
119725963022Sstsp tablemax = 0;
119825963022Sstsp nextarg = 1;
119925963022Sstsp memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
120025963022Sstsp
120125963022Sstsp /*
120225963022Sstsp * Scan the format for conversions (`%' character).
120325963022Sstsp */
120425963022Sstsp for (;;) {
120525963022Sstsp for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
120625963022Sstsp continue;
120725963022Sstsp if (ch == '\0')
120825963022Sstsp goto done;
120925963022Sstsp fmt++; /* skip over '%' */
121025963022Sstsp
121125963022Sstsp flags = 0;
121225963022Sstsp
121325963022Sstsp rflag: ch = *fmt++;
121425963022Sstsp reswitch: switch (ch) {
121525963022Sstsp case ' ':
121625963022Sstsp case '#':
121725963022Sstsp case '\'':
121825963022Sstsp goto rflag;
121925963022Sstsp case '*':
122025963022Sstsp ADDASTER();
122125963022Sstsp goto rflag;
122225963022Sstsp case '-':
122325963022Sstsp case '+':
122425963022Sstsp goto rflag;
122525963022Sstsp case '.':
122625963022Sstsp if ((ch = *fmt++) == '*') {
122725963022Sstsp ADDASTER();
122825963022Sstsp goto rflag;
122925963022Sstsp }
123025963022Sstsp while (is_digit(ch)) {
123125963022Sstsp ch = *fmt++;
123225963022Sstsp }
123325963022Sstsp goto reswitch;
123425963022Sstsp case '0':
123525963022Sstsp goto rflag;
123625963022Sstsp case '1': case '2': case '3': case '4':
123725963022Sstsp case '5': case '6': case '7': case '8': case '9':
123825963022Sstsp n = 0;
123925963022Sstsp do {
124025963022Sstsp APPEND_DIGIT(n ,ch);
124125963022Sstsp ch = *fmt++;
124225963022Sstsp } while (is_digit(ch));
124325963022Sstsp if (ch == '$') {
124425963022Sstsp nextarg = n;
124525963022Sstsp goto rflag;
124625963022Sstsp }
124725963022Sstsp goto reswitch;
124825963022Sstsp #ifdef FLOATING_POINT
124925963022Sstsp case 'L':
125025963022Sstsp flags |= LONGDBL;
125125963022Sstsp goto rflag;
125225963022Sstsp #endif
125325963022Sstsp case 'h':
125425963022Sstsp if (*fmt == 'h') {
125525963022Sstsp fmt++;
125625963022Sstsp flags |= CHARINT;
125725963022Sstsp } else {
125825963022Sstsp flags |= SHORTINT;
125925963022Sstsp }
126025963022Sstsp goto rflag;
126125963022Sstsp case 'l':
126225963022Sstsp if (*fmt == 'l') {
126325963022Sstsp fmt++;
126425963022Sstsp flags |= LLONGINT;
126525963022Sstsp } else {
126625963022Sstsp flags |= LONGINT;
126725963022Sstsp }
126825963022Sstsp goto rflag;
126925963022Sstsp case 'q':
127025963022Sstsp flags |= LLONGINT;
127125963022Sstsp goto rflag;
127225963022Sstsp case 't':
127325963022Sstsp flags |= PTRINT;
127425963022Sstsp goto rflag;
127525963022Sstsp case 'z':
127625963022Sstsp flags |= SIZEINT;
127725963022Sstsp goto rflag;
127825963022Sstsp case 'C':
127925963022Sstsp flags |= LONGINT;
128025963022Sstsp /*FALLTHROUGH*/
128125963022Sstsp case 'c':
128225963022Sstsp if (flags & LONGINT)
128325963022Sstsp ADDTYPE(T_WINT);
128425963022Sstsp else
128525963022Sstsp ADDTYPE(T_INT);
128625963022Sstsp break;
128725963022Sstsp case 'D':
128825963022Sstsp flags |= LONGINT;
128925963022Sstsp /*FALLTHROUGH*/
129025963022Sstsp case 'd':
129125963022Sstsp case 'i':
129225963022Sstsp ADDSARG();
129325963022Sstsp break;
129425963022Sstsp #ifdef FLOATING_POINT
129525963022Sstsp case 'a':
129625963022Sstsp case 'A':
129725963022Sstsp case 'e':
129825963022Sstsp case 'E':
129925963022Sstsp case 'f':
130025963022Sstsp case 'F':
130125963022Sstsp case 'g':
130225963022Sstsp case 'G':
130325963022Sstsp if (flags & LONGDBL)
130425963022Sstsp ADDTYPE(T_LONG_DOUBLE);
130525963022Sstsp else
130625963022Sstsp ADDTYPE(T_DOUBLE);
130725963022Sstsp break;
130825963022Sstsp #endif /* FLOATING_POINT */
130925963022Sstsp case 'n':
131025963022Sstsp if (flags & LLONGINT)
131125963022Sstsp ADDTYPE(TP_LLONG);
131225963022Sstsp else if (flags & LONGINT)
131325963022Sstsp ADDTYPE(TP_LONG);
131425963022Sstsp else if (flags & SHORTINT)
131525963022Sstsp ADDTYPE(TP_SHORT);
131625963022Sstsp else if (flags & PTRINT)
131725963022Sstsp ADDTYPE(TP_PTRINT);
131825963022Sstsp else if (flags & SIZEINT)
131925963022Sstsp ADDTYPE(TP_SSIZEINT);
132025963022Sstsp else if (flags & MAXINT)
132125963022Sstsp ADDTYPE(TP_MAXINT);
132225963022Sstsp else
132325963022Sstsp ADDTYPE(TP_INT);
132425963022Sstsp continue; /* no output */
132525963022Sstsp case 'O':
132625963022Sstsp flags |= LONGINT;
132725963022Sstsp /*FALLTHROUGH*/
132825963022Sstsp case 'o':
132925963022Sstsp ADDUARG();
133025963022Sstsp break;
133125963022Sstsp case 'p':
133225963022Sstsp ADDTYPE(TP_VOID);
133325963022Sstsp break;
133425963022Sstsp case 'S':
133525963022Sstsp flags |= LONGINT;
133625963022Sstsp /*FALLTHROUGH*/
133725963022Sstsp case 's':
133825963022Sstsp if (flags & LONGINT)
133925963022Sstsp ADDTYPE(TP_CHAR);
134025963022Sstsp else
134125963022Sstsp ADDTYPE(TP_WCHAR);
134225963022Sstsp break;
134325963022Sstsp case 'U':
134425963022Sstsp flags |= LONGINT;
134525963022Sstsp /*FALLTHROUGH*/
134625963022Sstsp case 'u':
134725963022Sstsp case 'X':
134825963022Sstsp case 'x':
134925963022Sstsp ADDUARG();
135025963022Sstsp break;
135125963022Sstsp default: /* "%?" prints ?, unless ? is NUL */
135225963022Sstsp if (ch == '\0')
135325963022Sstsp goto done;
135425963022Sstsp break;
135525963022Sstsp }
135625963022Sstsp }
135725963022Sstsp done:
135825963022Sstsp /*
135925963022Sstsp * Build the argument table.
136025963022Sstsp */
136125963022Sstsp if (tablemax >= STATIC_ARG_TBL_SIZE) {
136225963022Sstsp *argtablesiz = sizeof(union arg) * (tablemax + 1);
136325963022Sstsp *argtable = mmap(NULL, *argtablesiz,
136425963022Sstsp PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0);
136525963022Sstsp if (*argtable == MAP_FAILED)
136625963022Sstsp return (-1);
136725963022Sstsp }
136825963022Sstsp
136925963022Sstsp #if 0
137025963022Sstsp /* XXX is this required? */
137125963022Sstsp (*argtable)[0].intarg = 0;
137225963022Sstsp #endif
137325963022Sstsp for (n = 1; n <= tablemax; n++) {
137425963022Sstsp switch (typetable[n]) {
137525963022Sstsp case T_UNUSED:
137625963022Sstsp case T_CHAR:
137725963022Sstsp case T_U_CHAR:
137825963022Sstsp case T_SHORT:
137925963022Sstsp case T_U_SHORT:
138025963022Sstsp case T_INT:
138125963022Sstsp (*argtable)[n].intarg = va_arg(ap, int);
138225963022Sstsp break;
138325963022Sstsp case TP_SHORT:
138425963022Sstsp (*argtable)[n].pshortarg = va_arg(ap, short *);
138525963022Sstsp break;
138625963022Sstsp case T_U_INT:
138725963022Sstsp (*argtable)[n].uintarg = va_arg(ap, unsigned int);
138825963022Sstsp break;
138925963022Sstsp case TP_INT:
139025963022Sstsp (*argtable)[n].pintarg = va_arg(ap, int *);
139125963022Sstsp break;
139225963022Sstsp case T_LONG:
139325963022Sstsp (*argtable)[n].longarg = va_arg(ap, long);
139425963022Sstsp break;
139525963022Sstsp case T_U_LONG:
139625963022Sstsp (*argtable)[n].ulongarg = va_arg(ap, unsigned long);
139725963022Sstsp break;
139825963022Sstsp case TP_LONG:
139925963022Sstsp (*argtable)[n].plongarg = va_arg(ap, long *);
140025963022Sstsp break;
140125963022Sstsp case T_LLONG:
140225963022Sstsp (*argtable)[n].longlongarg = va_arg(ap, long long);
140325963022Sstsp break;
140425963022Sstsp case T_U_LLONG:
140525963022Sstsp (*argtable)[n].ulonglongarg = va_arg(ap, unsigned long long);
140625963022Sstsp break;
140725963022Sstsp case TP_LLONG:
140825963022Sstsp (*argtable)[n].plonglongarg = va_arg(ap, long long *);
140925963022Sstsp break;
141025963022Sstsp #ifdef FLOATING_POINT
141125963022Sstsp case T_DOUBLE:
141225963022Sstsp (*argtable)[n].doublearg = va_arg(ap, double);
141325963022Sstsp break;
141425963022Sstsp case T_LONG_DOUBLE:
141525963022Sstsp (*argtable)[n].longdoublearg = va_arg(ap, long double);
141625963022Sstsp break;
141725963022Sstsp #endif
141825963022Sstsp case TP_CHAR:
141925963022Sstsp (*argtable)[n].pchararg = va_arg(ap, char *);
142025963022Sstsp break;
142125963022Sstsp case TP_VOID:
142225963022Sstsp (*argtable)[n].pvoidarg = va_arg(ap, void *);
142325963022Sstsp break;
142425963022Sstsp case T_PTRINT:
142525963022Sstsp (*argtable)[n].ptrdiffarg = va_arg(ap, ptrdiff_t);
142625963022Sstsp break;
142725963022Sstsp case TP_PTRINT:
142825963022Sstsp (*argtable)[n].pptrdiffarg = va_arg(ap, ptrdiff_t *);
142925963022Sstsp break;
143025963022Sstsp case T_SIZEINT:
143125963022Sstsp (*argtable)[n].sizearg = va_arg(ap, size_t);
143225963022Sstsp break;
143325963022Sstsp case T_SSIZEINT:
143425963022Sstsp (*argtable)[n].ssizearg = va_arg(ap, ssize_t);
143525963022Sstsp break;
143625963022Sstsp case TP_SSIZEINT:
143725963022Sstsp (*argtable)[n].pssizearg = va_arg(ap, ssize_t *);
143825963022Sstsp break;
143925963022Sstsp case TP_MAXINT:
144025963022Sstsp (*argtable)[n].intmaxarg = va_arg(ap, intmax_t);
144125963022Sstsp break;
144225963022Sstsp case T_WINT:
144325963022Sstsp (*argtable)[n].wintarg = va_arg(ap, wint_t);
144425963022Sstsp break;
144525963022Sstsp case TP_WCHAR:
144625963022Sstsp (*argtable)[n].pwchararg = va_arg(ap, wchar_t *);
144725963022Sstsp break;
144825963022Sstsp }
144925963022Sstsp }
145025963022Sstsp goto finish;
145125963022Sstsp
145225963022Sstsp overflow:
145325963022Sstsp errno = ENOMEM;
145425963022Sstsp ret = -1;
145525963022Sstsp
145625963022Sstsp finish:
145725963022Sstsp if (typetable != NULL && typetable != stattypetable) {
145825963022Sstsp munmap(typetable, *argtablesiz);
145925963022Sstsp typetable = NULL;
146025963022Sstsp }
146125963022Sstsp return (ret);
146225963022Sstsp }
146325963022Sstsp
146425963022Sstsp /*
146525963022Sstsp * Increase the size of the type table.
146625963022Sstsp */
146725963022Sstsp static int
__grow_type_table(unsigned char ** typetable,int * tablesize)146825963022Sstsp __grow_type_table(unsigned char **typetable, int *tablesize)
146925963022Sstsp {
147025963022Sstsp unsigned char *oldtable = *typetable;
147125963022Sstsp int newsize = *tablesize * 2;
147225963022Sstsp
147325963022Sstsp if (newsize < getpagesize())
147425963022Sstsp newsize = getpagesize();
147525963022Sstsp
147625963022Sstsp if (*tablesize == STATIC_ARG_TBL_SIZE) {
147725963022Sstsp *typetable = mmap(NULL, newsize, PROT_WRITE|PROT_READ,
147825963022Sstsp MAP_ANON|MAP_PRIVATE, -1, 0);
147925963022Sstsp if (*typetable == MAP_FAILED)
148025963022Sstsp return (-1);
148125963022Sstsp bcopy(oldtable, *typetable, *tablesize);
148225963022Sstsp } else {
148325963022Sstsp unsigned char *new = mmap(NULL, newsize, PROT_WRITE|PROT_READ,
148425963022Sstsp MAP_ANON|MAP_PRIVATE, -1, 0);
148525963022Sstsp if (new == MAP_FAILED)
148625963022Sstsp return (-1);
148725963022Sstsp memmove(new, *typetable, *tablesize);
148825963022Sstsp munmap(*typetable, *tablesize);
148925963022Sstsp *typetable = new;
149025963022Sstsp }
149125963022Sstsp memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize));
149225963022Sstsp
149325963022Sstsp *tablesize = newsize;
149425963022Sstsp return (0);
149525963022Sstsp }
149625963022Sstsp
149725963022Sstsp
149825963022Sstsp #ifdef FLOATING_POINT
149925963022Sstsp static int
exponent(wchar_t * p0,int exp,int fmtch)150025963022Sstsp exponent(wchar_t *p0, int exp, int fmtch)
150125963022Sstsp {
150225963022Sstsp wchar_t *p, *t;
150325963022Sstsp wchar_t expbuf[MAXEXPDIG];
150425963022Sstsp
150525963022Sstsp p = p0;
150625963022Sstsp *p++ = fmtch;
150725963022Sstsp if (exp < 0) {
150825963022Sstsp exp = -exp;
150925963022Sstsp *p++ = '-';
151025963022Sstsp } else
151125963022Sstsp *p++ = '+';
151225963022Sstsp t = expbuf + MAXEXPDIG;
151325963022Sstsp if (exp > 9) {
151425963022Sstsp do {
151525963022Sstsp *--t = to_char(exp % 10);
151625963022Sstsp } while ((exp /= 10) > 9);
151725963022Sstsp *--t = to_char(exp);
151825963022Sstsp for (; t < expbuf + MAXEXPDIG; *p++ = *t++)
151925963022Sstsp /* nothing */;
152025963022Sstsp } else {
152125963022Sstsp /*
152225963022Sstsp * Exponents for decimal floating point conversions
152325963022Sstsp * (%[eEgG]) must be at least two characters long,
152425963022Sstsp * whereas exponents for hexadecimal conversions can
152525963022Sstsp * be only one character long.
152625963022Sstsp */
152725963022Sstsp if (fmtch == 'e' || fmtch == 'E')
152825963022Sstsp *p++ = '0';
152925963022Sstsp *p++ = to_char(exp);
153025963022Sstsp }
153125963022Sstsp return (p - p0);
153225963022Sstsp }
153325963022Sstsp #endif /* FLOATING_POINT */
1534