xref: /openbsd-src/lib/libc/stdio/vfwprintf.c (revision fc99cf9338942ecd9adc94ea08bf6188f0428c15)
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