vfprintf.c revision 128550
11573Srgrimes/*-
21573Srgrimes * Copyright (c) 1990, 1993
31573Srgrimes *	The Regents of the University of California.  All rights reserved.
41573Srgrimes *
51573Srgrimes * This code is derived from software contributed to Berkeley by
61573Srgrimes * Chris Torek.
71573Srgrimes *
81573Srgrimes * Redistribution and use in source and binary forms, with or without
91573Srgrimes * modification, are permitted provided that the following conditions
101573Srgrimes * are met:
111573Srgrimes * 1. Redistributions of source code must retain the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer.
131573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141573Srgrimes *    notice, this list of conditions and the following disclaimer in the
151573Srgrimes *    documentation and/or other materials provided with the distribution.
161573Srgrimes * 3. All advertising materials mentioning features or use of this software
171573Srgrimes *    must display the following acknowledgement:
181573Srgrimes *	This product includes software developed by the University of
191573Srgrimes *	California, Berkeley and its contributors.
201573Srgrimes * 4. Neither the name of the University nor the names of its contributors
211573Srgrimes *    may be used to endorse or promote products derived from this software
221573Srgrimes *    without specific prior written permission.
231573Srgrimes *
241573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
251573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
261573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
271573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
281573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
291573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
301573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
311573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
321573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
331573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
341573Srgrimes * SUCH DAMAGE.
351573Srgrimes */
361573Srgrimes
371573Srgrimes#if defined(LIBC_SCCS) && !defined(lint)
381573Srgrimesstatic char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 6/4/93";
391573Srgrimes#endif /* LIBC_SCCS and not lint */
4092986Sobrien#include <sys/cdefs.h>
4192986Sobrien__FBSDID("$FreeBSD: head/lib/libc/stdio/vfprintf.c 128550 2004-04-22 11:35:12Z tjr $");
421573Srgrimes
431573Srgrimes/*
441573Srgrimes * Actual printf innards.
451573Srgrimes *
461573Srgrimes * This code is large and complicated...
471573Srgrimes */
481573Srgrimes
4971579Sdeischen#include "namespace.h"
501573Srgrimes#include <sys/types.h>
511573Srgrimes
5287113Sfenner#include <ctype.h>
531573Srgrimes#include <limits.h>
5487490Sphantom#include <locale.h>
5587113Sfenner#include <stddef.h>
5687113Sfenner#include <stdint.h>
571573Srgrimes#include <stdio.h>
581573Srgrimes#include <stdlib.h>
591573Srgrimes#include <string.h>
60103633Stjr#include <wchar.h>
611573Srgrimes
621573Srgrimes#include <stdarg.h>
6371579Sdeischen#include "un-namespace.h"
641573Srgrimes
6571579Sdeischen#include "libc_private.h"
661573Srgrimes#include "local.h"
671573Srgrimes#include "fvwrite.h"
681573Srgrimes
69124667Sdas/* Define FLOATING_POINT to get floating point, HEXFLOAT to get %a. */
701573Srgrimes#define	FLOATING_POINT
71124667Sdas#define	HEXFLOAT
721573Srgrimes
7384922Sdfrunion arg {
7484962Sbde	int	intarg;
7584962Sbde	u_int	uintarg;
7684962Sbde	long	longarg;
7784962Sbde	u_long	ulongarg;
7887113Sfenner	long long longlongarg;
7987113Sfenner	unsigned long long ulonglongarg;
8087113Sfenner	ptrdiff_t ptrdiffarg;
8187113Sfenner	size_t	sizearg;
8287113Sfenner	intmax_t intmaxarg;
8387113Sfenner	uintmax_t uintmaxarg;
8484962Sbde	void	*pvoidarg;
8584962Sbde	char	*pchararg;
8687113Sfenner	signed char *pschararg;
8784962Sbde	short	*pshortarg;
8884962Sbde	int	*pintarg;
8984962Sbde	long	*plongarg;
9087113Sfenner	long long *plonglongarg;
9187113Sfenner	ptrdiff_t *pptrdiffarg;
9287113Sfenner	size_t	*psizearg;
9387113Sfenner	intmax_t *pintmaxarg;
9484922Sdfr#ifdef FLOATING_POINT
9584962Sbde	double	doublearg;
9684962Sbde	long double longdoublearg;
9784922Sdfr#endif
98103633Stjr	wint_t	wintarg;
99103633Stjr	wchar_t	*pwchararg;
10084922Sdfr};
10184922Sdfr
10287113Sfenner/*
10387113Sfenner * Type ids for argument type table.
10487113Sfenner */
10587113Sfennerenum typeid {
10687113Sfenner	T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT,
10787113Sfenner	T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG,
10887113Sfenner	T_PTRDIFFT, TP_PTRDIFFT, T_SIZET, TP_SIZET,
10987113Sfenner	T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR,
110103633Stjr	T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR
11187113Sfenner};
11287113Sfenner
11392905Sobrienstatic int	__sprint(FILE *, struct __suio *);
11492941Sobrienstatic int	__sbprintf(FILE *, const char *, va_list) __printflike(2, 0);
115113146Sdasstatic char	*__ujtoa(uintmax_t, char *, int, int, const char *, int, char,
11692941Sobrien		    const char *);
117113146Sdasstatic char	*__ultoa(u_long, char *, int, int, const char *, int, char,
11892941Sobrien		    const char *);
119103633Stjrstatic char	*__wcsconv(wchar_t *, int);
12092905Sobrienstatic void	__find_arguments(const char *, va_list, union arg **);
12192905Sobrienstatic void	__grow_type_table(int, enum typeid **, int *);
12216586Sjraynard
1231573Srgrimes/*
1241573Srgrimes * Flush out all the vectors defined by the given uio,
1251573Srgrimes * then reset it so that it can be reused.
1261573Srgrimes */
1271573Srgrimesstatic int
12871579Sdeischen__sprint(FILE *fp, struct __suio *uio)
1291573Srgrimes{
13071579Sdeischen	int err;
1311573Srgrimes
1321573Srgrimes	if (uio->uio_resid == 0) {
1331573Srgrimes		uio->uio_iovcnt = 0;
1341573Srgrimes		return (0);
1351573Srgrimes	}
1361573Srgrimes	err = __sfvwrite(fp, uio);
1371573Srgrimes	uio->uio_resid = 0;
1381573Srgrimes	uio->uio_iovcnt = 0;
1391573Srgrimes	return (err);
1401573Srgrimes}
1411573Srgrimes
1421573Srgrimes/*
1431573Srgrimes * Helper function for `fprintf to unbuffered unix file': creates a
1441573Srgrimes * temporary buffer.  We only work on write-only files; this avoids
1451573Srgrimes * worries about ungetc buffers and so forth.
1461573Srgrimes */
1471573Srgrimesstatic int
14871579Sdeischen__sbprintf(FILE *fp, const char *fmt, va_list ap)
1491573Srgrimes{
1501573Srgrimes	int ret;
1511573Srgrimes	FILE fake;
1521573Srgrimes	unsigned char buf[BUFSIZ];
1531573Srgrimes
1541573Srgrimes	/* copy the important variables */
1551573Srgrimes	fake._flags = fp->_flags & ~__SNBF;
1561573Srgrimes	fake._file = fp->_file;
1571573Srgrimes	fake._cookie = fp->_cookie;
1581573Srgrimes	fake._write = fp->_write;
159101776Stjr	fake._extra = fp->_extra;
1601573Srgrimes
1611573Srgrimes	/* set up the buffer */
1621573Srgrimes	fake._bf._base = fake._p = buf;
1631573Srgrimes	fake._bf._size = fake._w = sizeof(buf);
1641573Srgrimes	fake._lbfsize = 0;	/* not actually used, but Just In Case */
1651573Srgrimes
1661573Srgrimes	/* do the work, then copy any error status */
16771579Sdeischen	ret = __vfprintf(&fake, fmt, ap);
16871579Sdeischen	if (ret >= 0 && __fflush(&fake))
1691573Srgrimes		ret = EOF;
1701573Srgrimes	if (fake._flags & __SERR)
1711573Srgrimes		fp->_flags |= __SERR;
1721573Srgrimes	return (ret);
1731573Srgrimes}
1741573Srgrimes
1751573Srgrimes/*
1761573Srgrimes * Macros for converting digits to letters and vice versa
1771573Srgrimes */
1781573Srgrimes#define	to_digit(c)	((c) - '0')
1791573Srgrimes#define is_digit(c)	((unsigned)to_digit(c) <= 9)
1801573Srgrimes#define	to_char(n)	((n) + '0')
1811573Srgrimes
1821573Srgrimes/*
1831573Srgrimes * Convert an unsigned long to ASCII for printf purposes, returning
1841573Srgrimes * a pointer to the first character of the string representation.
1851573Srgrimes * Octal numbers can be forced to have a leading zero; hex numbers
1861573Srgrimes * use the given digits.
1871573Srgrimes */
1881573Srgrimesstatic char *
189113146Sdas__ultoa(u_long val, char *endp, int base, int octzero, const char *xdigs,
19087815Sphantom	int needgrp, char thousep, const char *grp)
1911573Srgrimes{
19292889Sobrien	char *cp = endp;
19392889Sobrien	long sval;
19487113Sfenner	int ndig;
1951573Srgrimes
1961573Srgrimes	/*
1971573Srgrimes	 * Handle the three cases separately, in the hope of getting
1981573Srgrimes	 * better/faster code.
1991573Srgrimes	 */
2001573Srgrimes	switch (base) {
2011573Srgrimes	case 10:
2021573Srgrimes		if (val < 10) {	/* many numbers are 1 digit */
2031573Srgrimes			*--cp = to_char(val);
2041573Srgrimes			return (cp);
2051573Srgrimes		}
20687113Sfenner		ndig = 0;
2071573Srgrimes		/*
2081573Srgrimes		 * On many machines, unsigned arithmetic is harder than
2091573Srgrimes		 * signed arithmetic, so we do at most one unsigned mod and
2101573Srgrimes		 * divide; this is sufficient to reduce the range of
2111573Srgrimes		 * the incoming value to where signed arithmetic works.
2121573Srgrimes		 */
2131573Srgrimes		if (val > LONG_MAX) {
2141573Srgrimes			*--cp = to_char(val % 10);
21587113Sfenner			ndig++;
2161573Srgrimes			sval = val / 10;
2171573Srgrimes		} else
2181573Srgrimes			sval = val;
2191573Srgrimes		do {
2201573Srgrimes			*--cp = to_char(sval % 10);
22187815Sphantom			ndig++;
22287815Sphantom			/*
22387815Sphantom			 * If (*grp == CHAR_MAX) then no more grouping
22487815Sphantom			 * should be performed.
22587815Sphantom			 */
22687818Sphantom			if (needgrp && ndig == *grp && *grp != CHAR_MAX
22787818Sphantom					&& sval > 9) {
22887815Sphantom				*--cp = thousep;
22987113Sfenner				ndig = 0;
23087815Sphantom				/*
23187815Sphantom				 * If (*(grp+1) == '\0') then we have to
23287815Sphantom				 * use *grp character (last grouping rule)
23387815Sphantom				 * for all next cases
23487815Sphantom				 */
23588057Sphantom				if (*(grp+1) != '\0')
23688057Sphantom					grp++;
23787113Sfenner			}
2381573Srgrimes			sval /= 10;
2391573Srgrimes		} while (sval != 0);
2401573Srgrimes		break;
2411573Srgrimes
2421573Srgrimes	case 8:
2431573Srgrimes		do {
2441573Srgrimes			*--cp = to_char(val & 7);
2451573Srgrimes			val >>= 3;
2461573Srgrimes		} while (val);
2471573Srgrimes		if (octzero && *cp != '0')
2481573Srgrimes			*--cp = '0';
2491573Srgrimes		break;
2501573Srgrimes
2511573Srgrimes	case 16:
2521573Srgrimes		do {
2531573Srgrimes			*--cp = xdigs[val & 15];
2541573Srgrimes			val >>= 4;
2551573Srgrimes		} while (val);
2561573Srgrimes		break;
2571573Srgrimes
2581573Srgrimes	default:			/* oops */
2591573Srgrimes		abort();
2601573Srgrimes	}
2611573Srgrimes	return (cp);
2621573Srgrimes}
2631573Srgrimes
26487113Sfenner/* Identical to __ultoa, but for intmax_t. */
2651573Srgrimesstatic char *
266113146Sdas__ujtoa(uintmax_t val, char *endp, int base, int octzero, const char *xdigs,
26787815Sphantom	int needgrp, char thousep, const char *grp)
2681573Srgrimes{
26971579Sdeischen	char *cp = endp;
27087113Sfenner	intmax_t sval;
27187113Sfenner	int ndig;
2721573Srgrimes
2731573Srgrimes	/* quick test for small values; __ultoa is typically much faster */
2741573Srgrimes	/* (perhaps instead we should run until small, then call __ultoa?) */
2751573Srgrimes	if (val <= ULONG_MAX)
27687113Sfenner		return (__ultoa((u_long)val, endp, base, octzero, xdigs,
27787815Sphantom		    needgrp, thousep, grp));
2781573Srgrimes	switch (base) {
2791573Srgrimes	case 10:
2801573Srgrimes		if (val < 10) {
2811573Srgrimes			*--cp = to_char(val % 10);
2821573Srgrimes			return (cp);
2831573Srgrimes		}
28487113Sfenner		ndig = 0;
28587113Sfenner		if (val > INTMAX_MAX) {
2861573Srgrimes			*--cp = to_char(val % 10);
28787113Sfenner			ndig++;
2881573Srgrimes			sval = val / 10;
2891573Srgrimes		} else
2901573Srgrimes			sval = val;
2911573Srgrimes		do {
2921573Srgrimes			*--cp = to_char(sval % 10);
29387815Sphantom			ndig++;
29487815Sphantom			/*
29587815Sphantom			 * If (*grp == CHAR_MAX) then no more grouping
29687815Sphantom			 * should be performed.
29787815Sphantom			 */
29887818Sphantom			if (needgrp && *grp != CHAR_MAX && ndig == *grp
29987818Sphantom					&& sval > 9) {
30087815Sphantom				*--cp = thousep;
30187113Sfenner				ndig = 0;
30287815Sphantom				/*
30387815Sphantom				 * If (*(grp+1) == '\0') then we have to
30487815Sphantom				 * use *grp character (last grouping rule)
30587815Sphantom				 * for all next cases
30687815Sphantom				 */
30788057Sphantom				if (*(grp+1) != '\0')
30888057Sphantom					grp++;
30988057Sphantom			}
3101573Srgrimes			sval /= 10;
3111573Srgrimes		} while (sval != 0);
3121573Srgrimes		break;
3131573Srgrimes
3141573Srgrimes	case 8:
3151573Srgrimes		do {
3161573Srgrimes			*--cp = to_char(val & 7);
3171573Srgrimes			val >>= 3;
3181573Srgrimes		} while (val);
3191573Srgrimes		if (octzero && *cp != '0')
3201573Srgrimes			*--cp = '0';
3211573Srgrimes		break;
3221573Srgrimes
3231573Srgrimes	case 16:
3241573Srgrimes		do {
3251573Srgrimes			*--cp = xdigs[val & 15];
3261573Srgrimes			val >>= 4;
3271573Srgrimes		} while (val);
3281573Srgrimes		break;
3291573Srgrimes
3301573Srgrimes	default:
3311573Srgrimes		abort();
3321573Srgrimes	}
3331573Srgrimes	return (cp);
3341573Srgrimes}
3351573Srgrimes
33671579Sdeischen/*
337103633Stjr * Convert a wide character string argument for the %ls format to a multibyte
338103633Stjr * string representation. ``prec'' specifies the maximum number of bytes
339103633Stjr * to output. If ``prec'' is greater than or equal to zero, we can't assume
340103633Stjr * that the wide char. string ends in a null character.
341103633Stjr */
342103633Stjrstatic char *
343103633Stjr__wcsconv(wchar_t *wcsarg, int prec)
344103633Stjr{
345128002Stjr	static const mbstate_t initial;
346128002Stjr	mbstate_t mbs;
347103633Stjr	char buf[MB_LEN_MAX];
348103633Stjr	wchar_t *p;
349103633Stjr	char *convbuf, *mbp;
350103633Stjr	size_t clen, nbytes;
351103633Stjr
352103633Stjr	/*
353103633Stjr	 * Determine the number of bytes to output and allocate space for
354103633Stjr	 * the output.
355103633Stjr	 */
356103633Stjr	if (prec >= 0) {
357103633Stjr		nbytes = 0;
358103633Stjr		p = wcsarg;
359128002Stjr		mbs = initial;
360103633Stjr		for (;;) {
361128002Stjr			clen = wcrtomb(buf, *p++, &mbs);
362103633Stjr			if (clen == 0 || clen == (size_t)-1 ||
363103633Stjr			    nbytes + clen > prec)
364103633Stjr				break;
365103633Stjr			nbytes += clen;
366103633Stjr		}
367103633Stjr	} else {
368103633Stjr		p = wcsarg;
369128002Stjr		mbs = initial;
370128002Stjr		nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
371103633Stjr		if (nbytes == (size_t)-1)
372103633Stjr			return (NULL);
373103633Stjr	}
374103633Stjr	if ((convbuf = malloc(nbytes + 1)) == NULL)
375103633Stjr		return (NULL);
376103633Stjr
377103633Stjr	/*
378103633Stjr	 * Fill the output buffer with the multibyte representations of as
379103633Stjr	 * many wide characters as will fit.
380103633Stjr	 */
381103633Stjr	mbp = convbuf;
382103633Stjr	p = wcsarg;
383128002Stjr	mbs = initial;
384103633Stjr	while (mbp - convbuf < nbytes) {
385128002Stjr		clen = wcrtomb(mbp, *p++, &mbs);
386103633Stjr		if (clen == 0 || clen == (size_t)-1)
387103633Stjr			break;
388103633Stjr		mbp += clen;
389103633Stjr	}
390113196Sache	if (clen == (size_t)-1) {
391113196Sache		free(convbuf);
392113196Sache		return (NULL);
393113196Sache	}
394103633Stjr	*mbp = '\0';
395103633Stjr
396103633Stjr	return (convbuf);
397103633Stjr}
398103633Stjr
399103633Stjr/*
40071579Sdeischen * MT-safe version
40171579Sdeischen */
40271579Sdeischenint
403103012Stjrvfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap)
404101914Srobert
40571579Sdeischen{
40671579Sdeischen	int ret;
40771579Sdeischen
40871579Sdeischen	FLOCKFILE(fp);
40971579Sdeischen	ret = __vfprintf(fp, fmt0, ap);
41071579Sdeischen	FUNLOCKFILE(fp);
41171579Sdeischen	return (ret);
41271579Sdeischen}
41371579Sdeischen
4141573Srgrimes#ifdef FLOATING_POINT
415113146Sdas
416113146Sdas#define	dtoa		__dtoa
417113146Sdas#define	freedtoa	__freedtoa
418113146Sdas
419113146Sdas#include <float.h>
4201573Srgrimes#include <math.h>
4211573Srgrimes#include "floatio.h"
422113146Sdas#include "gdtoa.h"
4231573Srgrimes
4241573Srgrimes#define	DEFPREC		6
4251573Srgrimes
42692905Sobrienstatic int exponent(char *, int, int);
4271573Srgrimes
428113142Sdas#endif /* FLOATING_POINT */
4291573Srgrimes
430113142Sdas/*
431113142Sdas * The size of the buffer we use as scratch space for integer
432113142Sdas * conversions, among other things.  Technically, we would need the
433113142Sdas * most space for base 10 conversions with thousands' grouping
434113142Sdas * characters between each pair of digits.  100 bytes is a
435113142Sdas * conservative overestimate even for a 128-bit uintmax_t.
436113142Sdas */
437113142Sdas#define	BUF	100
4381573Srgrimes
43921674Sjkh#define STATIC_ARG_TBL_SIZE 8           /* Size of static argument table. */
4401573Srgrimes
4411573Srgrimes/*
4421573Srgrimes * Flags used during conversion.
4431573Srgrimes */
4441573Srgrimes#define	ALT		0x001		/* alternate form */
4451573Srgrimes#define	LADJUST		0x004		/* left adjustment */
44631871Sbde#define	LONGDBL		0x008		/* long double */
4471573Srgrimes#define	LONGINT		0x010		/* long integer */
44887113Sfenner#define	LLONGINT	0x020		/* long long integer */
4491573Srgrimes#define	SHORTINT	0x040		/* short integer */
4501573Srgrimes#define	ZEROPAD		0x080		/* zero (as opposed to blank) pad */
45188057Sphantom#define	FPT		0x100		/* Floating point number */
45287815Sphantom#define	GROUPING	0x200		/* use grouping ("'" flag) */
45387113Sfenner					/* C99 additional size modifiers: */
45487815Sphantom#define	SIZET		0x400		/* size_t */
45587815Sphantom#define	PTRDIFFT	0x800		/* ptrdiff_t */
45687815Sphantom#define	INTMAXT		0x1000		/* intmax_t */
45787815Sphantom#define	CHARINT		0x2000		/* print char using int format */
45887113Sfenner
45971579Sdeischen/*
46071579Sdeischen * Non-MT-safe version
46171579Sdeischen */
4621573Srgrimesint
46371579Sdeischen__vfprintf(FILE *fp, const char *fmt0, va_list ap)
4641573Srgrimes{
46571579Sdeischen	char *fmt;		/* format string */
46671579Sdeischen	int ch;			/* character from fmt */
46771579Sdeischen	int n, n2;		/* handy integer (short term usage) */
46871579Sdeischen	char *cp;		/* handy char pointer (short term usage) */
46971579Sdeischen	struct __siov *iovp;	/* for PRINT macro */
47071579Sdeischen	int flags;		/* flags as above */
4711573Srgrimes	int ret;		/* return value accumulator */
4721573Srgrimes	int width;		/* width from format (%8d), or 0 */
473113146Sdas	int prec;		/* precision from format; <0 for N/A */
4741573Srgrimes	char sign;		/* sign prefix (' ', '+', '-', or \0) */
47587815Sphantom	char thousands_sep;	/* locale specific thousands separator */
47687815Sphantom	const char *grouping;	/* locale specific numeric grouping rules */
4771573Srgrimes#ifdef FLOATING_POINT
478113146Sdas	/*
479113146Sdas	 * We can decompose the printed representation of floating
480113146Sdas	 * point numbers into several parts, some of which may be empty:
481113146Sdas	 *
482113146Sdas	 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
483113146Sdas	 *    A       B     ---C---      D       E   F
484113146Sdas	 *
485113146Sdas	 * A:	'sign' holds this value if present; '\0' otherwise
486113146Sdas	 * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
487113146Sdas	 * C:	cp points to the string MMMNNN.  Leading and trailing
488113146Sdas	 *	zeros are not in the string and must be added.
489113146Sdas	 * D:	expchar holds this character; '\0' if no exponent, e.g. %f
490113146Sdas	 * F:	at least two digits for decimal, at least one digit for hex
491113146Sdas	 */
49287490Sphantom	char *decimal_point;	/* locale specific decimal point */
493113146Sdas	int signflag;		/* true if float is negative */
494113146Sdas	union {			/* floating point arguments %[aAeEfFgG] */
495113146Sdas		double dbl;
496113146Sdas		long double ldbl;
497113146Sdas	} fparg;
4981573Srgrimes	int expt;		/* integer value of exponent */
499113146Sdas	char expchar;		/* exponent character: [eEpP\0] */
500113146Sdas	char *dtoaend;		/* pointer to end of converted digits */
5011573Srgrimes	int expsize;		/* character count for expstr */
502113146Sdas	int lead;		/* sig figs before decimal or group sep */
503113146Sdas	int ndig;		/* actual number of digits returned by dtoa */
504113146Sdas	char expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */
50572523Stegge	char *dtoaresult;	/* buffer allocated by dtoa */
506113146Sdas	int nseps;		/* number of group separators with ' */
507113146Sdas	int nrepeats;		/* number of repeats of the last group */
5081573Srgrimes#endif
5091573Srgrimes	u_long	ulval;		/* integer arguments %[diouxX] */
51087113Sfenner	uintmax_t ujval;	/* %j, %ll, %q, %t, %z integers */
5111573Srgrimes	int base;		/* base for [diouxX] conversion */
5121573Srgrimes	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
51314727Sfenner	int realsz;		/* field size expanded by dprec, sign, etc */
5141573Srgrimes	int size;		/* size of converted field or string */
51531983Sache	int prsize;             /* max size of printed field */
516113146Sdas	const char *xdigs;     	/* digits for %[xX] conversion */
5171573Srgrimes#define NIOV 8
5181573Srgrimes	struct __suio uio;	/* output information: summary */
5191573Srgrimes	struct __siov iov[NIOV];/* ... and individual io vectors */
520113142Sdas	char buf[BUF];		/* buffer with space for digits of uintmax_t */
521113146Sdas	char ox[2];		/* space for 0x; ox[1] is either x, X, or \0 */
522103399Stjr	union arg *argtable;    /* args, built due to positional arg */
523103399Stjr	union arg statargtable [STATIC_ARG_TBL_SIZE];
524103399Stjr	int nextarg;            /* 1-based argument index */
525103399Stjr	va_list orgap;          /* original argument pointer */
526103633Stjr	char *convbuf;		/* wide to multibyte conversion result */
5271573Srgrimes
5281573Srgrimes	/*
5291573Srgrimes	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
5301573Srgrimes	 * fields occur frequently, increase PADSIZE and make the initialisers
5311573Srgrimes	 * below longer.
5321573Srgrimes	 */
5331573Srgrimes#define	PADSIZE	16		/* pad chunk size */
5341573Srgrimes	static char blanks[PADSIZE] =
5351573Srgrimes	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
5361573Srgrimes	static char zeroes[PADSIZE] =
5371573Srgrimes	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
5381573Srgrimes
539113146Sdas	static const char xdigs_lower[16] = "0123456789abcdef";
540113146Sdas	static const char xdigs_upper[16] = "0123456789ABCDEF";
541113146Sdas
5421573Srgrimes	/*
5431573Srgrimes	 * BEWARE, these `goto error' on error, and PAD uses `n'.
5441573Srgrimes	 */
5451573Srgrimes#define	PRINT(ptr, len) { \
5461573Srgrimes	iovp->iov_base = (ptr); \
5471573Srgrimes	iovp->iov_len = (len); \
5481573Srgrimes	uio.uio_resid += (len); \
5491573Srgrimes	iovp++; \
5501573Srgrimes	if (++uio.uio_iovcnt >= NIOV) { \
5511573Srgrimes		if (__sprint(fp, &uio)) \
5521573Srgrimes			goto error; \
5531573Srgrimes		iovp = iov; \
5541573Srgrimes	} \
5551573Srgrimes}
5561573Srgrimes#define	PAD(howmany, with) { \
5571573Srgrimes	if ((n = (howmany)) > 0) { \
5581573Srgrimes		while (n > PADSIZE) { \
5591573Srgrimes			PRINT(with, PADSIZE); \
5601573Srgrimes			n -= PADSIZE; \
5611573Srgrimes		} \
5621573Srgrimes		PRINT(with, n); \
5631573Srgrimes	} \
5641573Srgrimes}
565113191Sdas#define	PRINTANDPAD(p, ep, len, with) do {	\
566113191Sdas	n2 = (ep) - (p);       			\
567113191Sdas	if (n2 > (len))				\
568113191Sdas		n2 = (len);			\
569113191Sdas	if (n2 > 0)				\
570113191Sdas		PRINT((p), n2);			\
571113191Sdas	PAD((len) - (n2 > 0 ? n2 : 0), (with));	\
572113191Sdas} while(0)
5731573Srgrimes#define	FLUSH() { \
5741573Srgrimes	if (uio.uio_resid && __sprint(fp, &uio)) \
5751573Srgrimes		goto error; \
5761573Srgrimes	uio.uio_iovcnt = 0; \
5771573Srgrimes	iovp = iov; \
5781573Srgrimes}
5791573Srgrimes
580103399Stjr	/*
581103399Stjr	 * Get the argument indexed by nextarg.   If the argument table is
582103399Stjr	 * built, use it to get the argument.  If its not, get the next
583103399Stjr	 * argument (and arguments must be gotten sequentially).
584103399Stjr	 */
58521674Sjkh#define GETARG(type) \
586103399Stjr	((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
587103399Stjr	    (nextarg++, va_arg(ap, type)))
58821674Sjkh
5891573Srgrimes	/*
5901573Srgrimes	 * To extend shorts properly, we need both signed and unsigned
5911573Srgrimes	 * argument extraction methods.
5921573Srgrimes	 */
5931573Srgrimes#define	SARG() \
59421674Sjkh	(flags&LONGINT ? GETARG(long) : \
59521674Sjkh	    flags&SHORTINT ? (long)(short)GETARG(int) : \
59687113Sfenner	    flags&CHARINT ? (long)(signed char)GETARG(int) : \
59721674Sjkh	    (long)GETARG(int))
5981573Srgrimes#define	UARG() \
59921674Sjkh	(flags&LONGINT ? GETARG(u_long) : \
60021674Sjkh	    flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
60187113Sfenner	    flags&CHARINT ? (u_long)(u_char)GETARG(int) : \
60221674Sjkh	    (u_long)GETARG(u_int))
60387113Sfenner#define	INTMAX_SIZE	(INTMAXT|SIZET|PTRDIFFT|LLONGINT)
60487113Sfenner#define SJARG() \
60587113Sfenner	(flags&INTMAXT ? GETARG(intmax_t) : \
60687113Sfenner	    flags&SIZET ? (intmax_t)GETARG(size_t) : \
60787113Sfenner	    flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \
60887113Sfenner	    (intmax_t)GETARG(long long))
60987113Sfenner#define	UJARG() \
61087113Sfenner	(flags&INTMAXT ? GETARG(uintmax_t) : \
61187113Sfenner	    flags&SIZET ? (uintmax_t)GETARG(size_t) : \
61287113Sfenner	    flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \
61387113Sfenner	    (uintmax_t)GETARG(unsigned long long))
6141573Srgrimes
615103399Stjr	/*
616103399Stjr	 * Get * arguments, including the form *nn$.  Preserve the nextarg
617103399Stjr	 * that the argument can be gotten once the type is determined.
618103399Stjr	 */
61921674Sjkh#define GETASTER(val) \
620103399Stjr	n2 = 0; \
621103399Stjr	cp = fmt; \
622103399Stjr	while (is_digit(*cp)) { \
623103399Stjr		n2 = 10 * n2 + to_digit(*cp); \
624103399Stjr		cp++; \
625103399Stjr	} \
626103399Stjr	if (*cp == '$') { \
627103399Stjr		int hold = nextarg; \
628103399Stjr		if (argtable == NULL) { \
629103399Stjr			argtable = statargtable; \
630103399Stjr			__find_arguments (fmt0, orgap, &argtable); \
631103399Stjr		} \
632103399Stjr		nextarg = n2; \
63321674Sjkh		val = GETARG (int); \
634103399Stjr		nextarg = hold; \
635103399Stjr		fmt = ++cp; \
636103399Stjr	} else { \
637103399Stjr		val = GETARG (int); \
638103399Stjr	}
63987815Sphantom
64088057Sphantom
64187815Sphantom	thousands_sep = '\0';
64287815Sphantom	grouping = NULL;
643103633Stjr	convbuf = NULL;
64472523Stegge#ifdef FLOATING_POINT
64572523Stegge	dtoaresult = NULL;
64687113Sfenner	decimal_point = localeconv()->decimal_point;
64772523Stegge#endif
6481573Srgrimes	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
64971579Sdeischen	if (cantwrite(fp))
6501573Srgrimes		return (EOF);
6511573Srgrimes
6521573Srgrimes	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
6531573Srgrimes	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
65471579Sdeischen	    fp->_file >= 0)
6551573Srgrimes		return (__sbprintf(fp, fmt0, ap));
6561573Srgrimes
6571573Srgrimes	fmt = (char *)fmt0;
658103399Stjr	argtable = NULL;
659103399Stjr	nextarg = 1;
660103876Stjr	va_copy(orgap, ap);
6611573Srgrimes	uio.uio_iov = iovp = iov;
6621573Srgrimes	uio.uio_resid = 0;
6631573Srgrimes	uio.uio_iovcnt = 0;
6641573Srgrimes	ret = 0;
6651573Srgrimes
6661573Srgrimes	/*
6671573Srgrimes	 * Scan the format for conversions (`%' character).
6681573Srgrimes	 */
6691573Srgrimes	for (;;) {
6701573Srgrimes		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
6711573Srgrimes			/* void */;
6721573Srgrimes		if ((n = fmt - cp) != 0) {
67332253Sache			if ((unsigned)ret + n > INT_MAX) {
67431983Sache				ret = EOF;
67531983Sache				goto error;
67631983Sache			}
6771573Srgrimes			PRINT(cp, n);
6781573Srgrimes			ret += n;
6791573Srgrimes		}
6801573Srgrimes		if (ch == '\0')
6811573Srgrimes			goto done;
6821573Srgrimes		fmt++;		/* skip over '%' */
6831573Srgrimes
6841573Srgrimes		flags = 0;
6851573Srgrimes		dprec = 0;
6861573Srgrimes		width = 0;
6871573Srgrimes		prec = -1;
6881573Srgrimes		sign = '\0';
689113146Sdas		ox[1] = '\0';
6901573Srgrimes
6911573Srgrimesrflag:		ch = *fmt++;
6921573Srgrimesreswitch:	switch (ch) {
6931573Srgrimes		case ' ':
69488057Sphantom			/*-
6951573Srgrimes			 * ``If the space and + flags both appear, the space
6961573Srgrimes			 * flag will be ignored.''
6971573Srgrimes			 *	-- ANSI X3J11
6981573Srgrimes			 */
6991573Srgrimes			if (!sign)
7001573Srgrimes				sign = ' ';
7011573Srgrimes			goto rflag;
7021573Srgrimes		case '#':
7031573Srgrimes			flags |= ALT;
7041573Srgrimes			goto rflag;
7051573Srgrimes		case '*':
70688057Sphantom			/*-
7071573Srgrimes			 * ``A negative field width argument is taken as a
7081573Srgrimes			 * - flag followed by a positive field width.''
7091573Srgrimes			 *	-- ANSI X3J11
7101573Srgrimes			 * They don't exclude field widths read from args.
7111573Srgrimes			 */
71221674Sjkh			GETASTER (width);
71321674Sjkh			if (width >= 0)
7141573Srgrimes				goto rflag;
7151573Srgrimes			width = -width;
7161573Srgrimes			/* FALLTHROUGH */
7171573Srgrimes		case '-':
7181573Srgrimes			flags |= LADJUST;
7191573Srgrimes			goto rflag;
7201573Srgrimes		case '+':
7211573Srgrimes			sign = '+';
7221573Srgrimes			goto rflag;
72387113Sfenner		case '\'':
72487815Sphantom			flags |= GROUPING;
72587815Sphantom			thousands_sep = *(localeconv()->thousands_sep);
72687815Sphantom			grouping = localeconv()->grouping;
72787113Sfenner			goto rflag;
7281573Srgrimes		case '.':
7291573Srgrimes			if ((ch = *fmt++) == '*') {
730113191Sdas				GETASTER (prec);
7311573Srgrimes				goto rflag;
7321573Srgrimes			}
733113191Sdas			prec = 0;
7341573Srgrimes			while (is_digit(ch)) {
735113191Sdas				prec = 10 * prec + to_digit(ch);
7361573Srgrimes				ch = *fmt++;
7371573Srgrimes			}
7381573Srgrimes			goto reswitch;
7391573Srgrimes		case '0':
74088057Sphantom			/*-
7411573Srgrimes			 * ``Note that 0 is taken as a flag, not as the
7421573Srgrimes			 * beginning of a field width.''
7431573Srgrimes			 *	-- ANSI X3J11
7441573Srgrimes			 */
7451573Srgrimes			flags |= ZEROPAD;
7461573Srgrimes			goto rflag;
7471573Srgrimes		case '1': case '2': case '3': case '4':
7481573Srgrimes		case '5': case '6': case '7': case '8': case '9':
7491573Srgrimes			n = 0;
7501573Srgrimes			do {
7511573Srgrimes				n = 10 * n + to_digit(ch);
7521573Srgrimes				ch = *fmt++;
7531573Srgrimes			} while (is_digit(ch));
75421674Sjkh			if (ch == '$') {
75521674Sjkh				nextarg = n;
756103399Stjr				if (argtable == NULL) {
757103399Stjr					argtable = statargtable;
758103399Stjr					__find_arguments (fmt0, orgap,
759103399Stjr					    &argtable);
76021674Sjkh				}
76121674Sjkh				goto rflag;
762103399Stjr			}
7631573Srgrimes			width = n;
7641573Srgrimes			goto reswitch;
7651573Srgrimes#ifdef FLOATING_POINT
7661573Srgrimes		case 'L':
7671573Srgrimes			flags |= LONGDBL;
7681573Srgrimes			goto rflag;
7691573Srgrimes#endif
7701573Srgrimes		case 'h':
77187113Sfenner			if (flags & SHORTINT) {
77287113Sfenner				flags &= ~SHORTINT;
77387113Sfenner				flags |= CHARINT;
77487113Sfenner			} else
77587113Sfenner				flags |= SHORTINT;
7761573Srgrimes			goto rflag;
77787113Sfenner		case 'j':
77887113Sfenner			flags |= INTMAXT;
77987113Sfenner			goto rflag;
7801573Srgrimes		case 'l':
78187113Sfenner			if (flags & LONGINT) {
78287113Sfenner				flags &= ~LONGINT;
78387113Sfenner				flags |= LLONGINT;
78487113Sfenner			} else
78544674Sdfr				flags |= LONGINT;
7861573Srgrimes			goto rflag;
7871573Srgrimes		case 'q':
78887113Sfenner			flags |= LLONGINT;	/* not necessarily */
7891573Srgrimes			goto rflag;
79087113Sfenner		case 't':
79187113Sfenner			flags |= PTRDIFFT;
79287113Sfenner			goto rflag;
79387113Sfenner		case 'z':
79487113Sfenner			flags |= SIZET;
79587113Sfenner			goto rflag;
796105204Stjr		case 'C':
797105204Stjr			flags |= LONGINT;
798105204Stjr			/*FALLTHROUGH*/
7991573Srgrimes		case 'c':
800103633Stjr			if (flags & LONGINT) {
801128002Stjr				static const mbstate_t initial;
802128002Stjr				mbstate_t mbs;
803103633Stjr				size_t mbseqlen;
804103633Stjr
805128002Stjr				mbs = initial;
806103633Stjr				mbseqlen = wcrtomb(cp = buf,
807128002Stjr				    (wchar_t)GETARG(wint_t), &mbs);
808105234Stjr				if (mbseqlen == (size_t)-1) {
809105234Stjr					fp->_flags |= __SERR;
810103633Stjr					goto error;
811105234Stjr				}
812103633Stjr				size = (int)mbseqlen;
813103633Stjr			} else {
814103633Stjr				*(cp = buf) = GETARG(int);
815103633Stjr				size = 1;
816103633Stjr			}
8171573Srgrimes			sign = '\0';
8181573Srgrimes			break;
8191573Srgrimes		case 'D':
8201573Srgrimes			flags |= LONGINT;
8211573Srgrimes			/*FALLTHROUGH*/
8221573Srgrimes		case 'd':
8231573Srgrimes		case 'i':
82487113Sfenner			if (flags & INTMAX_SIZE) {
82587113Sfenner				ujval = SJARG();
82687113Sfenner				if ((intmax_t)ujval < 0) {
82787113Sfenner					ujval = -ujval;
8281573Srgrimes					sign = '-';
8291573Srgrimes				}
8301573Srgrimes			} else {
8311573Srgrimes				ulval = SARG();
8321573Srgrimes				if ((long)ulval < 0) {
8331573Srgrimes					ulval = -ulval;
8341573Srgrimes					sign = '-';
8351573Srgrimes				}
8361573Srgrimes			}
8371573Srgrimes			base = 10;
8381573Srgrimes			goto number;
8391573Srgrimes#ifdef FLOATING_POINT
84087113Sfenner#ifdef HEXFLOAT
84187113Sfenner		case 'a':
84287113Sfenner		case 'A':
843113146Sdas			if (ch == 'a') {
844113146Sdas				ox[1] = 'x';
845113146Sdas				xdigs = xdigs_lower;
846113146Sdas				expchar = 'p';
847113146Sdas			} else {
848113146Sdas				ox[1] = 'X';
849113146Sdas				xdigs = xdigs_upper;
850113146Sdas				expchar = 'P';
851113146Sdas			}
852124657Sdas			if (prec >= 0)
853124657Sdas				prec++;
854124657Sdas			if (dtoaresult != NULL)
855124657Sdas				freedtoa(dtoaresult);
856113146Sdas			if (flags & LONGDBL) {
857124657Sdas				fparg.ldbl = GETARG(long double);
858113146Sdas				dtoaresult = cp =
859113146Sdas				    __hldtoa(fparg.ldbl, xdigs, prec,
860113146Sdas				    &expt, &signflag, &dtoaend);
861113146Sdas			} else {
862113146Sdas				fparg.dbl = GETARG(double);
863113146Sdas				dtoaresult = cp =
864113146Sdas				    __hdtoa(fparg.dbl, xdigs, prec,
865113146Sdas				    &expt, &signflag, &dtoaend);
866113146Sdas			}
867124657Sdas			if (prec < 0)
868124657Sdas				prec = dtoaend - cp;
869124657Sdas			if (expt == INT_MAX)
870124657Sdas				ox[1] = '\0';
871124657Sdas			goto fp_common;
872124657Sdas#endif	/* HEXFLOAT */
8737033Sbde		case 'e':
8741573Srgrimes		case 'E':
875113146Sdas			expchar = ch;
876113146Sdas			if (prec < 0)	/* account for digit before decpt */
877113146Sdas				prec = DEFPREC + 1;
878113146Sdas			else
879113146Sdas				prec++;
880113146Sdas			goto fp_begin;
8817033Sbde		case 'f':
88287113Sfenner		case 'F':
883113146Sdas			expchar = '\0';
8847033Sbde			goto fp_begin;
8851573Srgrimes		case 'g':
8861573Srgrimes		case 'G':
887113146Sdas			expchar = ch - ('g' - 'e');
8887033Sbde			if (prec == 0)
8897033Sbde				prec = 1;
890113146Sdasfp_begin:
891113146Sdas			if (prec < 0)
8921573Srgrimes				prec = DEFPREC;
893113146Sdas			if (dtoaresult != NULL)
894113146Sdas				freedtoa(dtoaresult);
895113146Sdas			if (flags & LONGDBL) {
896113146Sdas				fparg.ldbl = GETARG(long double);
897113146Sdas				dtoaresult = cp =
898113146Sdas				    __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
899113146Sdas				    &expt, &signflag, &dtoaend);
900113146Sdas			} else {
901113146Sdas				fparg.dbl = GETARG(double);
902113146Sdas				dtoaresult = cp =
903113146Sdas				    dtoa(fparg.dbl, expchar ? 2 : 3, prec,
904113146Sdas				    &expt, &signflag, &dtoaend);
905113146Sdas				if (expt == 9999)
906113146Sdas					expt = INT_MAX;
9071573Srgrimes			}
908124657Sdasfp_common:
909113146Sdas			if (signflag)
910113146Sdas				sign = '-';
911113146Sdas			if (expt == INT_MAX) {	/* inf or nan */
912113146Sdas				if (*cp == 'N') {
913113146Sdas					cp = (ch >= 'a') ? "nan" : "NAN";
914113146Sdas					sign = '\0';
915113146Sdas				} else
916113146Sdas					cp = (ch >= 'a') ? "inf" : "INF";
9171573Srgrimes				size = 3;
9181573Srgrimes				break;
9191573Srgrimes			}
9201573Srgrimes			flags |= FPT;
921113146Sdas			ndig = dtoaend - cp;
9221573Srgrimes			if (ch == 'g' || ch == 'G') {
923113146Sdas				if (expt > -4 && expt <= prec) {
924113146Sdas					/* Make %[gG] smell like %[fF] */
925113146Sdas					expchar = '\0';
926113146Sdas					if (flags & ALT)
927113146Sdas						prec -= expt;
928113146Sdas					else
929113146Sdas						prec = ndig - expt;
930113146Sdas					if (prec < 0)
931113146Sdas						prec = 0;
932113723Sdas				} else {
933113723Sdas					/*
934113723Sdas					 * Make %[gG] smell like %[eE], but
935113723Sdas					 * trim trailing zeroes if no # flag.
936113723Sdas					 */
937113723Sdas					if (!(flags & ALT))
938113723Sdas						prec = ndig;
939113146Sdas				}
9408870Srgrimes			}
941113146Sdas			if (expchar) {
942113146Sdas				expsize = exponent(expstr, expt - 1, expchar);
943113146Sdas				size = expsize + prec;
944113191Sdas				if (prec > 1 || flags & ALT)
9451573Srgrimes					++size;
946113146Sdas			} else {
947113468Sdas				/* space for digits before decimal point */
948113468Sdas				if (expt > 0)
9491573Srgrimes					size = expt;
950113468Sdas				else	/* "0" */
951113468Sdas					size = 1;
952113468Sdas				/* space for decimal pt and following digits */
953113468Sdas				if (prec || flags & ALT)
954113468Sdas					size += prec + 1;
955113146Sdas				if (grouping && expt > 0) {
956113146Sdas					/* space for thousands' grouping */
957113146Sdas					nseps = nrepeats = 0;
958113146Sdas					lead = expt;
959113146Sdas					while (*grouping != CHAR_MAX) {
960113146Sdas						if (lead <= *grouping)
961113146Sdas							break;
962113146Sdas						lead -= *grouping;
963113146Sdas						if (*(grouping+1)) {
964113146Sdas							nseps++;
965113146Sdas							grouping++;
966113146Sdas						} else
967113146Sdas							nrepeats++;
968113146Sdas					}
969113146Sdas					size += nseps + nrepeats;
970113146Sdas				} else
971113194Sdas					lead = expt;
972113146Sdas			}
9731573Srgrimes			break;
9741573Srgrimes#endif /* FLOATING_POINT */
9751573Srgrimes		case 'n':
97687113Sfenner			/*
97787113Sfenner			 * Assignment-like behavior is specified if the
97887113Sfenner			 * value overflows or is otherwise unrepresentable.
97987113Sfenner			 * C99 says to use `signed char' for %hhn conversions.
98087113Sfenner			 */
98187113Sfenner			if (flags & LLONGINT)
98287113Sfenner				*GETARG(long long *) = ret;
98387113Sfenner			else if (flags & SIZET)
98487113Sfenner				*GETARG(ssize_t *) = (ssize_t)ret;
98587113Sfenner			else if (flags & PTRDIFFT)
98687113Sfenner				*GETARG(ptrdiff_t *) = ret;
98787113Sfenner			else if (flags & INTMAXT)
98887113Sfenner				*GETARG(intmax_t *) = ret;
9891573Srgrimes			else if (flags & LONGINT)
99031980Sache				*GETARG(long *) = ret;
9911573Srgrimes			else if (flags & SHORTINT)
99231980Sache				*GETARG(short *) = ret;
99387113Sfenner			else if (flags & CHARINT)
99487113Sfenner				*GETARG(signed char *) = ret;
9951573Srgrimes			else
99631980Sache				*GETARG(int *) = ret;
9971573Srgrimes			continue;	/* no output */
9981573Srgrimes		case 'O':
9991573Srgrimes			flags |= LONGINT;
10001573Srgrimes			/*FALLTHROUGH*/
10011573Srgrimes		case 'o':
100287113Sfenner			if (flags & INTMAX_SIZE)
100387113Sfenner				ujval = UJARG();
10041573Srgrimes			else
10051573Srgrimes				ulval = UARG();
10061573Srgrimes			base = 8;
10071573Srgrimes			goto nosign;
10081573Srgrimes		case 'p':
100988057Sphantom			/*-
10101573Srgrimes			 * ``The argument shall be a pointer to void.  The
10111573Srgrimes			 * value of the pointer is converted to a sequence
10121573Srgrimes			 * of printable characters, in an implementation-
10131573Srgrimes			 * defined manner.''
10141573Srgrimes			 *	-- ANSI X3J11
10151573Srgrimes			 */
101687113Sfenner			ujval = (uintmax_t)(uintptr_t)GETARG(void *);
10171573Srgrimes			base = 16;
1018113146Sdas			xdigs = xdigs_lower;
1019113146Sdas			flags = flags | INTMAXT;
1020113146Sdas			ox[1] = 'x';
10211573Srgrimes			goto nosign;
1022105204Stjr		case 'S':
1023105204Stjr			flags |= LONGINT;
1024105204Stjr			/*FALLTHROUGH*/
10251573Srgrimes		case 's':
1026103633Stjr			if (flags & LONGINT) {
1027103633Stjr				wchar_t *wcp;
1028103633Stjr
1029103633Stjr				if (convbuf != NULL)
1030103633Stjr					free(convbuf);
1031103633Stjr				if ((wcp = GETARG(wchar_t *)) == NULL)
1032103633Stjr					cp = "(null)";
1033103633Stjr				else {
1034103633Stjr					convbuf = __wcsconv(wcp, prec);
1035105234Stjr					if (convbuf == NULL) {
1036105234Stjr						fp->_flags |= __SERR;
1037103633Stjr						goto error;
1038105234Stjr					}
1039103633Stjr					cp = convbuf;
1040103633Stjr				}
1041103633Stjr			} else if ((cp = GETARG(char *)) == NULL)
10421573Srgrimes				cp = "(null)";
10431573Srgrimes			if (prec >= 0) {
10441573Srgrimes				/*
10451573Srgrimes				 * can't use strlen; can only look for the
10461573Srgrimes				 * NUL in the first `prec' characters, and
10471573Srgrimes				 * strlen() will go further.
10481573Srgrimes				 */
104916586Sjraynard				char *p = memchr(cp, 0, (size_t)prec);
10501573Srgrimes
10511573Srgrimes				if (p != NULL) {
10521573Srgrimes					size = p - cp;
10531573Srgrimes					if (size > prec)
10541573Srgrimes						size = prec;
10551573Srgrimes				} else
10561573Srgrimes					size = prec;
10571573Srgrimes			} else
10581573Srgrimes				size = strlen(cp);
10591573Srgrimes			sign = '\0';
10601573Srgrimes			break;
10611573Srgrimes		case 'U':
10621573Srgrimes			flags |= LONGINT;
10631573Srgrimes			/*FALLTHROUGH*/
10641573Srgrimes		case 'u':
106587113Sfenner			if (flags & INTMAX_SIZE)
106687113Sfenner				ujval = UJARG();
10671573Srgrimes			else
10681573Srgrimes				ulval = UARG();
10691573Srgrimes			base = 10;
10701573Srgrimes			goto nosign;
10711573Srgrimes		case 'X':
1072113146Sdas			xdigs = xdigs_upper;
10731573Srgrimes			goto hex;
10741573Srgrimes		case 'x':
1075113146Sdas			xdigs = xdigs_lower;
107687113Sfennerhex:
107787113Sfenner			if (flags & INTMAX_SIZE)
107887113Sfenner				ujval = UJARG();
10791573Srgrimes			else
10801573Srgrimes				ulval = UARG();
10811573Srgrimes			base = 16;
10821573Srgrimes			/* leading 0x/X only if non-zero */
10831573Srgrimes			if (flags & ALT &&
108487113Sfenner			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
1085113146Sdas				ox[1] = ch;
10861573Srgrimes
108787815Sphantom			flags &= ~GROUPING;
10881573Srgrimes			/* unsigned conversions */
10891573Srgrimesnosign:			sign = '\0';
109088057Sphantom			/*-
10911573Srgrimes			 * ``... diouXx conversions ... if a precision is
10921573Srgrimes			 * specified, the 0 flag will be ignored.''
10931573Srgrimes			 *	-- ANSI X3J11
10941573Srgrimes			 */
10951573Srgrimesnumber:			if ((dprec = prec) >= 0)
10961573Srgrimes				flags &= ~ZEROPAD;
10971573Srgrimes
109888057Sphantom			/*-
10991573Srgrimes			 * ``The result of converting a zero value with an
11001573Srgrimes			 * explicit precision of zero is no characters.''
11011573Srgrimes			 *	-- ANSI X3J11
11021573Srgrimes			 */
11031573Srgrimes			cp = buf + BUF;
110487113Sfenner			if (flags & INTMAX_SIZE) {
110587113Sfenner				if (ujval != 0 || prec != 0)
110687113Sfenner					cp = __ujtoa(ujval, cp, base,
110787815Sphantom					    flags & ALT, xdigs,
110887815Sphantom					    flags & GROUPING, thousands_sep,
110987815Sphantom					    grouping);
11101573Srgrimes			} else {
11111573Srgrimes				if (ulval != 0 || prec != 0)
11121573Srgrimes					cp = __ultoa(ulval, cp, base,
111387815Sphantom					    flags & ALT, xdigs,
111487815Sphantom					    flags & GROUPING, thousands_sep,
111587815Sphantom					    grouping);
11161573Srgrimes			}
11171573Srgrimes			size = buf + BUF - cp;
1118113142Sdas			if (size > BUF)	/* should never happen */
1119113142Sdas				abort();
11201573Srgrimes			break;
11211573Srgrimes		default:	/* "%?" prints ?, unless ? is NUL */
11221573Srgrimes			if (ch == '\0')
11231573Srgrimes				goto done;
11241573Srgrimes			/* pretend it was %c with argument ch */
11251573Srgrimes			cp = buf;
11261573Srgrimes			*cp = ch;
11271573Srgrimes			size = 1;
11281573Srgrimes			sign = '\0';
11291573Srgrimes			break;
11301573Srgrimes		}
11311573Srgrimes
11321573Srgrimes		/*
11331573Srgrimes		 * All reasonable formats wind up here.  At this point, `cp'
11341573Srgrimes		 * points to a string which (if not flags&LADJUST) should be
11351573Srgrimes		 * padded out to `width' places.  If flags&ZEROPAD, it should
11361573Srgrimes		 * first be prefixed by any sign or other prefix; otherwise,
11371573Srgrimes		 * it should be blank padded before the prefix is emitted.
11381573Srgrimes		 * After any left-hand padding and prefixing, emit zeroes
11391573Srgrimes		 * required by a decimal [diouxX] precision, then print the
11401573Srgrimes		 * string proper, then emit zeroes required by any leftover
11411573Srgrimes		 * floating precision; finally, if LADJUST, pad with blanks.
11421573Srgrimes		 *
11431573Srgrimes		 * Compute actual size, so we know how much to pad.
114414727Sfenner		 * size excludes decimal prec; realsz includes it.
11451573Srgrimes		 */
114614727Sfenner		realsz = dprec > size ? dprec : size;
11471573Srgrimes		if (sign)
114814727Sfenner			realsz++;
1149124657Sdas		if (ox[1])
115014727Sfenner			realsz += 2;
11511573Srgrimes
115231983Sache		prsize = width > realsz ? width : realsz;
115332253Sache		if ((unsigned)ret + prsize > INT_MAX) {
115431983Sache			ret = EOF;
115531983Sache			goto error;
115631983Sache		}
115731983Sache
11581573Srgrimes		/* right-adjusting blank padding */
11591573Srgrimes		if ((flags & (LADJUST|ZEROPAD)) == 0)
11601573Srgrimes			PAD(width - realsz, blanks);
11611573Srgrimes
11621573Srgrimes		/* prefix */
1163124657Sdas		if (sign)
11641573Srgrimes			PRINT(&sign, 1);
1165124657Sdas
1166124657Sdas		if (ox[1]) {	/* ox[1] is either x, X, or \0 */
11671573Srgrimes			ox[0] = '0';
11681573Srgrimes			PRINT(ox, 2);
11691573Srgrimes		}
11701573Srgrimes
11711573Srgrimes		/* right-adjusting zero padding */
11721573Srgrimes		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
11731573Srgrimes			PAD(width - realsz, zeroes);
11741573Srgrimes
11751573Srgrimes		/* leading zeroes from decimal precision */
117614727Sfenner		PAD(dprec - size, zeroes);
11771573Srgrimes
11781573Srgrimes		/* the string or number proper */
11791573Srgrimes#ifdef FLOATING_POINT
11801573Srgrimes		if ((flags & FPT) == 0) {
11811573Srgrimes			PRINT(cp, size);
11821573Srgrimes		} else {	/* glue together f_p fragments */
1183113146Sdas			if (!expchar) {	/* %[fF] or sufficiently short %[gG] */
1184113146Sdas				if (expt <= 0) {
1185113468Sdas					PRINT(zeroes, 1);
1186113468Sdas					if (prec || flags & ALT)
1187113468Sdas						PRINT(decimal_point, 1);
11881573Srgrimes					PAD(-expt, zeroes);
1189113191Sdas					/* already handled initial 0's */
1190113191Sdas					prec += expt;
11911573Srgrimes				} else {
1192113191Sdas					PRINTANDPAD(cp, dtoaend, lead, zeroes);
1193113146Sdas					cp += lead;
1194113146Sdas					if (grouping) {
1195113146Sdas						while (nseps>0 || nrepeats>0) {
1196113146Sdas							if (nrepeats > 0)
1197113146Sdas								nrepeats--;
1198113146Sdas							else {
1199113146Sdas								grouping--;
1200113146Sdas								nseps--;
1201113146Sdas							}
1202113146Sdas							PRINT(&thousands_sep,
1203113146Sdas							    1);
1204113191Sdas							PRINTANDPAD(cp,dtoaend,
1205113191Sdas							    *grouping, zeroes);
1206113146Sdas							cp += *grouping;
1207113146Sdas						}
1208113191Sdas						if (cp > dtoaend)
1209113191Sdas							cp = dtoaend;
1210113146Sdas					}
1211113146Sdas					if (prec || flags & ALT)
1212113146Sdas						PRINT(decimal_point,1);
12131573Srgrimes				}
1214113191Sdas				PRINTANDPAD(cp, dtoaend, prec, zeroes);
1215113146Sdas			} else {	/* %[eE] or sufficiently long %[gG] */
1216113191Sdas				if (prec > 1 || flags & ALT) {
1217113146Sdas					buf[0] = *cp++;
1218113146Sdas					buf[1] = *decimal_point;
1219113146Sdas					PRINT(buf, 2);
1220113146Sdas					PRINT(cp, ndig-1);
1221113146Sdas					PAD(prec - ndig, zeroes);
12221573Srgrimes				} else	/* XeYYY */
12231573Srgrimes					PRINT(cp, 1);
12241573Srgrimes				PRINT(expstr, expsize);
12251573Srgrimes			}
12261573Srgrimes		}
12271573Srgrimes#else
12281573Srgrimes		PRINT(cp, size);
12291573Srgrimes#endif
12301573Srgrimes		/* left-adjusting padding (always blank) */
12311573Srgrimes		if (flags & LADJUST)
12321573Srgrimes			PAD(width - realsz, blanks);
12331573Srgrimes
12341573Srgrimes		/* finally, adjust ret */
123531983Sache		ret += prsize;
12361573Srgrimes
12371573Srgrimes		FLUSH();	/* copy out the I/O vectors */
12381573Srgrimes	}
12391573Srgrimesdone:
12401573Srgrimes	FLUSH();
12411573Srgrimeserror:
124272523Stegge#ifdef FLOATING_POINT
124372523Stegge	if (dtoaresult != NULL)
1244113146Sdas		freedtoa(dtoaresult);
124572523Stegge#endif
1246103633Stjr	if (convbuf != NULL)
1247103633Stjr		free(convbuf);
124813545Sjulian	if (__sferror(fp))
124913545Sjulian		ret = EOF;
1250103399Stjr	if ((argtable != NULL) && (argtable != statargtable))
1251103399Stjr		free (argtable);
125213545Sjulian	return (ret);
12531573Srgrimes	/* NOTREACHED */
12541573Srgrimes}
12551573Srgrimes
125621674Sjkh/*
125721674Sjkh * Find all arguments when a positional parameter is encountered.  Returns a
125821674Sjkh * table, indexed by argument number, of pointers to each arguments.  The
125921674Sjkh * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
126070725Sarchie * It will be replaces with a malloc-ed one if it overflows.
126121674Sjkh */
126221674Sjkhstatic void
126384962Sbde__find_arguments (const char *fmt0, va_list ap, union arg **argtable)
126421674Sjkh{
126571579Sdeischen	char *fmt;		/* format string */
126671579Sdeischen	int ch;			/* character from fmt */
126771579Sdeischen	int n, n2;		/* handy integer (short term usage) */
126871579Sdeischen	char *cp;		/* handy char pointer (short term usage) */
126971579Sdeischen	int flags;		/* flags as above */
127021674Sjkh	int width;		/* width from format (%8d), or 0 */
127187113Sfenner	enum typeid *typetable; /* table of types */
127287113Sfenner	enum typeid stattypetable [STATIC_ARG_TBL_SIZE];
127321674Sjkh	int tablesize;		/* current size of type table */
127421674Sjkh	int tablemax;		/* largest used index in table */
127521674Sjkh	int nextarg;		/* 1-based argument index */
127621674Sjkh
127721674Sjkh	/*
127821674Sjkh	 * Add an argument type to the table, expanding if necessary.
127921674Sjkh	 */
128021674Sjkh#define ADDTYPE(type) \
128121674Sjkh	((nextarg >= tablesize) ? \
128221674Sjkh		__grow_type_table(nextarg, &typetable, &tablesize) : 0, \
128370725Sarchie	(nextarg > tablemax) ? tablemax = nextarg : 0, \
128470725Sarchie	typetable[nextarg++] = type)
128521674Sjkh
128621674Sjkh#define	ADDSARG() \
128787113Sfenner	((flags&INTMAXT) ? ADDTYPE(T_INTMAXT) : \
128887113Sfenner		((flags&SIZET) ? ADDTYPE(T_SIZET) : \
128987113Sfenner		((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \
129087113Sfenner		((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \
129187113Sfenner		((flags&LONGINT) ? ADDTYPE(T_LONG) : ADDTYPE(T_INT))))))
129221674Sjkh
129321674Sjkh#define	ADDUARG() \
129487113Sfenner	((flags&INTMAXT) ? ADDTYPE(T_UINTMAXT) : \
129587113Sfenner		((flags&SIZET) ? ADDTYPE(T_SIZET) : \
129687113Sfenner		((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \
129787113Sfenner		((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \
129887113Sfenner		((flags&LONGINT) ? ADDTYPE(T_U_LONG) : ADDTYPE(T_U_INT))))))
129921674Sjkh
130021674Sjkh	/*
130121674Sjkh	 * Add * arguments to the type array.
130221674Sjkh	 */
130321674Sjkh#define ADDASTER() \
130421674Sjkh	n2 = 0; \
130521674Sjkh	cp = fmt; \
130621674Sjkh	while (is_digit(*cp)) { \
130721674Sjkh		n2 = 10 * n2 + to_digit(*cp); \
130821674Sjkh		cp++; \
130921674Sjkh	} \
131021674Sjkh	if (*cp == '$') { \
131121674Sjkh		int hold = nextarg; \
131221674Sjkh		nextarg = n2; \
131321674Sjkh		ADDTYPE (T_INT); \
131421674Sjkh		nextarg = hold; \
131521674Sjkh		fmt = ++cp; \
131621674Sjkh	} else { \
131721674Sjkh		ADDTYPE (T_INT); \
131821674Sjkh	}
131921674Sjkh	fmt = (char *)fmt0;
132021674Sjkh	typetable = stattypetable;
132121674Sjkh	tablesize = STATIC_ARG_TBL_SIZE;
132221674Sjkh	tablemax = 0;
132321674Sjkh	nextarg = 1;
1324128550Stjr	for (n = 0; n < STATIC_ARG_TBL_SIZE; n++)
1325128550Stjr		typetable[n] = T_UNUSED;
132621674Sjkh
132721674Sjkh	/*
132821674Sjkh	 * Scan the format for conversions (`%' character).
132921674Sjkh	 */
133021674Sjkh	for (;;) {
133121674Sjkh		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
133221674Sjkh			/* void */;
133321674Sjkh		if (ch == '\0')
133421674Sjkh			goto done;
133521674Sjkh		fmt++;		/* skip over '%' */
133621674Sjkh
133721674Sjkh		flags = 0;
133821674Sjkh		width = 0;
133921674Sjkh
134021674Sjkhrflag:		ch = *fmt++;
134121674Sjkhreswitch:	switch (ch) {
134221674Sjkh		case ' ':
134321674Sjkh		case '#':
134421674Sjkh			goto rflag;
134521674Sjkh		case '*':
134621674Sjkh			ADDASTER ();
134721674Sjkh			goto rflag;
134821674Sjkh		case '-':
134921674Sjkh		case '+':
135087113Sfenner		case '\'':
135121674Sjkh			goto rflag;
135221674Sjkh		case '.':
135321674Sjkh			if ((ch = *fmt++) == '*') {
135421674Sjkh				ADDASTER ();
135521674Sjkh				goto rflag;
135621674Sjkh			}
135721674Sjkh			while (is_digit(ch)) {
135821674Sjkh				ch = *fmt++;
135921674Sjkh			}
136021674Sjkh			goto reswitch;
136121674Sjkh		case '0':
136221674Sjkh			goto rflag;
136321674Sjkh		case '1': case '2': case '3': case '4':
136421674Sjkh		case '5': case '6': case '7': case '8': case '9':
136521674Sjkh			n = 0;
136621674Sjkh			do {
136721674Sjkh				n = 10 * n + to_digit(ch);
136821674Sjkh				ch = *fmt++;
136921674Sjkh			} while (is_digit(ch));
137021674Sjkh			if (ch == '$') {
137121674Sjkh				nextarg = n;
137221674Sjkh				goto rflag;
137321674Sjkh			}
137421674Sjkh			width = n;
137521674Sjkh			goto reswitch;
13761573Srgrimes#ifdef FLOATING_POINT
137721674Sjkh		case 'L':
137821674Sjkh			flags |= LONGDBL;
137921674Sjkh			goto rflag;
138021674Sjkh#endif
138121674Sjkh		case 'h':
138287113Sfenner			if (flags & SHORTINT) {
138387113Sfenner				flags &= ~SHORTINT;
138487113Sfenner				flags |= CHARINT;
138587113Sfenner			} else
138687113Sfenner				flags |= SHORTINT;
138721674Sjkh			goto rflag;
138887113Sfenner		case 'j':
138987113Sfenner			flags |= INTMAXT;
139087113Sfenner			goto rflag;
139121674Sjkh		case 'l':
139287113Sfenner			if (flags & LONGINT) {
139387113Sfenner				flags &= ~LONGINT;
139487113Sfenner				flags |= LLONGINT;
139587113Sfenner			} else
139644674Sdfr				flags |= LONGINT;
139721674Sjkh			goto rflag;
139821674Sjkh		case 'q':
139987113Sfenner			flags |= LLONGINT;	/* not necessarily */
140021674Sjkh			goto rflag;
140187113Sfenner		case 't':
140287113Sfenner			flags |= PTRDIFFT;
140387113Sfenner			goto rflag;
140487113Sfenner		case 'z':
140587113Sfenner			flags |= SIZET;
140687113Sfenner			goto rflag;
1407105204Stjr		case 'C':
1408105204Stjr			flags |= LONGINT;
1409105204Stjr			/*FALLTHROUGH*/
141021674Sjkh		case 'c':
1411103633Stjr			if (flags & LONGINT)
1412103633Stjr				ADDTYPE(T_WINT);
1413103633Stjr			else
1414103633Stjr				ADDTYPE(T_INT);
141521674Sjkh			break;
141621674Sjkh		case 'D':
141721674Sjkh			flags |= LONGINT;
141821674Sjkh			/*FALLTHROUGH*/
141921674Sjkh		case 'd':
142021674Sjkh		case 'i':
142187113Sfenner			ADDSARG();
142221674Sjkh			break;
142321674Sjkh#ifdef FLOATING_POINT
142487113Sfenner#ifdef HEXFLOAT
142587113Sfenner		case 'a':
142687113Sfenner		case 'A':
142787113Sfenner#endif
142821674Sjkh		case 'e':
142921674Sjkh		case 'E':
143021674Sjkh		case 'f':
143121674Sjkh		case 'g':
143221674Sjkh		case 'G':
143321674Sjkh			if (flags & LONGDBL)
143421674Sjkh				ADDTYPE(T_LONG_DOUBLE);
143521674Sjkh			else
143621674Sjkh				ADDTYPE(T_DOUBLE);
143721674Sjkh			break;
143821674Sjkh#endif /* FLOATING_POINT */
143921674Sjkh		case 'n':
144087113Sfenner			if (flags & INTMAXT)
144187113Sfenner				ADDTYPE(TP_INTMAXT);
144287113Sfenner			else if (flags & PTRDIFFT)
144387113Sfenner				ADDTYPE(TP_PTRDIFFT);
144487113Sfenner			else if (flags & SIZET)
144587113Sfenner				ADDTYPE(TP_SIZET);
144687113Sfenner			else if (flags & LLONGINT)
144787113Sfenner				ADDTYPE(TP_LLONG);
144821674Sjkh			else if (flags & LONGINT)
144921674Sjkh				ADDTYPE(TP_LONG);
145021674Sjkh			else if (flags & SHORTINT)
145121674Sjkh				ADDTYPE(TP_SHORT);
145287113Sfenner			else if (flags & CHARINT)
145387113Sfenner				ADDTYPE(TP_SCHAR);
145421674Sjkh			else
145521674Sjkh				ADDTYPE(TP_INT);
145621674Sjkh			continue;	/* no output */
145721674Sjkh		case 'O':
145821674Sjkh			flags |= LONGINT;
145921674Sjkh			/*FALLTHROUGH*/
146021674Sjkh		case 'o':
146187113Sfenner			ADDUARG();
146221674Sjkh			break;
146321674Sjkh		case 'p':
146421674Sjkh			ADDTYPE(TP_VOID);
146521674Sjkh			break;
1466105204Stjr		case 'S':
1467105204Stjr			flags |= LONGINT;
1468105204Stjr			/*FALLTHROUGH*/
146921674Sjkh		case 's':
1470103633Stjr			if (flags & LONGINT)
1471103633Stjr				ADDTYPE(TP_WCHAR);
1472103633Stjr			else
1473103633Stjr				ADDTYPE(TP_CHAR);
147421674Sjkh			break;
147521674Sjkh		case 'U':
147621674Sjkh			flags |= LONGINT;
147721674Sjkh			/*FALLTHROUGH*/
147821674Sjkh		case 'u':
147921674Sjkh		case 'X':
148021674Sjkh		case 'x':
148187113Sfenner			ADDUARG();
148221674Sjkh			break;
148321674Sjkh		default:	/* "%?" prints ?, unless ? is NUL */
148421674Sjkh			if (ch == '\0')
148521674Sjkh				goto done;
148621674Sjkh			break;
148721674Sjkh		}
148821674Sjkh	}
148921674Sjkhdone:
149021674Sjkh	/*
149121674Sjkh	 * Build the argument table.
149221674Sjkh	 */
149321674Sjkh	if (tablemax >= STATIC_ARG_TBL_SIZE) {
149484922Sdfr		*argtable = (union arg *)
149584922Sdfr		    malloc (sizeof (union arg) * (tablemax + 1));
149621674Sjkh	}
14971573Srgrimes
149884922Sdfr	(*argtable) [0].intarg = 0;
149921674Sjkh	for (n = 1; n <= tablemax; n++) {
150021674Sjkh		switch (typetable [n]) {
150187113Sfenner		    case T_UNUSED: /* whoops! */
150284922Sdfr			(*argtable) [n].intarg = va_arg (ap, int);
150321674Sjkh			break;
150487113Sfenner		    case TP_SCHAR:
150587113Sfenner			(*argtable) [n].pschararg = va_arg (ap, signed char *);
150621674Sjkh			break;
150721674Sjkh		    case TP_SHORT:
150884922Sdfr			(*argtable) [n].pshortarg = va_arg (ap, short *);
150921674Sjkh			break;
151021674Sjkh		    case T_INT:
151184922Sdfr			(*argtable) [n].intarg = va_arg (ap, int);
151221674Sjkh			break;
151321674Sjkh		    case T_U_INT:
151484922Sdfr			(*argtable) [n].uintarg = va_arg (ap, unsigned int);
151521674Sjkh			break;
151621674Sjkh		    case TP_INT:
151784922Sdfr			(*argtable) [n].pintarg = va_arg (ap, int *);
151821674Sjkh			break;
151921674Sjkh		    case T_LONG:
152084922Sdfr			(*argtable) [n].longarg = va_arg (ap, long);
152121674Sjkh			break;
152221674Sjkh		    case T_U_LONG:
152384922Sdfr			(*argtable) [n].ulongarg = va_arg (ap, unsigned long);
152421674Sjkh			break;
152521674Sjkh		    case TP_LONG:
152684922Sdfr			(*argtable) [n].plongarg = va_arg (ap, long *);
152721674Sjkh			break;
152887113Sfenner		    case T_LLONG:
152987113Sfenner			(*argtable) [n].longlongarg = va_arg (ap, long long);
153021674Sjkh			break;
153187113Sfenner		    case T_U_LLONG:
153287113Sfenner			(*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long);
153321674Sjkh			break;
153487113Sfenner		    case TP_LLONG:
153587113Sfenner			(*argtable) [n].plonglongarg = va_arg (ap, long long *);
153621674Sjkh			break;
153787113Sfenner		    case T_PTRDIFFT:
153887113Sfenner			(*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t);
153987113Sfenner			break;
154087113Sfenner		    case TP_PTRDIFFT:
154187113Sfenner			(*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *);
154287113Sfenner			break;
154387113Sfenner		    case T_SIZET:
154487113Sfenner			(*argtable) [n].sizearg = va_arg (ap, size_t);
154587113Sfenner			break;
154687113Sfenner		    case TP_SIZET:
154787113Sfenner			(*argtable) [n].psizearg = va_arg (ap, ssize_t *);
154887113Sfenner			break;
154987113Sfenner		    case T_INTMAXT:
155087113Sfenner			(*argtable) [n].intmaxarg = va_arg (ap, intmax_t);
155187113Sfenner			break;
155287113Sfenner		    case T_UINTMAXT:
155387113Sfenner			(*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t);
155487113Sfenner			break;
155587113Sfenner		    case TP_INTMAXT:
155687113Sfenner			(*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *);
155787113Sfenner			break;
155884922Sdfr#ifdef FLOATING_POINT
155921674Sjkh		    case T_DOUBLE:
156084922Sdfr			(*argtable) [n].doublearg = va_arg (ap, double);
156121674Sjkh			break;
156221674Sjkh		    case T_LONG_DOUBLE:
156384922Sdfr			(*argtable) [n].longdoublearg = va_arg (ap, long double);
156421674Sjkh			break;
156584922Sdfr#endif
156621674Sjkh		    case TP_CHAR:
156784922Sdfr			(*argtable) [n].pchararg = va_arg (ap, char *);
156821674Sjkh			break;
156921674Sjkh		    case TP_VOID:
157084922Sdfr			(*argtable) [n].pvoidarg = va_arg (ap, void *);
157121674Sjkh			break;
1572103633Stjr		    case T_WINT:
1573103633Stjr			(*argtable) [n].wintarg = va_arg (ap, wint_t);
1574103633Stjr			break;
1575103633Stjr		    case TP_WCHAR:
1576103633Stjr			(*argtable) [n].pwchararg = va_arg (ap, wchar_t *);
1577103633Stjr			break;
157821674Sjkh		}
157921674Sjkh	}
158021674Sjkh
158121674Sjkh	if ((typetable != NULL) && (typetable != stattypetable))
158221674Sjkh		free (typetable);
158321674Sjkh}
158421674Sjkh
158521674Sjkh/*
158621674Sjkh * Increase the size of the type table.
158721674Sjkh */
158821674Sjkhstatic void
158987113Sfenner__grow_type_table (int nextarg, enum typeid **typetable, int *tablesize)
159021674Sjkh{
159187113Sfenner	enum typeid *const oldtable = *typetable;
159270725Sarchie	const int oldsize = *tablesize;
159387113Sfenner	enum typeid *newtable;
1594128550Stjr	int n, newsize = oldsize * 2;
159521674Sjkh
159670725Sarchie	if (newsize < nextarg + 1)
159770725Sarchie		newsize = nextarg + 1;
159870725Sarchie	if (oldsize == STATIC_ARG_TBL_SIZE) {
1599128550Stjr		if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL)
160070725Sarchie			abort();			/* XXX handle better */
1601128550Stjr		bcopy(oldtable, newtable, oldsize * sizeof(enum typeid));
160221674Sjkh	} else {
1603128550Stjr		newtable = reallocf(oldtable, newsize * sizeof(enum typeid));
1604128550Stjr		if (newtable == NULL)
160570725Sarchie			abort();			/* XXX handle better */
160621674Sjkh	}
1607128550Stjr	for (n = oldsize; n < newsize; n++)
1608128550Stjr		newtable[n] = T_UNUSED;
160921674Sjkh
161070725Sarchie	*typetable = newtable;
161121674Sjkh	*tablesize = newsize;
161221674Sjkh}
161321674Sjkh
161421674Sjkh
161521674Sjkh#ifdef FLOATING_POINT
161621674Sjkh
16171573Srgrimesstatic int
161871579Sdeischenexponent(char *p0, int exp, int fmtch)
16191573Srgrimes{
162071579Sdeischen	char *p, *t;
1621113142Sdas	char expbuf[MAXEXPDIG];
16221573Srgrimes
16231573Srgrimes	p = p0;
16241573Srgrimes	*p++ = fmtch;
16251573Srgrimes	if (exp < 0) {
16261573Srgrimes		exp = -exp;
16271573Srgrimes		*p++ = '-';
16281573Srgrimes	}
16291573Srgrimes	else
16301573Srgrimes		*p++ = '+';
1631113142Sdas	t = expbuf + MAXEXPDIG;
16321573Srgrimes	if (exp > 9) {
16331573Srgrimes		do {
16341573Srgrimes			*--t = to_char(exp % 10);
16351573Srgrimes		} while ((exp /= 10) > 9);
16361573Srgrimes		*--t = to_char(exp);
1637113142Sdas		for (; t < expbuf + MAXEXPDIG; *p++ = *t++);
16381573Srgrimes	}
16391573Srgrimes	else {
1640113146Sdas		/*
1641113146Sdas		 * Exponents for decimal floating point conversions
1642113146Sdas		 * (%[eEgG]) must be at least two characters long,
1643113146Sdas		 * whereas exponents for hexadecimal conversions can
1644113146Sdas		 * be only one character long.
1645113146Sdas		 */
1646113146Sdas		if (fmtch == 'e' || fmtch == 'E')
1647113146Sdas			*p++ = '0';
16481573Srgrimes		*p++ = to_char(exp);
16491573Srgrimes	}
16501573Srgrimes	return (p - p0);
16511573Srgrimes}
16521573Srgrimes#endif /* FLOATING_POINT */
1653