printf-pos.c revision 128819
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 128819 2004-05-02 10:55:06Z das $");
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
6984922Sdfrunion arg {
7084962Sbde	int	intarg;
7184962Sbde	u_int	uintarg;
7284962Sbde	long	longarg;
7384962Sbde	u_long	ulongarg;
7487113Sfenner	long long longlongarg;
7587113Sfenner	unsigned long long ulonglongarg;
7687113Sfenner	ptrdiff_t ptrdiffarg;
7787113Sfenner	size_t	sizearg;
7887113Sfenner	intmax_t intmaxarg;
7987113Sfenner	uintmax_t uintmaxarg;
8084962Sbde	void	*pvoidarg;
8184962Sbde	char	*pchararg;
8287113Sfenner	signed char *pschararg;
8384962Sbde	short	*pshortarg;
8484962Sbde	int	*pintarg;
8584962Sbde	long	*plongarg;
8687113Sfenner	long long *plonglongarg;
8787113Sfenner	ptrdiff_t *pptrdiffarg;
8887113Sfenner	size_t	*psizearg;
8987113Sfenner	intmax_t *pintmaxarg;
90128819Sdas#ifndef NO_FLOATING_POINT
9184962Sbde	double	doublearg;
9284962Sbde	long double longdoublearg;
9384922Sdfr#endif
94103633Stjr	wint_t	wintarg;
95103633Stjr	wchar_t	*pwchararg;
9684922Sdfr};
9784922Sdfr
9887113Sfenner/*
9987113Sfenner * Type ids for argument type table.
10087113Sfenner */
10187113Sfennerenum typeid {
10287113Sfenner	T_UNUSED, TP_SHORT, T_INT, T_U_INT, TP_INT,
10387113Sfenner	T_LONG, T_U_LONG, TP_LONG, T_LLONG, T_U_LLONG, TP_LLONG,
10487113Sfenner	T_PTRDIFFT, TP_PTRDIFFT, T_SIZET, TP_SIZET,
10587113Sfenner	T_INTMAXT, T_UINTMAXT, TP_INTMAXT, TP_VOID, TP_CHAR, TP_SCHAR,
106103633Stjr	T_DOUBLE, T_LONG_DOUBLE, T_WINT, TP_WCHAR
10787113Sfenner};
10887113Sfenner
10992905Sobrienstatic int	__sprint(FILE *, struct __suio *);
11092941Sobrienstatic int	__sbprintf(FILE *, const char *, va_list) __printflike(2, 0);
111113146Sdasstatic char	*__ujtoa(uintmax_t, char *, int, int, const char *, int, char,
11292941Sobrien		    const char *);
113113146Sdasstatic char	*__ultoa(u_long, char *, int, int, const char *, int, char,
11492941Sobrien		    const char *);
115103633Stjrstatic char	*__wcsconv(wchar_t *, int);
11692905Sobrienstatic void	__find_arguments(const char *, va_list, union arg **);
11792905Sobrienstatic void	__grow_type_table(int, enum typeid **, int *);
11816586Sjraynard
1191573Srgrimes/*
1201573Srgrimes * Flush out all the vectors defined by the given uio,
1211573Srgrimes * then reset it so that it can be reused.
1221573Srgrimes */
1231573Srgrimesstatic int
12471579Sdeischen__sprint(FILE *fp, struct __suio *uio)
1251573Srgrimes{
12671579Sdeischen	int err;
1271573Srgrimes
1281573Srgrimes	if (uio->uio_resid == 0) {
1291573Srgrimes		uio->uio_iovcnt = 0;
1301573Srgrimes		return (0);
1311573Srgrimes	}
1321573Srgrimes	err = __sfvwrite(fp, uio);
1331573Srgrimes	uio->uio_resid = 0;
1341573Srgrimes	uio->uio_iovcnt = 0;
1351573Srgrimes	return (err);
1361573Srgrimes}
1371573Srgrimes
1381573Srgrimes/*
1391573Srgrimes * Helper function for `fprintf to unbuffered unix file': creates a
1401573Srgrimes * temporary buffer.  We only work on write-only files; this avoids
1411573Srgrimes * worries about ungetc buffers and so forth.
1421573Srgrimes */
1431573Srgrimesstatic int
14471579Sdeischen__sbprintf(FILE *fp, const char *fmt, va_list ap)
1451573Srgrimes{
1461573Srgrimes	int ret;
1471573Srgrimes	FILE fake;
1481573Srgrimes	unsigned char buf[BUFSIZ];
1491573Srgrimes
1501573Srgrimes	/* copy the important variables */
1511573Srgrimes	fake._flags = fp->_flags & ~__SNBF;
1521573Srgrimes	fake._file = fp->_file;
1531573Srgrimes	fake._cookie = fp->_cookie;
1541573Srgrimes	fake._write = fp->_write;
155101776Stjr	fake._extra = fp->_extra;
1561573Srgrimes
1571573Srgrimes	/* set up the buffer */
1581573Srgrimes	fake._bf._base = fake._p = buf;
1591573Srgrimes	fake._bf._size = fake._w = sizeof(buf);
1601573Srgrimes	fake._lbfsize = 0;	/* not actually used, but Just In Case */
1611573Srgrimes
1621573Srgrimes	/* do the work, then copy any error status */
16371579Sdeischen	ret = __vfprintf(&fake, fmt, ap);
16471579Sdeischen	if (ret >= 0 && __fflush(&fake))
1651573Srgrimes		ret = EOF;
1661573Srgrimes	if (fake._flags & __SERR)
1671573Srgrimes		fp->_flags |= __SERR;
1681573Srgrimes	return (ret);
1691573Srgrimes}
1701573Srgrimes
1711573Srgrimes/*
1721573Srgrimes * Macros for converting digits to letters and vice versa
1731573Srgrimes */
1741573Srgrimes#define	to_digit(c)	((c) - '0')
1751573Srgrimes#define is_digit(c)	((unsigned)to_digit(c) <= 9)
1761573Srgrimes#define	to_char(n)	((n) + '0')
1771573Srgrimes
1781573Srgrimes/*
1791573Srgrimes * Convert an unsigned long to ASCII for printf purposes, returning
1801573Srgrimes * a pointer to the first character of the string representation.
1811573Srgrimes * Octal numbers can be forced to have a leading zero; hex numbers
1821573Srgrimes * use the given digits.
1831573Srgrimes */
1841573Srgrimesstatic char *
185113146Sdas__ultoa(u_long val, char *endp, int base, int octzero, const char *xdigs,
18687815Sphantom	int needgrp, char thousep, const char *grp)
1871573Srgrimes{
18892889Sobrien	char *cp = endp;
18992889Sobrien	long sval;
19087113Sfenner	int ndig;
1911573Srgrimes
1921573Srgrimes	/*
1931573Srgrimes	 * Handle the three cases separately, in the hope of getting
1941573Srgrimes	 * better/faster code.
1951573Srgrimes	 */
1961573Srgrimes	switch (base) {
1971573Srgrimes	case 10:
1981573Srgrimes		if (val < 10) {	/* many numbers are 1 digit */
1991573Srgrimes			*--cp = to_char(val);
2001573Srgrimes			return (cp);
2011573Srgrimes		}
20287113Sfenner		ndig = 0;
2031573Srgrimes		/*
2041573Srgrimes		 * On many machines, unsigned arithmetic is harder than
2051573Srgrimes		 * signed arithmetic, so we do at most one unsigned mod and
2061573Srgrimes		 * divide; this is sufficient to reduce the range of
2071573Srgrimes		 * the incoming value to where signed arithmetic works.
2081573Srgrimes		 */
2091573Srgrimes		if (val > LONG_MAX) {
2101573Srgrimes			*--cp = to_char(val % 10);
21187113Sfenner			ndig++;
2121573Srgrimes			sval = val / 10;
2131573Srgrimes		} else
2141573Srgrimes			sval = val;
2151573Srgrimes		do {
2161573Srgrimes			*--cp = to_char(sval % 10);
21787815Sphantom			ndig++;
21887815Sphantom			/*
21987815Sphantom			 * If (*grp == CHAR_MAX) then no more grouping
22087815Sphantom			 * should be performed.
22187815Sphantom			 */
22287818Sphantom			if (needgrp && ndig == *grp && *grp != CHAR_MAX
22387818Sphantom					&& sval > 9) {
22487815Sphantom				*--cp = thousep;
22587113Sfenner				ndig = 0;
22687815Sphantom				/*
22787815Sphantom				 * If (*(grp+1) == '\0') then we have to
22887815Sphantom				 * use *grp character (last grouping rule)
22987815Sphantom				 * for all next cases
23087815Sphantom				 */
23188057Sphantom				if (*(grp+1) != '\0')
23288057Sphantom					grp++;
23387113Sfenner			}
2341573Srgrimes			sval /= 10;
2351573Srgrimes		} while (sval != 0);
2361573Srgrimes		break;
2371573Srgrimes
2381573Srgrimes	case 8:
2391573Srgrimes		do {
2401573Srgrimes			*--cp = to_char(val & 7);
2411573Srgrimes			val >>= 3;
2421573Srgrimes		} while (val);
2431573Srgrimes		if (octzero && *cp != '0')
2441573Srgrimes			*--cp = '0';
2451573Srgrimes		break;
2461573Srgrimes
2471573Srgrimes	case 16:
2481573Srgrimes		do {
2491573Srgrimes			*--cp = xdigs[val & 15];
2501573Srgrimes			val >>= 4;
2511573Srgrimes		} while (val);
2521573Srgrimes		break;
2531573Srgrimes
2541573Srgrimes	default:			/* oops */
2551573Srgrimes		abort();
2561573Srgrimes	}
2571573Srgrimes	return (cp);
2581573Srgrimes}
2591573Srgrimes
26087113Sfenner/* Identical to __ultoa, but for intmax_t. */
2611573Srgrimesstatic char *
262113146Sdas__ujtoa(uintmax_t val, char *endp, int base, int octzero, const char *xdigs,
26387815Sphantom	int needgrp, char thousep, const char *grp)
2641573Srgrimes{
26571579Sdeischen	char *cp = endp;
26687113Sfenner	intmax_t sval;
26787113Sfenner	int ndig;
2681573Srgrimes
2691573Srgrimes	/* quick test for small values; __ultoa is typically much faster */
2701573Srgrimes	/* (perhaps instead we should run until small, then call __ultoa?) */
2711573Srgrimes	if (val <= ULONG_MAX)
27287113Sfenner		return (__ultoa((u_long)val, endp, base, octzero, xdigs,
27387815Sphantom		    needgrp, thousep, grp));
2741573Srgrimes	switch (base) {
2751573Srgrimes	case 10:
2761573Srgrimes		if (val < 10) {
2771573Srgrimes			*--cp = to_char(val % 10);
2781573Srgrimes			return (cp);
2791573Srgrimes		}
28087113Sfenner		ndig = 0;
28187113Sfenner		if (val > INTMAX_MAX) {
2821573Srgrimes			*--cp = to_char(val % 10);
28387113Sfenner			ndig++;
2841573Srgrimes			sval = val / 10;
2851573Srgrimes		} else
2861573Srgrimes			sval = val;
2871573Srgrimes		do {
2881573Srgrimes			*--cp = to_char(sval % 10);
28987815Sphantom			ndig++;
29087815Sphantom			/*
29187815Sphantom			 * If (*grp == CHAR_MAX) then no more grouping
29287815Sphantom			 * should be performed.
29387815Sphantom			 */
29487818Sphantom			if (needgrp && *grp != CHAR_MAX && ndig == *grp
29587818Sphantom					&& sval > 9) {
29687815Sphantom				*--cp = thousep;
29787113Sfenner				ndig = 0;
29887815Sphantom				/*
29987815Sphantom				 * If (*(grp+1) == '\0') then we have to
30087815Sphantom				 * use *grp character (last grouping rule)
30187815Sphantom				 * for all next cases
30287815Sphantom				 */
30388057Sphantom				if (*(grp+1) != '\0')
30488057Sphantom					grp++;
30588057Sphantom			}
3061573Srgrimes			sval /= 10;
3071573Srgrimes		} while (sval != 0);
3081573Srgrimes		break;
3091573Srgrimes
3101573Srgrimes	case 8:
3111573Srgrimes		do {
3121573Srgrimes			*--cp = to_char(val & 7);
3131573Srgrimes			val >>= 3;
3141573Srgrimes		} while (val);
3151573Srgrimes		if (octzero && *cp != '0')
3161573Srgrimes			*--cp = '0';
3171573Srgrimes		break;
3181573Srgrimes
3191573Srgrimes	case 16:
3201573Srgrimes		do {
3211573Srgrimes			*--cp = xdigs[val & 15];
3221573Srgrimes			val >>= 4;
3231573Srgrimes		} while (val);
3241573Srgrimes		break;
3251573Srgrimes
3261573Srgrimes	default:
3271573Srgrimes		abort();
3281573Srgrimes	}
3291573Srgrimes	return (cp);
3301573Srgrimes}
3311573Srgrimes
33271579Sdeischen/*
333103633Stjr * Convert a wide character string argument for the %ls format to a multibyte
334103633Stjr * string representation. ``prec'' specifies the maximum number of bytes
335103633Stjr * to output. If ``prec'' is greater than or equal to zero, we can't assume
336103633Stjr * that the wide char. string ends in a null character.
337103633Stjr */
338103633Stjrstatic char *
339103633Stjr__wcsconv(wchar_t *wcsarg, int prec)
340103633Stjr{
341128002Stjr	static const mbstate_t initial;
342128002Stjr	mbstate_t mbs;
343103633Stjr	char buf[MB_LEN_MAX];
344103633Stjr	wchar_t *p;
345103633Stjr	char *convbuf, *mbp;
346103633Stjr	size_t clen, nbytes;
347103633Stjr
348103633Stjr	/*
349103633Stjr	 * Determine the number of bytes to output and allocate space for
350103633Stjr	 * the output.
351103633Stjr	 */
352103633Stjr	if (prec >= 0) {
353103633Stjr		nbytes = 0;
354103633Stjr		p = wcsarg;
355128002Stjr		mbs = initial;
356103633Stjr		for (;;) {
357128002Stjr			clen = wcrtomb(buf, *p++, &mbs);
358103633Stjr			if (clen == 0 || clen == (size_t)-1 ||
359103633Stjr			    nbytes + clen > prec)
360103633Stjr				break;
361103633Stjr			nbytes += clen;
362103633Stjr		}
363103633Stjr	} else {
364103633Stjr		p = wcsarg;
365128002Stjr		mbs = initial;
366128002Stjr		nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
367103633Stjr		if (nbytes == (size_t)-1)
368103633Stjr			return (NULL);
369103633Stjr	}
370103633Stjr	if ((convbuf = malloc(nbytes + 1)) == NULL)
371103633Stjr		return (NULL);
372103633Stjr
373103633Stjr	/*
374103633Stjr	 * Fill the output buffer with the multibyte representations of as
375103633Stjr	 * many wide characters as will fit.
376103633Stjr	 */
377103633Stjr	mbp = convbuf;
378103633Stjr	p = wcsarg;
379128002Stjr	mbs = initial;
380103633Stjr	while (mbp - convbuf < nbytes) {
381128002Stjr		clen = wcrtomb(mbp, *p++, &mbs);
382103633Stjr		if (clen == 0 || clen == (size_t)-1)
383103633Stjr			break;
384103633Stjr		mbp += clen;
385103633Stjr	}
386113196Sache	if (clen == (size_t)-1) {
387113196Sache		free(convbuf);
388113196Sache		return (NULL);
389113196Sache	}
390103633Stjr	*mbp = '\0';
391103633Stjr
392103633Stjr	return (convbuf);
393103633Stjr}
394103633Stjr
395103633Stjr/*
39671579Sdeischen * MT-safe version
39771579Sdeischen */
39871579Sdeischenint
399103012Stjrvfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap)
400101914Srobert
40171579Sdeischen{
40271579Sdeischen	int ret;
40371579Sdeischen
40471579Sdeischen	FLOCKFILE(fp);
40571579Sdeischen	ret = __vfprintf(fp, fmt0, ap);
40671579Sdeischen	FUNLOCKFILE(fp);
40771579Sdeischen	return (ret);
40871579Sdeischen}
40971579Sdeischen
410128819Sdas#ifndef NO_FLOATING_POINT
411113146Sdas
412113146Sdas#define	dtoa		__dtoa
413113146Sdas#define	freedtoa	__freedtoa
414113146Sdas
415113146Sdas#include <float.h>
4161573Srgrimes#include <math.h>
4171573Srgrimes#include "floatio.h"
418113146Sdas#include "gdtoa.h"
4191573Srgrimes
4201573Srgrimes#define	DEFPREC		6
4211573Srgrimes
42292905Sobrienstatic int exponent(char *, int, int);
4231573Srgrimes
424128819Sdas#endif /* !NO_FLOATING_POINT */
4251573Srgrimes
426113142Sdas/*
427113142Sdas * The size of the buffer we use as scratch space for integer
428113142Sdas * conversions, among other things.  Technically, we would need the
429113142Sdas * most space for base 10 conversions with thousands' grouping
430113142Sdas * characters between each pair of digits.  100 bytes is a
431113142Sdas * conservative overestimate even for a 128-bit uintmax_t.
432113142Sdas */
433113142Sdas#define	BUF	100
4341573Srgrimes
43521674Sjkh#define STATIC_ARG_TBL_SIZE 8           /* Size of static argument table. */
4361573Srgrimes
4371573Srgrimes/*
4381573Srgrimes * Flags used during conversion.
4391573Srgrimes */
4401573Srgrimes#define	ALT		0x001		/* alternate form */
4411573Srgrimes#define	LADJUST		0x004		/* left adjustment */
44231871Sbde#define	LONGDBL		0x008		/* long double */
4431573Srgrimes#define	LONGINT		0x010		/* long integer */
44487113Sfenner#define	LLONGINT	0x020		/* long long integer */
4451573Srgrimes#define	SHORTINT	0x040		/* short integer */
4461573Srgrimes#define	ZEROPAD		0x080		/* zero (as opposed to blank) pad */
44788057Sphantom#define	FPT		0x100		/* Floating point number */
44887815Sphantom#define	GROUPING	0x200		/* use grouping ("'" flag) */
44987113Sfenner					/* C99 additional size modifiers: */
45087815Sphantom#define	SIZET		0x400		/* size_t */
45187815Sphantom#define	PTRDIFFT	0x800		/* ptrdiff_t */
45287815Sphantom#define	INTMAXT		0x1000		/* intmax_t */
45387815Sphantom#define	CHARINT		0x2000		/* print char using int format */
45487113Sfenner
45571579Sdeischen/*
45671579Sdeischen * Non-MT-safe version
45771579Sdeischen */
4581573Srgrimesint
45971579Sdeischen__vfprintf(FILE *fp, const char *fmt0, va_list ap)
4601573Srgrimes{
46171579Sdeischen	char *fmt;		/* format string */
46271579Sdeischen	int ch;			/* character from fmt */
46371579Sdeischen	int n, n2;		/* handy integer (short term usage) */
46471579Sdeischen	char *cp;		/* handy char pointer (short term usage) */
46571579Sdeischen	struct __siov *iovp;	/* for PRINT macro */
46671579Sdeischen	int flags;		/* flags as above */
4671573Srgrimes	int ret;		/* return value accumulator */
4681573Srgrimes	int width;		/* width from format (%8d), or 0 */
469113146Sdas	int prec;		/* precision from format; <0 for N/A */
4701573Srgrimes	char sign;		/* sign prefix (' ', '+', '-', or \0) */
47187815Sphantom	char thousands_sep;	/* locale specific thousands separator */
47287815Sphantom	const char *grouping;	/* locale specific numeric grouping rules */
473128819Sdas#ifndef NO_FLOATING_POINT
474113146Sdas	/*
475113146Sdas	 * We can decompose the printed representation of floating
476113146Sdas	 * point numbers into several parts, some of which may be empty:
477113146Sdas	 *
478113146Sdas	 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
479113146Sdas	 *    A       B     ---C---      D       E   F
480113146Sdas	 *
481113146Sdas	 * A:	'sign' holds this value if present; '\0' otherwise
482113146Sdas	 * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
483113146Sdas	 * C:	cp points to the string MMMNNN.  Leading and trailing
484113146Sdas	 *	zeros are not in the string and must be added.
485113146Sdas	 * D:	expchar holds this character; '\0' if no exponent, e.g. %f
486113146Sdas	 * F:	at least two digits for decimal, at least one digit for hex
487113146Sdas	 */
48887490Sphantom	char *decimal_point;	/* locale specific decimal point */
489113146Sdas	int signflag;		/* true if float is negative */
490113146Sdas	union {			/* floating point arguments %[aAeEfFgG] */
491113146Sdas		double dbl;
492113146Sdas		long double ldbl;
493113146Sdas	} fparg;
4941573Srgrimes	int expt;		/* integer value of exponent */
495113146Sdas	char expchar;		/* exponent character: [eEpP\0] */
496113146Sdas	char *dtoaend;		/* pointer to end of converted digits */
4971573Srgrimes	int expsize;		/* character count for expstr */
498113146Sdas	int lead;		/* sig figs before decimal or group sep */
499113146Sdas	int ndig;		/* actual number of digits returned by dtoa */
500113146Sdas	char expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */
50172523Stegge	char *dtoaresult;	/* buffer allocated by dtoa */
502113146Sdas	int nseps;		/* number of group separators with ' */
503113146Sdas	int nrepeats;		/* number of repeats of the last group */
5041573Srgrimes#endif
5051573Srgrimes	u_long	ulval;		/* integer arguments %[diouxX] */
50687113Sfenner	uintmax_t ujval;	/* %j, %ll, %q, %t, %z integers */
5071573Srgrimes	int base;		/* base for [diouxX] conversion */
5081573Srgrimes	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
50914727Sfenner	int realsz;		/* field size expanded by dprec, sign, etc */
5101573Srgrimes	int size;		/* size of converted field or string */
51131983Sache	int prsize;             /* max size of printed field */
512113146Sdas	const char *xdigs;     	/* digits for %[xX] conversion */
5131573Srgrimes#define NIOV 8
5141573Srgrimes	struct __suio uio;	/* output information: summary */
5151573Srgrimes	struct __siov iov[NIOV];/* ... and individual io vectors */
516113142Sdas	char buf[BUF];		/* buffer with space for digits of uintmax_t */
517113146Sdas	char ox[2];		/* space for 0x; ox[1] is either x, X, or \0 */
518103399Stjr	union arg *argtable;    /* args, built due to positional arg */
519103399Stjr	union arg statargtable [STATIC_ARG_TBL_SIZE];
520103399Stjr	int nextarg;            /* 1-based argument index */
521103399Stjr	va_list orgap;          /* original argument pointer */
522103633Stjr	char *convbuf;		/* wide to multibyte conversion result */
5231573Srgrimes
5241573Srgrimes	/*
5251573Srgrimes	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
5261573Srgrimes	 * fields occur frequently, increase PADSIZE and make the initialisers
5271573Srgrimes	 * below longer.
5281573Srgrimes	 */
5291573Srgrimes#define	PADSIZE	16		/* pad chunk size */
5301573Srgrimes	static char blanks[PADSIZE] =
5311573Srgrimes	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
5321573Srgrimes	static char zeroes[PADSIZE] =
5331573Srgrimes	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
5341573Srgrimes
535113146Sdas	static const char xdigs_lower[16] = "0123456789abcdef";
536113146Sdas	static const char xdigs_upper[16] = "0123456789ABCDEF";
537113146Sdas
5381573Srgrimes	/*
5391573Srgrimes	 * BEWARE, these `goto error' on error, and PAD uses `n'.
5401573Srgrimes	 */
5411573Srgrimes#define	PRINT(ptr, len) { \
5421573Srgrimes	iovp->iov_base = (ptr); \
5431573Srgrimes	iovp->iov_len = (len); \
5441573Srgrimes	uio.uio_resid += (len); \
5451573Srgrimes	iovp++; \
5461573Srgrimes	if (++uio.uio_iovcnt >= NIOV) { \
5471573Srgrimes		if (__sprint(fp, &uio)) \
5481573Srgrimes			goto error; \
5491573Srgrimes		iovp = iov; \
5501573Srgrimes	} \
5511573Srgrimes}
5521573Srgrimes#define	PAD(howmany, with) { \
5531573Srgrimes	if ((n = (howmany)) > 0) { \
5541573Srgrimes		while (n > PADSIZE) { \
5551573Srgrimes			PRINT(with, PADSIZE); \
5561573Srgrimes			n -= PADSIZE; \
5571573Srgrimes		} \
5581573Srgrimes		PRINT(with, n); \
5591573Srgrimes	} \
5601573Srgrimes}
561113191Sdas#define	PRINTANDPAD(p, ep, len, with) do {	\
562113191Sdas	n2 = (ep) - (p);       			\
563113191Sdas	if (n2 > (len))				\
564113191Sdas		n2 = (len);			\
565113191Sdas	if (n2 > 0)				\
566113191Sdas		PRINT((p), n2);			\
567113191Sdas	PAD((len) - (n2 > 0 ? n2 : 0), (with));	\
568113191Sdas} while(0)
5691573Srgrimes#define	FLUSH() { \
5701573Srgrimes	if (uio.uio_resid && __sprint(fp, &uio)) \
5711573Srgrimes		goto error; \
5721573Srgrimes	uio.uio_iovcnt = 0; \
5731573Srgrimes	iovp = iov; \
5741573Srgrimes}
5751573Srgrimes
576103399Stjr	/*
577103399Stjr	 * Get the argument indexed by nextarg.   If the argument table is
578103399Stjr	 * built, use it to get the argument.  If its not, get the next
579103399Stjr	 * argument (and arguments must be gotten sequentially).
580103399Stjr	 */
58121674Sjkh#define GETARG(type) \
582103399Stjr	((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
583103399Stjr	    (nextarg++, va_arg(ap, type)))
58421674Sjkh
5851573Srgrimes	/*
5861573Srgrimes	 * To extend shorts properly, we need both signed and unsigned
5871573Srgrimes	 * argument extraction methods.
5881573Srgrimes	 */
5891573Srgrimes#define	SARG() \
59021674Sjkh	(flags&LONGINT ? GETARG(long) : \
59121674Sjkh	    flags&SHORTINT ? (long)(short)GETARG(int) : \
59287113Sfenner	    flags&CHARINT ? (long)(signed char)GETARG(int) : \
59321674Sjkh	    (long)GETARG(int))
5941573Srgrimes#define	UARG() \
59521674Sjkh	(flags&LONGINT ? GETARG(u_long) : \
59621674Sjkh	    flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
59787113Sfenner	    flags&CHARINT ? (u_long)(u_char)GETARG(int) : \
59821674Sjkh	    (u_long)GETARG(u_int))
59987113Sfenner#define	INTMAX_SIZE	(INTMAXT|SIZET|PTRDIFFT|LLONGINT)
60087113Sfenner#define SJARG() \
60187113Sfenner	(flags&INTMAXT ? GETARG(intmax_t) : \
60287113Sfenner	    flags&SIZET ? (intmax_t)GETARG(size_t) : \
60387113Sfenner	    flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \
60487113Sfenner	    (intmax_t)GETARG(long long))
60587113Sfenner#define	UJARG() \
60687113Sfenner	(flags&INTMAXT ? GETARG(uintmax_t) : \
60787113Sfenner	    flags&SIZET ? (uintmax_t)GETARG(size_t) : \
60887113Sfenner	    flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \
60987113Sfenner	    (uintmax_t)GETARG(unsigned long long))
6101573Srgrimes
611103399Stjr	/*
612103399Stjr	 * Get * arguments, including the form *nn$.  Preserve the nextarg
613103399Stjr	 * that the argument can be gotten once the type is determined.
614103399Stjr	 */
61521674Sjkh#define GETASTER(val) \
616103399Stjr	n2 = 0; \
617103399Stjr	cp = fmt; \
618103399Stjr	while (is_digit(*cp)) { \
619103399Stjr		n2 = 10 * n2 + to_digit(*cp); \
620103399Stjr		cp++; \
621103399Stjr	} \
622103399Stjr	if (*cp == '$') { \
623103399Stjr		int hold = nextarg; \
624103399Stjr		if (argtable == NULL) { \
625103399Stjr			argtable = statargtable; \
626103399Stjr			__find_arguments (fmt0, orgap, &argtable); \
627103399Stjr		} \
628103399Stjr		nextarg = n2; \
62921674Sjkh		val = GETARG (int); \
630103399Stjr		nextarg = hold; \
631103399Stjr		fmt = ++cp; \
632103399Stjr	} else { \
633103399Stjr		val = GETARG (int); \
634103399Stjr	}
63587815Sphantom
63688057Sphantom
63787815Sphantom	thousands_sep = '\0';
63887815Sphantom	grouping = NULL;
639103633Stjr	convbuf = NULL;
640128819Sdas#ifndef NO_FLOATING_POINT
64172523Stegge	dtoaresult = NULL;
64287113Sfenner	decimal_point = localeconv()->decimal_point;
64372523Stegge#endif
6441573Srgrimes	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
64571579Sdeischen	if (cantwrite(fp))
6461573Srgrimes		return (EOF);
6471573Srgrimes
6481573Srgrimes	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
6491573Srgrimes	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
65071579Sdeischen	    fp->_file >= 0)
6511573Srgrimes		return (__sbprintf(fp, fmt0, ap));
6521573Srgrimes
6531573Srgrimes	fmt = (char *)fmt0;
654103399Stjr	argtable = NULL;
655103399Stjr	nextarg = 1;
656103876Stjr	va_copy(orgap, ap);
6571573Srgrimes	uio.uio_iov = iovp = iov;
6581573Srgrimes	uio.uio_resid = 0;
6591573Srgrimes	uio.uio_iovcnt = 0;
6601573Srgrimes	ret = 0;
6611573Srgrimes
6621573Srgrimes	/*
6631573Srgrimes	 * Scan the format for conversions (`%' character).
6641573Srgrimes	 */
6651573Srgrimes	for (;;) {
6661573Srgrimes		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
6671573Srgrimes			/* void */;
6681573Srgrimes		if ((n = fmt - cp) != 0) {
66932253Sache			if ((unsigned)ret + n > INT_MAX) {
67031983Sache				ret = EOF;
67131983Sache				goto error;
67231983Sache			}
6731573Srgrimes			PRINT(cp, n);
6741573Srgrimes			ret += n;
6751573Srgrimes		}
6761573Srgrimes		if (ch == '\0')
6771573Srgrimes			goto done;
6781573Srgrimes		fmt++;		/* skip over '%' */
6791573Srgrimes
6801573Srgrimes		flags = 0;
6811573Srgrimes		dprec = 0;
6821573Srgrimes		width = 0;
6831573Srgrimes		prec = -1;
6841573Srgrimes		sign = '\0';
685113146Sdas		ox[1] = '\0';
6861573Srgrimes
6871573Srgrimesrflag:		ch = *fmt++;
6881573Srgrimesreswitch:	switch (ch) {
6891573Srgrimes		case ' ':
69088057Sphantom			/*-
6911573Srgrimes			 * ``If the space and + flags both appear, the space
6921573Srgrimes			 * flag will be ignored.''
6931573Srgrimes			 *	-- ANSI X3J11
6941573Srgrimes			 */
6951573Srgrimes			if (!sign)
6961573Srgrimes				sign = ' ';
6971573Srgrimes			goto rflag;
6981573Srgrimes		case '#':
6991573Srgrimes			flags |= ALT;
7001573Srgrimes			goto rflag;
7011573Srgrimes		case '*':
70288057Sphantom			/*-
7031573Srgrimes			 * ``A negative field width argument is taken as a
7041573Srgrimes			 * - flag followed by a positive field width.''
7051573Srgrimes			 *	-- ANSI X3J11
7061573Srgrimes			 * They don't exclude field widths read from args.
7071573Srgrimes			 */
70821674Sjkh			GETASTER (width);
70921674Sjkh			if (width >= 0)
7101573Srgrimes				goto rflag;
7111573Srgrimes			width = -width;
7121573Srgrimes			/* FALLTHROUGH */
7131573Srgrimes		case '-':
7141573Srgrimes			flags |= LADJUST;
7151573Srgrimes			goto rflag;
7161573Srgrimes		case '+':
7171573Srgrimes			sign = '+';
7181573Srgrimes			goto rflag;
71987113Sfenner		case '\'':
72087815Sphantom			flags |= GROUPING;
72187815Sphantom			thousands_sep = *(localeconv()->thousands_sep);
72287815Sphantom			grouping = localeconv()->grouping;
72387113Sfenner			goto rflag;
7241573Srgrimes		case '.':
7251573Srgrimes			if ((ch = *fmt++) == '*') {
726113191Sdas				GETASTER (prec);
7271573Srgrimes				goto rflag;
7281573Srgrimes			}
729113191Sdas			prec = 0;
7301573Srgrimes			while (is_digit(ch)) {
731113191Sdas				prec = 10 * prec + to_digit(ch);
7321573Srgrimes				ch = *fmt++;
7331573Srgrimes			}
7341573Srgrimes			goto reswitch;
7351573Srgrimes		case '0':
73688057Sphantom			/*-
7371573Srgrimes			 * ``Note that 0 is taken as a flag, not as the
7381573Srgrimes			 * beginning of a field width.''
7391573Srgrimes			 *	-- ANSI X3J11
7401573Srgrimes			 */
7411573Srgrimes			flags |= ZEROPAD;
7421573Srgrimes			goto rflag;
7431573Srgrimes		case '1': case '2': case '3': case '4':
7441573Srgrimes		case '5': case '6': case '7': case '8': case '9':
7451573Srgrimes			n = 0;
7461573Srgrimes			do {
7471573Srgrimes				n = 10 * n + to_digit(ch);
7481573Srgrimes				ch = *fmt++;
7491573Srgrimes			} while (is_digit(ch));
75021674Sjkh			if (ch == '$') {
75121674Sjkh				nextarg = n;
752103399Stjr				if (argtable == NULL) {
753103399Stjr					argtable = statargtable;
754103399Stjr					__find_arguments (fmt0, orgap,
755103399Stjr					    &argtable);
75621674Sjkh				}
75721674Sjkh				goto rflag;
758103399Stjr			}
7591573Srgrimes			width = n;
7601573Srgrimes			goto reswitch;
761128819Sdas#ifndef NO_FLOATING_POINT
7621573Srgrimes		case 'L':
7631573Srgrimes			flags |= LONGDBL;
7641573Srgrimes			goto rflag;
7651573Srgrimes#endif
7661573Srgrimes		case 'h':
76787113Sfenner			if (flags & SHORTINT) {
76887113Sfenner				flags &= ~SHORTINT;
76987113Sfenner				flags |= CHARINT;
77087113Sfenner			} else
77187113Sfenner				flags |= SHORTINT;
7721573Srgrimes			goto rflag;
77387113Sfenner		case 'j':
77487113Sfenner			flags |= INTMAXT;
77587113Sfenner			goto rflag;
7761573Srgrimes		case 'l':
77787113Sfenner			if (flags & LONGINT) {
77887113Sfenner				flags &= ~LONGINT;
77987113Sfenner				flags |= LLONGINT;
78087113Sfenner			} else
78144674Sdfr				flags |= LONGINT;
7821573Srgrimes			goto rflag;
7831573Srgrimes		case 'q':
78487113Sfenner			flags |= LLONGINT;	/* not necessarily */
7851573Srgrimes			goto rflag;
78687113Sfenner		case 't':
78787113Sfenner			flags |= PTRDIFFT;
78887113Sfenner			goto rflag;
78987113Sfenner		case 'z':
79087113Sfenner			flags |= SIZET;
79187113Sfenner			goto rflag;
792105204Stjr		case 'C':
793105204Stjr			flags |= LONGINT;
794105204Stjr			/*FALLTHROUGH*/
7951573Srgrimes		case 'c':
796103633Stjr			if (flags & LONGINT) {
797128002Stjr				static const mbstate_t initial;
798128002Stjr				mbstate_t mbs;
799103633Stjr				size_t mbseqlen;
800103633Stjr
801128002Stjr				mbs = initial;
802103633Stjr				mbseqlen = wcrtomb(cp = buf,
803128002Stjr				    (wchar_t)GETARG(wint_t), &mbs);
804105234Stjr				if (mbseqlen == (size_t)-1) {
805105234Stjr					fp->_flags |= __SERR;
806103633Stjr					goto error;
807105234Stjr				}
808103633Stjr				size = (int)mbseqlen;
809103633Stjr			} else {
810103633Stjr				*(cp = buf) = GETARG(int);
811103633Stjr				size = 1;
812103633Stjr			}
8131573Srgrimes			sign = '\0';
8141573Srgrimes			break;
8151573Srgrimes		case 'D':
8161573Srgrimes			flags |= LONGINT;
8171573Srgrimes			/*FALLTHROUGH*/
8181573Srgrimes		case 'd':
8191573Srgrimes		case 'i':
82087113Sfenner			if (flags & INTMAX_SIZE) {
82187113Sfenner				ujval = SJARG();
82287113Sfenner				if ((intmax_t)ujval < 0) {
82387113Sfenner					ujval = -ujval;
8241573Srgrimes					sign = '-';
8251573Srgrimes				}
8261573Srgrimes			} else {
8271573Srgrimes				ulval = SARG();
8281573Srgrimes				if ((long)ulval < 0) {
8291573Srgrimes					ulval = -ulval;
8301573Srgrimes					sign = '-';
8311573Srgrimes				}
8321573Srgrimes			}
8331573Srgrimes			base = 10;
8341573Srgrimes			goto number;
835128819Sdas#ifndef NO_FLOATING_POINT
83687113Sfenner		case 'a':
83787113Sfenner		case 'A':
838113146Sdas			if (ch == 'a') {
839113146Sdas				ox[1] = 'x';
840113146Sdas				xdigs = xdigs_lower;
841113146Sdas				expchar = 'p';
842113146Sdas			} else {
843113146Sdas				ox[1] = 'X';
844113146Sdas				xdigs = xdigs_upper;
845113146Sdas				expchar = 'P';
846113146Sdas			}
847124657Sdas			if (prec >= 0)
848124657Sdas				prec++;
849124657Sdas			if (dtoaresult != NULL)
850124657Sdas				freedtoa(dtoaresult);
851113146Sdas			if (flags & LONGDBL) {
852124657Sdas				fparg.ldbl = GETARG(long double);
853113146Sdas				dtoaresult = cp =
854113146Sdas				    __hldtoa(fparg.ldbl, xdigs, prec,
855113146Sdas				    &expt, &signflag, &dtoaend);
856113146Sdas			} else {
857113146Sdas				fparg.dbl = GETARG(double);
858113146Sdas				dtoaresult = cp =
859113146Sdas				    __hdtoa(fparg.dbl, xdigs, prec,
860113146Sdas				    &expt, &signflag, &dtoaend);
861113146Sdas			}
862124657Sdas			if (prec < 0)
863124657Sdas				prec = dtoaend - cp;
864124657Sdas			if (expt == INT_MAX)
865124657Sdas				ox[1] = '\0';
866124657Sdas			goto fp_common;
8677033Sbde		case 'e':
8681573Srgrimes		case 'E':
869113146Sdas			expchar = ch;
870113146Sdas			if (prec < 0)	/* account for digit before decpt */
871113146Sdas				prec = DEFPREC + 1;
872113146Sdas			else
873113146Sdas				prec++;
874113146Sdas			goto fp_begin;
8757033Sbde		case 'f':
87687113Sfenner		case 'F':
877113146Sdas			expchar = '\0';
8787033Sbde			goto fp_begin;
8791573Srgrimes		case 'g':
8801573Srgrimes		case 'G':
881113146Sdas			expchar = ch - ('g' - 'e');
8827033Sbde			if (prec == 0)
8837033Sbde				prec = 1;
884113146Sdasfp_begin:
885113146Sdas			if (prec < 0)
8861573Srgrimes				prec = DEFPREC;
887113146Sdas			if (dtoaresult != NULL)
888113146Sdas				freedtoa(dtoaresult);
889113146Sdas			if (flags & LONGDBL) {
890113146Sdas				fparg.ldbl = GETARG(long double);
891113146Sdas				dtoaresult = cp =
892113146Sdas				    __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
893113146Sdas				    &expt, &signflag, &dtoaend);
894113146Sdas			} else {
895113146Sdas				fparg.dbl = GETARG(double);
896113146Sdas				dtoaresult = cp =
897113146Sdas				    dtoa(fparg.dbl, expchar ? 2 : 3, prec,
898113146Sdas				    &expt, &signflag, &dtoaend);
899113146Sdas				if (expt == 9999)
900113146Sdas					expt = INT_MAX;
9011573Srgrimes			}
902124657Sdasfp_common:
903113146Sdas			if (signflag)
904113146Sdas				sign = '-';
905113146Sdas			if (expt == INT_MAX) {	/* inf or nan */
906113146Sdas				if (*cp == 'N') {
907113146Sdas					cp = (ch >= 'a') ? "nan" : "NAN";
908113146Sdas					sign = '\0';
909113146Sdas				} else
910113146Sdas					cp = (ch >= 'a') ? "inf" : "INF";
9111573Srgrimes				size = 3;
9121573Srgrimes				break;
9131573Srgrimes			}
9141573Srgrimes			flags |= FPT;
915113146Sdas			ndig = dtoaend - cp;
9161573Srgrimes			if (ch == 'g' || ch == 'G') {
917113146Sdas				if (expt > -4 && expt <= prec) {
918113146Sdas					/* Make %[gG] smell like %[fF] */
919113146Sdas					expchar = '\0';
920113146Sdas					if (flags & ALT)
921113146Sdas						prec -= expt;
922113146Sdas					else
923113146Sdas						prec = ndig - expt;
924113146Sdas					if (prec < 0)
925113146Sdas						prec = 0;
926113723Sdas				} else {
927113723Sdas					/*
928113723Sdas					 * Make %[gG] smell like %[eE], but
929113723Sdas					 * trim trailing zeroes if no # flag.
930113723Sdas					 */
931113723Sdas					if (!(flags & ALT))
932113723Sdas						prec = ndig;
933113146Sdas				}
9348870Srgrimes			}
935113146Sdas			if (expchar) {
936113146Sdas				expsize = exponent(expstr, expt - 1, expchar);
937113146Sdas				size = expsize + prec;
938113191Sdas				if (prec > 1 || flags & ALT)
9391573Srgrimes					++size;
940113146Sdas			} else {
941113468Sdas				/* space for digits before decimal point */
942113468Sdas				if (expt > 0)
9431573Srgrimes					size = expt;
944113468Sdas				else	/* "0" */
945113468Sdas					size = 1;
946113468Sdas				/* space for decimal pt and following digits */
947113468Sdas				if (prec || flags & ALT)
948113468Sdas					size += prec + 1;
949113146Sdas				if (grouping && expt > 0) {
950113146Sdas					/* space for thousands' grouping */
951113146Sdas					nseps = nrepeats = 0;
952113146Sdas					lead = expt;
953113146Sdas					while (*grouping != CHAR_MAX) {
954113146Sdas						if (lead <= *grouping)
955113146Sdas							break;
956113146Sdas						lead -= *grouping;
957113146Sdas						if (*(grouping+1)) {
958113146Sdas							nseps++;
959113146Sdas							grouping++;
960113146Sdas						} else
961113146Sdas							nrepeats++;
962113146Sdas					}
963113146Sdas					size += nseps + nrepeats;
964113146Sdas				} else
965113194Sdas					lead = expt;
966113146Sdas			}
9671573Srgrimes			break;
968128819Sdas#endif /* !NO_FLOATING_POINT */
9691573Srgrimes		case 'n':
97087113Sfenner			/*
97187113Sfenner			 * Assignment-like behavior is specified if the
97287113Sfenner			 * value overflows or is otherwise unrepresentable.
97387113Sfenner			 * C99 says to use `signed char' for %hhn conversions.
97487113Sfenner			 */
97587113Sfenner			if (flags & LLONGINT)
97687113Sfenner				*GETARG(long long *) = ret;
97787113Sfenner			else if (flags & SIZET)
97887113Sfenner				*GETARG(ssize_t *) = (ssize_t)ret;
97987113Sfenner			else if (flags & PTRDIFFT)
98087113Sfenner				*GETARG(ptrdiff_t *) = ret;
98187113Sfenner			else if (flags & INTMAXT)
98287113Sfenner				*GETARG(intmax_t *) = ret;
9831573Srgrimes			else if (flags & LONGINT)
98431980Sache				*GETARG(long *) = ret;
9851573Srgrimes			else if (flags & SHORTINT)
98631980Sache				*GETARG(short *) = ret;
98787113Sfenner			else if (flags & CHARINT)
98887113Sfenner				*GETARG(signed char *) = ret;
9891573Srgrimes			else
99031980Sache				*GETARG(int *) = ret;
9911573Srgrimes			continue;	/* no output */
9921573Srgrimes		case 'O':
9931573Srgrimes			flags |= LONGINT;
9941573Srgrimes			/*FALLTHROUGH*/
9951573Srgrimes		case 'o':
99687113Sfenner			if (flags & INTMAX_SIZE)
99787113Sfenner				ujval = UJARG();
9981573Srgrimes			else
9991573Srgrimes				ulval = UARG();
10001573Srgrimes			base = 8;
10011573Srgrimes			goto nosign;
10021573Srgrimes		case 'p':
100388057Sphantom			/*-
10041573Srgrimes			 * ``The argument shall be a pointer to void.  The
10051573Srgrimes			 * value of the pointer is converted to a sequence
10061573Srgrimes			 * of printable characters, in an implementation-
10071573Srgrimes			 * defined manner.''
10081573Srgrimes			 *	-- ANSI X3J11
10091573Srgrimes			 */
101087113Sfenner			ujval = (uintmax_t)(uintptr_t)GETARG(void *);
10111573Srgrimes			base = 16;
1012113146Sdas			xdigs = xdigs_lower;
1013113146Sdas			flags = flags | INTMAXT;
1014113146Sdas			ox[1] = 'x';
10151573Srgrimes			goto nosign;
1016105204Stjr		case 'S':
1017105204Stjr			flags |= LONGINT;
1018105204Stjr			/*FALLTHROUGH*/
10191573Srgrimes		case 's':
1020103633Stjr			if (flags & LONGINT) {
1021103633Stjr				wchar_t *wcp;
1022103633Stjr
1023103633Stjr				if (convbuf != NULL)
1024103633Stjr					free(convbuf);
1025103633Stjr				if ((wcp = GETARG(wchar_t *)) == NULL)
1026103633Stjr					cp = "(null)";
1027103633Stjr				else {
1028103633Stjr					convbuf = __wcsconv(wcp, prec);
1029105234Stjr					if (convbuf == NULL) {
1030105234Stjr						fp->_flags |= __SERR;
1031103633Stjr						goto error;
1032105234Stjr					}
1033103633Stjr					cp = convbuf;
1034103633Stjr				}
1035103633Stjr			} else if ((cp = GETARG(char *)) == NULL)
10361573Srgrimes				cp = "(null)";
10371573Srgrimes			if (prec >= 0) {
10381573Srgrimes				/*
10391573Srgrimes				 * can't use strlen; can only look for the
10401573Srgrimes				 * NUL in the first `prec' characters, and
10411573Srgrimes				 * strlen() will go further.
10421573Srgrimes				 */
104316586Sjraynard				char *p = memchr(cp, 0, (size_t)prec);
10441573Srgrimes
10451573Srgrimes				if (p != NULL) {
10461573Srgrimes					size = p - cp;
10471573Srgrimes					if (size > prec)
10481573Srgrimes						size = prec;
10491573Srgrimes				} else
10501573Srgrimes					size = prec;
10511573Srgrimes			} else
10521573Srgrimes				size = strlen(cp);
10531573Srgrimes			sign = '\0';
10541573Srgrimes			break;
10551573Srgrimes		case 'U':
10561573Srgrimes			flags |= LONGINT;
10571573Srgrimes			/*FALLTHROUGH*/
10581573Srgrimes		case 'u':
105987113Sfenner			if (flags & INTMAX_SIZE)
106087113Sfenner				ujval = UJARG();
10611573Srgrimes			else
10621573Srgrimes				ulval = UARG();
10631573Srgrimes			base = 10;
10641573Srgrimes			goto nosign;
10651573Srgrimes		case 'X':
1066113146Sdas			xdigs = xdigs_upper;
10671573Srgrimes			goto hex;
10681573Srgrimes		case 'x':
1069113146Sdas			xdigs = xdigs_lower;
107087113Sfennerhex:
107187113Sfenner			if (flags & INTMAX_SIZE)
107287113Sfenner				ujval = UJARG();
10731573Srgrimes			else
10741573Srgrimes				ulval = UARG();
10751573Srgrimes			base = 16;
10761573Srgrimes			/* leading 0x/X only if non-zero */
10771573Srgrimes			if (flags & ALT &&
107887113Sfenner			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
1079113146Sdas				ox[1] = ch;
10801573Srgrimes
108187815Sphantom			flags &= ~GROUPING;
10821573Srgrimes			/* unsigned conversions */
10831573Srgrimesnosign:			sign = '\0';
108488057Sphantom			/*-
10851573Srgrimes			 * ``... diouXx conversions ... if a precision is
10861573Srgrimes			 * specified, the 0 flag will be ignored.''
10871573Srgrimes			 *	-- ANSI X3J11
10881573Srgrimes			 */
10891573Srgrimesnumber:			if ((dprec = prec) >= 0)
10901573Srgrimes				flags &= ~ZEROPAD;
10911573Srgrimes
109288057Sphantom			/*-
10931573Srgrimes			 * ``The result of converting a zero value with an
10941573Srgrimes			 * explicit precision of zero is no characters.''
10951573Srgrimes			 *	-- ANSI X3J11
10961573Srgrimes			 */
10971573Srgrimes			cp = buf + BUF;
109887113Sfenner			if (flags & INTMAX_SIZE) {
109987113Sfenner				if (ujval != 0 || prec != 0)
110087113Sfenner					cp = __ujtoa(ujval, cp, base,
110187815Sphantom					    flags & ALT, xdigs,
110287815Sphantom					    flags & GROUPING, thousands_sep,
110387815Sphantom					    grouping);
11041573Srgrimes			} else {
11051573Srgrimes				if (ulval != 0 || prec != 0)
11061573Srgrimes					cp = __ultoa(ulval, cp, base,
110787815Sphantom					    flags & ALT, xdigs,
110887815Sphantom					    flags & GROUPING, thousands_sep,
110987815Sphantom					    grouping);
11101573Srgrimes			}
11111573Srgrimes			size = buf + BUF - cp;
1112113142Sdas			if (size > BUF)	/* should never happen */
1113113142Sdas				abort();
11141573Srgrimes			break;
11151573Srgrimes		default:	/* "%?" prints ?, unless ? is NUL */
11161573Srgrimes			if (ch == '\0')
11171573Srgrimes				goto done;
11181573Srgrimes			/* pretend it was %c with argument ch */
11191573Srgrimes			cp = buf;
11201573Srgrimes			*cp = ch;
11211573Srgrimes			size = 1;
11221573Srgrimes			sign = '\0';
11231573Srgrimes			break;
11241573Srgrimes		}
11251573Srgrimes
11261573Srgrimes		/*
11271573Srgrimes		 * All reasonable formats wind up here.  At this point, `cp'
11281573Srgrimes		 * points to a string which (if not flags&LADJUST) should be
11291573Srgrimes		 * padded out to `width' places.  If flags&ZEROPAD, it should
11301573Srgrimes		 * first be prefixed by any sign or other prefix; otherwise,
11311573Srgrimes		 * it should be blank padded before the prefix is emitted.
11321573Srgrimes		 * After any left-hand padding and prefixing, emit zeroes
11331573Srgrimes		 * required by a decimal [diouxX] precision, then print the
11341573Srgrimes		 * string proper, then emit zeroes required by any leftover
11351573Srgrimes		 * floating precision; finally, if LADJUST, pad with blanks.
11361573Srgrimes		 *
11371573Srgrimes		 * Compute actual size, so we know how much to pad.
113814727Sfenner		 * size excludes decimal prec; realsz includes it.
11391573Srgrimes		 */
114014727Sfenner		realsz = dprec > size ? dprec : size;
11411573Srgrimes		if (sign)
114214727Sfenner			realsz++;
1143124657Sdas		if (ox[1])
114414727Sfenner			realsz += 2;
11451573Srgrimes
114631983Sache		prsize = width > realsz ? width : realsz;
114732253Sache		if ((unsigned)ret + prsize > INT_MAX) {
114831983Sache			ret = EOF;
114931983Sache			goto error;
115031983Sache		}
115131983Sache
11521573Srgrimes		/* right-adjusting blank padding */
11531573Srgrimes		if ((flags & (LADJUST|ZEROPAD)) == 0)
11541573Srgrimes			PAD(width - realsz, blanks);
11551573Srgrimes
11561573Srgrimes		/* prefix */
1157124657Sdas		if (sign)
11581573Srgrimes			PRINT(&sign, 1);
1159124657Sdas
1160124657Sdas		if (ox[1]) {	/* ox[1] is either x, X, or \0 */
11611573Srgrimes			ox[0] = '0';
11621573Srgrimes			PRINT(ox, 2);
11631573Srgrimes		}
11641573Srgrimes
11651573Srgrimes		/* right-adjusting zero padding */
11661573Srgrimes		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
11671573Srgrimes			PAD(width - realsz, zeroes);
11681573Srgrimes
11691573Srgrimes		/* leading zeroes from decimal precision */
117014727Sfenner		PAD(dprec - size, zeroes);
11711573Srgrimes
11721573Srgrimes		/* the string or number proper */
1173128819Sdas#ifndef NO_FLOATING_POINT
11741573Srgrimes		if ((flags & FPT) == 0) {
11751573Srgrimes			PRINT(cp, size);
11761573Srgrimes		} else {	/* glue together f_p fragments */
1177113146Sdas			if (!expchar) {	/* %[fF] or sufficiently short %[gG] */
1178113146Sdas				if (expt <= 0) {
1179113468Sdas					PRINT(zeroes, 1);
1180113468Sdas					if (prec || flags & ALT)
1181113468Sdas						PRINT(decimal_point, 1);
11821573Srgrimes					PAD(-expt, zeroes);
1183113191Sdas					/* already handled initial 0's */
1184113191Sdas					prec += expt;
11851573Srgrimes				} else {
1186113191Sdas					PRINTANDPAD(cp, dtoaend, lead, zeroes);
1187113146Sdas					cp += lead;
1188113146Sdas					if (grouping) {
1189113146Sdas						while (nseps>0 || nrepeats>0) {
1190113146Sdas							if (nrepeats > 0)
1191113146Sdas								nrepeats--;
1192113146Sdas							else {
1193113146Sdas								grouping--;
1194113146Sdas								nseps--;
1195113146Sdas							}
1196113146Sdas							PRINT(&thousands_sep,
1197113146Sdas							    1);
1198113191Sdas							PRINTANDPAD(cp,dtoaend,
1199113191Sdas							    *grouping, zeroes);
1200113146Sdas							cp += *grouping;
1201113146Sdas						}
1202113191Sdas						if (cp > dtoaend)
1203113191Sdas							cp = dtoaend;
1204113146Sdas					}
1205113146Sdas					if (prec || flags & ALT)
1206113146Sdas						PRINT(decimal_point,1);
12071573Srgrimes				}
1208113191Sdas				PRINTANDPAD(cp, dtoaend, prec, zeroes);
1209113146Sdas			} else {	/* %[eE] or sufficiently long %[gG] */
1210113191Sdas				if (prec > 1 || flags & ALT) {
1211113146Sdas					buf[0] = *cp++;
1212113146Sdas					buf[1] = *decimal_point;
1213113146Sdas					PRINT(buf, 2);
1214113146Sdas					PRINT(cp, ndig-1);
1215113146Sdas					PAD(prec - ndig, zeroes);
12161573Srgrimes				} else	/* XeYYY */
12171573Srgrimes					PRINT(cp, 1);
12181573Srgrimes				PRINT(expstr, expsize);
12191573Srgrimes			}
12201573Srgrimes		}
12211573Srgrimes#else
12221573Srgrimes		PRINT(cp, size);
12231573Srgrimes#endif
12241573Srgrimes		/* left-adjusting padding (always blank) */
12251573Srgrimes		if (flags & LADJUST)
12261573Srgrimes			PAD(width - realsz, blanks);
12271573Srgrimes
12281573Srgrimes		/* finally, adjust ret */
122931983Sache		ret += prsize;
12301573Srgrimes
12311573Srgrimes		FLUSH();	/* copy out the I/O vectors */
12321573Srgrimes	}
12331573Srgrimesdone:
12341573Srgrimes	FLUSH();
12351573Srgrimeserror:
1236128819Sdas#ifndef NO_FLOATING_POINT
123772523Stegge	if (dtoaresult != NULL)
1238113146Sdas		freedtoa(dtoaresult);
123972523Stegge#endif
1240103633Stjr	if (convbuf != NULL)
1241103633Stjr		free(convbuf);
124213545Sjulian	if (__sferror(fp))
124313545Sjulian		ret = EOF;
1244103399Stjr	if ((argtable != NULL) && (argtable != statargtable))
1245103399Stjr		free (argtable);
124613545Sjulian	return (ret);
12471573Srgrimes	/* NOTREACHED */
12481573Srgrimes}
12491573Srgrimes
125021674Sjkh/*
125121674Sjkh * Find all arguments when a positional parameter is encountered.  Returns a
125221674Sjkh * table, indexed by argument number, of pointers to each arguments.  The
125321674Sjkh * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
125470725Sarchie * It will be replaces with a malloc-ed one if it overflows.
125521674Sjkh */
125621674Sjkhstatic void
125784962Sbde__find_arguments (const char *fmt0, va_list ap, union arg **argtable)
125821674Sjkh{
125971579Sdeischen	char *fmt;		/* format string */
126071579Sdeischen	int ch;			/* character from fmt */
126171579Sdeischen	int n, n2;		/* handy integer (short term usage) */
126271579Sdeischen	char *cp;		/* handy char pointer (short term usage) */
126371579Sdeischen	int flags;		/* flags as above */
126421674Sjkh	int width;		/* width from format (%8d), or 0 */
126587113Sfenner	enum typeid *typetable; /* table of types */
126687113Sfenner	enum typeid stattypetable [STATIC_ARG_TBL_SIZE];
126721674Sjkh	int tablesize;		/* current size of type table */
126821674Sjkh	int tablemax;		/* largest used index in table */
126921674Sjkh	int nextarg;		/* 1-based argument index */
127021674Sjkh
127121674Sjkh	/*
127221674Sjkh	 * Add an argument type to the table, expanding if necessary.
127321674Sjkh	 */
127421674Sjkh#define ADDTYPE(type) \
127521674Sjkh	((nextarg >= tablesize) ? \
127621674Sjkh		__grow_type_table(nextarg, &typetable, &tablesize) : 0, \
127770725Sarchie	(nextarg > tablemax) ? tablemax = nextarg : 0, \
127870725Sarchie	typetable[nextarg++] = type)
127921674Sjkh
128021674Sjkh#define	ADDSARG() \
128187113Sfenner	((flags&INTMAXT) ? ADDTYPE(T_INTMAXT) : \
128287113Sfenner		((flags&SIZET) ? ADDTYPE(T_SIZET) : \
128387113Sfenner		((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \
128487113Sfenner		((flags&LLONGINT) ? ADDTYPE(T_LLONG) : \
128587113Sfenner		((flags&LONGINT) ? ADDTYPE(T_LONG) : ADDTYPE(T_INT))))))
128621674Sjkh
128721674Sjkh#define	ADDUARG() \
128887113Sfenner	((flags&INTMAXT) ? ADDTYPE(T_UINTMAXT) : \
128987113Sfenner		((flags&SIZET) ? ADDTYPE(T_SIZET) : \
129087113Sfenner		((flags&PTRDIFFT) ? ADDTYPE(T_PTRDIFFT) : \
129187113Sfenner		((flags&LLONGINT) ? ADDTYPE(T_U_LLONG) : \
129287113Sfenner		((flags&LONGINT) ? ADDTYPE(T_U_LONG) : ADDTYPE(T_U_INT))))))
129321674Sjkh
129421674Sjkh	/*
129521674Sjkh	 * Add * arguments to the type array.
129621674Sjkh	 */
129721674Sjkh#define ADDASTER() \
129821674Sjkh	n2 = 0; \
129921674Sjkh	cp = fmt; \
130021674Sjkh	while (is_digit(*cp)) { \
130121674Sjkh		n2 = 10 * n2 + to_digit(*cp); \
130221674Sjkh		cp++; \
130321674Sjkh	} \
130421674Sjkh	if (*cp == '$') { \
130521674Sjkh		int hold = nextarg; \
130621674Sjkh		nextarg = n2; \
130721674Sjkh		ADDTYPE (T_INT); \
130821674Sjkh		nextarg = hold; \
130921674Sjkh		fmt = ++cp; \
131021674Sjkh	} else { \
131121674Sjkh		ADDTYPE (T_INT); \
131221674Sjkh	}
131321674Sjkh	fmt = (char *)fmt0;
131421674Sjkh	typetable = stattypetable;
131521674Sjkh	tablesize = STATIC_ARG_TBL_SIZE;
131621674Sjkh	tablemax = 0;
131721674Sjkh	nextarg = 1;
1318128550Stjr	for (n = 0; n < STATIC_ARG_TBL_SIZE; n++)
1319128550Stjr		typetable[n] = T_UNUSED;
132021674Sjkh
132121674Sjkh	/*
132221674Sjkh	 * Scan the format for conversions (`%' character).
132321674Sjkh	 */
132421674Sjkh	for (;;) {
132521674Sjkh		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
132621674Sjkh			/* void */;
132721674Sjkh		if (ch == '\0')
132821674Sjkh			goto done;
132921674Sjkh		fmt++;		/* skip over '%' */
133021674Sjkh
133121674Sjkh		flags = 0;
133221674Sjkh		width = 0;
133321674Sjkh
133421674Sjkhrflag:		ch = *fmt++;
133521674Sjkhreswitch:	switch (ch) {
133621674Sjkh		case ' ':
133721674Sjkh		case '#':
133821674Sjkh			goto rflag;
133921674Sjkh		case '*':
134021674Sjkh			ADDASTER ();
134121674Sjkh			goto rflag;
134221674Sjkh		case '-':
134321674Sjkh		case '+':
134487113Sfenner		case '\'':
134521674Sjkh			goto rflag;
134621674Sjkh		case '.':
134721674Sjkh			if ((ch = *fmt++) == '*') {
134821674Sjkh				ADDASTER ();
134921674Sjkh				goto rflag;
135021674Sjkh			}
135121674Sjkh			while (is_digit(ch)) {
135221674Sjkh				ch = *fmt++;
135321674Sjkh			}
135421674Sjkh			goto reswitch;
135521674Sjkh		case '0':
135621674Sjkh			goto rflag;
135721674Sjkh		case '1': case '2': case '3': case '4':
135821674Sjkh		case '5': case '6': case '7': case '8': case '9':
135921674Sjkh			n = 0;
136021674Sjkh			do {
136121674Sjkh				n = 10 * n + to_digit(ch);
136221674Sjkh				ch = *fmt++;
136321674Sjkh			} while (is_digit(ch));
136421674Sjkh			if (ch == '$') {
136521674Sjkh				nextarg = n;
136621674Sjkh				goto rflag;
136721674Sjkh			}
136821674Sjkh			width = n;
136921674Sjkh			goto reswitch;
1370128819Sdas#ifndef NO_FLOATING_POINT
137121674Sjkh		case 'L':
137221674Sjkh			flags |= LONGDBL;
137321674Sjkh			goto rflag;
137421674Sjkh#endif
137521674Sjkh		case 'h':
137687113Sfenner			if (flags & SHORTINT) {
137787113Sfenner				flags &= ~SHORTINT;
137887113Sfenner				flags |= CHARINT;
137987113Sfenner			} else
138087113Sfenner				flags |= SHORTINT;
138121674Sjkh			goto rflag;
138287113Sfenner		case 'j':
138387113Sfenner			flags |= INTMAXT;
138487113Sfenner			goto rflag;
138521674Sjkh		case 'l':
138687113Sfenner			if (flags & LONGINT) {
138787113Sfenner				flags &= ~LONGINT;
138887113Sfenner				flags |= LLONGINT;
138987113Sfenner			} else
139044674Sdfr				flags |= LONGINT;
139121674Sjkh			goto rflag;
139221674Sjkh		case 'q':
139387113Sfenner			flags |= LLONGINT;	/* not necessarily */
139421674Sjkh			goto rflag;
139587113Sfenner		case 't':
139687113Sfenner			flags |= PTRDIFFT;
139787113Sfenner			goto rflag;
139887113Sfenner		case 'z':
139987113Sfenner			flags |= SIZET;
140087113Sfenner			goto rflag;
1401105204Stjr		case 'C':
1402105204Stjr			flags |= LONGINT;
1403105204Stjr			/*FALLTHROUGH*/
140421674Sjkh		case 'c':
1405103633Stjr			if (flags & LONGINT)
1406103633Stjr				ADDTYPE(T_WINT);
1407103633Stjr			else
1408103633Stjr				ADDTYPE(T_INT);
140921674Sjkh			break;
141021674Sjkh		case 'D':
141121674Sjkh			flags |= LONGINT;
141221674Sjkh			/*FALLTHROUGH*/
141321674Sjkh		case 'd':
141421674Sjkh		case 'i':
141587113Sfenner			ADDSARG();
141621674Sjkh			break;
1417128819Sdas#ifndef NO_FLOATING_POINT
141887113Sfenner		case 'a':
141987113Sfenner		case 'A':
142021674Sjkh		case 'e':
142121674Sjkh		case 'E':
142221674Sjkh		case 'f':
142321674Sjkh		case 'g':
142421674Sjkh		case 'G':
142521674Sjkh			if (flags & LONGDBL)
142621674Sjkh				ADDTYPE(T_LONG_DOUBLE);
142721674Sjkh			else
142821674Sjkh				ADDTYPE(T_DOUBLE);
142921674Sjkh			break;
1430128819Sdas#endif /* !NO_FLOATING_POINT */
143121674Sjkh		case 'n':
143287113Sfenner			if (flags & INTMAXT)
143387113Sfenner				ADDTYPE(TP_INTMAXT);
143487113Sfenner			else if (flags & PTRDIFFT)
143587113Sfenner				ADDTYPE(TP_PTRDIFFT);
143687113Sfenner			else if (flags & SIZET)
143787113Sfenner				ADDTYPE(TP_SIZET);
143887113Sfenner			else if (flags & LLONGINT)
143987113Sfenner				ADDTYPE(TP_LLONG);
144021674Sjkh			else if (flags & LONGINT)
144121674Sjkh				ADDTYPE(TP_LONG);
144221674Sjkh			else if (flags & SHORTINT)
144321674Sjkh				ADDTYPE(TP_SHORT);
144487113Sfenner			else if (flags & CHARINT)
144587113Sfenner				ADDTYPE(TP_SCHAR);
144621674Sjkh			else
144721674Sjkh				ADDTYPE(TP_INT);
144821674Sjkh			continue;	/* no output */
144921674Sjkh		case 'O':
145021674Sjkh			flags |= LONGINT;
145121674Sjkh			/*FALLTHROUGH*/
145221674Sjkh		case 'o':
145387113Sfenner			ADDUARG();
145421674Sjkh			break;
145521674Sjkh		case 'p':
145621674Sjkh			ADDTYPE(TP_VOID);
145721674Sjkh			break;
1458105204Stjr		case 'S':
1459105204Stjr			flags |= LONGINT;
1460105204Stjr			/*FALLTHROUGH*/
146121674Sjkh		case 's':
1462103633Stjr			if (flags & LONGINT)
1463103633Stjr				ADDTYPE(TP_WCHAR);
1464103633Stjr			else
1465103633Stjr				ADDTYPE(TP_CHAR);
146621674Sjkh			break;
146721674Sjkh		case 'U':
146821674Sjkh			flags |= LONGINT;
146921674Sjkh			/*FALLTHROUGH*/
147021674Sjkh		case 'u':
147121674Sjkh		case 'X':
147221674Sjkh		case 'x':
147387113Sfenner			ADDUARG();
147421674Sjkh			break;
147521674Sjkh		default:	/* "%?" prints ?, unless ? is NUL */
147621674Sjkh			if (ch == '\0')
147721674Sjkh				goto done;
147821674Sjkh			break;
147921674Sjkh		}
148021674Sjkh	}
148121674Sjkhdone:
148221674Sjkh	/*
148321674Sjkh	 * Build the argument table.
148421674Sjkh	 */
148521674Sjkh	if (tablemax >= STATIC_ARG_TBL_SIZE) {
148684922Sdfr		*argtable = (union arg *)
148784922Sdfr		    malloc (sizeof (union arg) * (tablemax + 1));
148821674Sjkh	}
14891573Srgrimes
149084922Sdfr	(*argtable) [0].intarg = 0;
149121674Sjkh	for (n = 1; n <= tablemax; n++) {
149221674Sjkh		switch (typetable [n]) {
149387113Sfenner		    case T_UNUSED: /* whoops! */
149484922Sdfr			(*argtable) [n].intarg = va_arg (ap, int);
149521674Sjkh			break;
149687113Sfenner		    case TP_SCHAR:
149787113Sfenner			(*argtable) [n].pschararg = va_arg (ap, signed char *);
149821674Sjkh			break;
149921674Sjkh		    case TP_SHORT:
150084922Sdfr			(*argtable) [n].pshortarg = va_arg (ap, short *);
150121674Sjkh			break;
150221674Sjkh		    case T_INT:
150384922Sdfr			(*argtable) [n].intarg = va_arg (ap, int);
150421674Sjkh			break;
150521674Sjkh		    case T_U_INT:
150684922Sdfr			(*argtable) [n].uintarg = va_arg (ap, unsigned int);
150721674Sjkh			break;
150821674Sjkh		    case TP_INT:
150984922Sdfr			(*argtable) [n].pintarg = va_arg (ap, int *);
151021674Sjkh			break;
151121674Sjkh		    case T_LONG:
151284922Sdfr			(*argtable) [n].longarg = va_arg (ap, long);
151321674Sjkh			break;
151421674Sjkh		    case T_U_LONG:
151584922Sdfr			(*argtable) [n].ulongarg = va_arg (ap, unsigned long);
151621674Sjkh			break;
151721674Sjkh		    case TP_LONG:
151884922Sdfr			(*argtable) [n].plongarg = va_arg (ap, long *);
151921674Sjkh			break;
152087113Sfenner		    case T_LLONG:
152187113Sfenner			(*argtable) [n].longlongarg = va_arg (ap, long long);
152221674Sjkh			break;
152387113Sfenner		    case T_U_LLONG:
152487113Sfenner			(*argtable) [n].ulonglongarg = va_arg (ap, unsigned long long);
152521674Sjkh			break;
152687113Sfenner		    case TP_LLONG:
152787113Sfenner			(*argtable) [n].plonglongarg = va_arg (ap, long long *);
152821674Sjkh			break;
152987113Sfenner		    case T_PTRDIFFT:
153087113Sfenner			(*argtable) [n].ptrdiffarg = va_arg (ap, ptrdiff_t);
153187113Sfenner			break;
153287113Sfenner		    case TP_PTRDIFFT:
153387113Sfenner			(*argtable) [n].pptrdiffarg = va_arg (ap, ptrdiff_t *);
153487113Sfenner			break;
153587113Sfenner		    case T_SIZET:
153687113Sfenner			(*argtable) [n].sizearg = va_arg (ap, size_t);
153787113Sfenner			break;
153887113Sfenner		    case TP_SIZET:
153987113Sfenner			(*argtable) [n].psizearg = va_arg (ap, ssize_t *);
154087113Sfenner			break;
154187113Sfenner		    case T_INTMAXT:
154287113Sfenner			(*argtable) [n].intmaxarg = va_arg (ap, intmax_t);
154387113Sfenner			break;
154487113Sfenner		    case T_UINTMAXT:
154587113Sfenner			(*argtable) [n].uintmaxarg = va_arg (ap, uintmax_t);
154687113Sfenner			break;
154787113Sfenner		    case TP_INTMAXT:
154887113Sfenner			(*argtable) [n].pintmaxarg = va_arg (ap, intmax_t *);
154987113Sfenner			break;
1550128819Sdas#ifndef NO_FLOATING_POINT
155121674Sjkh		    case T_DOUBLE:
155284922Sdfr			(*argtable) [n].doublearg = va_arg (ap, double);
155321674Sjkh			break;
155421674Sjkh		    case T_LONG_DOUBLE:
155584922Sdfr			(*argtable) [n].longdoublearg = va_arg (ap, long double);
155621674Sjkh			break;
155784922Sdfr#endif
155821674Sjkh		    case TP_CHAR:
155984922Sdfr			(*argtable) [n].pchararg = va_arg (ap, char *);
156021674Sjkh			break;
156121674Sjkh		    case TP_VOID:
156284922Sdfr			(*argtable) [n].pvoidarg = va_arg (ap, void *);
156321674Sjkh			break;
1564103633Stjr		    case T_WINT:
1565103633Stjr			(*argtable) [n].wintarg = va_arg (ap, wint_t);
1566103633Stjr			break;
1567103633Stjr		    case TP_WCHAR:
1568103633Stjr			(*argtable) [n].pwchararg = va_arg (ap, wchar_t *);
1569103633Stjr			break;
157021674Sjkh		}
157121674Sjkh	}
157221674Sjkh
157321674Sjkh	if ((typetable != NULL) && (typetable != stattypetable))
157421674Sjkh		free (typetable);
157521674Sjkh}
157621674Sjkh
157721674Sjkh/*
157821674Sjkh * Increase the size of the type table.
157921674Sjkh */
158021674Sjkhstatic void
158187113Sfenner__grow_type_table (int nextarg, enum typeid **typetable, int *tablesize)
158221674Sjkh{
158387113Sfenner	enum typeid *const oldtable = *typetable;
158470725Sarchie	const int oldsize = *tablesize;
158587113Sfenner	enum typeid *newtable;
1586128550Stjr	int n, newsize = oldsize * 2;
158721674Sjkh
158870725Sarchie	if (newsize < nextarg + 1)
158970725Sarchie		newsize = nextarg + 1;
159070725Sarchie	if (oldsize == STATIC_ARG_TBL_SIZE) {
1591128550Stjr		if ((newtable = malloc(newsize * sizeof(enum typeid))) == NULL)
159270725Sarchie			abort();			/* XXX handle better */
1593128550Stjr		bcopy(oldtable, newtable, oldsize * sizeof(enum typeid));
159421674Sjkh	} else {
1595128550Stjr		newtable = reallocf(oldtable, newsize * sizeof(enum typeid));
1596128550Stjr		if (newtable == NULL)
159770725Sarchie			abort();			/* XXX handle better */
159821674Sjkh	}
1599128550Stjr	for (n = oldsize; n < newsize; n++)
1600128550Stjr		newtable[n] = T_UNUSED;
160121674Sjkh
160270725Sarchie	*typetable = newtable;
160321674Sjkh	*tablesize = newsize;
160421674Sjkh}
160521674Sjkh
160621674Sjkh
1607128819Sdas#ifndef NO_FLOATING_POINT
160821674Sjkh
16091573Srgrimesstatic int
161071579Sdeischenexponent(char *p0, int exp, int fmtch)
16111573Srgrimes{
161271579Sdeischen	char *p, *t;
1613113142Sdas	char expbuf[MAXEXPDIG];
16141573Srgrimes
16151573Srgrimes	p = p0;
16161573Srgrimes	*p++ = fmtch;
16171573Srgrimes	if (exp < 0) {
16181573Srgrimes		exp = -exp;
16191573Srgrimes		*p++ = '-';
16201573Srgrimes	}
16211573Srgrimes	else
16221573Srgrimes		*p++ = '+';
1623113142Sdas	t = expbuf + MAXEXPDIG;
16241573Srgrimes	if (exp > 9) {
16251573Srgrimes		do {
16261573Srgrimes			*--t = to_char(exp % 10);
16271573Srgrimes		} while ((exp /= 10) > 9);
16281573Srgrimes		*--t = to_char(exp);
1629113142Sdas		for (; t < expbuf + MAXEXPDIG; *p++ = *t++);
16301573Srgrimes	}
16311573Srgrimes	else {
1632113146Sdas		/*
1633113146Sdas		 * Exponents for decimal floating point conversions
1634113146Sdas		 * (%[eEgG]) must be at least two characters long,
1635113146Sdas		 * whereas exponents for hexadecimal conversions can
1636113146Sdas		 * be only one character long.
1637113146Sdas		 */
1638113146Sdas		if (fmtch == 'e' || fmtch == 'E')
1639113146Sdas			*p++ = '0';
16401573Srgrimes		*p++ = to_char(exp);
16411573Srgrimes	}
16421573Srgrimes	return (p - p0);
16431573Srgrimes}
1644128819Sdas#endif /* !NO_FLOATING_POINT */
1645