xref: /freebsd/lib/libc/stdio/vfprintf.c (revision d4f9e326393e3298062a58338e2c94ef6baff8b5)
158f0484fSRodney W. Grimes /*-
28a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni  *
458f0484fSRodney W. Grimes  * Copyright (c) 1990, 1993
558f0484fSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
658f0484fSRodney W. Grimes  *
758f0484fSRodney W. Grimes  * This code is derived from software contributed to Berkeley by
858f0484fSRodney W. Grimes  * Chris Torek.
958f0484fSRodney W. Grimes  *
103c87aa1dSDavid Chisnall  * Copyright (c) 2011 The FreeBSD Foundation
115b5fa75aSEd Maste  *
123c87aa1dSDavid Chisnall  * Portions of this software were developed by David Chisnall
133c87aa1dSDavid Chisnall  * under sponsorship from the FreeBSD Foundation.
143c87aa1dSDavid Chisnall  *
1558f0484fSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
1658f0484fSRodney W. Grimes  * modification, are permitted provided that the following conditions
1758f0484fSRodney W. Grimes  * are met:
1858f0484fSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
1958f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
2058f0484fSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
2158f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
2258f0484fSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
231d8053c5SEd Maste  * 3. Neither the name of the University nor the names of its contributors
2458f0484fSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
2558f0484fSRodney W. Grimes  *    without specific prior written permission.
2658f0484fSRodney W. Grimes  *
2758f0484fSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2858f0484fSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2958f0484fSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3058f0484fSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3158f0484fSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3258f0484fSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3358f0484fSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3458f0484fSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3558f0484fSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3658f0484fSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3758f0484fSRodney W. Grimes  * SUCH DAMAGE.
3858f0484fSRodney W. Grimes  */
3958f0484fSRodney W. Grimes 
4058f0484fSRodney W. Grimes /*
4158f0484fSRodney W. Grimes  * Actual printf innards.
4258f0484fSRodney W. Grimes  *
4358f0484fSRodney W. Grimes  * This code is large and complicated...
4458f0484fSRodney W. Grimes  */
4558f0484fSRodney W. Grimes 
46d201fe46SDaniel Eischen #include "namespace.h"
4758f0484fSRodney W. Grimes #include <sys/types.h>
4858f0484fSRodney W. Grimes 
497735bb0fSBill Fenner #include <ctype.h>
50666d00d3SDavid Schultz #include <errno.h>
5158f0484fSRodney W. Grimes #include <limits.h>
527ae5c679SAlexey Zelkin #include <locale.h>
537735bb0fSBill Fenner #include <stddef.h>
547735bb0fSBill Fenner #include <stdint.h>
5558f0484fSRodney W. Grimes #include <stdio.h>
5658f0484fSRodney W. Grimes #include <stdlib.h>
5758f0484fSRodney W. Grimes #include <string.h>
58b9aac308STim J. Robbins #include <wchar.h>
5975067f4fSPoul-Henning Kamp #include <printf.h>
6058f0484fSRodney W. Grimes 
6158f0484fSRodney W. Grimes #include <stdarg.h>
623c87aa1dSDavid Chisnall #include "xlocale_private.h"
63d201fe46SDaniel Eischen #include "un-namespace.h"
6458f0484fSRodney W. Grimes 
65d201fe46SDaniel Eischen #include "libc_private.h"
6658f0484fSRodney W. Grimes #include "local.h"
6758f0484fSRodney W. Grimes #include "fvwrite.h"
682591efccSDavid Schultz #include "printflocal.h"
69e5abb5e6SDavid Schultz 
703c87aa1dSDavid Chisnall static int	__sprint(FILE *, struct __suio *, locale_t);
7174f1007fSDag-Erling Smørgrav static int	__sbprintf(FILE *, locale_t, int, const char *, va_list)
7274f1007fSDag-Erling Smørgrav 	__printflike(4, 0)
73a1805f7bSDavid Schultz 	__noinline;
74b9aac308STim J. Robbins static char	*__wcsconv(wchar_t *, int);
75ce51cf03SJames Raynard 
76814d1bc9SDavid Schultz #define	CHAR	char
77814d1bc9SDavid Schultz #include "printfcommon.h"
78814d1bc9SDavid Schultz 
7921ca178eSDavid Schultz struct grouping_state {
8021ca178eSDavid Schultz 	char *thousands_sep;	/* locale-specific thousands separator */
8121ca178eSDavid Schultz 	int thousep_len;	/* length of thousands_sep */
8221ca178eSDavid Schultz 	const char *grouping;	/* locale-specific numeric grouping rules */
8321ca178eSDavid Schultz 	int lead;		/* sig figs before decimal or group sep */
8421ca178eSDavid Schultz 	int nseps;		/* number of group separators with ' */
8521ca178eSDavid Schultz 	int nrepeats;		/* number of repeats of the last group */
8621ca178eSDavid Schultz };
8721ca178eSDavid Schultz 
8821ca178eSDavid Schultz /*
8921ca178eSDavid Schultz  * Initialize the thousands' grouping state in preparation to print a
9021ca178eSDavid Schultz  * number with ndigits digits. This routine returns the total number
9121ca178eSDavid Schultz  * of bytes that will be needed.
9221ca178eSDavid Schultz  */
9321ca178eSDavid Schultz static int
grouping_init(struct grouping_state * gs,int ndigits,locale_t loc)943c87aa1dSDavid Chisnall grouping_init(struct grouping_state *gs, int ndigits, locale_t loc)
9521ca178eSDavid Schultz {
9621ca178eSDavid Schultz 	struct lconv *locale;
9721ca178eSDavid Schultz 
983c87aa1dSDavid Chisnall 	locale = localeconv_l(loc);
9921ca178eSDavid Schultz 	gs->grouping = locale->grouping;
10021ca178eSDavid Schultz 	gs->thousands_sep = locale->thousands_sep;
10121ca178eSDavid Schultz 	gs->thousep_len = strlen(gs->thousands_sep);
10221ca178eSDavid Schultz 
10321ca178eSDavid Schultz 	gs->nseps = gs->nrepeats = 0;
10421ca178eSDavid Schultz 	gs->lead = ndigits;
10521ca178eSDavid Schultz 	while (*gs->grouping != CHAR_MAX) {
10621ca178eSDavid Schultz 		if (gs->lead <= *gs->grouping)
10721ca178eSDavid Schultz 			break;
10821ca178eSDavid Schultz 		gs->lead -= *gs->grouping;
10921ca178eSDavid Schultz 		if (*(gs->grouping+1)) {
11021ca178eSDavid Schultz 			gs->nseps++;
11121ca178eSDavid Schultz 			gs->grouping++;
11221ca178eSDavid Schultz 		} else
11321ca178eSDavid Schultz 			gs->nrepeats++;
11421ca178eSDavid Schultz 	}
11521ca178eSDavid Schultz 	return ((gs->nseps + gs->nrepeats) * gs->thousep_len);
11621ca178eSDavid Schultz }
11721ca178eSDavid Schultz 
11821ca178eSDavid Schultz /*
11921ca178eSDavid Schultz  * Print a number with thousands' separators.
12021ca178eSDavid Schultz  */
12121ca178eSDavid Schultz static int
grouping_print(struct grouping_state * gs,struct io_state * iop,const CHAR * cp,const CHAR * ep,locale_t locale)12221ca178eSDavid Schultz grouping_print(struct grouping_state *gs, struct io_state *iop,
1233c87aa1dSDavid Chisnall 	       const CHAR *cp, const CHAR *ep, locale_t locale)
12421ca178eSDavid Schultz {
12521ca178eSDavid Schultz 	const CHAR *cp0 = cp;
12621ca178eSDavid Schultz 
1273c87aa1dSDavid Chisnall 	if (io_printandpad(iop, cp, ep, gs->lead, zeroes, locale))
12821ca178eSDavid Schultz 		return (-1);
12921ca178eSDavid Schultz 	cp += gs->lead;
13021ca178eSDavid Schultz 	while (gs->nseps > 0 || gs->nrepeats > 0) {
13121ca178eSDavid Schultz 		if (gs->nrepeats > 0)
13221ca178eSDavid Schultz 			gs->nrepeats--;
13321ca178eSDavid Schultz 		else {
13421ca178eSDavid Schultz 			gs->grouping--;
13521ca178eSDavid Schultz 			gs->nseps--;
13621ca178eSDavid Schultz 		}
1373c87aa1dSDavid Chisnall 		if (io_print(iop, gs->thousands_sep, gs->thousep_len, locale))
13821ca178eSDavid Schultz 			return (-1);
1393c87aa1dSDavid Chisnall 		if (io_printandpad(iop, cp, ep, *gs->grouping, zeroes, locale))
14021ca178eSDavid Schultz 			return (-1);
14121ca178eSDavid Schultz 		cp += *gs->grouping;
14221ca178eSDavid Schultz 	}
14321ca178eSDavid Schultz 	if (cp > ep)
14421ca178eSDavid Schultz 		cp = ep;
14521ca178eSDavid Schultz 	return (cp - cp0);
14621ca178eSDavid Schultz }
14721ca178eSDavid Schultz 
14858f0484fSRodney W. Grimes /*
14958f0484fSRodney W. Grimes  * Flush out all the vectors defined by the given uio,
15058f0484fSRodney W. Grimes  * then reset it so that it can be reused.
15158f0484fSRodney W. Grimes  */
15258f0484fSRodney W. Grimes static int
__sprint(FILE * fp,struct __suio * uio,locale_t locale)1533c87aa1dSDavid Chisnall __sprint(FILE *fp, struct __suio *uio, locale_t locale)
15458f0484fSRodney W. Grimes {
155d201fe46SDaniel Eischen 	int err;
15658f0484fSRodney W. Grimes 
15758f0484fSRodney W. Grimes 	if (uio->uio_resid == 0) {
15858f0484fSRodney W. Grimes 		uio->uio_iovcnt = 0;
15958f0484fSRodney W. Grimes 		return (0);
16058f0484fSRodney W. Grimes 	}
16158f0484fSRodney W. Grimes 	err = __sfvwrite(fp, uio);
16258f0484fSRodney W. Grimes 	uio->uio_resid = 0;
16358f0484fSRodney W. Grimes 	uio->uio_iovcnt = 0;
16458f0484fSRodney W. Grimes 	return (err);
16558f0484fSRodney W. Grimes }
16658f0484fSRodney W. Grimes 
16758f0484fSRodney W. Grimes /*
16858f0484fSRodney W. Grimes  * Helper function for `fprintf to unbuffered unix file': creates a
16958f0484fSRodney W. Grimes  * temporary buffer.  We only work on write-only files; this avoids
17058f0484fSRodney W. Grimes  * worries about ungetc buffers and so forth.
17158f0484fSRodney W. Grimes  */
17258f0484fSRodney W. Grimes static int
__sbprintf(FILE * fp,locale_t locale,int serrno,const char * fmt,va_list ap)17374f1007fSDag-Erling Smørgrav __sbprintf(FILE *fp, locale_t locale, int serrno, const char *fmt, va_list ap)
17458f0484fSRodney W. Grimes {
17558f0484fSRodney W. Grimes 	int ret;
1761b0181dfSJohn Baldwin 	FILE fake = FAKE_FILE;
17758f0484fSRodney W. Grimes 	unsigned char buf[BUFSIZ];
17858f0484fSRodney W. Grimes 
179a1805f7bSDavid Schultz 	/* XXX This is probably not needed. */
180a1805f7bSDavid Schultz 	if (prepwrite(fp) != 0)
181a1805f7bSDavid Schultz 		return (EOF);
182a1805f7bSDavid Schultz 
18358f0484fSRodney W. Grimes 	/* copy the important variables */
18458f0484fSRodney W. Grimes 	fake._flags = fp->_flags & ~__SNBF;
18558f0484fSRodney W. Grimes 	fake._file = fp->_file;
18658f0484fSRodney W. Grimes 	fake._cookie = fp->_cookie;
18758f0484fSRodney W. Grimes 	fake._write = fp->_write;
1881e98f887SJohn Baldwin 	fake._orientation = fp->_orientation;
1891e98f887SJohn Baldwin 	fake._mbstate = fp->_mbstate;
19058f0484fSRodney W. Grimes 
19158f0484fSRodney W. Grimes 	/* set up the buffer */
19258f0484fSRodney W. Grimes 	fake._bf._base = fake._p = buf;
19358f0484fSRodney W. Grimes 	fake._bf._size = fake._w = sizeof(buf);
19458f0484fSRodney W. Grimes 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
19558f0484fSRodney W. Grimes 
19658f0484fSRodney W. Grimes 	/* do the work, then copy any error status */
19774f1007fSDag-Erling Smørgrav 	ret = __vfprintf(&fake, locale, serrno, fmt, ap);
198d201fe46SDaniel Eischen 	if (ret >= 0 && __fflush(&fake))
19958f0484fSRodney W. Grimes 		ret = EOF;
20058f0484fSRodney W. Grimes 	if (fake._flags & __SERR)
20158f0484fSRodney W. Grimes 		fp->_flags |= __SERR;
20258f0484fSRodney W. Grimes 	return (ret);
20358f0484fSRodney W. Grimes }
20458f0484fSRodney W. Grimes 
20558f0484fSRodney W. Grimes /*
206b9aac308STim J. Robbins  * Convert a wide character string argument for the %ls format to a multibyte
207d48c77b5STim J. Robbins  * string representation. If not -1, prec specifies the maximum number of
208d48c77b5STim J. Robbins  * bytes to output, and also means that we can't assume that the wide char.
209d48c77b5STim J. Robbins  * string ends is null-terminated.
210b9aac308STim J. Robbins  */
211b9aac308STim J. Robbins static char *
__wcsconv(wchar_t * wcsarg,int prec)212b9aac308STim J. Robbins __wcsconv(wchar_t *wcsarg, int prec)
213b9aac308STim J. Robbins {
21493996f6dSTim J. Robbins 	static const mbstate_t initial;
21593996f6dSTim J. Robbins 	mbstate_t mbs;
216b9aac308STim J. Robbins 	char buf[MB_LEN_MAX];
217b9aac308STim J. Robbins 	wchar_t *p;
218d48c77b5STim J. Robbins 	char *convbuf;
219b9aac308STim J. Robbins 	size_t clen, nbytes;
220b9aac308STim J. Robbins 
221d48c77b5STim J. Robbins 	/* Allocate space for the maximum number of bytes we could output. */
222d48c77b5STim J. Robbins 	if (prec < 0) {
223d48c77b5STim J. Robbins 		p = wcsarg;
224d48c77b5STim J. Robbins 		mbs = initial;
225d48c77b5STim J. Robbins 		nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
226d48c77b5STim J. Robbins 		if (nbytes == (size_t)-1)
227d48c77b5STim J. Robbins 			return (NULL);
228d48c77b5STim J. Robbins 	} else {
229b9aac308STim J. Robbins 		/*
230d48c77b5STim J. Robbins 		 * Optimisation: if the output precision is small enough,
231d48c77b5STim J. Robbins 		 * just allocate enough memory for the maximum instead of
232d48c77b5STim J. Robbins 		 * scanning the string.
233b9aac308STim J. Robbins 		 */
234d48c77b5STim J. Robbins 		if (prec < 128)
235d48c77b5STim J. Robbins 			nbytes = prec;
236d48c77b5STim J. Robbins 		else {
237b9aac308STim J. Robbins 			nbytes = 0;
238b9aac308STim J. Robbins 			p = wcsarg;
23993996f6dSTim J. Robbins 			mbs = initial;
240b9aac308STim J. Robbins 			for (;;) {
24193996f6dSTim J. Robbins 				clen = wcrtomb(buf, *p++, &mbs);
242b9aac308STim J. Robbins 				if (clen == 0 || clen == (size_t)-1 ||
243b9aac308STim J. Robbins 				    nbytes + clen > prec)
244b9aac308STim J. Robbins 					break;
245b9aac308STim J. Robbins 				nbytes += clen;
246b9aac308STim J. Robbins 			}
247d48c77b5STim J. Robbins 		}
248b9aac308STim J. Robbins 	}
249b9aac308STim J. Robbins 	if ((convbuf = malloc(nbytes + 1)) == NULL)
250b9aac308STim J. Robbins 		return (NULL);
251b9aac308STim J. Robbins 
252d48c77b5STim J. Robbins 	/* Fill the output buffer. */
253b9aac308STim J. Robbins 	p = wcsarg;
25493996f6dSTim J. Robbins 	mbs = initial;
255d48c77b5STim J. Robbins 	if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p,
256d48c77b5STim J. Robbins 	    nbytes, &mbs)) == (size_t)-1) {
2576f098a48SAndrey A. Chernov 		free(convbuf);
258b9aac308STim J. Robbins 		return (NULL);
2596f098a48SAndrey A. Chernov 	}
260d48c77b5STim J. Robbins 	convbuf[nbytes] = '\0';
261b9aac308STim J. Robbins 	return (convbuf);
262b9aac308STim J. Robbins }
263b9aac308STim J. Robbins 
264b9aac308STim J. Robbins /*
265d201fe46SDaniel Eischen  * MT-safe version
266d201fe46SDaniel Eischen  */
267d201fe46SDaniel Eischen int
vfprintf_l(FILE * __restrict fp,locale_t locale,const char * __restrict fmt0,va_list ap)2683c87aa1dSDavid Chisnall vfprintf_l(FILE * __restrict fp, locale_t locale, const char * __restrict fmt0,
2693c87aa1dSDavid Chisnall     va_list ap)
270d201fe46SDaniel Eischen {
27174f1007fSDag-Erling Smørgrav 	int serrno = errno;
272d201fe46SDaniel Eischen 	int ret;
2733c87aa1dSDavid Chisnall 	FIX_LOCALE(locale);
274d201fe46SDaniel Eischen 
275fda0a14fSKonstantin Belousov 	FLOCKFILE_CANCELSAFE(fp);
276a1805f7bSDavid Schultz 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
277a1805f7bSDavid Schultz 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
278a1805f7bSDavid Schultz 	    fp->_file >= 0)
27974f1007fSDag-Erling Smørgrav 		ret = __sbprintf(fp, locale, serrno, fmt0, ap);
280a1805f7bSDavid Schultz 	else
28174f1007fSDag-Erling Smørgrav 		ret = __vfprintf(fp, locale, serrno, fmt0, ap);
282fda0a14fSKonstantin Belousov 	FUNLOCKFILE_CANCELSAFE();
283d201fe46SDaniel Eischen 	return (ret);
284d201fe46SDaniel Eischen }
2853c87aa1dSDavid Chisnall int
vfprintf(FILE * __restrict fp,const char * __restrict fmt0,va_list ap)2863c87aa1dSDavid Chisnall vfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap)
2873c87aa1dSDavid Chisnall {
2883c87aa1dSDavid Chisnall 	return vfprintf_l(fp, __get_locale(), fmt0, ap);
2893c87aa1dSDavid Chisnall }
290d201fe46SDaniel Eischen 
29138cac8f8SDavid Schultz /*
29238cac8f8SDavid Schultz  * The size of the buffer we use as scratch space for integer
29321ca178eSDavid Schultz  * conversions, among other things.  We need enough space to
294*d4f9e326SAhmad Khalifa  * write a uintmax_t in binary.
29538cac8f8SDavid Schultz  */
296*d4f9e326SAhmad Khalifa #define BUF	(sizeof(uintmax_t) * CHAR_BIT)
29738cac8f8SDavid Schultz 
29858f0484fSRodney W. Grimes /*
299d201fe46SDaniel Eischen  * Non-MT-safe version
300d201fe46SDaniel Eischen  */
30158f0484fSRodney W. Grimes int
__vfprintf(FILE * fp,locale_t locale,int serrno,const char * fmt0,va_list ap)30274f1007fSDag-Erling Smørgrav __vfprintf(FILE *fp, locale_t locale, int serrno, const char *fmt0, va_list ap)
30358f0484fSRodney W. Grimes {
304d201fe46SDaniel Eischen 	char *fmt;		/* format string */
305d201fe46SDaniel Eischen 	int ch;			/* character from fmt */
306d201fe46SDaniel Eischen 	int n, n2;		/* handy integer (short term usage) */
307d201fe46SDaniel Eischen 	char *cp;		/* handy char pointer (short term usage) */
308d201fe46SDaniel Eischen 	int flags;		/* flags as above */
30958f0484fSRodney W. Grimes 	int ret;		/* return value accumulator */
31058f0484fSRodney W. Grimes 	int width;		/* width from format (%8d), or 0 */
311ebbad5ecSDavid Schultz 	int prec;		/* precision from format; <0 for N/A */
312f8876676SKonstantin Belousov 	int error;
313f8876676SKonstantin Belousov 	char errnomsg[NL_TEXTMAX];
31458f0484fSRodney W. Grimes 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
31521ca178eSDavid Schultz 	struct grouping_state gs; /* thousands' grouping info */
316814d1bc9SDavid Schultz 
3178de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
318ebbad5ecSDavid Schultz 	/*
319ebbad5ecSDavid Schultz 	 * We can decompose the printed representation of floating
320ebbad5ecSDavid Schultz 	 * point numbers into several parts, some of which may be empty:
321ebbad5ecSDavid Schultz 	 *
322ebbad5ecSDavid Schultz 	 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
323ebbad5ecSDavid Schultz 	 *    A       B     ---C---      D       E   F
324ebbad5ecSDavid Schultz 	 *
325ebbad5ecSDavid Schultz 	 * A:	'sign' holds this value if present; '\0' otherwise
326ebbad5ecSDavid Schultz 	 * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
327ebbad5ecSDavid Schultz 	 * C:	cp points to the string MMMNNN.  Leading and trailing
328ebbad5ecSDavid Schultz 	 *	zeros are not in the string and must be added.
329ebbad5ecSDavid Schultz 	 * D:	expchar holds this character; '\0' if no exponent, e.g. %f
330ebbad5ecSDavid Schultz 	 * F:	at least two digits for decimal, at least one digit for hex
331ebbad5ecSDavid Schultz 	 */
3327ae5c679SAlexey Zelkin 	char *decimal_point;	/* locale specific decimal point */
3335004a238SDavid Schultz 	int decpt_len;		/* length of decimal_point */
334ebbad5ecSDavid Schultz 	int signflag;		/* true if float is negative */
335ebbad5ecSDavid Schultz 	union {			/* floating point arguments %[aAeEfFgG] */
336ebbad5ecSDavid Schultz 		double dbl;
337ebbad5ecSDavid Schultz 		long double ldbl;
338ebbad5ecSDavid Schultz 	} fparg;
33958f0484fSRodney W. Grimes 	int expt;		/* integer value of exponent */
340ebbad5ecSDavid Schultz 	char expchar;		/* exponent character: [eEpP\0] */
341ebbad5ecSDavid Schultz 	char *dtoaend;		/* pointer to end of converted digits */
34258f0484fSRodney W. Grimes 	int expsize;		/* character count for expstr */
343ebbad5ecSDavid Schultz 	int ndig;		/* actual number of digits returned by dtoa */
344ebbad5ecSDavid Schultz 	char expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */
3452ffc61baSTor Egge 	char *dtoaresult;	/* buffer allocated by dtoa */
34658f0484fSRodney W. Grimes #endif
34758f0484fSRodney W. Grimes 	u_long	ulval;		/* integer arguments %[diouxX] */
3487735bb0fSBill Fenner 	uintmax_t ujval;	/* %j, %ll, %q, %t, %z integers */
34958f0484fSRodney W. Grimes 	int base;		/* base for [diouxX] conversion */
35058f0484fSRodney W. Grimes 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
351261a532aSBill Fenner 	int realsz;		/* field size expanded by dprec, sign, etc */
35258f0484fSRodney W. Grimes 	int size;		/* size of converted field or string */
35392e88f87SAndrey A. Chernov 	int prsize;             /* max size of printed field */
354ebbad5ecSDavid Schultz 	const char *xdigs;     	/* digits for %[xX] conversion */
355814d1bc9SDavid Schultz 	struct io_state io;	/* I/O buffering state */
35638cac8f8SDavid Schultz 	char buf[BUF];		/* buffer with space for digits of uintmax_t */
357ebbad5ecSDavid Schultz 	char ox[2];		/* space for 0x; ox[1] is either x, X, or \0 */
358a387081cSDoug Rabson 	union arg *argtable;    /* args, built due to positional arg */
359a387081cSDoug Rabson 	union arg statargtable [STATIC_ARG_TBL_SIZE];
360efb7e53dSJordan K. Hubbard 	int nextarg;            /* 1-based argument index */
361efb7e53dSJordan K. Hubbard 	va_list orgap;          /* original argument pointer */
362b9aac308STim J. Robbins 	char *convbuf;		/* wide to multibyte conversion result */
3631bf6c5f1SAndrey A. Chernov 	int savserr;
36458f0484fSRodney W. Grimes 
365ac9913a7SDavid Schultz 	static const char xdigs_lower[16] = "0123456789abcdef";
366ac9913a7SDavid Schultz 	static const char xdigs_upper[16] = "0123456789ABCDEF";
367ebbad5ecSDavid Schultz 
368814d1bc9SDavid Schultz 	/* BEWARE, these `goto error' on error. */
36958f0484fSRodney W. Grimes #define	PRINT(ptr, len) { \
3703c87aa1dSDavid Chisnall 	if (io_print(&io, (ptr), (len), locale))	\
37158f0484fSRodney W. Grimes 		goto error; \
37258f0484fSRodney W. Grimes }
37358f0484fSRodney W. Grimes #define	PAD(howmany, with) { \
3743c87aa1dSDavid Chisnall 	if (io_pad(&io, (howmany), (with), locale)) \
37558f0484fSRodney W. Grimes 		goto error; \
376814d1bc9SDavid Schultz }
377814d1bc9SDavid Schultz #define	PRINTANDPAD(p, ep, len, with) {	\
3783c87aa1dSDavid Chisnall 	if (io_printandpad(&io, (p), (ep), (len), (with), locale)) \
379814d1bc9SDavid Schultz 		goto error; \
380814d1bc9SDavid Schultz }
381814d1bc9SDavid Schultz #define	FLUSH() { \
3823c87aa1dSDavid Chisnall 	if (io_flush(&io, locale)) \
383814d1bc9SDavid Schultz 		goto error; \
38458f0484fSRodney W. Grimes }
38558f0484fSRodney W. Grimes 
38658f0484fSRodney W. Grimes 	/*
387efb7e53dSJordan K. Hubbard 	 * Get the argument indexed by nextarg.   If the argument table is
388efb7e53dSJordan K. Hubbard 	 * built, use it to get the argument.  If its not, get the next
389efb7e53dSJordan K. Hubbard 	 * argument (and arguments must be gotten sequentially).
390efb7e53dSJordan K. Hubbard 	 */
391efb7e53dSJordan K. Hubbard #define GETARG(type) \
392a387081cSDoug Rabson 	((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
393efb7e53dSJordan K. Hubbard 	    (nextarg++, va_arg(ap, type)))
394efb7e53dSJordan K. Hubbard 
395efb7e53dSJordan K. Hubbard 	/*
39658f0484fSRodney W. Grimes 	 * To extend shorts properly, we need both signed and unsigned
39758f0484fSRodney W. Grimes 	 * argument extraction methods.
39858f0484fSRodney W. Grimes 	 */
39958f0484fSRodney W. Grimes #define	SARG() \
400efb7e53dSJordan K. Hubbard 	(flags&LONGINT ? GETARG(long) : \
401efb7e53dSJordan K. Hubbard 	    flags&SHORTINT ? (long)(short)GETARG(int) : \
4027735bb0fSBill Fenner 	    flags&CHARINT ? (long)(signed char)GETARG(int) : \
403efb7e53dSJordan K. Hubbard 	    (long)GETARG(int))
40458f0484fSRodney W. Grimes #define	UARG() \
405efb7e53dSJordan K. Hubbard 	(flags&LONGINT ? GETARG(u_long) : \
406efb7e53dSJordan K. Hubbard 	    flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
4077735bb0fSBill Fenner 	    flags&CHARINT ? (u_long)(u_char)GETARG(int) : \
408efb7e53dSJordan K. Hubbard 	    (u_long)GETARG(u_int))
4097735bb0fSBill Fenner #define	INTMAX_SIZE	(INTMAXT|SIZET|PTRDIFFT|LLONGINT)
4107735bb0fSBill Fenner #define SJARG() \
4117735bb0fSBill Fenner 	(flags&INTMAXT ? GETARG(intmax_t) : \
4120881683bSDavid Schultz 	    flags&SIZET ? (intmax_t)GETARG(ssize_t) : \
4137735bb0fSBill Fenner 	    flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \
4147735bb0fSBill Fenner 	    (intmax_t)GETARG(long long))
4157735bb0fSBill Fenner #define	UJARG() \
4167735bb0fSBill Fenner 	(flags&INTMAXT ? GETARG(uintmax_t) : \
4177735bb0fSBill Fenner 	    flags&SIZET ? (uintmax_t)GETARG(size_t) : \
4187735bb0fSBill Fenner 	    flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \
4197735bb0fSBill Fenner 	    (uintmax_t)GETARG(unsigned long long))
420efb7e53dSJordan K. Hubbard 
421efb7e53dSJordan K. Hubbard 	/*
422efb7e53dSJordan K. Hubbard 	 * Get * arguments, including the form *nn$.  Preserve the nextarg
423efb7e53dSJordan K. Hubbard 	 * that the argument can be gotten once the type is determined.
424efb7e53dSJordan K. Hubbard 	 */
425efb7e53dSJordan K. Hubbard #define GETASTER(val) \
426efb7e53dSJordan K. Hubbard 	n2 = 0; \
427efb7e53dSJordan K. Hubbard 	cp = fmt; \
428efb7e53dSJordan K. Hubbard 	while (is_digit(*cp)) { \
429efb7e53dSJordan K. Hubbard 		n2 = 10 * n2 + to_digit(*cp); \
430efb7e53dSJordan K. Hubbard 		cp++; \
431efb7e53dSJordan K. Hubbard 	} \
432efb7e53dSJordan K. Hubbard 	if (*cp == '$') { \
433efb7e53dSJordan K. Hubbard 		int hold = nextarg; \
434efb7e53dSJordan K. Hubbard 		if (argtable == NULL) { \
435efb7e53dSJordan K. Hubbard 			argtable = statargtable; \
436e62e5ff9SDavid Schultz 			if (__find_arguments (fmt0, orgap, &argtable)) { \
437e62e5ff9SDavid Schultz 				ret = EOF; \
438e62e5ff9SDavid Schultz 				goto error; \
439e62e5ff9SDavid Schultz 			} \
440efb7e53dSJordan K. Hubbard 		} \
441efb7e53dSJordan K. Hubbard 		nextarg = n2; \
442efb7e53dSJordan K. Hubbard 		val = GETARG (int); \
443efb7e53dSJordan K. Hubbard 		nextarg = hold; \
444efb7e53dSJordan K. Hubbard 		fmt = ++cp; \
445efb7e53dSJordan K. Hubbard 	} else { \
446efb7e53dSJordan K. Hubbard 		val = GETARG (int); \
447efb7e53dSJordan K. Hubbard 	}
448efb7e53dSJordan K. Hubbard 
44933bff5d3SDavid Schultz 	if (__use_xprintf > 0)
45033bff5d3SDavid Schultz 		return (__xvprintf(fp, fmt0, ap));
45158f0484fSRodney W. Grimes 
45258f0484fSRodney W. Grimes 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
453450ead86SPedro F. Giffuni 	if (prepwrite(fp) != 0) {
454450ead86SPedro F. Giffuni 		errno = EBADF;
45558f0484fSRodney W. Grimes 		return (EOF);
456450ead86SPedro F. Giffuni 	}
45758f0484fSRodney W. Grimes 
4581bf6c5f1SAndrey A. Chernov 	savserr = fp->_flags & __SERR;
4591bf6c5f1SAndrey A. Chernov 	fp->_flags &= ~__SERR;
4601bf6c5f1SAndrey A. Chernov 
461e18701f4SDavid Schultz 	convbuf = NULL;
46258f0484fSRodney W. Grimes 	fmt = (char *)fmt0;
463efb7e53dSJordan K. Hubbard 	argtable = NULL;
464efb7e53dSJordan K. Hubbard 	nextarg = 1;
465d07090a8STim J. Robbins 	va_copy(orgap, ap);
466814d1bc9SDavid Schultz 	io_init(&io, fp);
46758f0484fSRodney W. Grimes 	ret = 0;
468e18701f4SDavid Schultz #ifndef NO_FLOATING_POINT
469e18701f4SDavid Schultz 	dtoaresult = NULL;
4703c87aa1dSDavid Chisnall 	decimal_point = localeconv_l(locale)->decimal_point;
4715004a238SDavid Schultz 	/* The overwhelmingly common case is decpt_len == 1. */
4725004a238SDavid Schultz 	decpt_len = (decimal_point[1] == '\0' ? 1 : strlen(decimal_point));
473e18701f4SDavid Schultz #endif
47458f0484fSRodney W. Grimes 
47558f0484fSRodney W. Grimes 	/*
47658f0484fSRodney W. Grimes 	 * Scan the format for conversions (`%' character).
47758f0484fSRodney W. Grimes 	 */
47858f0484fSRodney W. Grimes 	for (;;) {
47958f0484fSRodney W. Grimes 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
48058f0484fSRodney W. Grimes 			/* void */;
48158f0484fSRodney W. Grimes 		if ((n = fmt - cp) != 0) {
482b250f248SAndrey A. Chernov 			if ((unsigned)ret + n > INT_MAX) {
48392e88f87SAndrey A. Chernov 				ret = EOF;
484666d00d3SDavid Schultz 				errno = EOVERFLOW;
48592e88f87SAndrey A. Chernov 				goto error;
48692e88f87SAndrey A. Chernov 			}
48758f0484fSRodney W. Grimes 			PRINT(cp, n);
48858f0484fSRodney W. Grimes 			ret += n;
48958f0484fSRodney W. Grimes 		}
49058f0484fSRodney W. Grimes 		if (ch == '\0')
49158f0484fSRodney W. Grimes 			goto done;
49258f0484fSRodney W. Grimes 		fmt++;		/* skip over '%' */
49358f0484fSRodney W. Grimes 
49458f0484fSRodney W. Grimes 		flags = 0;
49558f0484fSRodney W. Grimes 		dprec = 0;
49658f0484fSRodney W. Grimes 		width = 0;
49758f0484fSRodney W. Grimes 		prec = -1;
49821ca178eSDavid Schultz 		gs.grouping = NULL;
49958f0484fSRodney W. Grimes 		sign = '\0';
500ebbad5ecSDavid Schultz 		ox[1] = '\0';
50158f0484fSRodney W. Grimes 
50258f0484fSRodney W. Grimes rflag:		ch = *fmt++;
50358f0484fSRodney W. Grimes reswitch:	switch (ch) {
50458f0484fSRodney W. Grimes 		case ' ':
5052e394b2fSAlexey Zelkin 			/*-
50658f0484fSRodney W. Grimes 			 * ``If the space and + flags both appear, the space
50758f0484fSRodney W. Grimes 			 * flag will be ignored.''
50858f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
50958f0484fSRodney W. Grimes 			 */
51058f0484fSRodney W. Grimes 			if (!sign)
51158f0484fSRodney W. Grimes 				sign = ' ';
51258f0484fSRodney W. Grimes 			goto rflag;
51358f0484fSRodney W. Grimes 		case '#':
51458f0484fSRodney W. Grimes 			flags |= ALT;
51558f0484fSRodney W. Grimes 			goto rflag;
51658f0484fSRodney W. Grimes 		case '*':
5172e394b2fSAlexey Zelkin 			/*-
51858f0484fSRodney W. Grimes 			 * ``A negative field width argument is taken as a
51958f0484fSRodney W. Grimes 			 * - flag followed by a positive field width.''
52058f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
52158f0484fSRodney W. Grimes 			 * They don't exclude field widths read from args.
52258f0484fSRodney W. Grimes 			 */
523efb7e53dSJordan K. Hubbard 			GETASTER (width);
524efb7e53dSJordan K. Hubbard 			if (width >= 0)
52558f0484fSRodney W. Grimes 				goto rflag;
52658f0484fSRodney W. Grimes 			width = -width;
52758f0484fSRodney W. Grimes 			/* FALLTHROUGH */
52858f0484fSRodney W. Grimes 		case '-':
52958f0484fSRodney W. Grimes 			flags |= LADJUST;
53058f0484fSRodney W. Grimes 			goto rflag;
53158f0484fSRodney W. Grimes 		case '+':
53258f0484fSRodney W. Grimes 			sign = '+';
53358f0484fSRodney W. Grimes 			goto rflag;
5347735bb0fSBill Fenner 		case '\'':
53598ee7635SAlexey Zelkin 			flags |= GROUPING;
5367735bb0fSBill Fenner 			goto rflag;
53758f0484fSRodney W. Grimes 		case '.':
53858f0484fSRodney W. Grimes 			if ((ch = *fmt++) == '*') {
5393b204b7dSDavid Schultz 				GETASTER (prec);
54058f0484fSRodney W. Grimes 				goto rflag;
54158f0484fSRodney W. Grimes 			}
5423b204b7dSDavid Schultz 			prec = 0;
54358f0484fSRodney W. Grimes 			while (is_digit(ch)) {
5443b204b7dSDavid Schultz 				prec = 10 * prec + to_digit(ch);
54558f0484fSRodney W. Grimes 				ch = *fmt++;
54658f0484fSRodney W. Grimes 			}
54758f0484fSRodney W. Grimes 			goto reswitch;
54858f0484fSRodney W. Grimes 		case '0':
5492e394b2fSAlexey Zelkin 			/*-
55058f0484fSRodney W. Grimes 			 * ``Note that 0 is taken as a flag, not as the
55158f0484fSRodney W. Grimes 			 * beginning of a field width.''
55258f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
55358f0484fSRodney W. Grimes 			 */
55458f0484fSRodney W. Grimes 			flags |= ZEROPAD;
55558f0484fSRodney W. Grimes 			goto rflag;
55658f0484fSRodney W. Grimes 		case '1': case '2': case '3': case '4':
55758f0484fSRodney W. Grimes 		case '5': case '6': case '7': case '8': case '9':
55858f0484fSRodney W. Grimes 			n = 0;
55958f0484fSRodney W. Grimes 			do {
56058f0484fSRodney W. Grimes 				n = 10 * n + to_digit(ch);
56158f0484fSRodney W. Grimes 				ch = *fmt++;
56258f0484fSRodney W. Grimes 			} while (is_digit(ch));
563efb7e53dSJordan K. Hubbard 			if (ch == '$') {
564efb7e53dSJordan K. Hubbard 				nextarg = n;
565efb7e53dSJordan K. Hubbard 				if (argtable == NULL) {
566efb7e53dSJordan K. Hubbard 					argtable = statargtable;
567e62e5ff9SDavid Schultz 					if (__find_arguments (fmt0, orgap,
568e62e5ff9SDavid Schultz 							      &argtable)) {
569e62e5ff9SDavid Schultz 						ret = EOF;
570e62e5ff9SDavid Schultz 						goto error;
571e62e5ff9SDavid Schultz 					}
572efb7e53dSJordan K. Hubbard 				}
573efb7e53dSJordan K. Hubbard 				goto rflag;
574efb7e53dSJordan K. Hubbard 			}
57558f0484fSRodney W. Grimes 			width = n;
57658f0484fSRodney W. Grimes 			goto reswitch;
5778de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
57858f0484fSRodney W. Grimes 		case 'L':
57958f0484fSRodney W. Grimes 			flags |= LONGDBL;
58058f0484fSRodney W. Grimes 			goto rflag;
58158f0484fSRodney W. Grimes #endif
58258f0484fSRodney W. Grimes 		case 'h':
5837735bb0fSBill Fenner 			if (flags & SHORTINT) {
5847735bb0fSBill Fenner 				flags &= ~SHORTINT;
5857735bb0fSBill Fenner 				flags |= CHARINT;
5867735bb0fSBill Fenner 			} else
58758f0484fSRodney W. Grimes 				flags |= SHORTINT;
58858f0484fSRodney W. Grimes 			goto rflag;
5897735bb0fSBill Fenner 		case 'j':
5907735bb0fSBill Fenner 			flags |= INTMAXT;
5917735bb0fSBill Fenner 			goto rflag;
59258f0484fSRodney W. Grimes 		case 'l':
5937735bb0fSBill Fenner 			if (flags & LONGINT) {
5947735bb0fSBill Fenner 				flags &= ~LONGINT;
5957735bb0fSBill Fenner 				flags |= LLONGINT;
5967735bb0fSBill Fenner 			} else
59758f0484fSRodney W. Grimes 				flags |= LONGINT;
59858f0484fSRodney W. Grimes 			goto rflag;
59958f0484fSRodney W. Grimes 		case 'q':
6007735bb0fSBill Fenner 			flags |= LLONGINT;	/* not necessarily */
6017735bb0fSBill Fenner 			goto rflag;
6027735bb0fSBill Fenner 		case 't':
6037735bb0fSBill Fenner 			flags |= PTRDIFFT;
6047735bb0fSBill Fenner 			goto rflag;
605bce0bef3SDag-Erling Smørgrav 		case 'w':
606bce0bef3SDag-Erling Smørgrav 			/*
607bce0bef3SDag-Erling Smørgrav 			 * Fixed-width integer types.  On all platforms we
608bce0bef3SDag-Erling Smørgrav 			 * support, int8_t is equivalent to char, int16_t
609bce0bef3SDag-Erling Smørgrav 			 * is equivalent to short, int32_t is equivalent
610bce0bef3SDag-Erling Smørgrav 			 * to int, int64_t is equivalent to long long int.
611bce0bef3SDag-Erling Smørgrav 			 * Furthermore, int_fast8_t, int_fast16_t and
612bce0bef3SDag-Erling Smørgrav 			 * int_fast32_t are equivalent to int, and
613bce0bef3SDag-Erling Smørgrav 			 * int_fast64_t is equivalent to long long int.
614bce0bef3SDag-Erling Smørgrav 			 */
615bce0bef3SDag-Erling Smørgrav 			flags &= ~(CHARINT|SHORTINT|LONGINT|LLONGINT|INTMAXT);
616bce0bef3SDag-Erling Smørgrav 			if (fmt[0] == 'f') {
617bce0bef3SDag-Erling Smørgrav 				flags |= FASTINT;
618bce0bef3SDag-Erling Smørgrav 				fmt++;
619bce0bef3SDag-Erling Smørgrav 			} else {
620bce0bef3SDag-Erling Smørgrav 				flags &= ~FASTINT;
621bce0bef3SDag-Erling Smørgrav 			}
622bce0bef3SDag-Erling Smørgrav 			if (fmt[0] == '8') {
623bce0bef3SDag-Erling Smørgrav 				if (!(flags & FASTINT))
624bce0bef3SDag-Erling Smørgrav 					flags |= CHARINT;
625bce0bef3SDag-Erling Smørgrav 				else
626bce0bef3SDag-Erling Smørgrav 					/* no flag set = 32 */ ;
627bce0bef3SDag-Erling Smørgrav 				fmt += 1;
628bce0bef3SDag-Erling Smørgrav 			} else if (fmt[0] == '1' && fmt[1] == '6') {
629bce0bef3SDag-Erling Smørgrav 				if (!(flags & FASTINT))
630bce0bef3SDag-Erling Smørgrav 					flags |= SHORTINT;
631bce0bef3SDag-Erling Smørgrav 				else
632bce0bef3SDag-Erling Smørgrav 					/* no flag set = 32 */ ;
633bce0bef3SDag-Erling Smørgrav 				fmt += 2;
634bce0bef3SDag-Erling Smørgrav 			} else if (fmt[0] == '3' && fmt[1] == '2') {
635bce0bef3SDag-Erling Smørgrav 				/* no flag set = 32 */ ;
636bce0bef3SDag-Erling Smørgrav 				fmt += 2;
637bce0bef3SDag-Erling Smørgrav 			} else if (fmt[0] == '6' && fmt[1] == '4') {
638bce0bef3SDag-Erling Smørgrav 				flags |= LLONGINT;
639bce0bef3SDag-Erling Smørgrav 				fmt += 2;
640bce0bef3SDag-Erling Smørgrav 			} else {
641bce0bef3SDag-Erling Smørgrav 				if (flags & FASTINT) {
642bce0bef3SDag-Erling Smørgrav 					flags &= ~FASTINT;
643bce0bef3SDag-Erling Smørgrav 					fmt--;
644bce0bef3SDag-Erling Smørgrav 				}
645bce0bef3SDag-Erling Smørgrav 				goto invalid;
646bce0bef3SDag-Erling Smørgrav 			}
647bce0bef3SDag-Erling Smørgrav 			goto rflag;
6487735bb0fSBill Fenner 		case 'z':
6497735bb0fSBill Fenner 			flags |= SIZET;
65058f0484fSRodney W. Grimes 			goto rflag;
651d9dc1603SDag-Erling Smørgrav 		case 'B':
652d9dc1603SDag-Erling Smørgrav 		case 'b':
653d9dc1603SDag-Erling Smørgrav 			if (flags & INTMAX_SIZE)
654d9dc1603SDag-Erling Smørgrav 				ujval = UJARG();
655d9dc1603SDag-Erling Smørgrav 			else
656d9dc1603SDag-Erling Smørgrav 				ulval = UARG();
657d9dc1603SDag-Erling Smørgrav 			base = 2;
658d9dc1603SDag-Erling Smørgrav 			/* leading 0b/B only if non-zero */
659d9dc1603SDag-Erling Smørgrav 			if (flags & ALT &&
660d9dc1603SDag-Erling Smørgrav 			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
661d9dc1603SDag-Erling Smørgrav 				ox[1] = ch;
662d9dc1603SDag-Erling Smørgrav 			goto nosign;
663d9dc1603SDag-Erling Smørgrav 			break;
664927ecbf3STim J. Robbins 		case 'C':
665927ecbf3STim J. Robbins 			flags |= LONGINT;
666927ecbf3STim J. Robbins 			/*FALLTHROUGH*/
66758f0484fSRodney W. Grimes 		case 'c':
668b9aac308STim J. Robbins 			if (flags & LONGINT) {
66993996f6dSTim J. Robbins 				static const mbstate_t initial;
67093996f6dSTim J. Robbins 				mbstate_t mbs;
671b9aac308STim J. Robbins 				size_t mbseqlen;
672b9aac308STim J. Robbins 
67393996f6dSTim J. Robbins 				mbs = initial;
674b9aac308STim J. Robbins 				mbseqlen = wcrtomb(cp = buf,
67593996f6dSTim J. Robbins 				    (wchar_t)GETARG(wint_t), &mbs);
6766180233fSTim J. Robbins 				if (mbseqlen == (size_t)-1) {
6776180233fSTim J. Robbins 					fp->_flags |= __SERR;
678b9aac308STim J. Robbins 					goto error;
6796180233fSTim J. Robbins 				}
680b9aac308STim J. Robbins 				size = (int)mbseqlen;
681b9aac308STim J. Robbins 			} else {
682efb7e53dSJordan K. Hubbard 				*(cp = buf) = GETARG(int);
68358f0484fSRodney W. Grimes 				size = 1;
684b9aac308STim J. Robbins 			}
68558f0484fSRodney W. Grimes 			sign = '\0';
68658f0484fSRodney W. Grimes 			break;
68758f0484fSRodney W. Grimes 		case 'D':
68858f0484fSRodney W. Grimes 			flags |= LONGINT;
68958f0484fSRodney W. Grimes 			/*FALLTHROUGH*/
69058f0484fSRodney W. Grimes 		case 'd':
69158f0484fSRodney W. Grimes 		case 'i':
6927735bb0fSBill Fenner 			if (flags & INTMAX_SIZE) {
6937735bb0fSBill Fenner 				ujval = SJARG();
6947735bb0fSBill Fenner 				if ((intmax_t)ujval < 0) {
6957735bb0fSBill Fenner 					ujval = -ujval;
69658f0484fSRodney W. Grimes 					sign = '-';
69758f0484fSRodney W. Grimes 				}
69858f0484fSRodney W. Grimes 			} else {
69958f0484fSRodney W. Grimes 				ulval = SARG();
70058f0484fSRodney W. Grimes 				if ((long)ulval < 0) {
70158f0484fSRodney W. Grimes 					ulval = -ulval;
70258f0484fSRodney W. Grimes 					sign = '-';
70358f0484fSRodney W. Grimes 				}
70458f0484fSRodney W. Grimes 			}
70558f0484fSRodney W. Grimes 			base = 10;
70658f0484fSRodney W. Grimes 			goto number;
7078de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
7087735bb0fSBill Fenner 		case 'a':
7097735bb0fSBill Fenner 		case 'A':
710ebbad5ecSDavid Schultz 			if (ch == 'a') {
711ebbad5ecSDavid Schultz 				ox[1] = 'x';
712ebbad5ecSDavid Schultz 				xdigs = xdigs_lower;
713ebbad5ecSDavid Schultz 				expchar = 'p';
714ebbad5ecSDavid Schultz 			} else {
715ebbad5ecSDavid Schultz 				ox[1] = 'X';
716ebbad5ecSDavid Schultz 				xdigs = xdigs_upper;
717ebbad5ecSDavid Schultz 				expchar = 'P';
718ebbad5ecSDavid Schultz 			}
719904322a5SDavid Schultz 			if (prec >= 0)
720904322a5SDavid Schultz 				prec++;
721904322a5SDavid Schultz 			if (dtoaresult != NULL)
722904322a5SDavid Schultz 				freedtoa(dtoaresult);
723ebbad5ecSDavid Schultz 			if (flags & LONGDBL) {
724904322a5SDavid Schultz 				fparg.ldbl = GETARG(long double);
725ebbad5ecSDavid Schultz 				dtoaresult = cp =
726ebbad5ecSDavid Schultz 				    __hldtoa(fparg.ldbl, xdigs, prec,
727ebbad5ecSDavid Schultz 				    &expt, &signflag, &dtoaend);
728ebbad5ecSDavid Schultz 			} else {
729ebbad5ecSDavid Schultz 				fparg.dbl = GETARG(double);
730ebbad5ecSDavid Schultz 				dtoaresult = cp =
731ebbad5ecSDavid Schultz 				    __hdtoa(fparg.dbl, xdigs, prec,
732ebbad5ecSDavid Schultz 				    &expt, &signflag, &dtoaend);
733ebbad5ecSDavid Schultz 			}
734904322a5SDavid Schultz 			if (prec < 0)
735904322a5SDavid Schultz 				prec = dtoaend - cp;
736904322a5SDavid Schultz 			if (expt == INT_MAX)
737904322a5SDavid Schultz 				ox[1] = '\0';
738904322a5SDavid Schultz 			goto fp_common;
739d26be6f0SBruce Evans 		case 'e':
74058f0484fSRodney W. Grimes 		case 'E':
741ebbad5ecSDavid Schultz 			expchar = ch;
742ebbad5ecSDavid Schultz 			if (prec < 0)	/* account for digit before decpt */
743ebbad5ecSDavid Schultz 				prec = DEFPREC + 1;
744ebbad5ecSDavid Schultz 			else
745ebbad5ecSDavid Schultz 				prec++;
746ebbad5ecSDavid Schultz 			goto fp_begin;
747d26be6f0SBruce Evans 		case 'f':
7487735bb0fSBill Fenner 		case 'F':
749ebbad5ecSDavid Schultz 			expchar = '\0';
750d26be6f0SBruce Evans 			goto fp_begin;
75158f0484fSRodney W. Grimes 		case 'g':
75258f0484fSRodney W. Grimes 		case 'G':
753ebbad5ecSDavid Schultz 			expchar = ch - ('g' - 'e');
754d26be6f0SBruce Evans 			if (prec == 0)
755d26be6f0SBruce Evans 				prec = 1;
756ebbad5ecSDavid Schultz fp_begin:
757ebbad5ecSDavid Schultz 			if (prec < 0)
75858f0484fSRodney W. Grimes 				prec = DEFPREC;
759ebbad5ecSDavid Schultz 			if (dtoaresult != NULL)
760ebbad5ecSDavid Schultz 				freedtoa(dtoaresult);
761ebbad5ecSDavid Schultz 			if (flags & LONGDBL) {
762ebbad5ecSDavid Schultz 				fparg.ldbl = GETARG(long double);
763ebbad5ecSDavid Schultz 				dtoaresult = cp =
764ebbad5ecSDavid Schultz 				    __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
765ebbad5ecSDavid Schultz 				    &expt, &signflag, &dtoaend);
766ebbad5ecSDavid Schultz 			} else {
767ebbad5ecSDavid Schultz 				fparg.dbl = GETARG(double);
768ebbad5ecSDavid Schultz 				dtoaresult = cp =
769ebbad5ecSDavid Schultz 				    dtoa(fparg.dbl, expchar ? 2 : 3, prec,
770ebbad5ecSDavid Schultz 				    &expt, &signflag, &dtoaend);
771ebbad5ecSDavid Schultz 				if (expt == 9999)
772ebbad5ecSDavid Schultz 					expt = INT_MAX;
77358f0484fSRodney W. Grimes 			}
774904322a5SDavid Schultz fp_common:
775ebbad5ecSDavid Schultz 			if (signflag)
776ebbad5ecSDavid Schultz 				sign = '-';
777ebbad5ecSDavid Schultz 			if (expt == INT_MAX) {	/* inf or nan */
778ebbad5ecSDavid Schultz 				if (*cp == 'N') {
779ebbad5ecSDavid Schultz 					cp = (ch >= 'a') ? "nan" : "NAN";
780ebbad5ecSDavid Schultz 					sign = '\0';
781ebbad5ecSDavid Schultz 				} else
782ebbad5ecSDavid Schultz 					cp = (ch >= 'a') ? "inf" : "INF";
78358f0484fSRodney W. Grimes 				size = 3;
784970a466cSDavid Schultz 				flags &= ~ZEROPAD;
78558f0484fSRodney W. Grimes 				break;
78658f0484fSRodney W. Grimes 			}
78758f0484fSRodney W. Grimes 			flags |= FPT;
788ebbad5ecSDavid Schultz 			ndig = dtoaend - cp;
78958f0484fSRodney W. Grimes 			if (ch == 'g' || ch == 'G') {
790ebbad5ecSDavid Schultz 				if (expt > -4 && expt <= prec) {
791ebbad5ecSDavid Schultz 					/* Make %[gG] smell like %[fF] */
792ebbad5ecSDavid Schultz 					expchar = '\0';
793ebbad5ecSDavid Schultz 					if (flags & ALT)
794ebbad5ecSDavid Schultz 						prec -= expt;
79558f0484fSRodney W. Grimes 					else
796ebbad5ecSDavid Schultz 						prec = ndig - expt;
797ebbad5ecSDavid Schultz 					if (prec < 0)
798ebbad5ecSDavid Schultz 						prec = 0;
7991f2a0cdfSDavid Schultz 				} else {
8001f2a0cdfSDavid Schultz 					/*
8011f2a0cdfSDavid Schultz 					 * Make %[gG] smell like %[eE], but
8021f2a0cdfSDavid Schultz 					 * trim trailing zeroes if no # flag.
8031f2a0cdfSDavid Schultz 					 */
8041f2a0cdfSDavid Schultz 					if (!(flags & ALT))
8051f2a0cdfSDavid Schultz 						prec = ndig;
80658f0484fSRodney W. Grimes 				}
807ebbad5ecSDavid Schultz 			}
808ebbad5ecSDavid Schultz 			if (expchar) {
809ebbad5ecSDavid Schultz 				expsize = exponent(expstr, expt - 1, expchar);
810ebbad5ecSDavid Schultz 				size = expsize + prec;
8113b204b7dSDavid Schultz 				if (prec > 1 || flags & ALT)
8125004a238SDavid Schultz 					size += decpt_len;
813ebbad5ecSDavid Schultz 			} else {
81481ae2e9aSDavid Schultz 				/* space for digits before decimal point */
81581ae2e9aSDavid Schultz 				if (expt > 0)
81658f0484fSRodney W. Grimes 					size = expt;
81781ae2e9aSDavid Schultz 				else	/* "0" */
81881ae2e9aSDavid Schultz 					size = 1;
81981ae2e9aSDavid Schultz 				/* space for decimal pt and following digits */
82058f0484fSRodney W. Grimes 				if (prec || flags & ALT)
8215004a238SDavid Schultz 					size += prec + decpt_len;
82221ca178eSDavid Schultz 				if ((flags & GROUPING) && expt > 0)
8233c87aa1dSDavid Chisnall 					size += grouping_init(&gs, expt, locale);
824ebbad5ecSDavid Schultz 			}
82558f0484fSRodney W. Grimes 			break;
8268de9e897SDavid Schultz #endif /* !NO_FLOATING_POINT */
827e95725feSKonstantin Belousov 		case 'm':
82874f1007fSDag-Erling Smørgrav 			error = __strerror_rl(serrno, errnomsg,
829f8876676SKonstantin Belousov 			    sizeof(errnomsg), locale);
830f8876676SKonstantin Belousov 			cp = error == 0 ? errnomsg : "<strerror failure>";
831e95725feSKonstantin Belousov 			size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
832e95725feSKonstantin Belousov 			sign = '\0';
833e95725feSKonstantin Belousov 			break;
83458f0484fSRodney W. Grimes 		case 'n':
8357735bb0fSBill Fenner 			/*
8367735bb0fSBill Fenner 			 * Assignment-like behavior is specified if the
8377735bb0fSBill Fenner 			 * value overflows or is otherwise unrepresentable.
8387735bb0fSBill Fenner 			 * C99 says to use `signed char' for %hhn conversions.
8397735bb0fSBill Fenner 			 */
8407735bb0fSBill Fenner 			if (flags & LLONGINT)
8417735bb0fSBill Fenner 				*GETARG(long long *) = ret;
8427735bb0fSBill Fenner 			else if (flags & SIZET)
8437735bb0fSBill Fenner 				*GETARG(ssize_t *) = (ssize_t)ret;
8447735bb0fSBill Fenner 			else if (flags & PTRDIFFT)
8457735bb0fSBill Fenner 				*GETARG(ptrdiff_t *) = ret;
8467735bb0fSBill Fenner 			else if (flags & INTMAXT)
8477735bb0fSBill Fenner 				*GETARG(intmax_t *) = ret;
84858f0484fSRodney W. Grimes 			else if (flags & LONGINT)
8496e690ad4SAndrey A. Chernov 				*GETARG(long *) = ret;
85058f0484fSRodney W. Grimes 			else if (flags & SHORTINT)
8516e690ad4SAndrey A. Chernov 				*GETARG(short *) = ret;
8527735bb0fSBill Fenner 			else if (flags & CHARINT)
8537735bb0fSBill Fenner 				*GETARG(signed char *) = ret;
85458f0484fSRodney W. Grimes 			else
8556e690ad4SAndrey A. Chernov 				*GETARG(int *) = ret;
85658f0484fSRodney W. Grimes 			continue;	/* no output */
85758f0484fSRodney W. Grimes 		case 'O':
85858f0484fSRodney W. Grimes 			flags |= LONGINT;
85958f0484fSRodney W. Grimes 			/*FALLTHROUGH*/
86058f0484fSRodney W. Grimes 		case 'o':
8617735bb0fSBill Fenner 			if (flags & INTMAX_SIZE)
8627735bb0fSBill Fenner 				ujval = UJARG();
86358f0484fSRodney W. Grimes 			else
86458f0484fSRodney W. Grimes 				ulval = UARG();
86558f0484fSRodney W. Grimes 			base = 8;
86658f0484fSRodney W. Grimes 			goto nosign;
86758f0484fSRodney W. Grimes 		case 'p':
8682e394b2fSAlexey Zelkin 			/*-
86958f0484fSRodney W. Grimes 			 * ``The argument shall be a pointer to void.  The
87058f0484fSRodney W. Grimes 			 * value of the pointer is converted to a sequence
87158f0484fSRodney W. Grimes 			 * of printable characters, in an implementation-
87258f0484fSRodney W. Grimes 			 * defined manner.''
87358f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
87458f0484fSRodney W. Grimes 			 */
8757735bb0fSBill Fenner 			ujval = (uintmax_t)(uintptr_t)GETARG(void *);
87658f0484fSRodney W. Grimes 			base = 16;
877ebbad5ecSDavid Schultz 			xdigs = xdigs_lower;
878ebbad5ecSDavid Schultz 			flags = flags | INTMAXT;
879ebbad5ecSDavid Schultz 			ox[1] = 'x';
88058f0484fSRodney W. Grimes 			goto nosign;
881927ecbf3STim J. Robbins 		case 'S':
882927ecbf3STim J. Robbins 			flags |= LONGINT;
883927ecbf3STim J. Robbins 			/*FALLTHROUGH*/
88458f0484fSRodney W. Grimes 		case 's':
885b9aac308STim J. Robbins 			if (flags & LONGINT) {
886b9aac308STim J. Robbins 				wchar_t *wcp;
887b9aac308STim J. Robbins 
888b9aac308STim J. Robbins 				if (convbuf != NULL)
889b9aac308STim J. Robbins 					free(convbuf);
890b9aac308STim J. Robbins 				if ((wcp = GETARG(wchar_t *)) == NULL)
891b9aac308STim J. Robbins 					cp = "(null)";
892b9aac308STim J. Robbins 				else {
893b9aac308STim J. Robbins 					convbuf = __wcsconv(wcp, prec);
8946180233fSTim J. Robbins 					if (convbuf == NULL) {
8956180233fSTim J. Robbins 						fp->_flags |= __SERR;
896b9aac308STim J. Robbins 						goto error;
8976180233fSTim J. Robbins 					}
898b9aac308STim J. Robbins 					cp = convbuf;
899b9aac308STim J. Robbins 				}
900b9aac308STim J. Robbins 			} else if ((cp = GETARG(char *)) == NULL)
90158f0484fSRodney W. Grimes 				cp = "(null)";
902353ce11cSDavid Schultz 			size = (prec >= 0) ? strnlen(cp, prec) : strlen(cp);
90358f0484fSRodney W. Grimes 			sign = '\0';
90458f0484fSRodney W. Grimes 			break;
90558f0484fSRodney W. Grimes 		case 'U':
90658f0484fSRodney W. Grimes 			flags |= LONGINT;
90758f0484fSRodney W. Grimes 			/*FALLTHROUGH*/
90858f0484fSRodney W. Grimes 		case 'u':
9097735bb0fSBill Fenner 			if (flags & INTMAX_SIZE)
9107735bb0fSBill Fenner 				ujval = UJARG();
91158f0484fSRodney W. Grimes 			else
91258f0484fSRodney W. Grimes 				ulval = UARG();
91358f0484fSRodney W. Grimes 			base = 10;
91458f0484fSRodney W. Grimes 			goto nosign;
91558f0484fSRodney W. Grimes 		case 'X':
916ebbad5ecSDavid Schultz 			xdigs = xdigs_upper;
91758f0484fSRodney W. Grimes 			goto hex;
91858f0484fSRodney W. Grimes 		case 'x':
919ebbad5ecSDavid Schultz 			xdigs = xdigs_lower;
9207735bb0fSBill Fenner hex:
9217735bb0fSBill Fenner 			if (flags & INTMAX_SIZE)
9227735bb0fSBill Fenner 				ujval = UJARG();
92358f0484fSRodney W. Grimes 			else
92458f0484fSRodney W. Grimes 				ulval = UARG();
92558f0484fSRodney W. Grimes 			base = 16;
92658f0484fSRodney W. Grimes 			/* leading 0x/X only if non-zero */
92758f0484fSRodney W. Grimes 			if (flags & ALT &&
9287735bb0fSBill Fenner 			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
929ebbad5ecSDavid Schultz 				ox[1] = ch;
93058f0484fSRodney W. Grimes 
93198ee7635SAlexey Zelkin 			flags &= ~GROUPING;
93258f0484fSRodney W. Grimes 			/* unsigned conversions */
93358f0484fSRodney W. Grimes nosign:			sign = '\0';
9342e394b2fSAlexey Zelkin 			/*-
93558f0484fSRodney W. Grimes 			 * ``... diouXx conversions ... if a precision is
93658f0484fSRodney W. Grimes 			 * specified, the 0 flag will be ignored.''
93758f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
93858f0484fSRodney W. Grimes 			 */
93958f0484fSRodney W. Grimes number:			if ((dprec = prec) >= 0)
94058f0484fSRodney W. Grimes 				flags &= ~ZEROPAD;
94158f0484fSRodney W. Grimes 
9422e394b2fSAlexey Zelkin 			/*-
94358f0484fSRodney W. Grimes 			 * ``The result of converting a zero value with an
94458f0484fSRodney W. Grimes 			 * explicit precision of zero is no characters.''
94558f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
9461be5319aSDavid Schultz 			 *
9471be5319aSDavid Schultz 			 * ``The C Standard is clear enough as is.  The call
9481be5319aSDavid Schultz 			 * printf("%#.0o", 0) should print 0.''
9491be5319aSDavid Schultz 			 *	-- Defect Report #151
95058f0484fSRodney W. Grimes 			 */
95158f0484fSRodney W. Grimes 			cp = buf + BUF;
9527735bb0fSBill Fenner 			if (flags & INTMAX_SIZE) {
9531be5319aSDavid Schultz 				if (ujval != 0 || prec != 0 ||
9541be5319aSDavid Schultz 				    (flags & ALT && base == 8))
9557735bb0fSBill Fenner 					cp = __ujtoa(ujval, cp, base,
95621ca178eSDavid Schultz 					    flags & ALT, xdigs);
95758f0484fSRodney W. Grimes 			} else {
9581be5319aSDavid Schultz 				if (ulval != 0 || prec != 0 ||
9591be5319aSDavid Schultz 				    (flags & ALT && base == 8))
96058f0484fSRodney W. Grimes 					cp = __ultoa(ulval, cp, base,
96121ca178eSDavid Schultz 					    flags & ALT, xdigs);
96258f0484fSRodney W. Grimes 			}
96358f0484fSRodney W. Grimes 			size = buf + BUF - cp;
96438cac8f8SDavid Schultz 			if (size > BUF)	/* should never happen */
96538cac8f8SDavid Schultz 				abort();
96621ca178eSDavid Schultz 			if ((flags & GROUPING) && size != 0)
9673c87aa1dSDavid Chisnall 				size += grouping_init(&gs, size, locale);
96858f0484fSRodney W. Grimes 			break;
96958f0484fSRodney W. Grimes 		default:	/* "%?" prints ?, unless ? is NUL */
97058f0484fSRodney W. Grimes 			if (ch == '\0')
97158f0484fSRodney W. Grimes 				goto done;
972bce0bef3SDag-Erling Smørgrav invalid:
97358f0484fSRodney W. Grimes 			/* pretend it was %c with argument ch */
97458f0484fSRodney W. Grimes 			cp = buf;
97558f0484fSRodney W. Grimes 			*cp = ch;
97658f0484fSRodney W. Grimes 			size = 1;
97758f0484fSRodney W. Grimes 			sign = '\0';
97858f0484fSRodney W. Grimes 			break;
97958f0484fSRodney W. Grimes 		}
98058f0484fSRodney W. Grimes 
98158f0484fSRodney W. Grimes 		/*
98258f0484fSRodney W. Grimes 		 * All reasonable formats wind up here.  At this point, `cp'
98358f0484fSRodney W. Grimes 		 * points to a string which (if not flags&LADJUST) should be
98458f0484fSRodney W. Grimes 		 * padded out to `width' places.  If flags&ZEROPAD, it should
98558f0484fSRodney W. Grimes 		 * first be prefixed by any sign or other prefix; otherwise,
98658f0484fSRodney W. Grimes 		 * it should be blank padded before the prefix is emitted.
98758f0484fSRodney W. Grimes 		 * After any left-hand padding and prefixing, emit zeroes
98858f0484fSRodney W. Grimes 		 * required by a decimal [diouxX] precision, then print the
98958f0484fSRodney W. Grimes 		 * string proper, then emit zeroes required by any leftover
99058f0484fSRodney W. Grimes 		 * floating precision; finally, if LADJUST, pad with blanks.
99158f0484fSRodney W. Grimes 		 *
99258f0484fSRodney W. Grimes 		 * Compute actual size, so we know how much to pad.
993261a532aSBill Fenner 		 * size excludes decimal prec; realsz includes it.
99458f0484fSRodney W. Grimes 		 */
995261a532aSBill Fenner 		realsz = dprec > size ? dprec : size;
99658f0484fSRodney W. Grimes 		if (sign)
997261a532aSBill Fenner 			realsz++;
998904322a5SDavid Schultz 		if (ox[1])
999261a532aSBill Fenner 			realsz += 2;
100058f0484fSRodney W. Grimes 
100192e88f87SAndrey A. Chernov 		prsize = width > realsz ? width : realsz;
1002b250f248SAndrey A. Chernov 		if ((unsigned)ret + prsize > INT_MAX) {
100392e88f87SAndrey A. Chernov 			ret = EOF;
1004666d00d3SDavid Schultz 			errno = EOVERFLOW;
100592e88f87SAndrey A. Chernov 			goto error;
100692e88f87SAndrey A. Chernov 		}
100792e88f87SAndrey A. Chernov 
100858f0484fSRodney W. Grimes 		/* right-adjusting blank padding */
100958f0484fSRodney W. Grimes 		if ((flags & (LADJUST|ZEROPAD)) == 0)
101058f0484fSRodney W. Grimes 			PAD(width - realsz, blanks);
101158f0484fSRodney W. Grimes 
101258f0484fSRodney W. Grimes 		/* prefix */
1013904322a5SDavid Schultz 		if (sign)
101458f0484fSRodney W. Grimes 			PRINT(&sign, 1);
1015904322a5SDavid Schultz 
1016904322a5SDavid Schultz 		if (ox[1]) {	/* ox[1] is either x, X, or \0 */
101758f0484fSRodney W. Grimes 			ox[0] = '0';
101858f0484fSRodney W. Grimes 			PRINT(ox, 2);
101958f0484fSRodney W. Grimes 		}
102058f0484fSRodney W. Grimes 
102158f0484fSRodney W. Grimes 		/* right-adjusting zero padding */
102258f0484fSRodney W. Grimes 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
102358f0484fSRodney W. Grimes 			PAD(width - realsz, zeroes);
102458f0484fSRodney W. Grimes 
102558f0484fSRodney W. Grimes 		/* the string or number proper */
10268de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
102758f0484fSRodney W. Grimes 		if ((flags & FPT) == 0) {
102821ca178eSDavid Schultz #endif
102921ca178eSDavid Schultz 			/* leading zeroes from decimal precision */
103021ca178eSDavid Schultz 			PAD(dprec - size, zeroes);
103121ca178eSDavid Schultz 			if (gs.grouping) {
10323c87aa1dSDavid Chisnall 				if (grouping_print(&gs, &io, cp, buf+BUF, locale) < 0)
103321ca178eSDavid Schultz 					goto error;
103421ca178eSDavid Schultz 			} else {
103558f0484fSRodney W. Grimes 				PRINT(cp, size);
103621ca178eSDavid Schultz 			}
103721ca178eSDavid Schultz #ifndef NO_FLOATING_POINT
103858f0484fSRodney W. Grimes 		} else {	/* glue together f_p fragments */
1039ebbad5ecSDavid Schultz 			if (!expchar) {	/* %[fF] or sufficiently short %[gG] */
1040ebbad5ecSDavid Schultz 				if (expt <= 0) {
104181ae2e9aSDavid Schultz 					PRINT(zeroes, 1);
104281ae2e9aSDavid Schultz 					if (prec || flags & ALT)
10435004a238SDavid Schultz 						PRINT(decimal_point,decpt_len);
104458f0484fSRodney W. Grimes 					PAD(-expt, zeroes);
10453b204b7dSDavid Schultz 					/* already handled initial 0's */
10463b204b7dSDavid Schultz 					prec += expt;
104758f0484fSRodney W. Grimes 				} else {
104821ca178eSDavid Schultz 					if (gs.grouping) {
104921ca178eSDavid Schultz 						n = grouping_print(&gs, &io,
10503c87aa1dSDavid Chisnall 						    cp, dtoaend, locale);
105121ca178eSDavid Schultz 						if (n < 0)
105221ca178eSDavid Schultz 							goto error;
105321ca178eSDavid Schultz 						cp += n;
105421ca178eSDavid Schultz 					} else {
10553b204b7dSDavid Schultz 						PRINTANDPAD(cp, dtoaend,
105621ca178eSDavid Schultz 						    expt, zeroes);
105721ca178eSDavid Schultz 						cp += expt;
1058ebbad5ecSDavid Schultz 					}
1059ebbad5ecSDavid Schultz 					if (prec || flags & ALT)
10605004a238SDavid Schultz 						PRINT(decimal_point,decpt_len);
1061ebbad5ecSDavid Schultz 				}
10623b204b7dSDavid Schultz 				PRINTANDPAD(cp, dtoaend, prec, zeroes);
1063ebbad5ecSDavid Schultz 			} else {	/* %[eE] or sufficiently long %[gG] */
10643b204b7dSDavid Schultz 				if (prec > 1 || flags & ALT) {
10655004a238SDavid Schultz 					PRINT(cp++, 1);
10665004a238SDavid Schultz 					PRINT(decimal_point, decpt_len);
106758f0484fSRodney W. Grimes 					PRINT(cp, ndig-1);
1068ebbad5ecSDavid Schultz 					PAD(prec - ndig, zeroes);
106958f0484fSRodney W. Grimes 				} else	/* XeYYY */
107058f0484fSRodney W. Grimes 					PRINT(cp, 1);
107158f0484fSRodney W. Grimes 				PRINT(expstr, expsize);
107258f0484fSRodney W. Grimes 			}
107358f0484fSRodney W. Grimes 		}
107458f0484fSRodney W. Grimes #endif
107558f0484fSRodney W. Grimes 		/* left-adjusting padding (always blank) */
107658f0484fSRodney W. Grimes 		if (flags & LADJUST)
107758f0484fSRodney W. Grimes 			PAD(width - realsz, blanks);
107858f0484fSRodney W. Grimes 
107958f0484fSRodney W. Grimes 		/* finally, adjust ret */
108092e88f87SAndrey A. Chernov 		ret += prsize;
108158f0484fSRodney W. Grimes 
108258f0484fSRodney W. Grimes 		FLUSH();	/* copy out the I/O vectors */
108358f0484fSRodney W. Grimes 	}
108458f0484fSRodney W. Grimes done:
108558f0484fSRodney W. Grimes 	FLUSH();
108658f0484fSRodney W. Grimes error:
1087096ad104SDag-Erling Smørgrav 	va_end(orgap);
10888de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
10892ffc61baSTor Egge 	if (dtoaresult != NULL)
1090ebbad5ecSDavid Schultz 		freedtoa(dtoaresult);
10912ffc61baSTor Egge #endif
1092b9aac308STim J. Robbins 	if (convbuf != NULL)
1093b9aac308STim J. Robbins 		free(convbuf);
1094f70177e7SJulian Elischer 	if (__sferror(fp))
1095f70177e7SJulian Elischer 		ret = EOF;
10961bf6c5f1SAndrey A. Chernov 	else
10971bf6c5f1SAndrey A. Chernov 		fp->_flags |= savserr;
1098efb7e53dSJordan K. Hubbard 	if ((argtable != NULL) && (argtable != statargtable))
1099efb7e53dSJordan K. Hubbard 		free (argtable);
1100f70177e7SJulian Elischer 	return (ret);
110158f0484fSRodney W. Grimes 	/* NOTREACHED */
110258f0484fSRodney W. Grimes }
110358f0484fSRodney W. Grimes 
1104