xref: /freebsd/lib/libc/stdio/vfwprintf.c (revision 8a16b7a18f5d0b031f09832fd7752fba717e2a97)
1c5604d0aSTim J. Robbins /*-
2*8a16b7a1SPedro F. Giffuni  * SPDX-License-Identifier: BSD-3-Clause
3*8a16b7a1SPedro F. Giffuni  *
4c5604d0aSTim J. Robbins  * Copyright (c) 1990, 1993
5c5604d0aSTim J. Robbins  *	The Regents of the University of California.  All rights reserved.
6c5604d0aSTim J. Robbins  *
7c5604d0aSTim J. Robbins  * This code is derived from software contributed to Berkeley by
8c5604d0aSTim J. Robbins  * Chris Torek.
9c5604d0aSTim J. Robbins  *
103c87aa1dSDavid Chisnall  * Copyright (c) 2011 The FreeBSD Foundation
113c87aa1dSDavid Chisnall  * All rights reserved.
123c87aa1dSDavid Chisnall  * Portions of this software were developed by David Chisnall
133c87aa1dSDavid Chisnall  * under sponsorship from the FreeBSD Foundation.
143c87aa1dSDavid Chisnall  *
15c5604d0aSTim J. Robbins  * Redistribution and use in source and binary forms, with or without
16c5604d0aSTim J. Robbins  * modification, are permitted provided that the following conditions
17c5604d0aSTim J. Robbins  * are met:
18c5604d0aSTim J. Robbins  * 1. Redistributions of source code must retain the above copyright
19c5604d0aSTim J. Robbins  *    notice, this list of conditions and the following disclaimer.
20c5604d0aSTim J. Robbins  * 2. Redistributions in binary form must reproduce the above copyright
21c5604d0aSTim J. Robbins  *    notice, this list of conditions and the following disclaimer in the
22c5604d0aSTim J. Robbins  *    documentation and/or other materials provided with the distribution.
231d8053c5SEd Maste  * 3. Neither the name of the University nor the names of its contributors
24c5604d0aSTim J. Robbins  *    may be used to endorse or promote products derived from this software
25c5604d0aSTim J. Robbins  *    without specific prior written permission.
26c5604d0aSTim J. Robbins  *
27c5604d0aSTim J. Robbins  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28c5604d0aSTim J. Robbins  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29c5604d0aSTim J. Robbins  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30c5604d0aSTim J. Robbins  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31c5604d0aSTim J. Robbins  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32c5604d0aSTim J. Robbins  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33c5604d0aSTim J. Robbins  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34c5604d0aSTim J. Robbins  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35c5604d0aSTim J. Robbins  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36c5604d0aSTim J. Robbins  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37c5604d0aSTim J. Robbins  * SUCH DAMAGE.
38c5604d0aSTim J. Robbins  */
39c5604d0aSTim J. Robbins 
40c5604d0aSTim J. Robbins #if 0
41c5604d0aSTim J. Robbins #if defined(LIBC_SCCS) && !defined(lint)
42c5604d0aSTim J. Robbins static char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 6/4/93";
43c5604d0aSTim J. Robbins #endif /* LIBC_SCCS and not lint */
44c5604d0aSTim J. Robbins #endif
45af152640SDavid E. O'Brien #include <sys/cdefs.h>
46c5604d0aSTim J. Robbins __FBSDID("$FreeBSD$");
47c5604d0aSTim J. Robbins 
48c5604d0aSTim J. Robbins /*
49c5604d0aSTim J. Robbins  * Actual wprintf innards.
50c5604d0aSTim J. Robbins  *
51c5604d0aSTim J. Robbins  * Avoid making gratuitous changes to this source file; it should be kept
52c5604d0aSTim J. Robbins  * as close as possible to vfprintf.c for ease of maintenance.
53c5604d0aSTim J. Robbins  */
54c5604d0aSTim J. Robbins 
55c5604d0aSTim J. Robbins #include "namespace.h"
56c5604d0aSTim J. Robbins #include <sys/types.h>
57c5604d0aSTim J. Robbins 
58c5604d0aSTim J. Robbins #include <ctype.h>
59666d00d3SDavid Schultz #include <errno.h>
60c5604d0aSTim J. Robbins #include <limits.h>
61c5604d0aSTim J. Robbins #include <locale.h>
62c5604d0aSTim J. Robbins #include <stdarg.h>
63c5604d0aSTim J. Robbins #include <stddef.h>
64c5604d0aSTim J. Robbins #include <stdint.h>
65c5604d0aSTim J. Robbins #include <stdio.h>
66c5604d0aSTim J. Robbins #include <stdlib.h>
67c5604d0aSTim J. Robbins #include <string.h>
68c5604d0aSTim J. Robbins #include <wchar.h>
69c5604d0aSTim J. Robbins #include <wctype.h>
70c5604d0aSTim J. Robbins #include "un-namespace.h"
71c5604d0aSTim J. Robbins 
72c5604d0aSTim J. Robbins #include "libc_private.h"
73c5604d0aSTim J. Robbins #include "local.h"
74c5604d0aSTim J. Robbins #include "fvwrite.h"
752591efccSDavid Schultz #include "printflocal.h"
763c87aa1dSDavid Chisnall #include "xlocale_private.h"
77e5abb5e6SDavid Schultz 
783c87aa1dSDavid Chisnall static int	__sprint(FILE *, struct __suio *, locale_t);
793c87aa1dSDavid Chisnall static int	__sbprintf(FILE *, locale_t, const wchar_t *, va_list) __noinline;
803c87aa1dSDavid Chisnall static wint_t	__xfputwc(wchar_t, FILE *, locale_t);
81c5604d0aSTim J. Robbins static wchar_t	*__mbsconv(char *, int);
82c5604d0aSTim J. Robbins 
83814d1bc9SDavid Schultz #define	CHAR	wchar_t
84814d1bc9SDavid Schultz #include "printfcommon.h"
85814d1bc9SDavid Schultz 
8621ca178eSDavid Schultz struct grouping_state {
8721ca178eSDavid Schultz 	wchar_t thousands_sep;	/* locale-specific thousands separator */
8821ca178eSDavid Schultz 	const char *grouping;	/* locale-specific numeric grouping rules */
8921ca178eSDavid Schultz 	int lead;		/* sig figs before decimal or group sep */
9021ca178eSDavid Schultz 	int nseps;		/* number of group separators with ' */
9121ca178eSDavid Schultz 	int nrepeats;		/* number of repeats of the last group */
9221ca178eSDavid Schultz };
9321ca178eSDavid Schultz 
945004a238SDavid Schultz static const mbstate_t initial_mbs;
955004a238SDavid Schultz 
965004a238SDavid Schultz static inline wchar_t
973c87aa1dSDavid Chisnall get_decpt(locale_t locale)
985004a238SDavid Schultz {
995004a238SDavid Schultz 	mbstate_t mbs;
1005004a238SDavid Schultz 	wchar_t decpt;
1015004a238SDavid Schultz 	int nconv;
1025004a238SDavid Schultz 
1035004a238SDavid Schultz 	mbs = initial_mbs;
1043c87aa1dSDavid Chisnall 	nconv = mbrtowc(&decpt, localeconv_l(locale)->decimal_point, MB_CUR_MAX, &mbs);
1055004a238SDavid Schultz 	if (nconv == (size_t)-1 || nconv == (size_t)-2)
1065004a238SDavid Schultz 		decpt = '.';    /* failsafe */
1075004a238SDavid Schultz 	return (decpt);
1085004a238SDavid Schultz }
1095004a238SDavid Schultz 
11021ca178eSDavid Schultz static inline wchar_t
1113c87aa1dSDavid Chisnall get_thousep(locale_t locale)
11221ca178eSDavid Schultz {
11321ca178eSDavid Schultz 	mbstate_t mbs;
11421ca178eSDavid Schultz 	wchar_t thousep;
11521ca178eSDavid Schultz 	int nconv;
11621ca178eSDavid Schultz 
11721ca178eSDavid Schultz 	mbs = initial_mbs;
1183c87aa1dSDavid Chisnall 	nconv = mbrtowc(&thousep, localeconv_l(locale)->thousands_sep,
11921ca178eSDavid Schultz 	    MB_CUR_MAX, &mbs);
12021ca178eSDavid Schultz 	if (nconv == (size_t)-1 || nconv == (size_t)-2)
12121ca178eSDavid Schultz 		thousep = '\0';    /* failsafe */
12221ca178eSDavid Schultz 	return (thousep);
12321ca178eSDavid Schultz }
12421ca178eSDavid Schultz 
12521ca178eSDavid Schultz /*
12621ca178eSDavid Schultz  * Initialize the thousands' grouping state in preparation to print a
12721ca178eSDavid Schultz  * number with ndigits digits. This routine returns the total number
12821ca178eSDavid Schultz  * of wide characters that will be printed.
12921ca178eSDavid Schultz  */
13021ca178eSDavid Schultz static int
1313c87aa1dSDavid Chisnall grouping_init(struct grouping_state *gs, int ndigits, locale_t locale)
13221ca178eSDavid Schultz {
13321ca178eSDavid Schultz 
1343c87aa1dSDavid Chisnall 	gs->grouping = localeconv_l(locale)->grouping;
1353c87aa1dSDavid Chisnall 	gs->thousands_sep = get_thousep(locale);
13621ca178eSDavid Schultz 
13721ca178eSDavid Schultz 	gs->nseps = gs->nrepeats = 0;
13821ca178eSDavid Schultz 	gs->lead = ndigits;
13921ca178eSDavid Schultz 	while (*gs->grouping != CHAR_MAX) {
14021ca178eSDavid Schultz 		if (gs->lead <= *gs->grouping)
14121ca178eSDavid Schultz 			break;
14221ca178eSDavid Schultz 		gs->lead -= *gs->grouping;
14321ca178eSDavid Schultz 		if (*(gs->grouping+1)) {
14421ca178eSDavid Schultz 			gs->nseps++;
14521ca178eSDavid Schultz 			gs->grouping++;
14621ca178eSDavid Schultz 		} else
14721ca178eSDavid Schultz 			gs->nrepeats++;
14821ca178eSDavid Schultz 	}
14921ca178eSDavid Schultz 	return (gs->nseps + gs->nrepeats);
15021ca178eSDavid Schultz }
15121ca178eSDavid Schultz 
15221ca178eSDavid Schultz /*
15321ca178eSDavid Schultz  * Print a number with thousands' separators.
15421ca178eSDavid Schultz  */
15521ca178eSDavid Schultz static int
15621ca178eSDavid Schultz grouping_print(struct grouping_state *gs, struct io_state *iop,
1573c87aa1dSDavid Chisnall 	       const CHAR *cp, const CHAR *ep, locale_t locale)
15821ca178eSDavid Schultz {
15921ca178eSDavid Schultz 	const CHAR *cp0 = cp;
16021ca178eSDavid Schultz 
1613c87aa1dSDavid Chisnall 	if (io_printandpad(iop, cp, ep, gs->lead, zeroes, locale))
16221ca178eSDavid Schultz 		return (-1);
16321ca178eSDavid Schultz 	cp += gs->lead;
16421ca178eSDavid Schultz 	while (gs->nseps > 0 || gs->nrepeats > 0) {
16521ca178eSDavid Schultz 		if (gs->nrepeats > 0)
16621ca178eSDavid Schultz 			gs->nrepeats--;
16721ca178eSDavid Schultz 		else {
16821ca178eSDavid Schultz 			gs->grouping--;
16921ca178eSDavid Schultz 			gs->nseps--;
17021ca178eSDavid Schultz 		}
1713c87aa1dSDavid Chisnall 		if (io_print(iop, &gs->thousands_sep, 1, locale))
17221ca178eSDavid Schultz 			return (-1);
1733c87aa1dSDavid Chisnall 		if (io_printandpad(iop, cp, ep, *gs->grouping, zeroes, locale))
17421ca178eSDavid Schultz 			return (-1);
17521ca178eSDavid Schultz 		cp += *gs->grouping;
17621ca178eSDavid Schultz 	}
17721ca178eSDavid Schultz 	if (cp > ep)
17821ca178eSDavid Schultz 		cp = ep;
17921ca178eSDavid Schultz 	return (cp - cp0);
18021ca178eSDavid Schultz }
18121ca178eSDavid Schultz 
18221ca178eSDavid Schultz 
183814d1bc9SDavid Schultz /*
184814d1bc9SDavid Schultz  * Flush out all the vectors defined by the given uio,
185814d1bc9SDavid Schultz  * then reset it so that it can be reused.
186814d1bc9SDavid Schultz  *
187814d1bc9SDavid Schultz  * XXX The fact that we do this a character at a time and convert to a
188814d1bc9SDavid Schultz  * multibyte character sequence even if the destination is a wide
189814d1bc9SDavid Schultz  * string eclipses the benefits of buffering.
190814d1bc9SDavid Schultz  */
191814d1bc9SDavid Schultz static int
1923c87aa1dSDavid Chisnall __sprint(FILE *fp, struct __suio *uio, locale_t locale)
193814d1bc9SDavid Schultz {
194814d1bc9SDavid Schultz 	struct __siov *iov;
195814d1bc9SDavid Schultz 	wchar_t *p;
196814d1bc9SDavid Schultz 	int i, len;
197814d1bc9SDavid Schultz 
198814d1bc9SDavid Schultz 	iov = uio->uio_iov;
199814d1bc9SDavid Schultz 	for (; uio->uio_resid != 0; uio->uio_resid -= len, iov++) {
200814d1bc9SDavid Schultz 		p = (wchar_t *)iov->iov_base;
201814d1bc9SDavid Schultz 		len = iov->iov_len;
202814d1bc9SDavid Schultz 		for (i = 0; i < len; i++) {
2033c87aa1dSDavid Chisnall 			if (__xfputwc(p[i], fp, locale) == WEOF)
204814d1bc9SDavid Schultz 				return (-1);
205814d1bc9SDavid Schultz 		}
206814d1bc9SDavid Schultz 	}
207814d1bc9SDavid Schultz 	uio->uio_iovcnt = 0;
208814d1bc9SDavid Schultz 	return (0);
209814d1bc9SDavid Schultz }
210814d1bc9SDavid Schultz 
211c5604d0aSTim J. Robbins /*
212c5604d0aSTim J. Robbins  * Helper function for `fprintf to unbuffered unix file': creates a
213c5604d0aSTim J. Robbins  * temporary buffer.  We only work on write-only files; this avoids
214c5604d0aSTim J. Robbins  * worries about ungetc buffers and so forth.
215c5604d0aSTim J. Robbins  */
216c5604d0aSTim J. Robbins static int
2173c87aa1dSDavid Chisnall __sbprintf(FILE *fp, locale_t locale, const wchar_t *fmt, va_list ap)
218c5604d0aSTim J. Robbins {
219c5604d0aSTim J. Robbins 	int ret;
220c5604d0aSTim J. Robbins 	FILE fake;
221c5604d0aSTim J. Robbins 	unsigned char buf[BUFSIZ];
222c5604d0aSTim J. Robbins 
223a1805f7bSDavid Schultz 	/* XXX This is probably not needed. */
224a1805f7bSDavid Schultz 	if (prepwrite(fp) != 0)
225a1805f7bSDavid Schultz 		return (EOF);
226a1805f7bSDavid Schultz 
227c5604d0aSTim J. Robbins 	/* copy the important variables */
228c5604d0aSTim J. Robbins 	fake._flags = fp->_flags & ~__SNBF;
229c5604d0aSTim J. Robbins 	fake._file = fp->_file;
230c5604d0aSTim J. Robbins 	fake._cookie = fp->_cookie;
231c5604d0aSTim J. Robbins 	fake._write = fp->_write;
2321e98f887SJohn Baldwin 	fake._orientation = fp->_orientation;
2331e98f887SJohn Baldwin 	fake._mbstate = fp->_mbstate;
234c5604d0aSTim J. Robbins 
235c5604d0aSTim J. Robbins 	/* set up the buffer */
236c5604d0aSTim J. Robbins 	fake._bf._base = fake._p = buf;
237c5604d0aSTim J. Robbins 	fake._bf._size = fake._w = sizeof(buf);
238c5604d0aSTim J. Robbins 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
239c5604d0aSTim J. Robbins 
240c5604d0aSTim J. Robbins 	/* do the work, then copy any error status */
2413c87aa1dSDavid Chisnall 	ret = __vfwprintf(&fake, locale, fmt, ap);
242c5604d0aSTim J. Robbins 	if (ret >= 0 && __fflush(&fake))
243c5604d0aSTim J. Robbins 		ret = WEOF;
244c5604d0aSTim J. Robbins 	if (fake._flags & __SERR)
245c5604d0aSTim J. Robbins 		fp->_flags |= __SERR;
246c5604d0aSTim J. Robbins 	return (ret);
247c5604d0aSTim J. Robbins }
248c5604d0aSTim J. Robbins 
249c5604d0aSTim J. Robbins /*
250909a17f4STim J. Robbins  * Like __fputwc, but handles fake string (__SSTR) files properly.
251909a17f4STim J. Robbins  * File must already be locked.
252909a17f4STim J. Robbins  */
253909a17f4STim J. Robbins static wint_t
2543c87aa1dSDavid Chisnall __xfputwc(wchar_t wc, FILE *fp, locale_t locale)
255909a17f4STim J. Robbins {
25693996f6dSTim J. Robbins 	mbstate_t mbs;
257909a17f4STim J. Robbins 	char buf[MB_LEN_MAX];
258909a17f4STim J. Robbins 	struct __suio uio;
259909a17f4STim J. Robbins 	struct __siov iov;
26084d9142fSJacques Vidrine 	size_t len;
261909a17f4STim J. Robbins 
262909a17f4STim J. Robbins 	if ((fp->_flags & __SSTR) == 0)
2633c87aa1dSDavid Chisnall 		return (__fputwc(wc, fp, locale));
264909a17f4STim J. Robbins 
2655004a238SDavid Schultz 	mbs = initial_mbs;
26693996f6dSTim J. Robbins 	if ((len = wcrtomb(buf, wc, &mbs)) == (size_t)-1) {
267909a17f4STim J. Robbins 		fp->_flags |= __SERR;
268909a17f4STim J. Robbins 		return (WEOF);
269909a17f4STim J. Robbins 	}
270909a17f4STim J. Robbins 	uio.uio_iov = &iov;
271909a17f4STim J. Robbins 	uio.uio_resid = len;
272909a17f4STim J. Robbins 	uio.uio_iovcnt = 1;
273909a17f4STim J. Robbins 	iov.iov_base = buf;
274909a17f4STim J. Robbins 	iov.iov_len = len;
275909a17f4STim J. Robbins 	return (__sfvwrite(fp, &uio) != EOF ? (wint_t)wc : WEOF);
276909a17f4STim J. Robbins }
277909a17f4STim J. Robbins 
278909a17f4STim J. Robbins /*
279c5604d0aSTim J. Robbins  * Convert a multibyte character string argument for the %s format to a wide
280c5604d0aSTim J. Robbins  * string representation. ``prec'' specifies the maximum number of bytes
281c5604d0aSTim J. Robbins  * to output. If ``prec'' is greater than or equal to zero, we can't assume
282c5604d0aSTim J. Robbins  * that the multibyte char. string ends in a null character.
283c5604d0aSTim J. Robbins  */
284c5604d0aSTim J. Robbins static wchar_t *
285c5604d0aSTim J. Robbins __mbsconv(char *mbsarg, int prec)
286c5604d0aSTim J. Robbins {
28793996f6dSTim J. Robbins 	mbstate_t mbs;
288c5604d0aSTim J. Robbins 	wchar_t *convbuf, *wcp;
289c5604d0aSTim J. Robbins 	const char *p;
290c5604d0aSTim J. Robbins 	size_t insize, nchars, nconv;
291c5604d0aSTim J. Robbins 
292adfd6b31STim J. Robbins 	if (mbsarg == NULL)
293adfd6b31STim J. Robbins 		return (NULL);
294adfd6b31STim J. Robbins 
295c5604d0aSTim J. Robbins 	/*
296c5604d0aSTim J. Robbins 	 * Supplied argument is a multibyte string; convert it to wide
297c5604d0aSTim J. Robbins 	 * characters first.
298c5604d0aSTim J. Robbins 	 */
299c5604d0aSTim J. Robbins 	if (prec >= 0) {
300c5604d0aSTim J. Robbins 		/*
301c5604d0aSTim J. Robbins 		 * String is not guaranteed to be NUL-terminated. Find the
302c5604d0aSTim J. Robbins 		 * number of characters to print.
303c5604d0aSTim J. Robbins 		 */
304c5604d0aSTim J. Robbins 		p = mbsarg;
305ab5b2fafSGarrett Wollman 		insize = nchars = nconv = 0;
3065004a238SDavid Schultz 		mbs = initial_mbs;
307c5604d0aSTim J. Robbins 		while (nchars != (size_t)prec) {
30893996f6dSTim J. Robbins 			nconv = mbrlen(p, MB_CUR_MAX, &mbs);
309c5604d0aSTim J. Robbins 			if (nconv == 0 || nconv == (size_t)-1 ||
310c5604d0aSTim J. Robbins 			    nconv == (size_t)-2)
311c5604d0aSTim J. Robbins 				break;
312c5604d0aSTim J. Robbins 			p += nconv;
313c5604d0aSTim J. Robbins 			nchars++;
314c5604d0aSTim J. Robbins 			insize += nconv;
315c5604d0aSTim J. Robbins 		}
316c5604d0aSTim J. Robbins 		if (nconv == (size_t)-1 || nconv == (size_t)-2)
317c5604d0aSTim J. Robbins 			return (NULL);
31822d725b5SColin Percival 	} else {
319c5604d0aSTim J. Robbins 		insize = strlen(mbsarg);
32022d725b5SColin Percival 		nconv = 0;
32122d725b5SColin Percival 	}
322c5604d0aSTim J. Robbins 
323c5604d0aSTim J. Robbins 	/*
324c5604d0aSTim J. Robbins 	 * Allocate buffer for the result and perform the conversion,
325c5604d0aSTim J. Robbins 	 * converting at most `size' bytes of the input multibyte string to
326c5604d0aSTim J. Robbins 	 * wide characters for printing.
327c5604d0aSTim J. Robbins 	 */
328c5604d0aSTim J. Robbins 	convbuf = malloc((insize + 1) * sizeof(*convbuf));
329c5604d0aSTim J. Robbins 	if (convbuf == NULL)
330c5604d0aSTim J. Robbins 		return (NULL);
331c5604d0aSTim J. Robbins 	wcp = convbuf;
332c5604d0aSTim J. Robbins 	p = mbsarg;
3335004a238SDavid Schultz 	mbs = initial_mbs;
334c5604d0aSTim J. Robbins 	while (insize != 0) {
33593996f6dSTim J. Robbins 		nconv = mbrtowc(wcp, p, insize, &mbs);
336c5604d0aSTim J. Robbins 		if (nconv == 0 || nconv == (size_t)-1 || nconv == (size_t)-2)
337c5604d0aSTim J. Robbins 			break;
338c5604d0aSTim J. Robbins 		wcp++;
339c5604d0aSTim J. Robbins 		p += nconv;
340c5604d0aSTim J. Robbins 		insize -= nconv;
341c5604d0aSTim J. Robbins 	}
342c5604d0aSTim J. Robbins 	if (nconv == (size_t)-1 || nconv == (size_t)-2) {
343c5604d0aSTim J. Robbins 		free(convbuf);
344c5604d0aSTim J. Robbins 		return (NULL);
345c5604d0aSTim J. Robbins 	}
346c5604d0aSTim J. Robbins 	*wcp = L'\0';
347c5604d0aSTim J. Robbins 
348c5604d0aSTim J. Robbins 	return (convbuf);
349c5604d0aSTim J. Robbins }
350c5604d0aSTim J. Robbins 
351c5604d0aSTim J. Robbins /*
352c5604d0aSTim J. Robbins  * MT-safe version
353c5604d0aSTim J. Robbins  */
354c5604d0aSTim J. Robbins int
3553c87aa1dSDavid Chisnall vfwprintf_l(FILE * __restrict fp, locale_t locale,
3563c87aa1dSDavid Chisnall 		const wchar_t * __restrict fmt0, va_list ap)
357c5604d0aSTim J. Robbins 
358c5604d0aSTim J. Robbins {
359c5604d0aSTim J. Robbins 	int ret;
3603c87aa1dSDavid Chisnall 	FIX_LOCALE(locale);
361fda0a14fSKonstantin Belousov 	FLOCKFILE_CANCELSAFE(fp);
362a1805f7bSDavid Schultz 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
363a1805f7bSDavid Schultz 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
364a1805f7bSDavid Schultz 	    fp->_file >= 0)
3653c87aa1dSDavid Chisnall 		ret = __sbprintf(fp, locale, fmt0, ap);
366a1805f7bSDavid Schultz 	else
3673c87aa1dSDavid Chisnall 		ret = __vfwprintf(fp, locale, fmt0, ap);
368fda0a14fSKonstantin Belousov 	FUNLOCKFILE_CANCELSAFE();
369c5604d0aSTim J. Robbins 	return (ret);
370c5604d0aSTim J. Robbins }
3713c87aa1dSDavid Chisnall int
3723c87aa1dSDavid Chisnall vfwprintf(FILE * __restrict fp, const wchar_t * __restrict fmt0, va_list ap)
3733c87aa1dSDavid Chisnall {
3743c87aa1dSDavid Chisnall 	return vfwprintf_l(fp, __get_locale(), fmt0, ap);
3753c87aa1dSDavid Chisnall }
376c5604d0aSTim J. Robbins 
37738cac8f8SDavid Schultz /*
37838cac8f8SDavid Schultz  * The size of the buffer we use as scratch space for integer
37921ca178eSDavid Schultz  * conversions, among other things.  We need enough space to
38021ca178eSDavid Schultz  * write a uintmax_t in octal (plus one byte).
38138cac8f8SDavid Schultz  */
38221ca178eSDavid Schultz #if UINTMAX_MAX <= UINT64_MAX
38321ca178eSDavid Schultz #define	BUF	32
38421ca178eSDavid Schultz #else
38521ca178eSDavid Schultz #error "BUF must be large enough to format a uintmax_t"
38621ca178eSDavid Schultz #endif
38738cac8f8SDavid Schultz 
388c5604d0aSTim J. Robbins /*
389c5604d0aSTim J. Robbins  * Non-MT-safe version
390c5604d0aSTim J. Robbins  */
391c5604d0aSTim J. Robbins int
3923c87aa1dSDavid Chisnall __vfwprintf(FILE *fp, locale_t locale, const wchar_t *fmt0, va_list ap)
393c5604d0aSTim J. Robbins {
394c5604d0aSTim J. Robbins 	wchar_t *fmt;		/* format string */
395c5604d0aSTim J. Robbins 	wchar_t ch;		/* character from fmt */
396814d1bc9SDavid Schultz 	int n, n2;		/* handy integer (short term usage) */
397c5604d0aSTim J. Robbins 	wchar_t *cp;		/* handy char pointer (short term usage) */
398c5604d0aSTim J. Robbins 	int flags;		/* flags as above */
399c5604d0aSTim J. Robbins 	int ret;		/* return value accumulator */
400c5604d0aSTim J. Robbins 	int width;		/* width from format (%8d), or 0 */
401adfd6b31STim J. Robbins 	int prec;		/* precision from format; <0 for N/A */
402c5604d0aSTim J. Robbins 	wchar_t sign;		/* sign prefix (' ', '+', '-', or \0) */
40321ca178eSDavid Schultz 	struct grouping_state gs; /* thousands' grouping info */
404ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT
405adfd6b31STim J. Robbins 	/*
406adfd6b31STim J. Robbins 	 * We can decompose the printed representation of floating
407adfd6b31STim J. Robbins 	 * point numbers into several parts, some of which may be empty:
408adfd6b31STim J. Robbins 	 *
409adfd6b31STim J. Robbins 	 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
410adfd6b31STim J. Robbins 	 *    A       B     ---C---      D       E   F
411adfd6b31STim J. Robbins 	 *
412adfd6b31STim J. Robbins 	 * A:	'sign' holds this value if present; '\0' otherwise
413adfd6b31STim J. Robbins 	 * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
414adfd6b31STim J. Robbins 	 * C:	cp points to the string MMMNNN.  Leading and trailing
415adfd6b31STim J. Robbins 	 *	zeros are not in the string and must be added.
416adfd6b31STim J. Robbins 	 * D:	expchar holds this character; '\0' if no exponent, e.g. %f
417adfd6b31STim J. Robbins 	 * F:	at least two digits for decimal, at least one digit for hex
418adfd6b31STim J. Robbins 	 */
4195004a238SDavid Schultz 	wchar_t decimal_point;	/* locale specific decimal point */
420adfd6b31STim J. Robbins 	int signflag;		/* true if float is negative */
421adfd6b31STim J. Robbins 	union {			/* floating point arguments %[aAeEfFgG] */
422adfd6b31STim J. Robbins 		double dbl;
423adfd6b31STim J. Robbins 		long double ldbl;
424adfd6b31STim J. Robbins 	} fparg;
425c5604d0aSTim J. Robbins 	int expt;		/* integer value of exponent */
426adfd6b31STim J. Robbins 	char expchar;		/* exponent character: [eEpP\0] */
427adfd6b31STim J. Robbins 	char *dtoaend;		/* pointer to end of converted digits */
428c5604d0aSTim J. Robbins 	int expsize;		/* character count for expstr */
429adfd6b31STim J. Robbins 	int ndig;		/* actual number of digits returned by dtoa */
430adfd6b31STim J. Robbins 	wchar_t expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */
431adfd6b31STim J. Robbins 	char *dtoaresult;	/* buffer allocated by dtoa */
432c5604d0aSTim J. Robbins #endif
433c5604d0aSTim J. Robbins 	u_long	ulval;		/* integer arguments %[diouxX] */
434c5604d0aSTim J. Robbins 	uintmax_t ujval;	/* %j, %ll, %q, %t, %z integers */
435c5604d0aSTim J. Robbins 	int base;		/* base for [diouxX] conversion */
436c5604d0aSTim J. Robbins 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
437c5604d0aSTim J. Robbins 	int realsz;		/* field size expanded by dprec, sign, etc */
438c5604d0aSTim J. Robbins 	int size;		/* size of converted field or string */
439c5604d0aSTim J. Robbins 	int prsize;             /* max size of printed field */
4407b7e3509SDavid Schultz 	const char *xdigs;	/* digits for [xX] conversion */
441814d1bc9SDavid Schultz 	struct io_state io;	/* I/O buffering state */
44238cac8f8SDavid Schultz 	wchar_t buf[BUF];	/* buffer with space for digits of uintmax_t */
443c5604d0aSTim J. Robbins 	wchar_t ox[2];		/* space for 0x hex-prefix */
444c5604d0aSTim J. Robbins 	union arg *argtable;	/* args, built due to positional arg */
445c5604d0aSTim J. Robbins 	union arg statargtable [STATIC_ARG_TBL_SIZE];
446c5604d0aSTim J. Robbins 	int nextarg;		/* 1-based argument index */
447c5604d0aSTim J. Robbins 	va_list orgap;		/* original argument pointer */
448c5604d0aSTim J. Robbins 	wchar_t *convbuf;	/* multibyte to wide conversion result */
4491bf6c5f1SAndrey A. Chernov 	int savserr;
450c5604d0aSTim J. Robbins 
4517b7e3509SDavid Schultz 	static const char xdigs_lower[16] = "0123456789abcdef";
4527b7e3509SDavid Schultz 	static const char xdigs_upper[16] = "0123456789ABCDEF";
453adfd6b31STim J. Robbins 
454814d1bc9SDavid Schultz 	/* BEWARE, these `goto error' on error. */
455c5604d0aSTim J. Robbins #define	PRINT(ptr, len)	do {			\
4563c87aa1dSDavid Chisnall 	if (io_print(&io, (ptr), (len), locale))	\
457814d1bc9SDavid Schultz 		goto error; \
458c5604d0aSTim J. Robbins } while (0)
459814d1bc9SDavid Schultz #define	PAD(howmany, with) { \
4603c87aa1dSDavid Chisnall 	if (io_pad(&io, (howmany), (with), locale)) \
461814d1bc9SDavid Schultz 		goto error; \
462814d1bc9SDavid Schultz }
463814d1bc9SDavid Schultz #define	PRINTANDPAD(p, ep, len, with) {	\
4643c87aa1dSDavid Chisnall 	if (io_printandpad(&io, (p), (ep), (len), (with), locale)) \
465814d1bc9SDavid Schultz 		goto error; \
466814d1bc9SDavid Schultz }
467814d1bc9SDavid Schultz #define	FLUSH() { \
4683c87aa1dSDavid Chisnall 	if (io_flush(&io, locale)) \
469814d1bc9SDavid Schultz 		goto error; \
470814d1bc9SDavid Schultz }
471c5604d0aSTim J. Robbins 
472c5604d0aSTim J. Robbins 	/*
473c5604d0aSTim J. Robbins 	 * Get the argument indexed by nextarg.   If the argument table is
474c5604d0aSTim J. Robbins 	 * built, use it to get the argument.  If its not, get the next
475c5604d0aSTim J. Robbins 	 * argument (and arguments must be gotten sequentially).
476c5604d0aSTim J. Robbins 	 */
477c5604d0aSTim J. Robbins #define GETARG(type) \
478c5604d0aSTim J. Robbins 	((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
479c5604d0aSTim J. Robbins 	    (nextarg++, va_arg(ap, type)))
480c5604d0aSTim J. Robbins 
481c5604d0aSTim J. Robbins 	/*
482c5604d0aSTim J. Robbins 	 * To extend shorts properly, we need both signed and unsigned
483c5604d0aSTim J. Robbins 	 * argument extraction methods.
484c5604d0aSTim J. Robbins 	 */
485c5604d0aSTim J. Robbins #define	SARG() \
486c5604d0aSTim J. Robbins 	(flags&LONGINT ? GETARG(long) : \
487c5604d0aSTim J. Robbins 	    flags&SHORTINT ? (long)(short)GETARG(int) : \
488c5604d0aSTim J. Robbins 	    flags&CHARINT ? (long)(signed char)GETARG(int) : \
489c5604d0aSTim J. Robbins 	    (long)GETARG(int))
490c5604d0aSTim J. Robbins #define	UARG() \
491c5604d0aSTim J. Robbins 	(flags&LONGINT ? GETARG(u_long) : \
492c5604d0aSTim J. Robbins 	    flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
493c5604d0aSTim J. Robbins 	    flags&CHARINT ? (u_long)(u_char)GETARG(int) : \
494c5604d0aSTim J. Robbins 	    (u_long)GETARG(u_int))
495c5604d0aSTim J. Robbins #define	INTMAX_SIZE	(INTMAXT|SIZET|PTRDIFFT|LLONGINT)
496c5604d0aSTim J. Robbins #define SJARG() \
497c5604d0aSTim J. Robbins 	(flags&INTMAXT ? GETARG(intmax_t) : \
4980881683bSDavid Schultz 	    flags&SIZET ? (intmax_t)GETARG(ssize_t) : \
499c5604d0aSTim J. Robbins 	    flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \
500c5604d0aSTim J. Robbins 	    (intmax_t)GETARG(long long))
501c5604d0aSTim J. Robbins #define	UJARG() \
502c5604d0aSTim J. Robbins 	(flags&INTMAXT ? GETARG(uintmax_t) : \
503c5604d0aSTim J. Robbins 	    flags&SIZET ? (uintmax_t)GETARG(size_t) : \
504c5604d0aSTim J. Robbins 	    flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \
505c5604d0aSTim J. Robbins 	    (uintmax_t)GETARG(unsigned long long))
506c5604d0aSTim J. Robbins 
507c5604d0aSTim J. Robbins 	/*
508c5604d0aSTim J. Robbins 	 * Get * arguments, including the form *nn$.  Preserve the nextarg
509c5604d0aSTim J. Robbins 	 * that the argument can be gotten once the type is determined.
510c5604d0aSTim J. Robbins 	 */
511c5604d0aSTim J. Robbins #define GETASTER(val) \
512c5604d0aSTim J. Robbins 	n2 = 0; \
513c5604d0aSTim J. Robbins 	cp = fmt; \
514c5604d0aSTim J. Robbins 	while (is_digit(*cp)) { \
515c5604d0aSTim J. Robbins 		n2 = 10 * n2 + to_digit(*cp); \
516c5604d0aSTim J. Robbins 		cp++; \
517c5604d0aSTim J. Robbins 	} \
518c5604d0aSTim J. Robbins 	if (*cp == '$') { \
519c5604d0aSTim J. Robbins 		int hold = nextarg; \
520c5604d0aSTim J. Robbins 		if (argtable == NULL) { \
521c5604d0aSTim J. Robbins 			argtable = statargtable; \
522e62e5ff9SDavid Schultz 			if (__find_warguments (fmt0, orgap, &argtable)) { \
523e62e5ff9SDavid Schultz 				ret = EOF; \
524e62e5ff9SDavid Schultz 				goto error; \
525e62e5ff9SDavid Schultz 			} \
526c5604d0aSTim J. Robbins 		} \
527c5604d0aSTim J. Robbins 		nextarg = n2; \
528c5604d0aSTim J. Robbins 		val = GETARG (int); \
529c5604d0aSTim J. Robbins 		nextarg = hold; \
530c5604d0aSTim J. Robbins 		fmt = ++cp; \
531c5604d0aSTim J. Robbins 	} else { \
532c5604d0aSTim J. Robbins 		val = GETARG (int); \
533c5604d0aSTim J. Robbins 	}
534c5604d0aSTim J. Robbins 
535c5604d0aSTim J. Robbins 
536c5604d0aSTim J. Robbins 	/* sorry, fwprintf(read_only_file, L"") returns WEOF, not 0 */
537450ead86SPedro F. Giffuni 	if (prepwrite(fp) != 0) {
538450ead86SPedro F. Giffuni 		errno = EBADF;
539c5604d0aSTim J. Robbins 		return (EOF);
540450ead86SPedro F. Giffuni 	}
541c5604d0aSTim J. Robbins 
5421bf6c5f1SAndrey A. Chernov 	savserr = fp->_flags & __SERR;
5431bf6c5f1SAndrey A. Chernov 	fp->_flags &= ~__SERR;
5441bf6c5f1SAndrey A. Chernov 
545e18701f4SDavid Schultz 	convbuf = NULL;
546c5604d0aSTim J. Robbins 	fmt = (wchar_t *)fmt0;
547c5604d0aSTim J. Robbins 	argtable = NULL;
548c5604d0aSTim J. Robbins 	nextarg = 1;
549d07090a8STim J. Robbins 	va_copy(orgap, ap);
550814d1bc9SDavid Schultz 	io_init(&io, fp);
551c5604d0aSTim J. Robbins 	ret = 0;
552e18701f4SDavid Schultz #ifndef NO_FLOATING_POINT
5533c87aa1dSDavid Chisnall 	decimal_point = get_decpt(locale);
554e18701f4SDavid Schultz #endif
555c5604d0aSTim J. Robbins 
556c5604d0aSTim J. Robbins 	/*
557c5604d0aSTim J. Robbins 	 * Scan the format for conversions (`%' character).
558c5604d0aSTim J. Robbins 	 */
559c5604d0aSTim J. Robbins 	for (;;) {
560c5604d0aSTim J. Robbins 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
561c5604d0aSTim J. Robbins 			/* void */;
562c5604d0aSTim J. Robbins 		if ((n = fmt - cp) != 0) {
563c5604d0aSTim J. Robbins 			if ((unsigned)ret + n > INT_MAX) {
564c5604d0aSTim J. Robbins 				ret = EOF;
565666d00d3SDavid Schultz 				errno = EOVERFLOW;
566c5604d0aSTim J. Robbins 				goto error;
567c5604d0aSTim J. Robbins 			}
568c5604d0aSTim J. Robbins 			PRINT(cp, n);
569c5604d0aSTim J. Robbins 			ret += n;
570c5604d0aSTim J. Robbins 		}
571c5604d0aSTim J. Robbins 		if (ch == '\0')
572c5604d0aSTim J. Robbins 			goto done;
573c5604d0aSTim J. Robbins 		fmt++;		/* skip over '%' */
574c5604d0aSTim J. Robbins 
575c5604d0aSTim J. Robbins 		flags = 0;
576c5604d0aSTim J. Robbins 		dprec = 0;
577c5604d0aSTim J. Robbins 		width = 0;
578c5604d0aSTim J. Robbins 		prec = -1;
57921ca178eSDavid Schultz 		gs.grouping = NULL;
580c5604d0aSTim J. Robbins 		sign = '\0';
581adfd6b31STim J. Robbins 		ox[1] = '\0';
582c5604d0aSTim J. Robbins 
583c5604d0aSTim J. Robbins rflag:		ch = *fmt++;
584c5604d0aSTim J. Robbins reswitch:	switch (ch) {
585c5604d0aSTim J. Robbins 		case ' ':
586c5604d0aSTim J. Robbins 			/*-
587c5604d0aSTim J. Robbins 			 * ``If the space and + flags both appear, the space
588c5604d0aSTim J. Robbins 			 * flag will be ignored.''
589c5604d0aSTim J. Robbins 			 *	-- ANSI X3J11
590c5604d0aSTim J. Robbins 			 */
591c5604d0aSTim J. Robbins 			if (!sign)
592c5604d0aSTim J. Robbins 				sign = ' ';
593c5604d0aSTim J. Robbins 			goto rflag;
594c5604d0aSTim J. Robbins 		case '#':
595c5604d0aSTim J. Robbins 			flags |= ALT;
596c5604d0aSTim J. Robbins 			goto rflag;
597c5604d0aSTim J. Robbins 		case '*':
598c5604d0aSTim J. Robbins 			/*-
599c5604d0aSTim J. Robbins 			 * ``A negative field width argument is taken as a
600c5604d0aSTim J. Robbins 			 * - flag followed by a positive field width.''
601c5604d0aSTim J. Robbins 			 *	-- ANSI X3J11
602c5604d0aSTim J. Robbins 			 * They don't exclude field widths read from args.
603c5604d0aSTim J. Robbins 			 */
604c5604d0aSTim J. Robbins 			GETASTER (width);
605c5604d0aSTim J. Robbins 			if (width >= 0)
606c5604d0aSTim J. Robbins 				goto rflag;
607c5604d0aSTim J. Robbins 			width = -width;
608c5604d0aSTim J. Robbins 			/* FALLTHROUGH */
609c5604d0aSTim J. Robbins 		case '-':
610c5604d0aSTim J. Robbins 			flags |= LADJUST;
611c5604d0aSTim J. Robbins 			goto rflag;
612c5604d0aSTim J. Robbins 		case '+':
613c5604d0aSTim J. Robbins 			sign = '+';
614c5604d0aSTim J. Robbins 			goto rflag;
615c5604d0aSTim J. Robbins 		case '\'':
616c5604d0aSTim J. Robbins 			flags |= GROUPING;
617c5604d0aSTim J. Robbins 			goto rflag;
618c5604d0aSTim J. Robbins 		case '.':
619c5604d0aSTim J. Robbins 			if ((ch = *fmt++) == '*') {
620adfd6b31STim J. Robbins 				GETASTER (prec);
621c5604d0aSTim J. Robbins 				goto rflag;
622c5604d0aSTim J. Robbins 			}
623adfd6b31STim J. Robbins 			prec = 0;
624c5604d0aSTim J. Robbins 			while (is_digit(ch)) {
625adfd6b31STim J. Robbins 				prec = 10 * prec + to_digit(ch);
626c5604d0aSTim J. Robbins 				ch = *fmt++;
627c5604d0aSTim J. Robbins 			}
628c5604d0aSTim J. Robbins 			goto reswitch;
629c5604d0aSTim J. Robbins 		case '0':
630c5604d0aSTim J. Robbins 			/*-
631c5604d0aSTim J. Robbins 			 * ``Note that 0 is taken as a flag, not as the
632c5604d0aSTim J. Robbins 			 * beginning of a field width.''
633c5604d0aSTim J. Robbins 			 *	-- ANSI X3J11
634c5604d0aSTim J. Robbins 			 */
635c5604d0aSTim J. Robbins 			flags |= ZEROPAD;
636c5604d0aSTim J. Robbins 			goto rflag;
637c5604d0aSTim J. Robbins 		case '1': case '2': case '3': case '4':
638c5604d0aSTim J. Robbins 		case '5': case '6': case '7': case '8': case '9':
639c5604d0aSTim J. Robbins 			n = 0;
640c5604d0aSTim J. Robbins 			do {
641c5604d0aSTim J. Robbins 				n = 10 * n + to_digit(ch);
642c5604d0aSTim J. Robbins 				ch = *fmt++;
643c5604d0aSTim J. Robbins 			} while (is_digit(ch));
644c5604d0aSTim J. Robbins 			if (ch == '$') {
645c5604d0aSTim J. Robbins 				nextarg = n;
646c5604d0aSTim J. Robbins 				if (argtable == NULL) {
647c5604d0aSTim J. Robbins 					argtable = statargtable;
648e62e5ff9SDavid Schultz 					if (__find_warguments (fmt0, orgap,
649e62e5ff9SDavid Schultz 							       &argtable)) {
650e62e5ff9SDavid Schultz 						ret = EOF;
651e62e5ff9SDavid Schultz 						goto error;
652e62e5ff9SDavid Schultz 					}
653c5604d0aSTim J. Robbins 				}
654c5604d0aSTim J. Robbins 				goto rflag;
655c5604d0aSTim J. Robbins 			}
656c5604d0aSTim J. Robbins 			width = n;
657c5604d0aSTim J. Robbins 			goto reswitch;
658ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT
659c5604d0aSTim J. Robbins 		case 'L':
660c5604d0aSTim J. Robbins 			flags |= LONGDBL;
661c5604d0aSTim J. Robbins 			goto rflag;
662c5604d0aSTim J. Robbins #endif
663c5604d0aSTim J. Robbins 		case 'h':
664c5604d0aSTim J. Robbins 			if (flags & SHORTINT) {
665c5604d0aSTim J. Robbins 				flags &= ~SHORTINT;
666c5604d0aSTim J. Robbins 				flags |= CHARINT;
667c5604d0aSTim J. Robbins 			} else
668c5604d0aSTim J. Robbins 				flags |= SHORTINT;
669c5604d0aSTim J. Robbins 			goto rflag;
670c5604d0aSTim J. Robbins 		case 'j':
671c5604d0aSTim J. Robbins 			flags |= INTMAXT;
672c5604d0aSTim J. Robbins 			goto rflag;
673c5604d0aSTim J. Robbins 		case 'l':
674c5604d0aSTim J. Robbins 			if (flags & LONGINT) {
675c5604d0aSTim J. Robbins 				flags &= ~LONGINT;
676c5604d0aSTim J. Robbins 				flags |= LLONGINT;
677c5604d0aSTim J. Robbins 			} else
678c5604d0aSTim J. Robbins 				flags |= LONGINT;
679c5604d0aSTim J. Robbins 			goto rflag;
680c5604d0aSTim J. Robbins 		case 'q':
681c5604d0aSTim J. Robbins 			flags |= LLONGINT;	/* not necessarily */
682c5604d0aSTim J. Robbins 			goto rflag;
683c5604d0aSTim J. Robbins 		case 't':
684c5604d0aSTim J. Robbins 			flags |= PTRDIFFT;
685c5604d0aSTim J. Robbins 			goto rflag;
686c5604d0aSTim J. Robbins 		case 'z':
687c5604d0aSTim J. Robbins 			flags |= SIZET;
688c5604d0aSTim J. Robbins 			goto rflag;
689927ecbf3STim J. Robbins 		case 'C':
690927ecbf3STim J. Robbins 			flags |= LONGINT;
691927ecbf3STim J. Robbins 			/*FALLTHROUGH*/
692c5604d0aSTim J. Robbins 		case 'c':
693c5604d0aSTim J. Robbins 			if (flags & LONGINT)
694c5604d0aSTim J. Robbins 				*(cp = buf) = (wchar_t)GETARG(wint_t);
695c5604d0aSTim J. Robbins 			else
696c5604d0aSTim J. Robbins 				*(cp = buf) = (wchar_t)btowc(GETARG(int));
697c5604d0aSTim J. Robbins 			size = 1;
698c5604d0aSTim J. Robbins 			sign = '\0';
699c5604d0aSTim J. Robbins 			break;
700c5604d0aSTim J. Robbins 		case 'D':
701c5604d0aSTim J. Robbins 			flags |= LONGINT;
702c5604d0aSTim J. Robbins 			/*FALLTHROUGH*/
703c5604d0aSTim J. Robbins 		case 'd':
704c5604d0aSTim J. Robbins 		case 'i':
705c5604d0aSTim J. Robbins 			if (flags & INTMAX_SIZE) {
706c5604d0aSTim J. Robbins 				ujval = SJARG();
707c5604d0aSTim J. Robbins 				if ((intmax_t)ujval < 0) {
708c5604d0aSTim J. Robbins 					ujval = -ujval;
709c5604d0aSTim J. Robbins 					sign = '-';
710c5604d0aSTim J. Robbins 				}
711c5604d0aSTim J. Robbins 			} else {
712c5604d0aSTim J. Robbins 				ulval = SARG();
713c5604d0aSTim J. Robbins 				if ((long)ulval < 0) {
714c5604d0aSTim J. Robbins 					ulval = -ulval;
715c5604d0aSTim J. Robbins 					sign = '-';
716c5604d0aSTim J. Robbins 				}
717c5604d0aSTim J. Robbins 			}
718c5604d0aSTim J. Robbins 			base = 10;
719c5604d0aSTim J. Robbins 			goto number;
720ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT
721c5604d0aSTim J. Robbins 		case 'a':
722c5604d0aSTim J. Robbins 		case 'A':
723adfd6b31STim J. Robbins 			if (ch == 'a') {
724adfd6b31STim J. Robbins 				ox[1] = 'x';
725adfd6b31STim J. Robbins 				xdigs = xdigs_lower;
726adfd6b31STim J. Robbins 				expchar = 'p';
727adfd6b31STim J. Robbins 			} else {
728adfd6b31STim J. Robbins 				ox[1] = 'X';
729adfd6b31STim J. Robbins 				xdigs = xdigs_upper;
730adfd6b31STim J. Robbins 				expchar = 'P';
731adfd6b31STim J. Robbins 			}
7327b7e3509SDavid Schultz 			if (prec >= 0)
7337b7e3509SDavid Schultz 				prec++;
734adfd6b31STim J. Robbins 			if (flags & LONGDBL) {
7357b7e3509SDavid Schultz 				fparg.ldbl = GETARG(long double);
736adfd6b31STim J. Robbins 				dtoaresult =
737adfd6b31STim J. Robbins 				    __hldtoa(fparg.ldbl, xdigs, prec,
738adfd6b31STim J. Robbins 				        &expt, &signflag, &dtoaend);
739adfd6b31STim J. Robbins 			} else {
740adfd6b31STim J. Robbins 				fparg.dbl = GETARG(double);
741adfd6b31STim J. Robbins 				dtoaresult =
742adfd6b31STim J. Robbins 				    __hdtoa(fparg.dbl, xdigs, prec,
743adfd6b31STim J. Robbins 				        &expt, &signflag, &dtoaend);
744adfd6b31STim J. Robbins 			}
7457b7e3509SDavid Schultz 			if (prec < 0)
7467b7e3509SDavid Schultz 				prec = dtoaend - dtoaresult;
7477b7e3509SDavid Schultz 			if (expt == INT_MAX)
7487b7e3509SDavid Schultz 				ox[1] = '\0';
749adfd6b31STim J. Robbins 			if (convbuf != NULL)
750adfd6b31STim J. Robbins 				free(convbuf);
7517b7e3509SDavid Schultz 			ndig = dtoaend - dtoaresult;
752adfd6b31STim J. Robbins 			cp = convbuf = __mbsconv(dtoaresult, -1);
753adfd6b31STim J. Robbins 			freedtoa(dtoaresult);
7547b7e3509SDavid Schultz 			goto fp_common;
755c5604d0aSTim J. Robbins 		case 'e':
756c5604d0aSTim J. Robbins 		case 'E':
757adfd6b31STim J. Robbins 			expchar = ch;
758adfd6b31STim J. Robbins 			if (prec < 0)	/* account for digit before decpt */
759adfd6b31STim J. Robbins 				prec = DEFPREC + 1;
760adfd6b31STim J. Robbins 			else
761adfd6b31STim J. Robbins 				prec++;
762adfd6b31STim J. Robbins 			goto fp_begin;
763c5604d0aSTim J. Robbins 		case 'f':
764c5604d0aSTim J. Robbins 		case 'F':
765adfd6b31STim J. Robbins 			expchar = '\0';
766c5604d0aSTim J. Robbins 			goto fp_begin;
767c5604d0aSTim J. Robbins 		case 'g':
768c5604d0aSTim J. Robbins 		case 'G':
769adfd6b31STim J. Robbins 			expchar = ch - ('g' - 'e');
770c5604d0aSTim J. Robbins 			if (prec == 0)
771c5604d0aSTim J. Robbins 				prec = 1;
772adfd6b31STim J. Robbins fp_begin:
773adfd6b31STim J. Robbins 			if (prec < 0)
774c5604d0aSTim J. Robbins 				prec = DEFPREC;
775adfd6b31STim J. Robbins 			if (convbuf != NULL)
776adfd6b31STim J. Robbins 				free(convbuf);
777adfd6b31STim J. Robbins 			if (flags & LONGDBL) {
778adfd6b31STim J. Robbins 				fparg.ldbl = GETARG(long double);
779adfd6b31STim J. Robbins 				dtoaresult =
780adfd6b31STim J. Robbins 				    __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
781adfd6b31STim J. Robbins 				    &expt, &signflag, &dtoaend);
782adfd6b31STim J. Robbins 			} else {
783adfd6b31STim J. Robbins 				fparg.dbl = GETARG(double);
784adfd6b31STim J. Robbins 				dtoaresult =
785adfd6b31STim J. Robbins 				    dtoa(fparg.dbl, expchar ? 2 : 3, prec,
786adfd6b31STim J. Robbins 				    &expt, &signflag, &dtoaend);
787adfd6b31STim J. Robbins 				if (expt == 9999)
788adfd6b31STim J. Robbins 					expt = INT_MAX;
789c5604d0aSTim J. Robbins 			}
790adfd6b31STim J. Robbins 			ndig = dtoaend - dtoaresult;
791adfd6b31STim J. Robbins 			cp = convbuf = __mbsconv(dtoaresult, -1);
792adfd6b31STim J. Robbins 			freedtoa(dtoaresult);
7937b7e3509SDavid Schultz fp_common:
794adfd6b31STim J. Robbins 			if (signflag)
795adfd6b31STim J. Robbins 				sign = '-';
796adfd6b31STim J. Robbins 			if (expt == INT_MAX) {	/* inf or nan */
797adfd6b31STim J. Robbins 				if (*cp == 'N') {
798adfd6b31STim J. Robbins 					cp = (ch >= 'a') ? L"nan" : L"NAN";
799adfd6b31STim J. Robbins 					sign = '\0';
800adfd6b31STim J. Robbins 				} else
801adfd6b31STim J. Robbins 					cp = (ch >= 'a') ? L"inf" : L"INF";
802c5604d0aSTim J. Robbins 				size = 3;
8038da510f8SDavid Schultz 				flags &= ~ZEROPAD;
804c5604d0aSTim J. Robbins 				break;
805c5604d0aSTim J. Robbins 			}
806c5604d0aSTim J. Robbins 			flags |= FPT;
807c5604d0aSTim J. Robbins 			if (ch == 'g' || ch == 'G') {
808adfd6b31STim J. Robbins 				if (expt > -4 && expt <= prec) {
809adfd6b31STim J. Robbins 					/* Make %[gG] smell like %[fF] */
810adfd6b31STim J. Robbins 					expchar = '\0';
811adfd6b31STim J. Robbins 					if (flags & ALT)
812adfd6b31STim J. Robbins 						prec -= expt;
813c5604d0aSTim J. Robbins 					else
814adfd6b31STim J. Robbins 						prec = ndig - expt;
815adfd6b31STim J. Robbins 					if (prec < 0)
816adfd6b31STim J. Robbins 						prec = 0;
8171f2a0cdfSDavid Schultz 				} else {
8181f2a0cdfSDavid Schultz 					/*
8191f2a0cdfSDavid Schultz 					 * Make %[gG] smell like %[eE], but
8201f2a0cdfSDavid Schultz 					 * trim trailing zeroes if no # flag.
8211f2a0cdfSDavid Schultz 					 */
8221f2a0cdfSDavid Schultz 					if (!(flags & ALT))
8231f2a0cdfSDavid Schultz 						prec = ndig;
824c5604d0aSTim J. Robbins 				}
825adfd6b31STim J. Robbins 			}
826adfd6b31STim J. Robbins 			if (expchar) {
827adfd6b31STim J. Robbins 				expsize = exponent(expstr, expt - 1, expchar);
828adfd6b31STim J. Robbins 				size = expsize + prec;
829adfd6b31STim J. Robbins 				if (prec > 1 || flags & ALT)
830c5604d0aSTim J. Robbins 					++size;
831adfd6b31STim J. Robbins 			} else {
832d73c448bSTim J. Robbins 				/* space for digits before decimal point */
833d73c448bSTim J. Robbins 				if (expt > 0)
834c5604d0aSTim J. Robbins 					size = expt;
835d73c448bSTim J. Robbins 				else	/* "0" */
836d73c448bSTim J. Robbins 					size = 1;
837d73c448bSTim J. Robbins 				/* space for decimal pt and following digits */
838c5604d0aSTim J. Robbins 				if (prec || flags & ALT)
839c5604d0aSTim J. Robbins 					size += prec + 1;
84021ca178eSDavid Schultz 				if ((flags & GROUPING) && expt > 0)
8413c87aa1dSDavid Chisnall 					size += grouping_init(&gs, expt, locale);
842adfd6b31STim J. Robbins 			}
843c5604d0aSTim J. Robbins 			break;
844ce2551adSDavid Schultz #endif /* !NO_FLOATING_POINT */
845c5604d0aSTim J. Robbins 		case 'n':
846c5604d0aSTim J. Robbins 			/*
847c5604d0aSTim J. Robbins 			 * Assignment-like behavior is specified if the
848c5604d0aSTim J. Robbins 			 * value overflows or is otherwise unrepresentable.
849c5604d0aSTim J. Robbins 			 * C99 says to use `signed char' for %hhn conversions.
850c5604d0aSTim J. Robbins 			 */
851c5604d0aSTim J. Robbins 			if (flags & LLONGINT)
852c5604d0aSTim J. Robbins 				*GETARG(long long *) = ret;
853c5604d0aSTim J. Robbins 			else if (flags & SIZET)
854c5604d0aSTim J. Robbins 				*GETARG(ssize_t *) = (ssize_t)ret;
855c5604d0aSTim J. Robbins 			else if (flags & PTRDIFFT)
856c5604d0aSTim J. Robbins 				*GETARG(ptrdiff_t *) = ret;
857c5604d0aSTim J. Robbins 			else if (flags & INTMAXT)
858c5604d0aSTim J. Robbins 				*GETARG(intmax_t *) = ret;
859c5604d0aSTim J. Robbins 			else if (flags & LONGINT)
860c5604d0aSTim J. Robbins 				*GETARG(long *) = ret;
861c5604d0aSTim J. Robbins 			else if (flags & SHORTINT)
862c5604d0aSTim J. Robbins 				*GETARG(short *) = ret;
863c5604d0aSTim J. Robbins 			else if (flags & CHARINT)
864c5604d0aSTim J. Robbins 				*GETARG(signed char *) = ret;
865c5604d0aSTim J. Robbins 			else
866c5604d0aSTim J. Robbins 				*GETARG(int *) = ret;
867c5604d0aSTim J. Robbins 			continue;	/* no output */
868c5604d0aSTim J. Robbins 		case 'O':
869c5604d0aSTim J. Robbins 			flags |= LONGINT;
870c5604d0aSTim J. Robbins 			/*FALLTHROUGH*/
871c5604d0aSTim J. Robbins 		case 'o':
872c5604d0aSTim J. Robbins 			if (flags & INTMAX_SIZE)
873c5604d0aSTim J. Robbins 				ujval = UJARG();
874c5604d0aSTim J. Robbins 			else
875c5604d0aSTim J. Robbins 				ulval = UARG();
876c5604d0aSTim J. Robbins 			base = 8;
877c5604d0aSTim J. Robbins 			goto nosign;
878c5604d0aSTim J. Robbins 		case 'p':
879c5604d0aSTim J. Robbins 			/*-
880c5604d0aSTim J. Robbins 			 * ``The argument shall be a pointer to void.  The
881c5604d0aSTim J. Robbins 			 * value of the pointer is converted to a sequence
882c5604d0aSTim J. Robbins 			 * of printable characters, in an implementation-
883c5604d0aSTim J. Robbins 			 * defined manner.''
884c5604d0aSTim J. Robbins 			 *	-- ANSI X3J11
885c5604d0aSTim J. Robbins 			 */
886c5604d0aSTim J. Robbins 			ujval = (uintmax_t)(uintptr_t)GETARG(void *);
887c5604d0aSTim J. Robbins 			base = 16;
888adfd6b31STim J. Robbins 			xdigs = xdigs_lower;
889adfd6b31STim J. Robbins 			flags = flags | INTMAXT;
890adfd6b31STim J. Robbins 			ox[1] = 'x';
891c5604d0aSTim J. Robbins 			goto nosign;
892927ecbf3STim J. Robbins 		case 'S':
893927ecbf3STim J. Robbins 			flags |= LONGINT;
894927ecbf3STim J. Robbins 			/*FALLTHROUGH*/
895c5604d0aSTim J. Robbins 		case 's':
896c5604d0aSTim J. Robbins 			if (flags & LONGINT) {
897c5604d0aSTim J. Robbins 				if ((cp = GETARG(wchar_t *)) == NULL)
898c5604d0aSTim J. Robbins 					cp = L"(null)";
899c5604d0aSTim J. Robbins 			} else {
900c5604d0aSTim J. Robbins 				char *mbp;
901c5604d0aSTim J. Robbins 
902c5604d0aSTim J. Robbins 				if (convbuf != NULL)
903c5604d0aSTim J. Robbins 					free(convbuf);
904c5604d0aSTim J. Robbins 				if ((mbp = GETARG(char *)) == NULL)
905c5604d0aSTim J. Robbins 					cp = L"(null)";
906c5604d0aSTim J. Robbins 				else {
907c5604d0aSTim J. Robbins 					convbuf = __mbsconv(mbp, prec);
9086180233fSTim J. Robbins 					if (convbuf == NULL) {
9096180233fSTim J. Robbins 						fp->_flags |= __SERR;
910c5604d0aSTim J. Robbins 						goto error;
9116180233fSTim J. Robbins 					}
912c5604d0aSTim J. Robbins 					cp = convbuf;
913c5604d0aSTim J. Robbins 				}
914c5604d0aSTim J. Robbins 			}
915353ce11cSDavid Schultz 			size = (prec >= 0) ? wcsnlen(cp, prec) : wcslen(cp);
916c5604d0aSTim J. Robbins 			sign = '\0';
917c5604d0aSTim J. Robbins 			break;
918c5604d0aSTim J. Robbins 		case 'U':
919c5604d0aSTim J. Robbins 			flags |= LONGINT;
920c5604d0aSTim J. Robbins 			/*FALLTHROUGH*/
921c5604d0aSTim J. Robbins 		case 'u':
922c5604d0aSTim J. Robbins 			if (flags & INTMAX_SIZE)
923c5604d0aSTim J. Robbins 				ujval = UJARG();
924c5604d0aSTim J. Robbins 			else
925c5604d0aSTim J. Robbins 				ulval = UARG();
926c5604d0aSTim J. Robbins 			base = 10;
927c5604d0aSTim J. Robbins 			goto nosign;
928c5604d0aSTim J. Robbins 		case 'X':
929adfd6b31STim J. Robbins 			xdigs = xdigs_upper;
930c5604d0aSTim J. Robbins 			goto hex;
931c5604d0aSTim J. Robbins 		case 'x':
932adfd6b31STim J. Robbins 			xdigs = xdigs_lower;
933c5604d0aSTim J. Robbins hex:
934c5604d0aSTim J. Robbins 			if (flags & INTMAX_SIZE)
935c5604d0aSTim J. Robbins 				ujval = UJARG();
936c5604d0aSTim J. Robbins 			else
937c5604d0aSTim J. Robbins 				ulval = UARG();
938c5604d0aSTim J. Robbins 			base = 16;
939c5604d0aSTim J. Robbins 			/* leading 0x/X only if non-zero */
940c5604d0aSTim J. Robbins 			if (flags & ALT &&
941c5604d0aSTim J. Robbins 			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
942adfd6b31STim J. Robbins 				ox[1] = ch;
943c5604d0aSTim J. Robbins 
944c5604d0aSTim J. Robbins 			flags &= ~GROUPING;
945c5604d0aSTim J. Robbins 			/* unsigned conversions */
946c5604d0aSTim J. Robbins nosign:			sign = '\0';
947c5604d0aSTim J. Robbins 			/*-
948c5604d0aSTim J. Robbins 			 * ``... diouXx conversions ... if a precision is
949c5604d0aSTim J. Robbins 			 * specified, the 0 flag will be ignored.''
950c5604d0aSTim J. Robbins 			 *	-- ANSI X3J11
951c5604d0aSTim J. Robbins 			 */
952c5604d0aSTim J. Robbins number:			if ((dprec = prec) >= 0)
953c5604d0aSTim J. Robbins 				flags &= ~ZEROPAD;
954c5604d0aSTim J. Robbins 
955c5604d0aSTim J. Robbins 			/*-
956c5604d0aSTim J. Robbins 			 * ``The result of converting a zero value with an
957c5604d0aSTim J. Robbins 			 * explicit precision of zero is no characters.''
958c5604d0aSTim J. Robbins 			 *	-- ANSI X3J11
9591be5319aSDavid Schultz 			 *
9601be5319aSDavid Schultz 			 * ``The C Standard is clear enough as is.  The call
9611be5319aSDavid Schultz 			 * printf("%#.0o", 0) should print 0.''
9621be5319aSDavid Schultz 			 *	-- Defect Report #151
963c5604d0aSTim J. Robbins 			 */
964c5604d0aSTim J. Robbins 			cp = buf + BUF;
965c5604d0aSTim J. Robbins 			if (flags & INTMAX_SIZE) {
9661be5319aSDavid Schultz 				if (ujval != 0 || prec != 0 ||
9671be5319aSDavid Schultz 				    (flags & ALT && base == 8))
968c5604d0aSTim J. Robbins 					cp = __ujtoa(ujval, cp, base,
96921ca178eSDavid Schultz 					    flags & ALT, xdigs);
970c5604d0aSTim J. Robbins 			} else {
9711be5319aSDavid Schultz 				if (ulval != 0 || prec != 0 ||
9721be5319aSDavid Schultz 				    (flags & ALT && base == 8))
973c5604d0aSTim J. Robbins 					cp = __ultoa(ulval, cp, base,
97421ca178eSDavid Schultz 					    flags & ALT, xdigs);
975c5604d0aSTim J. Robbins 			}
976c5604d0aSTim J. Robbins 			size = buf + BUF - cp;
97738cac8f8SDavid Schultz 			if (size > BUF)	/* should never happen */
97838cac8f8SDavid Schultz 				abort();
97921ca178eSDavid Schultz 			if ((flags & GROUPING) && size != 0)
9803c87aa1dSDavid Chisnall 				size += grouping_init(&gs, size, locale);
981c5604d0aSTim J. Robbins 			break;
982c5604d0aSTim J. Robbins 		default:	/* "%?" prints ?, unless ? is NUL */
983c5604d0aSTim J. Robbins 			if (ch == '\0')
984c5604d0aSTim J. Robbins 				goto done;
985c5604d0aSTim J. Robbins 			/* pretend it was %c with argument ch */
986c5604d0aSTim J. Robbins 			cp = buf;
987c5604d0aSTim J. Robbins 			*cp = ch;
988c5604d0aSTim J. Robbins 			size = 1;
989c5604d0aSTim J. Robbins 			sign = '\0';
990c5604d0aSTim J. Robbins 			break;
991c5604d0aSTim J. Robbins 		}
992c5604d0aSTim J. Robbins 
993c5604d0aSTim J. Robbins 		/*
994c5604d0aSTim J. Robbins 		 * All reasonable formats wind up here.  At this point, `cp'
995c5604d0aSTim J. Robbins 		 * points to a string which (if not flags&LADJUST) should be
996c5604d0aSTim J. Robbins 		 * padded out to `width' places.  If flags&ZEROPAD, it should
997c5604d0aSTim J. Robbins 		 * first be prefixed by any sign or other prefix; otherwise,
998c5604d0aSTim J. Robbins 		 * it should be blank padded before the prefix is emitted.
999c5604d0aSTim J. Robbins 		 * After any left-hand padding and prefixing, emit zeroes
1000c5604d0aSTim J. Robbins 		 * required by a decimal [diouxX] precision, then print the
1001c5604d0aSTim J. Robbins 		 * string proper, then emit zeroes required by any leftover
1002c5604d0aSTim J. Robbins 		 * floating precision; finally, if LADJUST, pad with blanks.
1003c5604d0aSTim J. Robbins 		 *
1004c5604d0aSTim J. Robbins 		 * Compute actual size, so we know how much to pad.
1005c5604d0aSTim J. Robbins 		 * size excludes decimal prec; realsz includes it.
1006c5604d0aSTim J. Robbins 		 */
1007c5604d0aSTim J. Robbins 		realsz = dprec > size ? dprec : size;
1008c5604d0aSTim J. Robbins 		if (sign)
1009c5604d0aSTim J. Robbins 			realsz++;
10107b7e3509SDavid Schultz 		if (ox[1])
1011c5604d0aSTim J. Robbins 			realsz += 2;
1012c5604d0aSTim J. Robbins 
1013c5604d0aSTim J. Robbins 		prsize = width > realsz ? width : realsz;
1014c5604d0aSTim J. Robbins 		if ((unsigned)ret + prsize > INT_MAX) {
1015c5604d0aSTim J. Robbins 			ret = EOF;
1016666d00d3SDavid Schultz 			errno = EOVERFLOW;
1017c5604d0aSTim J. Robbins 			goto error;
1018c5604d0aSTim J. Robbins 		}
1019c5604d0aSTim J. Robbins 
1020c5604d0aSTim J. Robbins 		/* right-adjusting blank padding */
1021c5604d0aSTim J. Robbins 		if ((flags & (LADJUST|ZEROPAD)) == 0)
1022c5604d0aSTim J. Robbins 			PAD(width - realsz, blanks);
1023c5604d0aSTim J. Robbins 
1024c5604d0aSTim J. Robbins 		/* prefix */
10257b7e3509SDavid Schultz 		if (sign)
1026c5604d0aSTim J. Robbins 			PRINT(&sign, 1);
10277b7e3509SDavid Schultz 
10287b7e3509SDavid Schultz 		if (ox[1]) {	/* ox[1] is either x, X, or \0 */
1029c5604d0aSTim J. Robbins 			ox[0] = '0';
1030c5604d0aSTim J. Robbins 			PRINT(ox, 2);
1031c5604d0aSTim J. Robbins 		}
1032c5604d0aSTim J. Robbins 
1033c5604d0aSTim J. Robbins 		/* right-adjusting zero padding */
1034c5604d0aSTim J. Robbins 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
1035c5604d0aSTim J. Robbins 			PAD(width - realsz, zeroes);
1036c5604d0aSTim J. Robbins 
1037c5604d0aSTim J. Robbins 		/* the string or number proper */
1038ce2551adSDavid Schultz #ifndef NO_FLOATING_POINT
1039c5604d0aSTim J. Robbins 		if ((flags & FPT) == 0) {
104021ca178eSDavid Schultz #endif
104121ca178eSDavid Schultz 			/* leading zeroes from decimal precision */
104221ca178eSDavid Schultz 			PAD(dprec - size, zeroes);
104321ca178eSDavid Schultz 			if (gs.grouping) {
10443c87aa1dSDavid Chisnall 				if (grouping_print(&gs, &io, cp, buf+BUF, locale) < 0)
104521ca178eSDavid Schultz 					goto error;
104621ca178eSDavid Schultz 			} else {
1047c5604d0aSTim J. Robbins 				PRINT(cp, size);
104821ca178eSDavid Schultz 			}
104921ca178eSDavid Schultz #ifndef NO_FLOATING_POINT
1050c5604d0aSTim J. Robbins 		} else {	/* glue together f_p fragments */
1051adfd6b31STim J. Robbins 			if (!expchar) {	/* %[fF] or sufficiently short %[gG] */
1052adfd6b31STim J. Robbins 				if (expt <= 0) {
1053d73c448bSTim J. Robbins 					PRINT(zeroes, 1);
10545004a238SDavid Schultz 					if (prec || flags & ALT)
10555004a238SDavid Schultz 						PRINT(&decimal_point, 1);
1056c5604d0aSTim J. Robbins 					PAD(-expt, zeroes);
1057adfd6b31STim J. Robbins 					/* already handled initial 0's */
1058adfd6b31STim J. Robbins 					prec += expt;
1059c5604d0aSTim J. Robbins 				} else {
106021ca178eSDavid Schultz 					if (gs.grouping) {
106121ca178eSDavid Schultz 						n = grouping_print(&gs, &io,
10623c87aa1dSDavid Chisnall 						    cp, convbuf + ndig, locale);
106321ca178eSDavid Schultz 						if (n < 0)
106421ca178eSDavid Schultz 							goto error;
106521ca178eSDavid Schultz 						cp += n;
106621ca178eSDavid Schultz 					} else {
106721ca178eSDavid Schultz 						PRINTANDPAD(cp, convbuf + ndig,
106821ca178eSDavid Schultz 						    expt, zeroes);
106921ca178eSDavid Schultz 						cp += expt;
1070adfd6b31STim J. Robbins 					}
10715004a238SDavid Schultz 					if (prec || flags & ALT)
10725004a238SDavid Schultz 						PRINT(&decimal_point, 1);
1073adfd6b31STim J. Robbins 				}
1074adfd6b31STim J. Robbins 				PRINTANDPAD(cp, convbuf + ndig, prec, zeroes);
1075adfd6b31STim J. Robbins 			} else {	/* %[eE] or sufficiently long %[gG] */
1076adfd6b31STim J. Robbins 				if (prec > 1 || flags & ALT) {
1077adfd6b31STim J. Robbins 					buf[0] = *cp++;
10785004a238SDavid Schultz 					buf[1] = decimal_point;
1079adfd6b31STim J. Robbins 					PRINT(buf, 2);
1080c5604d0aSTim J. Robbins 					PRINT(cp, ndig-1);
1081adfd6b31STim J. Robbins 					PAD(prec - ndig, zeroes);
1082c5604d0aSTim J. Robbins 				} else	/* XeYYY */
1083c5604d0aSTim J. Robbins 					PRINT(cp, 1);
1084c5604d0aSTim J. Robbins 				PRINT(expstr, expsize);
1085c5604d0aSTim J. Robbins 			}
1086c5604d0aSTim J. Robbins 		}
1087c5604d0aSTim J. Robbins #endif
1088c5604d0aSTim J. Robbins 		/* left-adjusting padding (always blank) */
1089c5604d0aSTim J. Robbins 		if (flags & LADJUST)
1090c5604d0aSTim J. Robbins 			PAD(width - realsz, blanks);
1091c5604d0aSTim J. Robbins 
1092c5604d0aSTim J. Robbins 		/* finally, adjust ret */
1093c5604d0aSTim J. Robbins 		ret += prsize;
1094814d1bc9SDavid Schultz 
1095814d1bc9SDavid Schultz 		FLUSH();	/* copy out the I/O vectors */
1096c5604d0aSTim J. Robbins 	}
1097c5604d0aSTim J. Robbins done:
1098814d1bc9SDavid Schultz 	FLUSH();
1099c5604d0aSTim J. Robbins error:
1100096ad104SDag-Erling Smørgrav 	va_end(orgap);
1101c5604d0aSTim J. Robbins 	if (convbuf != NULL)
1102c5604d0aSTim J. Robbins 		free(convbuf);
1103c5604d0aSTim J. Robbins 	if (__sferror(fp))
1104c5604d0aSTim J. Robbins 		ret = EOF;
11051bf6c5f1SAndrey A. Chernov 	else
11061bf6c5f1SAndrey A. Chernov 		fp->_flags |= savserr;
1107c5604d0aSTim J. Robbins 	if ((argtable != NULL) && (argtable != statargtable))
1108c5604d0aSTim J. Robbins 		free (argtable);
1109c5604d0aSTim J. Robbins 	return (ret);
1110c5604d0aSTim J. Robbins 	/* NOTREACHED */
1111c5604d0aSTim J. Robbins }
1112