vfprintf.c revision 31871
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)
3816586Sjraynard#if 0
391573Srgrimesstatic char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 6/4/93";
4016586Sjraynard#endif
4116586Sjraynardstatic const char rcsid[] =
4231871Sbde		"$Id: vfprintf.c,v 1.12 1997/02/22 15:02:40 peter Exp $";
431573Srgrimes#endif /* LIBC_SCCS and not lint */
441573Srgrimes
451573Srgrimes/*
461573Srgrimes * Actual printf innards.
471573Srgrimes *
481573Srgrimes * This code is large and complicated...
491573Srgrimes */
501573Srgrimes
511573Srgrimes#include <sys/types.h>
521573Srgrimes
531573Srgrimes#include <limits.h>
541573Srgrimes#include <stdio.h>
551573Srgrimes#include <stdlib.h>
561573Srgrimes#include <string.h>
571573Srgrimes
581573Srgrimes#if __STDC__
591573Srgrimes#include <stdarg.h>
601573Srgrimes#else
611573Srgrimes#include <varargs.h>
621573Srgrimes#endif
631573Srgrimes
641573Srgrimes#include "local.h"
651573Srgrimes#include "fvwrite.h"
6613545Sjulian#ifdef _THREAD_SAFE
6713545Sjulian#include <pthread.h>
6813545Sjulian#include "pthread_private.h"
6913545Sjulian#endif
701573Srgrimes
711573Srgrimes/* Define FLOATING_POINT to get floating point. */
721573Srgrimes#define	FLOATING_POINT
731573Srgrimes
7416586Sjraynardstatic int	__sprint __P((FILE *, struct __suio *));
7516586Sjraynardstatic int	__sbprintf __P((FILE *, const char *, va_list));
7616586Sjraynardstatic char *	__ultoa __P((u_long, char *, int, int, char *));
7716586Sjraynardstatic char *	__uqtoa __P((u_quad_t, char *, int, int, char *));
7821674Sjkhstatic void	__find_arguments __P((const char *, va_list, void ***));
7921674Sjkhstatic void	__grow_type_table __P((int, unsigned char **, int *));
8016586Sjraynard
811573Srgrimes/*
821573Srgrimes * Flush out all the vectors defined by the given uio,
831573Srgrimes * then reset it so that it can be reused.
841573Srgrimes */
851573Srgrimesstatic int
861573Srgrimes__sprint(fp, uio)
871573Srgrimes	FILE *fp;
881573Srgrimes	register struct __suio *uio;
891573Srgrimes{
901573Srgrimes	register int err;
911573Srgrimes
921573Srgrimes	if (uio->uio_resid == 0) {
931573Srgrimes		uio->uio_iovcnt = 0;
941573Srgrimes		return (0);
951573Srgrimes	}
961573Srgrimes	err = __sfvwrite(fp, uio);
971573Srgrimes	uio->uio_resid = 0;
981573Srgrimes	uio->uio_iovcnt = 0;
991573Srgrimes	return (err);
1001573Srgrimes}
1011573Srgrimes
1021573Srgrimes/*
1031573Srgrimes * Helper function for `fprintf to unbuffered unix file': creates a
1041573Srgrimes * temporary buffer.  We only work on write-only files; this avoids
1051573Srgrimes * worries about ungetc buffers and so forth.
1061573Srgrimes */
1071573Srgrimesstatic int
1081573Srgrimes__sbprintf(fp, fmt, ap)
1091573Srgrimes	register FILE *fp;
1101573Srgrimes	const char *fmt;
1111573Srgrimes	va_list ap;
1121573Srgrimes{
1131573Srgrimes	int ret;
1141573Srgrimes	FILE fake;
1151573Srgrimes	unsigned char buf[BUFSIZ];
1161573Srgrimes
1171573Srgrimes	/* copy the important variables */
1181573Srgrimes	fake._flags = fp->_flags & ~__SNBF;
1191573Srgrimes	fake._file = fp->_file;
1201573Srgrimes	fake._cookie = fp->_cookie;
1211573Srgrimes	fake._write = fp->_write;
1221573Srgrimes
1231573Srgrimes	/* set up the buffer */
1241573Srgrimes	fake._bf._base = fake._p = buf;
1251573Srgrimes	fake._bf._size = fake._w = sizeof(buf);
1261573Srgrimes	fake._lbfsize = 0;	/* not actually used, but Just In Case */
1271573Srgrimes
1281573Srgrimes	/* do the work, then copy any error status */
1291573Srgrimes	ret = vfprintf(&fake, fmt, ap);
1301573Srgrimes	if (ret >= 0 && fflush(&fake))
1311573Srgrimes		ret = EOF;
1321573Srgrimes	if (fake._flags & __SERR)
1331573Srgrimes		fp->_flags |= __SERR;
1341573Srgrimes	return (ret);
1351573Srgrimes}
1361573Srgrimes
1371573Srgrimes/*
1381573Srgrimes * Macros for converting digits to letters and vice versa
1391573Srgrimes */
1401573Srgrimes#define	to_digit(c)	((c) - '0')
1411573Srgrimes#define is_digit(c)	((unsigned)to_digit(c) <= 9)
1421573Srgrimes#define	to_char(n)	((n) + '0')
1431573Srgrimes
1441573Srgrimes/*
1451573Srgrimes * Convert an unsigned long to ASCII for printf purposes, returning
1461573Srgrimes * a pointer to the first character of the string representation.
1471573Srgrimes * Octal numbers can be forced to have a leading zero; hex numbers
1481573Srgrimes * use the given digits.
1491573Srgrimes */
1501573Srgrimesstatic char *
1511573Srgrimes__ultoa(val, endp, base, octzero, xdigs)
1521573Srgrimes	register u_long val;
1531573Srgrimes	char *endp;
1541573Srgrimes	int base, octzero;
1551573Srgrimes	char *xdigs;
1561573Srgrimes{
1571573Srgrimes	register char *cp = endp;
1581573Srgrimes	register long sval;
1591573Srgrimes
1601573Srgrimes	/*
1611573Srgrimes	 * Handle the three cases separately, in the hope of getting
1621573Srgrimes	 * better/faster code.
1631573Srgrimes	 */
1641573Srgrimes	switch (base) {
1651573Srgrimes	case 10:
1661573Srgrimes		if (val < 10) {	/* many numbers are 1 digit */
1671573Srgrimes			*--cp = to_char(val);
1681573Srgrimes			return (cp);
1691573Srgrimes		}
1701573Srgrimes		/*
1711573Srgrimes		 * On many machines, unsigned arithmetic is harder than
1721573Srgrimes		 * signed arithmetic, so we do at most one unsigned mod and
1731573Srgrimes		 * divide; this is sufficient to reduce the range of
1741573Srgrimes		 * the incoming value to where signed arithmetic works.
1751573Srgrimes		 */
1761573Srgrimes		if (val > LONG_MAX) {
1771573Srgrimes			*--cp = to_char(val % 10);
1781573Srgrimes			sval = val / 10;
1791573Srgrimes		} else
1801573Srgrimes			sval = val;
1811573Srgrimes		do {
1821573Srgrimes			*--cp = to_char(sval % 10);
1831573Srgrimes			sval /= 10;
1841573Srgrimes		} while (sval != 0);
1851573Srgrimes		break;
1861573Srgrimes
1871573Srgrimes	case 8:
1881573Srgrimes		do {
1891573Srgrimes			*--cp = to_char(val & 7);
1901573Srgrimes			val >>= 3;
1911573Srgrimes		} while (val);
1921573Srgrimes		if (octzero && *cp != '0')
1931573Srgrimes			*--cp = '0';
1941573Srgrimes		break;
1951573Srgrimes
1961573Srgrimes	case 16:
1971573Srgrimes		do {
1981573Srgrimes			*--cp = xdigs[val & 15];
1991573Srgrimes			val >>= 4;
2001573Srgrimes		} while (val);
2011573Srgrimes		break;
2021573Srgrimes
2031573Srgrimes	default:			/* oops */
2041573Srgrimes		abort();
2051573Srgrimes	}
2061573Srgrimes	return (cp);
2071573Srgrimes}
2081573Srgrimes
2091573Srgrimes/* Identical to __ultoa, but for quads. */
2101573Srgrimesstatic char *
2111573Srgrimes__uqtoa(val, endp, base, octzero, xdigs)
2121573Srgrimes	register u_quad_t val;
2131573Srgrimes	char *endp;
2141573Srgrimes	int base, octzero;
2151573Srgrimes	char *xdigs;
2161573Srgrimes{
2171573Srgrimes	register char *cp = endp;
2181573Srgrimes	register quad_t sval;
2191573Srgrimes
2201573Srgrimes	/* quick test for small values; __ultoa is typically much faster */
2211573Srgrimes	/* (perhaps instead we should run until small, then call __ultoa?) */
2221573Srgrimes	if (val <= ULONG_MAX)
2231573Srgrimes		return (__ultoa((u_long)val, endp, base, octzero, xdigs));
2241573Srgrimes	switch (base) {
2251573Srgrimes	case 10:
2261573Srgrimes		if (val < 10) {
2271573Srgrimes			*--cp = to_char(val % 10);
2281573Srgrimes			return (cp);
2291573Srgrimes		}
2301573Srgrimes		if (val > QUAD_MAX) {
2311573Srgrimes			*--cp = to_char(val % 10);
2321573Srgrimes			sval = val / 10;
2331573Srgrimes		} else
2341573Srgrimes			sval = val;
2351573Srgrimes		do {
2361573Srgrimes			*--cp = to_char(sval % 10);
2371573Srgrimes			sval /= 10;
2381573Srgrimes		} while (sval != 0);
2391573Srgrimes		break;
2401573Srgrimes
2411573Srgrimes	case 8:
2421573Srgrimes		do {
2431573Srgrimes			*--cp = to_char(val & 7);
2441573Srgrimes			val >>= 3;
2451573Srgrimes		} while (val);
2461573Srgrimes		if (octzero && *cp != '0')
2471573Srgrimes			*--cp = '0';
2481573Srgrimes		break;
2491573Srgrimes
2501573Srgrimes	case 16:
2511573Srgrimes		do {
2521573Srgrimes			*--cp = xdigs[val & 15];
2531573Srgrimes			val >>= 4;
2541573Srgrimes		} while (val);
2551573Srgrimes		break;
2561573Srgrimes
2571573Srgrimes	default:
2581573Srgrimes		abort();
2591573Srgrimes	}
2601573Srgrimes	return (cp);
2611573Srgrimes}
2621573Srgrimes
2631573Srgrimes#ifdef FLOATING_POINT
2641573Srgrimes#include <math.h>
2651573Srgrimes#include "floatio.h"
2661573Srgrimes
2671573Srgrimes#define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
2681573Srgrimes#define	DEFPREC		6
2691573Srgrimes
2701573Srgrimesstatic char *cvt __P((double, int, int, char *, int *, int, int *));
2711573Srgrimesstatic int exponent __P((char *, int, int));
2721573Srgrimes
2731573Srgrimes#else /* no FLOATING_POINT */
2741573Srgrimes
2751573Srgrimes#define	BUF		68
2761573Srgrimes
2771573Srgrimes#endif /* FLOATING_POINT */
2781573Srgrimes
27921674Sjkh#define STATIC_ARG_TBL_SIZE 8           /* Size of static argument table. */
2801573Srgrimes
2811573Srgrimes/*
2821573Srgrimes * Flags used during conversion.
2831573Srgrimes */
2841573Srgrimes#define	ALT		0x001		/* alternate form */
2851573Srgrimes#define	HEXPREFIX	0x002		/* add 0x or 0X prefix */
2861573Srgrimes#define	LADJUST		0x004		/* left adjustment */
28731871Sbde#define	LONGDBL		0x008		/* long double */
2881573Srgrimes#define	LONGINT		0x010		/* long integer */
2891573Srgrimes#define	QUADINT		0x020		/* quad integer */
2901573Srgrimes#define	SHORTINT	0x040		/* short integer */
2911573Srgrimes#define	ZEROPAD		0x080		/* zero (as opposed to blank) pad */
2921573Srgrimes#define FPT		0x100		/* Floating point number */
2931573Srgrimesint
2941573Srgrimesvfprintf(fp, fmt0, ap)
2951573Srgrimes	FILE *fp;
2961573Srgrimes	const char *fmt0;
2971573Srgrimes	va_list ap;
2981573Srgrimes{
2991573Srgrimes	register char *fmt;	/* format string */
3001573Srgrimes	register int ch;	/* character from fmt */
30121674Sjkh	register int n, n2;	/* handy integer (short term usage) */
3021573Srgrimes	register char *cp;	/* handy char pointer (short term usage) */
3031573Srgrimes	register struct __siov *iovp;/* for PRINT macro */
3041573Srgrimes	register int flags;	/* flags as above */
3051573Srgrimes	int ret;		/* return value accumulator */
3061573Srgrimes	int width;		/* width from format (%8d), or 0 */
3071573Srgrimes	int prec;		/* precision from format (%.3d), or -1 */
3081573Srgrimes	char sign;		/* sign prefix (' ', '+', '-', or \0) */
3091573Srgrimes#ifdef FLOATING_POINT
3101573Srgrimes	char softsign;		/* temporary negative sign for floats */
3111573Srgrimes	double _double;		/* double precision arguments %[eEfgG] */
3121573Srgrimes	int expt;		/* integer value of exponent */
3131573Srgrimes	int expsize;		/* character count for expstr */
3141573Srgrimes	int ndig;		/* actual number of digits returned by cvt */
3151573Srgrimes	char expstr[7];		/* buffer for exponent string */
3161573Srgrimes#endif
3171573Srgrimes	u_long	ulval;		/* integer arguments %[diouxX] */
3181573Srgrimes	u_quad_t uqval;		/* %q integers */
3191573Srgrimes	int base;		/* base for [diouxX] conversion */
3201573Srgrimes	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
32114727Sfenner	int realsz;		/* field size expanded by dprec, sign, etc */
3221573Srgrimes	int size;		/* size of converted field or string */
3231573Srgrimes	char *xdigs;		/* digits for [xX] conversion */
3241573Srgrimes#define NIOV 8
3251573Srgrimes	struct __suio uio;	/* output information: summary */
3261573Srgrimes	struct __siov iov[NIOV];/* ... and individual io vectors */
3271573Srgrimes	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
3281573Srgrimes	char ox[2];		/* space for 0x hex-prefix */
32921674Sjkh        void **argtable;        /* args, built due to positional arg */
33021674Sjkh        void *statargtable [STATIC_ARG_TBL_SIZE];
33121674Sjkh        int nextarg;            /* 1-based argument index */
33221674Sjkh        va_list orgap;          /* original argument pointer */
3331573Srgrimes
3341573Srgrimes	/*
3351573Srgrimes	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
3361573Srgrimes	 * fields occur frequently, increase PADSIZE and make the initialisers
3371573Srgrimes	 * below longer.
3381573Srgrimes	 */
3391573Srgrimes#define	PADSIZE	16		/* pad chunk size */
3401573Srgrimes	static char blanks[PADSIZE] =
3411573Srgrimes	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
3421573Srgrimes	static char zeroes[PADSIZE] =
3431573Srgrimes	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
3441573Srgrimes
3451573Srgrimes	/*
3461573Srgrimes	 * BEWARE, these `goto error' on error, and PAD uses `n'.
3471573Srgrimes	 */
3481573Srgrimes#define	PRINT(ptr, len) { \
3491573Srgrimes	iovp->iov_base = (ptr); \
3501573Srgrimes	iovp->iov_len = (len); \
3511573Srgrimes	uio.uio_resid += (len); \
3521573Srgrimes	iovp++; \
3531573Srgrimes	if (++uio.uio_iovcnt >= NIOV) { \
3541573Srgrimes		if (__sprint(fp, &uio)) \
3551573Srgrimes			goto error; \
3561573Srgrimes		iovp = iov; \
3571573Srgrimes	} \
3581573Srgrimes}
3591573Srgrimes#define	PAD(howmany, with) { \
3601573Srgrimes	if ((n = (howmany)) > 0) { \
3611573Srgrimes		while (n > PADSIZE) { \
3621573Srgrimes			PRINT(with, PADSIZE); \
3631573Srgrimes			n -= PADSIZE; \
3641573Srgrimes		} \
3651573Srgrimes		PRINT(with, n); \
3661573Srgrimes	} \
3671573Srgrimes}
3681573Srgrimes#define	FLUSH() { \
3691573Srgrimes	if (uio.uio_resid && __sprint(fp, &uio)) \
3701573Srgrimes		goto error; \
3711573Srgrimes	uio.uio_iovcnt = 0; \
3721573Srgrimes	iovp = iov; \
3731573Srgrimes}
3741573Srgrimes
37521674Sjkh        /*
37621674Sjkh         * Get the argument indexed by nextarg.   If the argument table is
37721674Sjkh         * built, use it to get the argument.  If its not, get the next
37821674Sjkh         * argument (and arguments must be gotten sequentially).
37921674Sjkh         */
38021674Sjkh#define GETARG(type) \
38121674Sjkh        ((argtable != NULL) ? *((type*)(argtable[nextarg++])) : \
38221674Sjkh            (nextarg++, va_arg(ap, type)))
38321674Sjkh
3841573Srgrimes	/*
3851573Srgrimes	 * To extend shorts properly, we need both signed and unsigned
3861573Srgrimes	 * argument extraction methods.
3871573Srgrimes	 */
3881573Srgrimes#define	SARG() \
38921674Sjkh	(flags&LONGINT ? GETARG(long) : \
39021674Sjkh	    flags&SHORTINT ? (long)(short)GETARG(int) : \
39121674Sjkh	    (long)GETARG(int))
3921573Srgrimes#define	UARG() \
39321674Sjkh	(flags&LONGINT ? GETARG(u_long) : \
39421674Sjkh	    flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
39521674Sjkh	    (u_long)GETARG(u_int))
3961573Srgrimes
39721674Sjkh        /*
39821674Sjkh         * Get * arguments, including the form *nn$.  Preserve the nextarg
39921674Sjkh         * that the argument can be gotten once the type is determined.
40021674Sjkh         */
40121674Sjkh#define GETASTER(val) \
40221674Sjkh        n2 = 0; \
40321674Sjkh        cp = fmt; \
40421674Sjkh        while (is_digit(*cp)) { \
40521674Sjkh                n2 = 10 * n2 + to_digit(*cp); \
40621674Sjkh                cp++; \
40721674Sjkh        } \
40821674Sjkh        if (*cp == '$') { \
40921674Sjkh            	int hold = nextarg; \
41021674Sjkh                if (argtable == NULL) { \
41121674Sjkh                        argtable = statargtable; \
41221674Sjkh                        __find_arguments (fmt0, orgap, &argtable); \
41321674Sjkh                } \
41421674Sjkh                nextarg = n2; \
41521674Sjkh                val = GETARG (int); \
41621674Sjkh                nextarg = hold; \
41721674Sjkh                fmt = ++cp; \
41821674Sjkh        } else { \
41921674Sjkh		val = GETARG (int); \
42021674Sjkh        }
42121674Sjkh
42221674Sjkh
42313545Sjulian#ifdef _THREAD_SAFE
42413545Sjulian	_thread_flockfile(fp,__FILE__,__LINE__);
42513545Sjulian#endif
4261573Srgrimes	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
42713545Sjulian	if (cantwrite(fp)) {
42813545Sjulian#ifdef _THREAD_SAFE
42913545Sjulian		_thread_funlockfile(fp);
43013545Sjulian#endif
4311573Srgrimes		return (EOF);
43213545Sjulian	}
4331573Srgrimes
4341573Srgrimes	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
4351573Srgrimes	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
43613545Sjulian	    fp->_file >= 0) {
43713545Sjulian#ifdef _THREAD_SAFE
43813545Sjulian		_thread_funlockfile(fp);
43913545Sjulian#endif
4401573Srgrimes		return (__sbprintf(fp, fmt0, ap));
44113545Sjulian	}
4421573Srgrimes
4431573Srgrimes	fmt = (char *)fmt0;
44421674Sjkh        argtable = NULL;
44521674Sjkh        nextarg = 1;
44621674Sjkh        orgap = ap;
4471573Srgrimes	uio.uio_iov = iovp = iov;
4481573Srgrimes	uio.uio_resid = 0;
4491573Srgrimes	uio.uio_iovcnt = 0;
4501573Srgrimes	ret = 0;
4511573Srgrimes
4521573Srgrimes	/*
4531573Srgrimes	 * Scan the format for conversions (`%' character).
4541573Srgrimes	 */
4551573Srgrimes	for (;;) {
4561573Srgrimes		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
4571573Srgrimes			/* void */;
4581573Srgrimes		if ((n = fmt - cp) != 0) {
4591573Srgrimes			PRINT(cp, n);
4601573Srgrimes			ret += n;
4611573Srgrimes		}
4621573Srgrimes		if (ch == '\0')
4631573Srgrimes			goto done;
4641573Srgrimes		fmt++;		/* skip over '%' */
4651573Srgrimes
4661573Srgrimes		flags = 0;
4671573Srgrimes		dprec = 0;
4681573Srgrimes		width = 0;
4691573Srgrimes		prec = -1;
4701573Srgrimes		sign = '\0';
4711573Srgrimes
4721573Srgrimesrflag:		ch = *fmt++;
4731573Srgrimesreswitch:	switch (ch) {
4741573Srgrimes		case ' ':
4751573Srgrimes			/*
4761573Srgrimes			 * ``If the space and + flags both appear, the space
4771573Srgrimes			 * flag will be ignored.''
4781573Srgrimes			 *	-- ANSI X3J11
4791573Srgrimes			 */
4801573Srgrimes			if (!sign)
4811573Srgrimes				sign = ' ';
4821573Srgrimes			goto rflag;
4831573Srgrimes		case '#':
4841573Srgrimes			flags |= ALT;
4851573Srgrimes			goto rflag;
4861573Srgrimes		case '*':
4871573Srgrimes			/*
4881573Srgrimes			 * ``A negative field width argument is taken as a
4891573Srgrimes			 * - flag followed by a positive field width.''
4901573Srgrimes			 *	-- ANSI X3J11
4911573Srgrimes			 * They don't exclude field widths read from args.
4921573Srgrimes			 */
49321674Sjkh			GETASTER (width);
49421674Sjkh			if (width >= 0)
4951573Srgrimes				goto rflag;
4961573Srgrimes			width = -width;
4971573Srgrimes			/* FALLTHROUGH */
4981573Srgrimes		case '-':
4991573Srgrimes			flags |= LADJUST;
5001573Srgrimes			goto rflag;
5011573Srgrimes		case '+':
5021573Srgrimes			sign = '+';
5031573Srgrimes			goto rflag;
5041573Srgrimes		case '.':
5051573Srgrimes			if ((ch = *fmt++) == '*') {
50621674Sjkh				GETASTER (n);
5071573Srgrimes				prec = n < 0 ? -1 : n;
5081573Srgrimes				goto rflag;
5091573Srgrimes			}
5101573Srgrimes			n = 0;
5111573Srgrimes			while (is_digit(ch)) {
5121573Srgrimes				n = 10 * n + to_digit(ch);
5131573Srgrimes				ch = *fmt++;
5141573Srgrimes			}
5151573Srgrimes			prec = n < 0 ? -1 : n;
5161573Srgrimes			goto reswitch;
5171573Srgrimes		case '0':
5181573Srgrimes			/*
5191573Srgrimes			 * ``Note that 0 is taken as a flag, not as the
5201573Srgrimes			 * beginning of a field width.''
5211573Srgrimes			 *	-- ANSI X3J11
5221573Srgrimes			 */
5231573Srgrimes			flags |= ZEROPAD;
5241573Srgrimes			goto rflag;
5251573Srgrimes		case '1': case '2': case '3': case '4':
5261573Srgrimes		case '5': case '6': case '7': case '8': case '9':
5271573Srgrimes			n = 0;
5281573Srgrimes			do {
5291573Srgrimes				n = 10 * n + to_digit(ch);
5301573Srgrimes				ch = *fmt++;
5311573Srgrimes			} while (is_digit(ch));
53221674Sjkh			if (ch == '$') {
53321674Sjkh				nextarg = n;
53421674Sjkh                        	if (argtable == NULL) {
53521674Sjkh                                	argtable = statargtable;
53621674Sjkh                                	__find_arguments (fmt0, orgap,
53721674Sjkh						&argtable);
53821674Sjkh				}
53921674Sjkh				goto rflag;
54021674Sjkh                        }
5411573Srgrimes			width = n;
5421573Srgrimes			goto reswitch;
5431573Srgrimes#ifdef FLOATING_POINT
5441573Srgrimes		case 'L':
5451573Srgrimes			flags |= LONGDBL;
5461573Srgrimes			goto rflag;
5471573Srgrimes#endif
5481573Srgrimes		case 'h':
5491573Srgrimes			flags |= SHORTINT;
5501573Srgrimes			goto rflag;
5511573Srgrimes		case 'l':
5521573Srgrimes			flags |= LONGINT;
5531573Srgrimes			goto rflag;
5541573Srgrimes		case 'q':
5551573Srgrimes			flags |= QUADINT;
5561573Srgrimes			goto rflag;
5571573Srgrimes		case 'c':
55821674Sjkh			*(cp = buf) = GETARG(int);
5591573Srgrimes			size = 1;
5601573Srgrimes			sign = '\0';
5611573Srgrimes			break;
5621573Srgrimes		case 'D':
5631573Srgrimes			flags |= LONGINT;
5641573Srgrimes			/*FALLTHROUGH*/
5651573Srgrimes		case 'd':
5661573Srgrimes		case 'i':
5671573Srgrimes			if (flags & QUADINT) {
56821674Sjkh				uqval = GETARG(quad_t);
5691573Srgrimes				if ((quad_t)uqval < 0) {
5701573Srgrimes					uqval = -uqval;
5711573Srgrimes					sign = '-';
5721573Srgrimes				}
5731573Srgrimes			} else {
5741573Srgrimes				ulval = SARG();
5751573Srgrimes				if ((long)ulval < 0) {
5761573Srgrimes					ulval = -ulval;
5771573Srgrimes					sign = '-';
5781573Srgrimes				}
5791573Srgrimes			}
5801573Srgrimes			base = 10;
5811573Srgrimes			goto number;
5821573Srgrimes#ifdef FLOATING_POINT
5837033Sbde		case 'e':
5841573Srgrimes		case 'E':
5857033Sbde		case 'f':
5867033Sbde			goto fp_begin;
5871573Srgrimes		case 'g':
5881573Srgrimes		case 'G':
5897033Sbde			if (prec == 0)
5907033Sbde				prec = 1;
5917033Sbdefp_begin:		if (prec == -1)
5921573Srgrimes				prec = DEFPREC;
5937033Sbde			if (flags & LONGDBL)
59431871Sbde				/* XXX this loses precision. */
59521674Sjkh				_double = (double)GETARG(long double);
5967033Sbde			else
59721674Sjkh				_double = GETARG(double);
5981573Srgrimes			/* do this before tricky precision changes */
5991573Srgrimes			if (isinf(_double)) {
6001573Srgrimes				if (_double < 0)
6011573Srgrimes					sign = '-';
6021573Srgrimes				cp = "Inf";
6031573Srgrimes				size = 3;
6041573Srgrimes				break;
6051573Srgrimes			}
6061573Srgrimes			if (isnan(_double)) {
6071573Srgrimes				cp = "NaN";
6081573Srgrimes				size = 3;
6091573Srgrimes				break;
6101573Srgrimes			}
6111573Srgrimes			flags |= FPT;
6121573Srgrimes			cp = cvt(_double, prec, flags, &softsign,
6131573Srgrimes				&expt, ch, &ndig);
6141573Srgrimes			if (ch == 'g' || ch == 'G') {
6151573Srgrimes				if (expt <= -4 || expt > prec)
6161573Srgrimes					ch = (ch == 'g') ? 'e' : 'E';
6171573Srgrimes				else
6181573Srgrimes					ch = 'g';
6198870Srgrimes			}
6201573Srgrimes			if (ch <= 'e') {	/* 'e' or 'E' fmt */
6211573Srgrimes				--expt;
6221573Srgrimes				expsize = exponent(expstr, expt, ch);
6231573Srgrimes				size = expsize + ndig;
6241573Srgrimes				if (ndig > 1 || flags & ALT)
6251573Srgrimes					++size;
6261573Srgrimes			} else if (ch == 'f') {		/* f fmt */
6271573Srgrimes				if (expt > 0) {
6281573Srgrimes					size = expt;
6291573Srgrimes					if (prec || flags & ALT)
6301573Srgrimes						size += prec + 1;
6311573Srgrimes				} else	/* "0.X" */
6321573Srgrimes					size = prec + 2;
6331573Srgrimes			} else if (expt >= ndig) {	/* fixed g fmt */
6341573Srgrimes				size = expt;
6351573Srgrimes				if (flags & ALT)
6361573Srgrimes					++size;
6371573Srgrimes			} else
6381573Srgrimes				size = ndig + (expt > 0 ?
6391573Srgrimes					1 : 2 - expt);
6401573Srgrimes
6411573Srgrimes			if (softsign)
6421573Srgrimes				sign = '-';
6431573Srgrimes			break;
6441573Srgrimes#endif /* FLOATING_POINT */
6451573Srgrimes		case 'n':
6461573Srgrimes			if (flags & QUADINT)
64721674Sjkh				*GETARG(quad_t *) = ret;
6481573Srgrimes			else if (flags & LONGINT)
64921674Sjkh				*GETARG(long *) = ret;
6501573Srgrimes			else if (flags & SHORTINT)
65121674Sjkh				*GETARG(short *) = ret;
6521573Srgrimes			else
65321674Sjkh				*GETARG(int *) = ret;
6541573Srgrimes			continue;	/* no output */
6551573Srgrimes		case 'O':
6561573Srgrimes			flags |= LONGINT;
6571573Srgrimes			/*FALLTHROUGH*/
6581573Srgrimes		case 'o':
6591573Srgrimes			if (flags & QUADINT)
66021674Sjkh				uqval = GETARG(u_quad_t);
6611573Srgrimes			else
6621573Srgrimes				ulval = UARG();
6631573Srgrimes			base = 8;
6641573Srgrimes			goto nosign;
6651573Srgrimes		case 'p':
6661573Srgrimes			/*
6671573Srgrimes			 * ``The argument shall be a pointer to void.  The
6681573Srgrimes			 * value of the pointer is converted to a sequence
6691573Srgrimes			 * of printable characters, in an implementation-
6701573Srgrimes			 * defined manner.''
6711573Srgrimes			 *	-- ANSI X3J11
6721573Srgrimes			 */
67321674Sjkh			ulval = (u_long)GETARG(void *);
6741573Srgrimes			base = 16;
6751573Srgrimes			xdigs = "0123456789abcdef";
6761573Srgrimes			flags = (flags & ~QUADINT) | HEXPREFIX;
6771573Srgrimes			ch = 'x';
6781573Srgrimes			goto nosign;
6791573Srgrimes		case 's':
68021674Sjkh			if ((cp = GETARG(char *)) == NULL)
6811573Srgrimes				cp = "(null)";
6821573Srgrimes			if (prec >= 0) {
6831573Srgrimes				/*
6841573Srgrimes				 * can't use strlen; can only look for the
6851573Srgrimes				 * NUL in the first `prec' characters, and
6861573Srgrimes				 * strlen() will go further.
6871573Srgrimes				 */
68816586Sjraynard				char *p = memchr(cp, 0, (size_t)prec);
6891573Srgrimes
6901573Srgrimes				if (p != NULL) {
6911573Srgrimes					size = p - cp;
6921573Srgrimes					if (size > prec)
6931573Srgrimes						size = prec;
6941573Srgrimes				} else
6951573Srgrimes					size = prec;
6961573Srgrimes			} else
6971573Srgrimes				size = strlen(cp);
6981573Srgrimes			sign = '\0';
6991573Srgrimes			break;
7001573Srgrimes		case 'U':
7011573Srgrimes			flags |= LONGINT;
7021573Srgrimes			/*FALLTHROUGH*/
7031573Srgrimes		case 'u':
7041573Srgrimes			if (flags & QUADINT)
70521674Sjkh				uqval = GETARG(u_quad_t);
7061573Srgrimes			else
7071573Srgrimes				ulval = UARG();
7081573Srgrimes			base = 10;
7091573Srgrimes			goto nosign;
7101573Srgrimes		case 'X':
7111573Srgrimes			xdigs = "0123456789ABCDEF";
7121573Srgrimes			goto hex;
7131573Srgrimes		case 'x':
7141573Srgrimes			xdigs = "0123456789abcdef";
7151573Srgrimeshex:			if (flags & QUADINT)
71621674Sjkh				uqval = GETARG(u_quad_t);
7171573Srgrimes			else
7181573Srgrimes				ulval = UARG();
7191573Srgrimes			base = 16;
7201573Srgrimes			/* leading 0x/X only if non-zero */
7211573Srgrimes			if (flags & ALT &&
7221573Srgrimes			    (flags & QUADINT ? uqval != 0 : ulval != 0))
7231573Srgrimes				flags |= HEXPREFIX;
7241573Srgrimes
7251573Srgrimes			/* unsigned conversions */
7261573Srgrimesnosign:			sign = '\0';
7271573Srgrimes			/*
7281573Srgrimes			 * ``... diouXx conversions ... if a precision is
7291573Srgrimes			 * specified, the 0 flag will be ignored.''
7301573Srgrimes			 *	-- ANSI X3J11
7311573Srgrimes			 */
7321573Srgrimesnumber:			if ((dprec = prec) >= 0)
7331573Srgrimes				flags &= ~ZEROPAD;
7341573Srgrimes
7351573Srgrimes			/*
7361573Srgrimes			 * ``The result of converting a zero value with an
7371573Srgrimes			 * explicit precision of zero is no characters.''
7381573Srgrimes			 *	-- ANSI X3J11
7391573Srgrimes			 */
7401573Srgrimes			cp = buf + BUF;
7411573Srgrimes			if (flags & QUADINT) {
7421573Srgrimes				if (uqval != 0 || prec != 0)
7431573Srgrimes					cp = __uqtoa(uqval, cp, base,
7441573Srgrimes					    flags & ALT, xdigs);
7451573Srgrimes			} else {
7461573Srgrimes				if (ulval != 0 || prec != 0)
7471573Srgrimes					cp = __ultoa(ulval, cp, base,
7481573Srgrimes					    flags & ALT, xdigs);
7491573Srgrimes			}
7501573Srgrimes			size = buf + BUF - cp;
7511573Srgrimes			break;
7521573Srgrimes		default:	/* "%?" prints ?, unless ? is NUL */
7531573Srgrimes			if (ch == '\0')
7541573Srgrimes				goto done;
7551573Srgrimes			/* pretend it was %c with argument ch */
7561573Srgrimes			cp = buf;
7571573Srgrimes			*cp = ch;
7581573Srgrimes			size = 1;
7591573Srgrimes			sign = '\0';
7601573Srgrimes			break;
7611573Srgrimes		}
7621573Srgrimes
7631573Srgrimes		/*
7641573Srgrimes		 * All reasonable formats wind up here.  At this point, `cp'
7651573Srgrimes		 * points to a string which (if not flags&LADJUST) should be
7661573Srgrimes		 * padded out to `width' places.  If flags&ZEROPAD, it should
7671573Srgrimes		 * first be prefixed by any sign or other prefix; otherwise,
7681573Srgrimes		 * it should be blank padded before the prefix is emitted.
7691573Srgrimes		 * After any left-hand padding and prefixing, emit zeroes
7701573Srgrimes		 * required by a decimal [diouxX] precision, then print the
7711573Srgrimes		 * string proper, then emit zeroes required by any leftover
7721573Srgrimes		 * floating precision; finally, if LADJUST, pad with blanks.
7731573Srgrimes		 *
7741573Srgrimes		 * Compute actual size, so we know how much to pad.
77514727Sfenner		 * size excludes decimal prec; realsz includes it.
7761573Srgrimes		 */
77714727Sfenner		realsz = dprec > size ? dprec : size;
7781573Srgrimes		if (sign)
77914727Sfenner			realsz++;
7801573Srgrimes		else if (flags & HEXPREFIX)
78114727Sfenner			realsz += 2;
7821573Srgrimes
7831573Srgrimes		/* right-adjusting blank padding */
7841573Srgrimes		if ((flags & (LADJUST|ZEROPAD)) == 0)
7851573Srgrimes			PAD(width - realsz, blanks);
7861573Srgrimes
7871573Srgrimes		/* prefix */
7881573Srgrimes		if (sign) {
7891573Srgrimes			PRINT(&sign, 1);
7901573Srgrimes		} else if (flags & HEXPREFIX) {
7911573Srgrimes			ox[0] = '0';
7921573Srgrimes			ox[1] = ch;
7931573Srgrimes			PRINT(ox, 2);
7941573Srgrimes		}
7951573Srgrimes
7961573Srgrimes		/* right-adjusting zero padding */
7971573Srgrimes		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
7981573Srgrimes			PAD(width - realsz, zeroes);
7991573Srgrimes
8001573Srgrimes		/* leading zeroes from decimal precision */
80114727Sfenner		PAD(dprec - size, zeroes);
8021573Srgrimes
8031573Srgrimes		/* the string or number proper */
8041573Srgrimes#ifdef FLOATING_POINT
8051573Srgrimes		if ((flags & FPT) == 0) {
8061573Srgrimes			PRINT(cp, size);
8071573Srgrimes		} else {	/* glue together f_p fragments */
8081573Srgrimes			if (ch >= 'f') {	/* 'f' or 'g' */
8091573Srgrimes				if (_double == 0) {
8107033Sbde					/* kludge for __dtoa irregularity */
8117649Sbde					if (expt >= ndig &&
8127649Sbde					    (flags & ALT) == 0) {
8131573Srgrimes						PRINT("0", 1);
8141573Srgrimes					} else {
8151573Srgrimes						PRINT("0.", 2);
8161573Srgrimes						PAD(ndig - 1, zeroes);
8171573Srgrimes					}
8181573Srgrimes				} else if (expt <= 0) {
8191573Srgrimes					PRINT("0.", 2);
8201573Srgrimes					PAD(-expt, zeroes);
8211573Srgrimes					PRINT(cp, ndig);
8221573Srgrimes				} else if (expt >= ndig) {
8231573Srgrimes					PRINT(cp, ndig);
8241573Srgrimes					PAD(expt - ndig, zeroes);
8251573Srgrimes					if (flags & ALT)
8261573Srgrimes						PRINT(".", 1);
8271573Srgrimes				} else {
8281573Srgrimes					PRINT(cp, expt);
8291573Srgrimes					cp += expt;
8301573Srgrimes					PRINT(".", 1);
8311573Srgrimes					PRINT(cp, ndig-expt);
8321573Srgrimes				}
8331573Srgrimes			} else {	/* 'e' or 'E' */
8341573Srgrimes				if (ndig > 1 || flags & ALT) {
8351573Srgrimes					ox[0] = *cp++;
8361573Srgrimes					ox[1] = '.';
8371573Srgrimes					PRINT(ox, 2);
8387036Sbde					if (_double) {
8391573Srgrimes						PRINT(cp, ndig-1);
8401573Srgrimes					} else	/* 0.[0..] */
8411573Srgrimes						/* __dtoa irregularity */
8421573Srgrimes						PAD(ndig - 1, zeroes);
8431573Srgrimes				} else	/* XeYYY */
8441573Srgrimes					PRINT(cp, 1);
8451573Srgrimes				PRINT(expstr, expsize);
8461573Srgrimes			}
8471573Srgrimes		}
8481573Srgrimes#else
8491573Srgrimes		PRINT(cp, size);
8501573Srgrimes#endif
8511573Srgrimes		/* left-adjusting padding (always blank) */
8521573Srgrimes		if (flags & LADJUST)
8531573Srgrimes			PAD(width - realsz, blanks);
8541573Srgrimes
8551573Srgrimes		/* finally, adjust ret */
8561573Srgrimes		ret += width > realsz ? width : realsz;
8571573Srgrimes
8581573Srgrimes		FLUSH();	/* copy out the I/O vectors */
8591573Srgrimes	}
8601573Srgrimesdone:
8611573Srgrimes	FLUSH();
8621573Srgrimeserror:
86313545Sjulian	if (__sferror(fp))
86413545Sjulian		ret = EOF;
86513545Sjulian#ifdef _THREAD_SAFE
86613545Sjulian	_thread_funlockfile(fp);
86713545Sjulian#endif
86821674Sjkh        if ((argtable != NULL) && (argtable != statargtable))
86921674Sjkh                free (argtable);
87013545Sjulian	return (ret);
8711573Srgrimes	/* NOTREACHED */
8721573Srgrimes}
8731573Srgrimes
87421674Sjkh/*
87521674Sjkh * Type ids for argument type table.
87621674Sjkh */
87721674Sjkh#define T_UNUSED	0
87821674Sjkh#define T_SHORT		1
87921674Sjkh#define T_U_SHORT	2
88021674Sjkh#define TP_SHORT	3
88121674Sjkh#define T_INT		4
88221674Sjkh#define T_U_INT		5
88321674Sjkh#define TP_INT		6
88421674Sjkh#define T_LONG		7
88521674Sjkh#define T_U_LONG	8
88621674Sjkh#define TP_LONG		9
88721674Sjkh#define T_QUAD		10
88821674Sjkh#define T_U_QUAD	11
88921674Sjkh#define TP_QUAD		12
89021674Sjkh#define T_DOUBLE	13
89121674Sjkh#define T_LONG_DOUBLE	14
89221674Sjkh#define TP_CHAR		15
89321674Sjkh#define TP_VOID		16
89421674Sjkh
89521674Sjkh/*
89621674Sjkh * Find all arguments when a positional parameter is encountered.  Returns a
89721674Sjkh * table, indexed by argument number, of pointers to each arguments.  The
89821674Sjkh * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries.
89921674Sjkh * It will be replaces with a malloc-ed on if it overflows.
90021674Sjkh */
90121674Sjkhstatic void
90221674Sjkh__find_arguments (fmt0, ap, argtable)
90321674Sjkh	const char *fmt0;
90421674Sjkh	va_list ap;
90521674Sjkh	void ***argtable;
90621674Sjkh{
90721674Sjkh	register char *fmt;	/* format string */
90821674Sjkh	register int ch;	/* character from fmt */
90921674Sjkh	register int n, n2;	/* handy integer (short term usage) */
91021674Sjkh	register char *cp;	/* handy char pointer (short term usage) */
91121674Sjkh	register int flags;	/* flags as above */
91221674Sjkh	int width;		/* width from format (%8d), or 0 */
91321674Sjkh	unsigned char *typetable; /* table of types */
91421674Sjkh	unsigned char stattypetable [STATIC_ARG_TBL_SIZE];
91521674Sjkh	int tablesize;		/* current size of type table */
91621674Sjkh	int tablemax;		/* largest used index in table */
91721674Sjkh	int nextarg;		/* 1-based argument index */
91821674Sjkh
91921674Sjkh	/*
92021674Sjkh	 * Add an argument type to the table, expanding if necessary.
92121674Sjkh	 */
92221674Sjkh#define ADDTYPE(type) \
92321674Sjkh	((nextarg >= tablesize) ? \
92421674Sjkh		__grow_type_table(nextarg, &typetable, &tablesize) : 0, \
92521674Sjkh	typetable[nextarg++] = type, \
92621674Sjkh	(nextarg > tablemax) ? tablemax = nextarg : 0)
92721674Sjkh
92821674Sjkh#define	ADDSARG() \
92921674Sjkh	((flags&LONGINT) ? ADDTYPE(T_LONG) : \
93021674Sjkh		((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT)))
93121674Sjkh
93221674Sjkh#define	ADDUARG() \
93321674Sjkh	((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \
93421674Sjkh		((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT)))
93521674Sjkh
93621674Sjkh	/*
93721674Sjkh	 * Add * arguments to the type array.
93821674Sjkh	 */
93921674Sjkh#define ADDASTER() \
94021674Sjkh	n2 = 0; \
94121674Sjkh	cp = fmt; \
94221674Sjkh	while (is_digit(*cp)) { \
94321674Sjkh		n2 = 10 * n2 + to_digit(*cp); \
94421674Sjkh		cp++; \
94521674Sjkh	} \
94621674Sjkh	if (*cp == '$') { \
94721674Sjkh		int hold = nextarg; \
94821674Sjkh		nextarg = n2; \
94921674Sjkh		ADDTYPE (T_INT); \
95021674Sjkh		nextarg = hold; \
95121674Sjkh		fmt = ++cp; \
95221674Sjkh	} else { \
95321674Sjkh		ADDTYPE (T_INT); \
95421674Sjkh	}
95521674Sjkh	fmt = (char *)fmt0;
95621674Sjkh	typetable = stattypetable;
95721674Sjkh	tablesize = STATIC_ARG_TBL_SIZE;
95821674Sjkh	tablemax = 0;
95921674Sjkh	nextarg = 1;
96021674Sjkh	memset (typetable, T_UNUSED, STATIC_ARG_TBL_SIZE);
96121674Sjkh
96221674Sjkh	/*
96321674Sjkh	 * Scan the format for conversions (`%' character).
96421674Sjkh	 */
96521674Sjkh	for (;;) {
96621674Sjkh		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
96721674Sjkh			/* void */;
96821674Sjkh		if (ch == '\0')
96921674Sjkh			goto done;
97021674Sjkh		fmt++;		/* skip over '%' */
97121674Sjkh
97221674Sjkh		flags = 0;
97321674Sjkh		width = 0;
97421674Sjkh
97521674Sjkhrflag:		ch = *fmt++;
97621674Sjkhreswitch:	switch (ch) {
97721674Sjkh		case ' ':
97821674Sjkh		case '#':
97921674Sjkh			goto rflag;
98021674Sjkh		case '*':
98121674Sjkh			ADDASTER ();
98221674Sjkh			goto rflag;
98321674Sjkh		case '-':
98421674Sjkh		case '+':
98521674Sjkh			goto rflag;
98621674Sjkh		case '.':
98721674Sjkh			if ((ch = *fmt++) == '*') {
98821674Sjkh				ADDASTER ();
98921674Sjkh				goto rflag;
99021674Sjkh			}
99121674Sjkh			while (is_digit(ch)) {
99221674Sjkh				ch = *fmt++;
99321674Sjkh			}
99421674Sjkh			goto reswitch;
99521674Sjkh		case '0':
99621674Sjkh			goto rflag;
99721674Sjkh		case '1': case '2': case '3': case '4':
99821674Sjkh		case '5': case '6': case '7': case '8': case '9':
99921674Sjkh			n = 0;
100021674Sjkh			do {
100121674Sjkh				n = 10 * n + to_digit(ch);
100221674Sjkh				ch = *fmt++;
100321674Sjkh			} while (is_digit(ch));
100421674Sjkh			if (ch == '$') {
100521674Sjkh				nextarg = n;
100621674Sjkh				goto rflag;
100721674Sjkh			}
100821674Sjkh			width = n;
100921674Sjkh			goto reswitch;
10101573Srgrimes#ifdef FLOATING_POINT
101121674Sjkh		case 'L':
101221674Sjkh			flags |= LONGDBL;
101321674Sjkh			goto rflag;
101421674Sjkh#endif
101521674Sjkh		case 'h':
101621674Sjkh			flags |= SHORTINT;
101721674Sjkh			goto rflag;
101821674Sjkh		case 'l':
101921674Sjkh			flags |= LONGINT;
102021674Sjkh			goto rflag;
102121674Sjkh		case 'q':
102221674Sjkh			flags |= QUADINT;
102321674Sjkh			goto rflag;
102421674Sjkh		case 'c':
102521674Sjkh			ADDTYPE(T_INT);
102621674Sjkh			break;
102721674Sjkh		case 'D':
102821674Sjkh			flags |= LONGINT;
102921674Sjkh			/*FALLTHROUGH*/
103021674Sjkh		case 'd':
103121674Sjkh		case 'i':
103221674Sjkh			if (flags & QUADINT) {
103321674Sjkh				ADDTYPE(T_QUAD);
103421674Sjkh			} else {
103521674Sjkh				ADDSARG();
103621674Sjkh			}
103721674Sjkh			break;
103821674Sjkh#ifdef FLOATING_POINT
103921674Sjkh		case 'e':
104021674Sjkh		case 'E':
104121674Sjkh		case 'f':
104221674Sjkh		case 'g':
104321674Sjkh		case 'G':
104421674Sjkh			if (flags & LONGDBL)
104521674Sjkh				ADDTYPE(T_LONG_DOUBLE);
104621674Sjkh			else
104721674Sjkh				ADDTYPE(T_DOUBLE);
104821674Sjkh			break;
104921674Sjkh#endif /* FLOATING_POINT */
105021674Sjkh		case 'n':
105121674Sjkh			if (flags & QUADINT)
105221674Sjkh				ADDTYPE(TP_QUAD);
105321674Sjkh			else if (flags & LONGINT)
105421674Sjkh				ADDTYPE(TP_LONG);
105521674Sjkh			else if (flags & SHORTINT)
105621674Sjkh				ADDTYPE(TP_SHORT);
105721674Sjkh			else
105821674Sjkh				ADDTYPE(TP_INT);
105921674Sjkh			continue;	/* no output */
106021674Sjkh		case 'O':
106121674Sjkh			flags |= LONGINT;
106221674Sjkh			/*FALLTHROUGH*/
106321674Sjkh		case 'o':
106421674Sjkh			if (flags & QUADINT)
106521674Sjkh				ADDTYPE(T_U_QUAD);
106621674Sjkh			else
106721674Sjkh				ADDUARG();
106821674Sjkh			break;
106921674Sjkh		case 'p':
107021674Sjkh			ADDTYPE(TP_VOID);
107121674Sjkh			break;
107221674Sjkh		case 's':
107321674Sjkh			ADDTYPE(TP_CHAR);
107421674Sjkh			break;
107521674Sjkh		case 'U':
107621674Sjkh			flags |= LONGINT;
107721674Sjkh			/*FALLTHROUGH*/
107821674Sjkh		case 'u':
107921674Sjkh			if (flags & QUADINT)
108021674Sjkh				ADDTYPE(T_U_QUAD);
108121674Sjkh			else
108221674Sjkh				ADDUARG();
108321674Sjkh			break;
108421674Sjkh		case 'X':
108521674Sjkh		case 'x':
108621674Sjkh			if (flags & QUADINT)
108721674Sjkh				ADDTYPE(T_U_QUAD);
108821674Sjkh			else
108921674Sjkh				ADDUARG();
109021674Sjkh			break;
109121674Sjkh		default:	/* "%?" prints ?, unless ? is NUL */
109221674Sjkh			if (ch == '\0')
109321674Sjkh				goto done;
109421674Sjkh			break;
109521674Sjkh		}
109621674Sjkh	}
109721674Sjkhdone:
109821674Sjkh	/*
109921674Sjkh	 * Build the argument table.
110021674Sjkh	 */
110121674Sjkh	if (tablemax >= STATIC_ARG_TBL_SIZE) {
110221674Sjkh		*argtable = (void **)
110321674Sjkh		    malloc (sizeof (void *) * (tablemax + 1));
110421674Sjkh	}
11051573Srgrimes
110621674Sjkh	(*argtable) [0] = NULL;
110721674Sjkh	for (n = 1; n <= tablemax; n++) {
110821674Sjkh		(*argtable) [n] = ap;
110921674Sjkh		switch (typetable [n]) {
111021674Sjkh		    case T_UNUSED:
111121674Sjkh			(void) va_arg (ap, int);
111221674Sjkh			break;
111321674Sjkh		    case T_SHORT:
111421674Sjkh			(void) va_arg (ap, int);
111521674Sjkh			break;
111621674Sjkh		    case T_U_SHORT:
111721674Sjkh			(void) va_arg (ap, int);
111821674Sjkh			break;
111921674Sjkh		    case TP_SHORT:
112021674Sjkh			(void) va_arg (ap, short *);
112121674Sjkh			break;
112221674Sjkh		    case T_INT:
112321674Sjkh			(void) va_arg (ap, int);
112421674Sjkh			break;
112521674Sjkh		    case T_U_INT:
112621674Sjkh			(void) va_arg (ap, unsigned int);
112721674Sjkh			break;
112821674Sjkh		    case TP_INT:
112921674Sjkh			(void) va_arg (ap, int *);
113021674Sjkh			break;
113121674Sjkh		    case T_LONG:
113221674Sjkh			(void) va_arg (ap, long);
113321674Sjkh			break;
113421674Sjkh		    case T_U_LONG:
113521674Sjkh			(void) va_arg (ap, unsigned long);
113621674Sjkh			break;
113721674Sjkh		    case TP_LONG:
113821674Sjkh			(void) va_arg (ap, long *);
113921674Sjkh			break;
114021674Sjkh		    case T_QUAD:
114121674Sjkh			(void) va_arg (ap, quad_t);
114221674Sjkh			break;
114321674Sjkh		    case T_U_QUAD:
114421674Sjkh			(void) va_arg (ap, u_quad_t);
114521674Sjkh			break;
114621674Sjkh		    case TP_QUAD:
114721674Sjkh			(void) va_arg (ap, quad_t *);
114821674Sjkh			break;
114921674Sjkh		    case T_DOUBLE:
115021674Sjkh			(void) va_arg (ap, double);
115121674Sjkh			break;
115221674Sjkh		    case T_LONG_DOUBLE:
115321674Sjkh			(void) va_arg (ap, long double);
115421674Sjkh			break;
115521674Sjkh		    case TP_CHAR:
115621674Sjkh			(void) va_arg (ap, char *);
115721674Sjkh			break;
115821674Sjkh		    case TP_VOID:
115921674Sjkh			(void) va_arg (ap, void *);
116021674Sjkh			break;
116121674Sjkh		}
116221674Sjkh	}
116321674Sjkh
116421674Sjkh	if ((typetable != NULL) && (typetable != stattypetable))
116521674Sjkh		free (typetable);
116621674Sjkh}
116721674Sjkh
116821674Sjkh/*
116921674Sjkh * Increase the size of the type table.
117021674Sjkh */
117121674Sjkhstatic void
117221674Sjkh__grow_type_table (nextarg, typetable, tablesize)
117321674Sjkh	int nextarg;
117421674Sjkh	unsigned char **typetable;
117521674Sjkh	int *tablesize;
117621674Sjkh{
117721674Sjkh	unsigned char *oldtable = *typetable;
117821674Sjkh	int newsize = *tablesize * 2;
117921674Sjkh
118021674Sjkh	if (*tablesize == STATIC_ARG_TBL_SIZE) {
118121674Sjkh		*typetable = (unsigned char *)
118221674Sjkh		    malloc (sizeof (unsigned char) * newsize);
118321674Sjkh		bcopy (oldtable, *typetable, *tablesize);
118421674Sjkh	} else {
118521674Sjkh		*typetable = (unsigned char *)
118621674Sjkh		    realloc (typetable, sizeof (unsigned char) * newsize);
118721674Sjkh
118821674Sjkh	}
118921674Sjkh	memset (&typetable [*tablesize], T_UNUSED, (newsize - *tablesize));
119021674Sjkh
119121674Sjkh	*tablesize = newsize;
119221674Sjkh}
119321674Sjkh
119421674Sjkh
119521674Sjkh#ifdef FLOATING_POINT
119621674Sjkh
11971573Srgrimesextern char *__dtoa __P((double, int, int, int *, int *, char **));
11981573Srgrimes
11991573Srgrimesstatic char *
12001573Srgrimescvt(value, ndigits, flags, sign, decpt, ch, length)
12011573Srgrimes	double value;
12021573Srgrimes	int ndigits, flags, *decpt, ch, *length;
12031573Srgrimes	char *sign;
12041573Srgrimes{
12051573Srgrimes	int mode, dsgn;
12061573Srgrimes	char *digits, *bp, *rve;
12071573Srgrimes
12081573Srgrimes	if (ch == 'f')
12097033Sbde		mode = 3;		/* ndigits after the decimal point */
12101573Srgrimes	else {
12117033Sbde		/*
12128870Srgrimes		 * To obtain ndigits after the decimal point for the 'e'
12138870Srgrimes		 * and 'E' formats, round to ndigits + 1 significant
12147033Sbde		 * figures.
12157033Sbde		 */
12167033Sbde		if (ch == 'e' || ch == 'E')
12177033Sbde			ndigits++;
12187033Sbde		mode = 2;		/* ndigits significant digits */
12191573Srgrimes	}
12201573Srgrimes	if (value < 0) {
12211573Srgrimes		value = -value;
12221573Srgrimes		*sign = '-';
12231573Srgrimes	} else
12241573Srgrimes		*sign = '\000';
12251573Srgrimes	digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
12267033Sbde	if ((ch != 'g' && ch != 'G') || flags & ALT) {
12277033Sbde		/* print trailing zeros */
12281573Srgrimes		bp = digits + ndigits;
12291573Srgrimes		if (ch == 'f') {
12301573Srgrimes			if (*digits == '0' && value)
12311573Srgrimes				*decpt = -ndigits + 1;
12321573Srgrimes			bp += *decpt;
12331573Srgrimes		}
12341573Srgrimes		if (value == 0)	/* kludge for __dtoa irregularity */
12351573Srgrimes			rve = bp;
12361573Srgrimes		while (rve < bp)
12371573Srgrimes			*rve++ = '0';
12381573Srgrimes	}
12391573Srgrimes	*length = rve - digits;
12401573Srgrimes	return (digits);
12411573Srgrimes}
12421573Srgrimes
12431573Srgrimesstatic int
12441573Srgrimesexponent(p0, exp, fmtch)
12451573Srgrimes	char *p0;
12461573Srgrimes	int exp, fmtch;
12471573Srgrimes{
12481573Srgrimes	register char *p, *t;
12491573Srgrimes	char expbuf[MAXEXP];
12501573Srgrimes
12511573Srgrimes	p = p0;
12521573Srgrimes	*p++ = fmtch;
12531573Srgrimes	if (exp < 0) {
12541573Srgrimes		exp = -exp;
12551573Srgrimes		*p++ = '-';
12561573Srgrimes	}
12571573Srgrimes	else
12581573Srgrimes		*p++ = '+';
12591573Srgrimes	t = expbuf + MAXEXP;
12601573Srgrimes	if (exp > 9) {
12611573Srgrimes		do {
12621573Srgrimes			*--t = to_char(exp % 10);
12631573Srgrimes		} while ((exp /= 10) > 9);
12641573Srgrimes		*--t = to_char(exp);
12651573Srgrimes		for (; t < expbuf + MAXEXP; *p++ = *t++);
12661573Srgrimes	}
12671573Srgrimes	else {
12681573Srgrimes		*p++ = '0';
12691573Srgrimes		*p++ = to_char(exp);
12701573Srgrimes	}
12711573Srgrimes	return (p - p0);
12721573Srgrimes}
12731573Srgrimes#endif /* FLOATING_POINT */
1274