xref: /freebsd/lib/libc/stdio/vfprintf.c (revision 33bff5d3e4de99a8be7d1b61bc1ee7d021146bab)
158f0484fSRodney W. Grimes /*-
258f0484fSRodney W. Grimes  * Copyright (c) 1990, 1993
358f0484fSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
458f0484fSRodney W. Grimes  *
558f0484fSRodney W. Grimes  * This code is derived from software contributed to Berkeley by
658f0484fSRodney W. Grimes  * Chris Torek.
758f0484fSRodney W. Grimes  *
858f0484fSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
958f0484fSRodney W. Grimes  * modification, are permitted provided that the following conditions
1058f0484fSRodney W. Grimes  * are met:
1158f0484fSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
1258f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
1358f0484fSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
1458f0484fSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
1558f0484fSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
1658f0484fSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
1758f0484fSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
1858f0484fSRodney W. Grimes  *    without specific prior written permission.
1958f0484fSRodney W. Grimes  *
2058f0484fSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2158f0484fSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2258f0484fSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2358f0484fSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2458f0484fSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2558f0484fSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2658f0484fSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2758f0484fSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2858f0484fSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2958f0484fSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3058f0484fSRodney W. Grimes  * SUCH DAMAGE.
3158f0484fSRodney W. Grimes  */
3258f0484fSRodney W. Grimes 
3358f0484fSRodney W. Grimes #if defined(LIBC_SCCS) && !defined(lint)
3458f0484fSRodney W. Grimes static char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 6/4/93";
3558f0484fSRodney W. Grimes #endif /* LIBC_SCCS and not lint */
36333fc21eSDavid E. O'Brien #include <sys/cdefs.h>
37333fc21eSDavid E. O'Brien __FBSDID("$FreeBSD$");
3858f0484fSRodney W. Grimes 
3958f0484fSRodney W. Grimes /*
4058f0484fSRodney W. Grimes  * Actual printf innards.
4158f0484fSRodney W. Grimes  *
4258f0484fSRodney W. Grimes  * This code is large and complicated...
4358f0484fSRodney W. Grimes  */
4458f0484fSRodney W. Grimes 
45d201fe46SDaniel Eischen #include "namespace.h"
4658f0484fSRodney W. Grimes #include <sys/types.h>
4758f0484fSRodney W. Grimes 
487735bb0fSBill Fenner #include <ctype.h>
4958f0484fSRodney W. Grimes #include <limits.h>
507ae5c679SAlexey Zelkin #include <locale.h>
517735bb0fSBill Fenner #include <stddef.h>
527735bb0fSBill Fenner #include <stdint.h>
5358f0484fSRodney W. Grimes #include <stdio.h>
5458f0484fSRodney W. Grimes #include <stdlib.h>
5558f0484fSRodney W. Grimes #include <string.h>
56b9aac308STim J. Robbins #include <wchar.h>
5775067f4fSPoul-Henning Kamp #include <printf.h>
5858f0484fSRodney W. Grimes 
5958f0484fSRodney W. Grimes #include <stdarg.h>
60d201fe46SDaniel Eischen #include "un-namespace.h"
6158f0484fSRodney W. Grimes 
62d201fe46SDaniel Eischen #include "libc_private.h"
6358f0484fSRodney W. Grimes #include "local.h"
6458f0484fSRodney W. Grimes #include "fvwrite.h"
652591efccSDavid Schultz #include "printflocal.h"
66e5abb5e6SDavid Schultz 
67c05ac53bSDavid E. O'Brien static int	__sprint(FILE *, struct __suio *);
681372519bSDavid E. O'Brien static int	__sbprintf(FILE *, const char *, va_list) __printflike(2, 0);
69ebbad5ecSDavid Schultz static char	*__ujtoa(uintmax_t, char *, int, int, const char *, int, char,
701372519bSDavid E. O'Brien 		    const char *);
71ebbad5ecSDavid Schultz static char	*__ultoa(u_long, char *, int, int, const char *, int, char,
721372519bSDavid E. O'Brien 		    const char *);
73b9aac308STim J. Robbins static char	*__wcsconv(wchar_t *, int);
74ce51cf03SJames Raynard 
7558f0484fSRodney W. Grimes /*
7658f0484fSRodney W. Grimes  * Flush out all the vectors defined by the given uio,
7758f0484fSRodney W. Grimes  * then reset it so that it can be reused.
7858f0484fSRodney W. Grimes  */
7958f0484fSRodney W. Grimes static int
80d201fe46SDaniel Eischen __sprint(FILE *fp, struct __suio *uio)
8158f0484fSRodney W. Grimes {
82d201fe46SDaniel Eischen 	int err;
8358f0484fSRodney W. Grimes 
8458f0484fSRodney W. Grimes 	if (uio->uio_resid == 0) {
8558f0484fSRodney W. Grimes 		uio->uio_iovcnt = 0;
8658f0484fSRodney W. Grimes 		return (0);
8758f0484fSRodney W. Grimes 	}
8858f0484fSRodney W. Grimes 	err = __sfvwrite(fp, uio);
8958f0484fSRodney W. Grimes 	uio->uio_resid = 0;
9058f0484fSRodney W. Grimes 	uio->uio_iovcnt = 0;
9158f0484fSRodney W. Grimes 	return (err);
9258f0484fSRodney W. Grimes }
9358f0484fSRodney W. Grimes 
9458f0484fSRodney W. Grimes /*
9558f0484fSRodney W. Grimes  * Helper function for `fprintf to unbuffered unix file': creates a
9658f0484fSRodney W. Grimes  * temporary buffer.  We only work on write-only files; this avoids
9758f0484fSRodney W. Grimes  * worries about ungetc buffers and so forth.
9858f0484fSRodney W. Grimes  */
9958f0484fSRodney W. Grimes static int
100d201fe46SDaniel Eischen __sbprintf(FILE *fp, const char *fmt, va_list ap)
10158f0484fSRodney W. Grimes {
10258f0484fSRodney W. Grimes 	int ret;
10358f0484fSRodney W. Grimes 	FILE fake;
10458f0484fSRodney W. Grimes 	unsigned char buf[BUFSIZ];
10558f0484fSRodney W. Grimes 
10658f0484fSRodney W. Grimes 	/* copy the important variables */
10758f0484fSRodney W. Grimes 	fake._flags = fp->_flags & ~__SNBF;
10858f0484fSRodney W. Grimes 	fake._file = fp->_file;
10958f0484fSRodney W. Grimes 	fake._cookie = fp->_cookie;
11058f0484fSRodney W. Grimes 	fake._write = fp->_write;
1111e98f887SJohn Baldwin 	fake._orientation = fp->_orientation;
1121e98f887SJohn Baldwin 	fake._mbstate = fp->_mbstate;
11358f0484fSRodney W. Grimes 
11458f0484fSRodney W. Grimes 	/* set up the buffer */
11558f0484fSRodney W. Grimes 	fake._bf._base = fake._p = buf;
11658f0484fSRodney W. Grimes 	fake._bf._size = fake._w = sizeof(buf);
11758f0484fSRodney W. Grimes 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
11858f0484fSRodney W. Grimes 
11958f0484fSRodney W. Grimes 	/* do the work, then copy any error status */
120d201fe46SDaniel Eischen 	ret = __vfprintf(&fake, fmt, ap);
121d201fe46SDaniel Eischen 	if (ret >= 0 && __fflush(&fake))
12258f0484fSRodney W. Grimes 		ret = EOF;
12358f0484fSRodney W. Grimes 	if (fake._flags & __SERR)
12458f0484fSRodney W. Grimes 		fp->_flags |= __SERR;
12558f0484fSRodney W. Grimes 	return (ret);
12658f0484fSRodney W. Grimes }
12758f0484fSRodney W. Grimes 
12858f0484fSRodney W. Grimes /*
12958f0484fSRodney W. Grimes  * Convert an unsigned long to ASCII for printf purposes, returning
13058f0484fSRodney W. Grimes  * a pointer to the first character of the string representation.
13158f0484fSRodney W. Grimes  * Octal numbers can be forced to have a leading zero; hex numbers
13258f0484fSRodney W. Grimes  * use the given digits.
13358f0484fSRodney W. Grimes  */
13458f0484fSRodney W. Grimes static char *
135ebbad5ecSDavid Schultz __ultoa(u_long val, char *endp, int base, int octzero, const char *xdigs,
13698ee7635SAlexey Zelkin 	int needgrp, char thousep, const char *grp)
13758f0484fSRodney W. Grimes {
1388fb3f3f6SDavid E. O'Brien 	char *cp = endp;
1398fb3f3f6SDavid E. O'Brien 	long sval;
1407735bb0fSBill Fenner 	int ndig;
14158f0484fSRodney W. Grimes 
14258f0484fSRodney W. Grimes 	/*
14358f0484fSRodney W. Grimes 	 * Handle the three cases separately, in the hope of getting
14458f0484fSRodney W. Grimes 	 * better/faster code.
14558f0484fSRodney W. Grimes 	 */
14658f0484fSRodney W. Grimes 	switch (base) {
14758f0484fSRodney W. Grimes 	case 10:
14858f0484fSRodney W. Grimes 		if (val < 10) {	/* many numbers are 1 digit */
14958f0484fSRodney W. Grimes 			*--cp = to_char(val);
15058f0484fSRodney W. Grimes 			return (cp);
15158f0484fSRodney W. Grimes 		}
1527735bb0fSBill Fenner 		ndig = 0;
15358f0484fSRodney W. Grimes 		/*
15458f0484fSRodney W. Grimes 		 * On many machines, unsigned arithmetic is harder than
15558f0484fSRodney W. Grimes 		 * signed arithmetic, so we do at most one unsigned mod and
15658f0484fSRodney W. Grimes 		 * divide; this is sufficient to reduce the range of
15758f0484fSRodney W. Grimes 		 * the incoming value to where signed arithmetic works.
15858f0484fSRodney W. Grimes 		 */
15958f0484fSRodney W. Grimes 		if (val > LONG_MAX) {
16058f0484fSRodney W. Grimes 			*--cp = to_char(val % 10);
1617735bb0fSBill Fenner 			ndig++;
16258f0484fSRodney W. Grimes 			sval = val / 10;
16358f0484fSRodney W. Grimes 		} else
16458f0484fSRodney W. Grimes 			sval = val;
16558f0484fSRodney W. Grimes 		do {
16658f0484fSRodney W. Grimes 			*--cp = to_char(sval % 10);
16798ee7635SAlexey Zelkin 			ndig++;
16898ee7635SAlexey Zelkin 			/*
16998ee7635SAlexey Zelkin 			 * If (*grp == CHAR_MAX) then no more grouping
17098ee7635SAlexey Zelkin 			 * should be performed.
17198ee7635SAlexey Zelkin 			 */
172243e90d6SAlexey Zelkin 			if (needgrp && ndig == *grp && *grp != CHAR_MAX
173243e90d6SAlexey Zelkin 					&& sval > 9) {
17498ee7635SAlexey Zelkin 				*--cp = thousep;
1757735bb0fSBill Fenner 				ndig = 0;
17698ee7635SAlexey Zelkin 				/*
17798ee7635SAlexey Zelkin 				 * If (*(grp+1) == '\0') then we have to
17898ee7635SAlexey Zelkin 				 * use *grp character (last grouping rule)
17998ee7635SAlexey Zelkin 				 * for all next cases
18098ee7635SAlexey Zelkin 				 */
1812e394b2fSAlexey Zelkin 				if (*(grp+1) != '\0')
1822e394b2fSAlexey Zelkin 					grp++;
1837735bb0fSBill Fenner 			}
18458f0484fSRodney W. Grimes 			sval /= 10;
18558f0484fSRodney W. Grimes 		} while (sval != 0);
18658f0484fSRodney W. Grimes 		break;
18758f0484fSRodney W. Grimes 
18858f0484fSRodney W. Grimes 	case 8:
18958f0484fSRodney W. Grimes 		do {
19058f0484fSRodney W. Grimes 			*--cp = to_char(val & 7);
19158f0484fSRodney W. Grimes 			val >>= 3;
19258f0484fSRodney W. Grimes 		} while (val);
19358f0484fSRodney W. Grimes 		if (octzero && *cp != '0')
19458f0484fSRodney W. Grimes 			*--cp = '0';
19558f0484fSRodney W. Grimes 		break;
19658f0484fSRodney W. Grimes 
19758f0484fSRodney W. Grimes 	case 16:
19858f0484fSRodney W. Grimes 		do {
19958f0484fSRodney W. Grimes 			*--cp = xdigs[val & 15];
20058f0484fSRodney W. Grimes 			val >>= 4;
20158f0484fSRodney W. Grimes 		} while (val);
20258f0484fSRodney W. Grimes 		break;
20358f0484fSRodney W. Grimes 
20458f0484fSRodney W. Grimes 	default:			/* oops */
20558f0484fSRodney W. Grimes 		abort();
20658f0484fSRodney W. Grimes 	}
20758f0484fSRodney W. Grimes 	return (cp);
20858f0484fSRodney W. Grimes }
20958f0484fSRodney W. Grimes 
2107735bb0fSBill Fenner /* Identical to __ultoa, but for intmax_t. */
21158f0484fSRodney W. Grimes static char *
212ebbad5ecSDavid Schultz __ujtoa(uintmax_t val, char *endp, int base, int octzero, const char *xdigs,
21398ee7635SAlexey Zelkin 	int needgrp, char thousep, const char *grp)
21458f0484fSRodney W. Grimes {
215d201fe46SDaniel Eischen 	char *cp = endp;
2167735bb0fSBill Fenner 	intmax_t sval;
2177735bb0fSBill Fenner 	int ndig;
21858f0484fSRodney W. Grimes 
21958f0484fSRodney W. Grimes 	/* quick test for small values; __ultoa is typically much faster */
22058f0484fSRodney W. Grimes 	/* (perhaps instead we should run until small, then call __ultoa?) */
22158f0484fSRodney W. Grimes 	if (val <= ULONG_MAX)
2227735bb0fSBill Fenner 		return (__ultoa((u_long)val, endp, base, octzero, xdigs,
22398ee7635SAlexey Zelkin 		    needgrp, thousep, grp));
22458f0484fSRodney W. Grimes 	switch (base) {
22558f0484fSRodney W. Grimes 	case 10:
22658f0484fSRodney W. Grimes 		if (val < 10) {
22758f0484fSRodney W. Grimes 			*--cp = to_char(val % 10);
22858f0484fSRodney W. Grimes 			return (cp);
22958f0484fSRodney W. Grimes 		}
2307735bb0fSBill Fenner 		ndig = 0;
2317735bb0fSBill Fenner 		if (val > INTMAX_MAX) {
23258f0484fSRodney W. Grimes 			*--cp = to_char(val % 10);
2337735bb0fSBill Fenner 			ndig++;
23458f0484fSRodney W. Grimes 			sval = val / 10;
23558f0484fSRodney W. Grimes 		} else
23658f0484fSRodney W. Grimes 			sval = val;
23758f0484fSRodney W. Grimes 		do {
23858f0484fSRodney W. Grimes 			*--cp = to_char(sval % 10);
23998ee7635SAlexey Zelkin 			ndig++;
24098ee7635SAlexey Zelkin 			/*
24198ee7635SAlexey Zelkin 			 * If (*grp == CHAR_MAX) then no more grouping
24298ee7635SAlexey Zelkin 			 * should be performed.
24398ee7635SAlexey Zelkin 			 */
244243e90d6SAlexey Zelkin 			if (needgrp && *grp != CHAR_MAX && ndig == *grp
245243e90d6SAlexey Zelkin 					&& sval > 9) {
24698ee7635SAlexey Zelkin 				*--cp = thousep;
2477735bb0fSBill Fenner 				ndig = 0;
24898ee7635SAlexey Zelkin 				/*
24998ee7635SAlexey Zelkin 				 * If (*(grp+1) == '\0') then we have to
25098ee7635SAlexey Zelkin 				 * use *grp character (last grouping rule)
25198ee7635SAlexey Zelkin 				 * for all next cases
25298ee7635SAlexey Zelkin 				 */
2532e394b2fSAlexey Zelkin 				if (*(grp+1) != '\0')
2542e394b2fSAlexey Zelkin 					grp++;
2557735bb0fSBill Fenner 			}
25658f0484fSRodney W. Grimes 			sval /= 10;
25758f0484fSRodney W. Grimes 		} while (sval != 0);
25858f0484fSRodney W. Grimes 		break;
25958f0484fSRodney W. Grimes 
26058f0484fSRodney W. Grimes 	case 8:
26158f0484fSRodney W. Grimes 		do {
26258f0484fSRodney W. Grimes 			*--cp = to_char(val & 7);
26358f0484fSRodney W. Grimes 			val >>= 3;
26458f0484fSRodney W. Grimes 		} while (val);
26558f0484fSRodney W. Grimes 		if (octzero && *cp != '0')
26658f0484fSRodney W. Grimes 			*--cp = '0';
26758f0484fSRodney W. Grimes 		break;
26858f0484fSRodney W. Grimes 
26958f0484fSRodney W. Grimes 	case 16:
27058f0484fSRodney W. Grimes 		do {
27158f0484fSRodney W. Grimes 			*--cp = xdigs[val & 15];
27258f0484fSRodney W. Grimes 			val >>= 4;
27358f0484fSRodney W. Grimes 		} while (val);
27458f0484fSRodney W. Grimes 		break;
27558f0484fSRodney W. Grimes 
27658f0484fSRodney W. Grimes 	default:
27758f0484fSRodney W. Grimes 		abort();
27858f0484fSRodney W. Grimes 	}
27958f0484fSRodney W. Grimes 	return (cp);
28058f0484fSRodney W. Grimes }
28158f0484fSRodney W. Grimes 
282d201fe46SDaniel Eischen /*
283b9aac308STim J. Robbins  * Convert a wide character string argument for the %ls format to a multibyte
284d48c77b5STim J. Robbins  * string representation. If not -1, prec specifies the maximum number of
285d48c77b5STim J. Robbins  * bytes to output, and also means that we can't assume that the wide char.
286d48c77b5STim J. Robbins  * string ends is null-terminated.
287b9aac308STim J. Robbins  */
288b9aac308STim J. Robbins static char *
289b9aac308STim J. Robbins __wcsconv(wchar_t *wcsarg, int prec)
290b9aac308STim J. Robbins {
29193996f6dSTim J. Robbins 	static const mbstate_t initial;
29293996f6dSTim J. Robbins 	mbstate_t mbs;
293b9aac308STim J. Robbins 	char buf[MB_LEN_MAX];
294b9aac308STim J. Robbins 	wchar_t *p;
295d48c77b5STim J. Robbins 	char *convbuf;
296b9aac308STim J. Robbins 	size_t clen, nbytes;
297b9aac308STim J. Robbins 
298d48c77b5STim J. Robbins 	/* Allocate space for the maximum number of bytes we could output. */
299d48c77b5STim J. Robbins 	if (prec < 0) {
300d48c77b5STim J. Robbins 		p = wcsarg;
301d48c77b5STim J. Robbins 		mbs = initial;
302d48c77b5STim J. Robbins 		nbytes = wcsrtombs(NULL, (const wchar_t **)&p, 0, &mbs);
303d48c77b5STim J. Robbins 		if (nbytes == (size_t)-1)
304d48c77b5STim J. Robbins 			return (NULL);
305d48c77b5STim J. Robbins 	} else {
306b9aac308STim J. Robbins 		/*
307d48c77b5STim J. Robbins 		 * Optimisation: if the output precision is small enough,
308d48c77b5STim J. Robbins 		 * just allocate enough memory for the maximum instead of
309d48c77b5STim J. Robbins 		 * scanning the string.
310b9aac308STim J. Robbins 		 */
311d48c77b5STim J. Robbins 		if (prec < 128)
312d48c77b5STim J. Robbins 			nbytes = prec;
313d48c77b5STim J. Robbins 		else {
314b9aac308STim J. Robbins 			nbytes = 0;
315b9aac308STim J. Robbins 			p = wcsarg;
31693996f6dSTim J. Robbins 			mbs = initial;
317b9aac308STim J. Robbins 			for (;;) {
31893996f6dSTim J. Robbins 				clen = wcrtomb(buf, *p++, &mbs);
319b9aac308STim J. Robbins 				if (clen == 0 || clen == (size_t)-1 ||
320b9aac308STim J. Robbins 				    nbytes + clen > prec)
321b9aac308STim J. Robbins 					break;
322b9aac308STim J. Robbins 				nbytes += clen;
323b9aac308STim J. Robbins 			}
324d48c77b5STim J. Robbins 		}
325b9aac308STim J. Robbins 	}
326b9aac308STim J. Robbins 	if ((convbuf = malloc(nbytes + 1)) == NULL)
327b9aac308STim J. Robbins 		return (NULL);
328b9aac308STim J. Robbins 
329d48c77b5STim J. Robbins 	/* Fill the output buffer. */
330b9aac308STim J. Robbins 	p = wcsarg;
33193996f6dSTim J. Robbins 	mbs = initial;
332d48c77b5STim J. Robbins 	if ((nbytes = wcsrtombs(convbuf, (const wchar_t **)&p,
333d48c77b5STim J. Robbins 	    nbytes, &mbs)) == (size_t)-1) {
3346f098a48SAndrey A. Chernov 		free(convbuf);
335b9aac308STim J. Robbins 		return (NULL);
3366f098a48SAndrey A. Chernov 	}
337d48c77b5STim J. Robbins 	convbuf[nbytes] = '\0';
338b9aac308STim J. Robbins 	return (convbuf);
339b9aac308STim J. Robbins }
340b9aac308STim J. Robbins 
341b9aac308STim J. Robbins /*
342d201fe46SDaniel Eischen  * MT-safe version
343d201fe46SDaniel Eischen  */
344d201fe46SDaniel Eischen int
345f8418db7SRobert Drehmel vfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap)
346f8418db7SRobert Drehmel 
347d201fe46SDaniel Eischen {
348d201fe46SDaniel Eischen 	int ret;
349d201fe46SDaniel Eischen 
350d201fe46SDaniel Eischen 	FLOCKFILE(fp);
351d201fe46SDaniel Eischen 	ret = __vfprintf(fp, fmt0, ap);
352d201fe46SDaniel Eischen 	FUNLOCKFILE(fp);
353d201fe46SDaniel Eischen 	return (ret);
354d201fe46SDaniel Eischen }
355d201fe46SDaniel Eischen 
3568de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
357ebbad5ecSDavid Schultz 
358ebbad5ecSDavid Schultz #define	dtoa		__dtoa
359ebbad5ecSDavid Schultz #define	freedtoa	__freedtoa
360ebbad5ecSDavid Schultz 
361ebbad5ecSDavid Schultz #include <float.h>
36258f0484fSRodney W. Grimes #include <math.h>
36358f0484fSRodney W. Grimes #include "floatio.h"
364ebbad5ecSDavid Schultz #include "gdtoa.h"
36558f0484fSRodney W. Grimes 
36658f0484fSRodney W. Grimes #define	DEFPREC		6
36758f0484fSRodney W. Grimes 
368c05ac53bSDavid E. O'Brien static int exponent(char *, int, int);
36958f0484fSRodney W. Grimes 
3708de9e897SDavid Schultz #endif /* !NO_FLOATING_POINT */
37158f0484fSRodney W. Grimes 
37238cac8f8SDavid Schultz /*
37338cac8f8SDavid Schultz  * The size of the buffer we use as scratch space for integer
37438cac8f8SDavid Schultz  * conversions, among other things.  Technically, we would need the
37538cac8f8SDavid Schultz  * most space for base 10 conversions with thousands' grouping
37638cac8f8SDavid Schultz  * characters between each pair of digits.  100 bytes is a
37738cac8f8SDavid Schultz  * conservative overestimate even for a 128-bit uintmax_t.
37838cac8f8SDavid Schultz  */
37938cac8f8SDavid Schultz #define	BUF	100
38038cac8f8SDavid Schultz 
38158f0484fSRodney W. Grimes /*
382d201fe46SDaniel Eischen  * Non-MT-safe version
383d201fe46SDaniel Eischen  */
38458f0484fSRodney W. Grimes int
385d201fe46SDaniel Eischen __vfprintf(FILE *fp, const char *fmt0, va_list ap)
38658f0484fSRodney W. Grimes {
387d201fe46SDaniel Eischen 	char *fmt;		/* format string */
388d201fe46SDaniel Eischen 	int ch;			/* character from fmt */
389d201fe46SDaniel Eischen 	int n, n2;		/* handy integer (short term usage) */
390d201fe46SDaniel Eischen 	char *cp;		/* handy char pointer (short term usage) */
391d201fe46SDaniel Eischen 	struct __siov *iovp;	/* for PRINT macro */
392d201fe46SDaniel Eischen 	int flags;		/* flags as above */
39358f0484fSRodney W. Grimes 	int ret;		/* return value accumulator */
39458f0484fSRodney W. Grimes 	int width;		/* width from format (%8d), or 0 */
395ebbad5ecSDavid Schultz 	int prec;		/* precision from format; <0 for N/A */
39658f0484fSRodney W. Grimes 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
39798ee7635SAlexey Zelkin 	char thousands_sep;	/* locale specific thousands separator */
39898ee7635SAlexey Zelkin 	const char *grouping;	/* locale specific numeric grouping rules */
3998de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
400ebbad5ecSDavid Schultz 	/*
401ebbad5ecSDavid Schultz 	 * We can decompose the printed representation of floating
402ebbad5ecSDavid Schultz 	 * point numbers into several parts, some of which may be empty:
403ebbad5ecSDavid Schultz 	 *
404ebbad5ecSDavid Schultz 	 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ
405ebbad5ecSDavid Schultz 	 *    A       B     ---C---      D       E   F
406ebbad5ecSDavid Schultz 	 *
407ebbad5ecSDavid Schultz 	 * A:	'sign' holds this value if present; '\0' otherwise
408ebbad5ecSDavid Schultz 	 * B:	ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal
409ebbad5ecSDavid Schultz 	 * C:	cp points to the string MMMNNN.  Leading and trailing
410ebbad5ecSDavid Schultz 	 *	zeros are not in the string and must be added.
411ebbad5ecSDavid Schultz 	 * D:	expchar holds this character; '\0' if no exponent, e.g. %f
412ebbad5ecSDavid Schultz 	 * F:	at least two digits for decimal, at least one digit for hex
413ebbad5ecSDavid Schultz 	 */
4147ae5c679SAlexey Zelkin 	char *decimal_point;	/* locale specific decimal point */
415ebbad5ecSDavid Schultz 	int signflag;		/* true if float is negative */
416ebbad5ecSDavid Schultz 	union {			/* floating point arguments %[aAeEfFgG] */
417ebbad5ecSDavid Schultz 		double dbl;
418ebbad5ecSDavid Schultz 		long double ldbl;
419ebbad5ecSDavid Schultz 	} fparg;
42058f0484fSRodney W. Grimes 	int expt;		/* integer value of exponent */
421ebbad5ecSDavid Schultz 	char expchar;		/* exponent character: [eEpP\0] */
422ebbad5ecSDavid Schultz 	char *dtoaend;		/* pointer to end of converted digits */
42358f0484fSRodney W. Grimes 	int expsize;		/* character count for expstr */
424ebbad5ecSDavid Schultz 	int lead;		/* sig figs before decimal or group sep */
425ebbad5ecSDavid Schultz 	int ndig;		/* actual number of digits returned by dtoa */
426ebbad5ecSDavid Schultz 	char expstr[MAXEXPDIG+2];	/* buffer for exponent string: e+ZZZ */
4272ffc61baSTor Egge 	char *dtoaresult;	/* buffer allocated by dtoa */
428ebbad5ecSDavid Schultz 	int nseps;		/* number of group separators with ' */
429ebbad5ecSDavid Schultz 	int nrepeats;		/* number of repeats of the last group */
43058f0484fSRodney W. Grimes #endif
43158f0484fSRodney W. Grimes 	u_long	ulval;		/* integer arguments %[diouxX] */
4327735bb0fSBill Fenner 	uintmax_t ujval;	/* %j, %ll, %q, %t, %z integers */
43358f0484fSRodney W. Grimes 	int base;		/* base for [diouxX] conversion */
43458f0484fSRodney W. Grimes 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
435261a532aSBill Fenner 	int realsz;		/* field size expanded by dprec, sign, etc */
43658f0484fSRodney W. Grimes 	int size;		/* size of converted field or string */
43792e88f87SAndrey A. Chernov 	int prsize;             /* max size of printed field */
438ebbad5ecSDavid Schultz 	const char *xdigs;     	/* digits for %[xX] conversion */
43958f0484fSRodney W. Grimes #define NIOV 8
44058f0484fSRodney W. Grimes 	struct __suio uio;	/* output information: summary */
44158f0484fSRodney W. Grimes 	struct __siov iov[NIOV];/* ... and individual io vectors */
44238cac8f8SDavid Schultz 	char buf[BUF];		/* buffer with space for digits of uintmax_t */
443ebbad5ecSDavid Schultz 	char ox[2];		/* space for 0x; ox[1] is either x, X, or \0 */
444a387081cSDoug Rabson 	union arg *argtable;    /* args, built due to positional arg */
445a387081cSDoug Rabson 	union arg statargtable [STATIC_ARG_TBL_SIZE];
446efb7e53dSJordan K. Hubbard 	int nextarg;            /* 1-based argument index */
447efb7e53dSJordan K. Hubbard 	va_list orgap;          /* original argument pointer */
448b9aac308STim J. Robbins 	char *convbuf;		/* wide to multibyte conversion result */
44958f0484fSRodney W. Grimes 
45058f0484fSRodney W. Grimes 	/*
45158f0484fSRodney W. Grimes 	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
45258f0484fSRodney W. Grimes 	 * fields occur frequently, increase PADSIZE and make the initialisers
45358f0484fSRodney W. Grimes 	 * below longer.
45458f0484fSRodney W. Grimes 	 */
45558f0484fSRodney W. Grimes #define	PADSIZE	16		/* pad chunk size */
45658f0484fSRodney W. Grimes 	static char blanks[PADSIZE] =
45758f0484fSRodney W. Grimes 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
45858f0484fSRodney W. Grimes 	static char zeroes[PADSIZE] =
45958f0484fSRodney W. Grimes 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
46058f0484fSRodney W. Grimes 
461ac9913a7SDavid Schultz 	static const char xdigs_lower[16] = "0123456789abcdef";
462ac9913a7SDavid Schultz 	static const char xdigs_upper[16] = "0123456789ABCDEF";
463ebbad5ecSDavid Schultz 
46458f0484fSRodney W. Grimes 	/*
46558f0484fSRodney W. Grimes 	 * BEWARE, these `goto error' on error, and PAD uses `n'.
46658f0484fSRodney W. Grimes 	 */
46758f0484fSRodney W. Grimes #define	PRINT(ptr, len) { \
46858f0484fSRodney W. Grimes 	iovp->iov_base = (ptr); \
46958f0484fSRodney W. Grimes 	iovp->iov_len = (len); \
47058f0484fSRodney W. Grimes 	uio.uio_resid += (len); \
47158f0484fSRodney W. Grimes 	iovp++; \
47258f0484fSRodney W. Grimes 	if (++uio.uio_iovcnt >= NIOV) { \
47358f0484fSRodney W. Grimes 		if (__sprint(fp, &uio)) \
47458f0484fSRodney W. Grimes 			goto error; \
47558f0484fSRodney W. Grimes 		iovp = iov; \
47658f0484fSRodney W. Grimes 	} \
47758f0484fSRodney W. Grimes }
47858f0484fSRodney W. Grimes #define	PAD(howmany, with) { \
47958f0484fSRodney W. Grimes 	if ((n = (howmany)) > 0) { \
48058f0484fSRodney W. Grimes 		while (n > PADSIZE) { \
48158f0484fSRodney W. Grimes 			PRINT(with, PADSIZE); \
48258f0484fSRodney W. Grimes 			n -= PADSIZE; \
48358f0484fSRodney W. Grimes 		} \
48458f0484fSRodney W. Grimes 		PRINT(with, n); \
48558f0484fSRodney W. Grimes 	} \
48658f0484fSRodney W. Grimes }
4873b204b7dSDavid Schultz #define	PRINTANDPAD(p, ep, len, with) do {	\
4883b204b7dSDavid Schultz 	n2 = (ep) - (p);       			\
4893b204b7dSDavid Schultz 	if (n2 > (len))				\
4903b204b7dSDavid Schultz 		n2 = (len);			\
4913b204b7dSDavid Schultz 	if (n2 > 0)				\
4923b204b7dSDavid Schultz 		PRINT((p), n2);			\
4933b204b7dSDavid Schultz 	PAD((len) - (n2 > 0 ? n2 : 0), (with));	\
4943b204b7dSDavid Schultz } while(0)
49558f0484fSRodney W. Grimes #define	FLUSH() { \
49658f0484fSRodney W. Grimes 	if (uio.uio_resid && __sprint(fp, &uio)) \
49758f0484fSRodney W. Grimes 		goto error; \
49858f0484fSRodney W. Grimes 	uio.uio_iovcnt = 0; \
49958f0484fSRodney W. Grimes 	iovp = iov; \
50058f0484fSRodney W. Grimes }
50158f0484fSRodney W. Grimes 
50258f0484fSRodney W. Grimes 	/*
503efb7e53dSJordan K. Hubbard 	 * Get the argument indexed by nextarg.   If the argument table is
504efb7e53dSJordan K. Hubbard 	 * built, use it to get the argument.  If its not, get the next
505efb7e53dSJordan K. Hubbard 	 * argument (and arguments must be gotten sequentially).
506efb7e53dSJordan K. Hubbard 	 */
507efb7e53dSJordan K. Hubbard #define GETARG(type) \
508a387081cSDoug Rabson 	((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \
509efb7e53dSJordan K. Hubbard 	    (nextarg++, va_arg(ap, type)))
510efb7e53dSJordan K. Hubbard 
511efb7e53dSJordan K. Hubbard 	/*
51258f0484fSRodney W. Grimes 	 * To extend shorts properly, we need both signed and unsigned
51358f0484fSRodney W. Grimes 	 * argument extraction methods.
51458f0484fSRodney W. Grimes 	 */
51558f0484fSRodney W. Grimes #define	SARG() \
516efb7e53dSJordan K. Hubbard 	(flags&LONGINT ? GETARG(long) : \
517efb7e53dSJordan K. Hubbard 	    flags&SHORTINT ? (long)(short)GETARG(int) : \
5187735bb0fSBill Fenner 	    flags&CHARINT ? (long)(signed char)GETARG(int) : \
519efb7e53dSJordan K. Hubbard 	    (long)GETARG(int))
52058f0484fSRodney W. Grimes #define	UARG() \
521efb7e53dSJordan K. Hubbard 	(flags&LONGINT ? GETARG(u_long) : \
522efb7e53dSJordan K. Hubbard 	    flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \
5237735bb0fSBill Fenner 	    flags&CHARINT ? (u_long)(u_char)GETARG(int) : \
524efb7e53dSJordan K. Hubbard 	    (u_long)GETARG(u_int))
5257735bb0fSBill Fenner #define	INTMAX_SIZE	(INTMAXT|SIZET|PTRDIFFT|LLONGINT)
5267735bb0fSBill Fenner #define SJARG() \
5277735bb0fSBill Fenner 	(flags&INTMAXT ? GETARG(intmax_t) : \
5287735bb0fSBill Fenner 	    flags&SIZET ? (intmax_t)GETARG(size_t) : \
5297735bb0fSBill Fenner 	    flags&PTRDIFFT ? (intmax_t)GETARG(ptrdiff_t) : \
5307735bb0fSBill Fenner 	    (intmax_t)GETARG(long long))
5317735bb0fSBill Fenner #define	UJARG() \
5327735bb0fSBill Fenner 	(flags&INTMAXT ? GETARG(uintmax_t) : \
5337735bb0fSBill Fenner 	    flags&SIZET ? (uintmax_t)GETARG(size_t) : \
5347735bb0fSBill Fenner 	    flags&PTRDIFFT ? (uintmax_t)GETARG(ptrdiff_t) : \
5357735bb0fSBill Fenner 	    (uintmax_t)GETARG(unsigned long long))
536efb7e53dSJordan K. Hubbard 
537efb7e53dSJordan K. Hubbard 	/*
538efb7e53dSJordan K. Hubbard 	 * Get * arguments, including the form *nn$.  Preserve the nextarg
539efb7e53dSJordan K. Hubbard 	 * that the argument can be gotten once the type is determined.
540efb7e53dSJordan K. Hubbard 	 */
541efb7e53dSJordan K. Hubbard #define GETASTER(val) \
542efb7e53dSJordan K. Hubbard 	n2 = 0; \
543efb7e53dSJordan K. Hubbard 	cp = fmt; \
544efb7e53dSJordan K. Hubbard 	while (is_digit(*cp)) { \
545efb7e53dSJordan K. Hubbard 		n2 = 10 * n2 + to_digit(*cp); \
546efb7e53dSJordan K. Hubbard 		cp++; \
547efb7e53dSJordan K. Hubbard 	} \
548efb7e53dSJordan K. Hubbard 	if (*cp == '$') { \
549efb7e53dSJordan K. Hubbard 		int hold = nextarg; \
550efb7e53dSJordan K. Hubbard 		if (argtable == NULL) { \
551efb7e53dSJordan K. Hubbard 			argtable = statargtable; \
552e62e5ff9SDavid Schultz 			if (__find_arguments (fmt0, orgap, &argtable)) { \
553e62e5ff9SDavid Schultz 				ret = EOF; \
554e62e5ff9SDavid Schultz 				goto error; \
555e62e5ff9SDavid Schultz 			} \
556efb7e53dSJordan K. Hubbard 		} \
557efb7e53dSJordan K. Hubbard 		nextarg = n2; \
558efb7e53dSJordan K. Hubbard 		val = GETARG (int); \
559efb7e53dSJordan K. Hubbard 		nextarg = hold; \
560efb7e53dSJordan K. Hubbard 		fmt = ++cp; \
561efb7e53dSJordan K. Hubbard 	} else { \
562efb7e53dSJordan K. Hubbard 		val = GETARG (int); \
563efb7e53dSJordan K. Hubbard 	}
564efb7e53dSJordan K. Hubbard 
56533bff5d3SDavid Schultz 	if (__use_xprintf == 0 && getenv("USE_XPRINTF"))
56633bff5d3SDavid Schultz 		__use_xprintf = 1;
56733bff5d3SDavid Schultz 	if (__use_xprintf > 0)
56833bff5d3SDavid Schultz 		return (__xvprintf(fp, fmt0, ap));
56958f0484fSRodney W. Grimes 
57098ee7635SAlexey Zelkin 	thousands_sep = '\0';
57198ee7635SAlexey Zelkin 	grouping = NULL;
572b9aac308STim J. Robbins 	convbuf = NULL;
5738de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
5742ffc61baSTor Egge 	dtoaresult = NULL;
5757735bb0fSBill Fenner 	decimal_point = localeconv()->decimal_point;
5762ffc61baSTor Egge #endif
57758f0484fSRodney W. Grimes 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
57852183d46SDavid Schultz 	if (prepwrite(fp) != 0)
57958f0484fSRodney W. Grimes 		return (EOF);
58058f0484fSRodney W. Grimes 
58158f0484fSRodney W. Grimes 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
58258f0484fSRodney W. Grimes 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
583d201fe46SDaniel Eischen 	    fp->_file >= 0)
58458f0484fSRodney W. Grimes 		return (__sbprintf(fp, fmt0, ap));
58558f0484fSRodney W. Grimes 
58658f0484fSRodney W. Grimes 	fmt = (char *)fmt0;
587efb7e53dSJordan K. Hubbard 	argtable = NULL;
588efb7e53dSJordan K. Hubbard 	nextarg = 1;
589d07090a8STim J. Robbins 	va_copy(orgap, ap);
59058f0484fSRodney W. Grimes 	uio.uio_iov = iovp = iov;
59158f0484fSRodney W. Grimes 	uio.uio_resid = 0;
59258f0484fSRodney W. Grimes 	uio.uio_iovcnt = 0;
59358f0484fSRodney W. Grimes 	ret = 0;
59458f0484fSRodney W. Grimes 
59558f0484fSRodney W. Grimes 	/*
59658f0484fSRodney W. Grimes 	 * Scan the format for conversions (`%' character).
59758f0484fSRodney W. Grimes 	 */
59858f0484fSRodney W. Grimes 	for (;;) {
59958f0484fSRodney W. Grimes 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
60058f0484fSRodney W. Grimes 			/* void */;
60158f0484fSRodney W. Grimes 		if ((n = fmt - cp) != 0) {
602b250f248SAndrey A. Chernov 			if ((unsigned)ret + n > INT_MAX) {
60392e88f87SAndrey A. Chernov 				ret = EOF;
60492e88f87SAndrey A. Chernov 				goto error;
60592e88f87SAndrey A. Chernov 			}
60658f0484fSRodney W. Grimes 			PRINT(cp, n);
60758f0484fSRodney W. Grimes 			ret += n;
60858f0484fSRodney W. Grimes 		}
60958f0484fSRodney W. Grimes 		if (ch == '\0')
61058f0484fSRodney W. Grimes 			goto done;
61158f0484fSRodney W. Grimes 		fmt++;		/* skip over '%' */
61258f0484fSRodney W. Grimes 
61358f0484fSRodney W. Grimes 		flags = 0;
61458f0484fSRodney W. Grimes 		dprec = 0;
61558f0484fSRodney W. Grimes 		width = 0;
61658f0484fSRodney W. Grimes 		prec = -1;
61758f0484fSRodney W. Grimes 		sign = '\0';
618ebbad5ecSDavid Schultz 		ox[1] = '\0';
61958f0484fSRodney W. Grimes 
62058f0484fSRodney W. Grimes rflag:		ch = *fmt++;
62158f0484fSRodney W. Grimes reswitch:	switch (ch) {
62258f0484fSRodney W. Grimes 		case ' ':
6232e394b2fSAlexey Zelkin 			/*-
62458f0484fSRodney W. Grimes 			 * ``If the space and + flags both appear, the space
62558f0484fSRodney W. Grimes 			 * flag will be ignored.''
62658f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
62758f0484fSRodney W. Grimes 			 */
62858f0484fSRodney W. Grimes 			if (!sign)
62958f0484fSRodney W. Grimes 				sign = ' ';
63058f0484fSRodney W. Grimes 			goto rflag;
63158f0484fSRodney W. Grimes 		case '#':
63258f0484fSRodney W. Grimes 			flags |= ALT;
63358f0484fSRodney W. Grimes 			goto rflag;
63458f0484fSRodney W. Grimes 		case '*':
6352e394b2fSAlexey Zelkin 			/*-
63658f0484fSRodney W. Grimes 			 * ``A negative field width argument is taken as a
63758f0484fSRodney W. Grimes 			 * - flag followed by a positive field width.''
63858f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
63958f0484fSRodney W. Grimes 			 * They don't exclude field widths read from args.
64058f0484fSRodney W. Grimes 			 */
641efb7e53dSJordan K. Hubbard 			GETASTER (width);
642efb7e53dSJordan K. Hubbard 			if (width >= 0)
64358f0484fSRodney W. Grimes 				goto rflag;
64458f0484fSRodney W. Grimes 			width = -width;
64558f0484fSRodney W. Grimes 			/* FALLTHROUGH */
64658f0484fSRodney W. Grimes 		case '-':
64758f0484fSRodney W. Grimes 			flags |= LADJUST;
64858f0484fSRodney W. Grimes 			goto rflag;
64958f0484fSRodney W. Grimes 		case '+':
65058f0484fSRodney W. Grimes 			sign = '+';
65158f0484fSRodney W. Grimes 			goto rflag;
6527735bb0fSBill Fenner 		case '\'':
65398ee7635SAlexey Zelkin 			flags |= GROUPING;
65498ee7635SAlexey Zelkin 			thousands_sep = *(localeconv()->thousands_sep);
65598ee7635SAlexey Zelkin 			grouping = localeconv()->grouping;
6567735bb0fSBill Fenner 			goto rflag;
65758f0484fSRodney W. Grimes 		case '.':
65858f0484fSRodney W. Grimes 			if ((ch = *fmt++) == '*') {
6593b204b7dSDavid Schultz 				GETASTER (prec);
66058f0484fSRodney W. Grimes 				goto rflag;
66158f0484fSRodney W. Grimes 			}
6623b204b7dSDavid Schultz 			prec = 0;
66358f0484fSRodney W. Grimes 			while (is_digit(ch)) {
6643b204b7dSDavid Schultz 				prec = 10 * prec + to_digit(ch);
66558f0484fSRodney W. Grimes 				ch = *fmt++;
66658f0484fSRodney W. Grimes 			}
66758f0484fSRodney W. Grimes 			goto reswitch;
66858f0484fSRodney W. Grimes 		case '0':
6692e394b2fSAlexey Zelkin 			/*-
67058f0484fSRodney W. Grimes 			 * ``Note that 0 is taken as a flag, not as the
67158f0484fSRodney W. Grimes 			 * beginning of a field width.''
67258f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
67358f0484fSRodney W. Grimes 			 */
67458f0484fSRodney W. Grimes 			flags |= ZEROPAD;
67558f0484fSRodney W. Grimes 			goto rflag;
67658f0484fSRodney W. Grimes 		case '1': case '2': case '3': case '4':
67758f0484fSRodney W. Grimes 		case '5': case '6': case '7': case '8': case '9':
67858f0484fSRodney W. Grimes 			n = 0;
67958f0484fSRodney W. Grimes 			do {
68058f0484fSRodney W. Grimes 				n = 10 * n + to_digit(ch);
68158f0484fSRodney W. Grimes 				ch = *fmt++;
68258f0484fSRodney W. Grimes 			} while (is_digit(ch));
683efb7e53dSJordan K. Hubbard 			if (ch == '$') {
684efb7e53dSJordan K. Hubbard 				nextarg = n;
685efb7e53dSJordan K. Hubbard 				if (argtable == NULL) {
686efb7e53dSJordan K. Hubbard 					argtable = statargtable;
687e62e5ff9SDavid Schultz 					if (__find_arguments (fmt0, orgap,
688e62e5ff9SDavid Schultz 							      &argtable)) {
689e62e5ff9SDavid Schultz 						ret = EOF;
690e62e5ff9SDavid Schultz 						goto error;
691e62e5ff9SDavid Schultz 					}
692efb7e53dSJordan K. Hubbard 				}
693efb7e53dSJordan K. Hubbard 				goto rflag;
694efb7e53dSJordan K. Hubbard 			}
69558f0484fSRodney W. Grimes 			width = n;
69658f0484fSRodney W. Grimes 			goto reswitch;
6978de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
69858f0484fSRodney W. Grimes 		case 'L':
69958f0484fSRodney W. Grimes 			flags |= LONGDBL;
70058f0484fSRodney W. Grimes 			goto rflag;
70158f0484fSRodney W. Grimes #endif
70258f0484fSRodney W. Grimes 		case 'h':
7037735bb0fSBill Fenner 			if (flags & SHORTINT) {
7047735bb0fSBill Fenner 				flags &= ~SHORTINT;
7057735bb0fSBill Fenner 				flags |= CHARINT;
7067735bb0fSBill Fenner 			} else
70758f0484fSRodney W. Grimes 				flags |= SHORTINT;
70858f0484fSRodney W. Grimes 			goto rflag;
7097735bb0fSBill Fenner 		case 'j':
7107735bb0fSBill Fenner 			flags |= INTMAXT;
7117735bb0fSBill Fenner 			goto rflag;
71258f0484fSRodney W. Grimes 		case 'l':
7137735bb0fSBill Fenner 			if (flags & LONGINT) {
7147735bb0fSBill Fenner 				flags &= ~LONGINT;
7157735bb0fSBill Fenner 				flags |= LLONGINT;
7167735bb0fSBill Fenner 			} else
71758f0484fSRodney W. Grimes 				flags |= LONGINT;
71858f0484fSRodney W. Grimes 			goto rflag;
71958f0484fSRodney W. Grimes 		case 'q':
7207735bb0fSBill Fenner 			flags |= LLONGINT;	/* not necessarily */
7217735bb0fSBill Fenner 			goto rflag;
7227735bb0fSBill Fenner 		case 't':
7237735bb0fSBill Fenner 			flags |= PTRDIFFT;
7247735bb0fSBill Fenner 			goto rflag;
7257735bb0fSBill Fenner 		case 'z':
7267735bb0fSBill Fenner 			flags |= SIZET;
72758f0484fSRodney W. Grimes 			goto rflag;
728927ecbf3STim J. Robbins 		case 'C':
729927ecbf3STim J. Robbins 			flags |= LONGINT;
730927ecbf3STim J. Robbins 			/*FALLTHROUGH*/
73158f0484fSRodney W. Grimes 		case 'c':
732b9aac308STim J. Robbins 			if (flags & LONGINT) {
73393996f6dSTim J. Robbins 				static const mbstate_t initial;
73493996f6dSTim J. Robbins 				mbstate_t mbs;
735b9aac308STim J. Robbins 				size_t mbseqlen;
736b9aac308STim J. Robbins 
73793996f6dSTim J. Robbins 				mbs = initial;
738b9aac308STim J. Robbins 				mbseqlen = wcrtomb(cp = buf,
73993996f6dSTim J. Robbins 				    (wchar_t)GETARG(wint_t), &mbs);
7406180233fSTim J. Robbins 				if (mbseqlen == (size_t)-1) {
7416180233fSTim J. Robbins 					fp->_flags |= __SERR;
742b9aac308STim J. Robbins 					goto error;
7436180233fSTim J. Robbins 				}
744b9aac308STim J. Robbins 				size = (int)mbseqlen;
745b9aac308STim J. Robbins 			} else {
746efb7e53dSJordan K. Hubbard 				*(cp = buf) = GETARG(int);
74758f0484fSRodney W. Grimes 				size = 1;
748b9aac308STim J. Robbins 			}
74958f0484fSRodney W. Grimes 			sign = '\0';
75058f0484fSRodney W. Grimes 			break;
75158f0484fSRodney W. Grimes 		case 'D':
75258f0484fSRodney W. Grimes 			flags |= LONGINT;
75358f0484fSRodney W. Grimes 			/*FALLTHROUGH*/
75458f0484fSRodney W. Grimes 		case 'd':
75558f0484fSRodney W. Grimes 		case 'i':
7567735bb0fSBill Fenner 			if (flags & INTMAX_SIZE) {
7577735bb0fSBill Fenner 				ujval = SJARG();
7587735bb0fSBill Fenner 				if ((intmax_t)ujval < 0) {
7597735bb0fSBill Fenner 					ujval = -ujval;
76058f0484fSRodney W. Grimes 					sign = '-';
76158f0484fSRodney W. Grimes 				}
76258f0484fSRodney W. Grimes 			} else {
76358f0484fSRodney W. Grimes 				ulval = SARG();
76458f0484fSRodney W. Grimes 				if ((long)ulval < 0) {
76558f0484fSRodney W. Grimes 					ulval = -ulval;
76658f0484fSRodney W. Grimes 					sign = '-';
76758f0484fSRodney W. Grimes 				}
76858f0484fSRodney W. Grimes 			}
76958f0484fSRodney W. Grimes 			base = 10;
77058f0484fSRodney W. Grimes 			goto number;
7718de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
7727735bb0fSBill Fenner 		case 'a':
7737735bb0fSBill Fenner 		case 'A':
774ebbad5ecSDavid Schultz 			if (ch == 'a') {
775ebbad5ecSDavid Schultz 				ox[1] = 'x';
776ebbad5ecSDavid Schultz 				xdigs = xdigs_lower;
777ebbad5ecSDavid Schultz 				expchar = 'p';
778ebbad5ecSDavid Schultz 			} else {
779ebbad5ecSDavid Schultz 				ox[1] = 'X';
780ebbad5ecSDavid Schultz 				xdigs = xdigs_upper;
781ebbad5ecSDavid Schultz 				expchar = 'P';
782ebbad5ecSDavid Schultz 			}
783904322a5SDavid Schultz 			if (prec >= 0)
784904322a5SDavid Schultz 				prec++;
785904322a5SDavid Schultz 			if (dtoaresult != NULL)
786904322a5SDavid Schultz 				freedtoa(dtoaresult);
787ebbad5ecSDavid Schultz 			if (flags & LONGDBL) {
788904322a5SDavid Schultz 				fparg.ldbl = GETARG(long double);
789ebbad5ecSDavid Schultz 				dtoaresult = cp =
790ebbad5ecSDavid Schultz 				    __hldtoa(fparg.ldbl, xdigs, prec,
791ebbad5ecSDavid Schultz 				    &expt, &signflag, &dtoaend);
792ebbad5ecSDavid Schultz 			} else {
793ebbad5ecSDavid Schultz 				fparg.dbl = GETARG(double);
794ebbad5ecSDavid Schultz 				dtoaresult = cp =
795ebbad5ecSDavid Schultz 				    __hdtoa(fparg.dbl, xdigs, prec,
796ebbad5ecSDavid Schultz 				    &expt, &signflag, &dtoaend);
797ebbad5ecSDavid Schultz 			}
798904322a5SDavid Schultz 			if (prec < 0)
799904322a5SDavid Schultz 				prec = dtoaend - cp;
800904322a5SDavid Schultz 			if (expt == INT_MAX)
801904322a5SDavid Schultz 				ox[1] = '\0';
802904322a5SDavid Schultz 			goto fp_common;
803d26be6f0SBruce Evans 		case 'e':
80458f0484fSRodney W. Grimes 		case 'E':
805ebbad5ecSDavid Schultz 			expchar = ch;
806ebbad5ecSDavid Schultz 			if (prec < 0)	/* account for digit before decpt */
807ebbad5ecSDavid Schultz 				prec = DEFPREC + 1;
808ebbad5ecSDavid Schultz 			else
809ebbad5ecSDavid Schultz 				prec++;
810ebbad5ecSDavid Schultz 			goto fp_begin;
811d26be6f0SBruce Evans 		case 'f':
8127735bb0fSBill Fenner 		case 'F':
813ebbad5ecSDavid Schultz 			expchar = '\0';
814d26be6f0SBruce Evans 			goto fp_begin;
81558f0484fSRodney W. Grimes 		case 'g':
81658f0484fSRodney W. Grimes 		case 'G':
817ebbad5ecSDavid Schultz 			expchar = ch - ('g' - 'e');
818d26be6f0SBruce Evans 			if (prec == 0)
819d26be6f0SBruce Evans 				prec = 1;
820ebbad5ecSDavid Schultz fp_begin:
821ebbad5ecSDavid Schultz 			if (prec < 0)
82258f0484fSRodney W. Grimes 				prec = DEFPREC;
823ebbad5ecSDavid Schultz 			if (dtoaresult != NULL)
824ebbad5ecSDavid Schultz 				freedtoa(dtoaresult);
825ebbad5ecSDavid Schultz 			if (flags & LONGDBL) {
826ebbad5ecSDavid Schultz 				fparg.ldbl = GETARG(long double);
827ebbad5ecSDavid Schultz 				dtoaresult = cp =
828ebbad5ecSDavid Schultz 				    __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec,
829ebbad5ecSDavid Schultz 				    &expt, &signflag, &dtoaend);
830ebbad5ecSDavid Schultz 			} else {
831ebbad5ecSDavid Schultz 				fparg.dbl = GETARG(double);
832ebbad5ecSDavid Schultz 				dtoaresult = cp =
833ebbad5ecSDavid Schultz 				    dtoa(fparg.dbl, expchar ? 2 : 3, prec,
834ebbad5ecSDavid Schultz 				    &expt, &signflag, &dtoaend);
835ebbad5ecSDavid Schultz 				if (expt == 9999)
836ebbad5ecSDavid Schultz 					expt = INT_MAX;
83758f0484fSRodney W. Grimes 			}
838904322a5SDavid Schultz fp_common:
839ebbad5ecSDavid Schultz 			if (signflag)
840ebbad5ecSDavid Schultz 				sign = '-';
841ebbad5ecSDavid Schultz 			if (expt == INT_MAX) {	/* inf or nan */
842ebbad5ecSDavid Schultz 				if (*cp == 'N') {
843ebbad5ecSDavid Schultz 					cp = (ch >= 'a') ? "nan" : "NAN";
844ebbad5ecSDavid Schultz 					sign = '\0';
845ebbad5ecSDavid Schultz 				} else
846ebbad5ecSDavid Schultz 					cp = (ch >= 'a') ? "inf" : "INF";
84758f0484fSRodney W. Grimes 				size = 3;
848970a466cSDavid Schultz 				flags &= ~ZEROPAD;
84958f0484fSRodney W. Grimes 				break;
85058f0484fSRodney W. Grimes 			}
85158f0484fSRodney W. Grimes 			flags |= FPT;
852ebbad5ecSDavid Schultz 			ndig = dtoaend - cp;
85358f0484fSRodney W. Grimes 			if (ch == 'g' || ch == 'G') {
854ebbad5ecSDavid Schultz 				if (expt > -4 && expt <= prec) {
855ebbad5ecSDavid Schultz 					/* Make %[gG] smell like %[fF] */
856ebbad5ecSDavid Schultz 					expchar = '\0';
857ebbad5ecSDavid Schultz 					if (flags & ALT)
858ebbad5ecSDavid Schultz 						prec -= expt;
85958f0484fSRodney W. Grimes 					else
860ebbad5ecSDavid Schultz 						prec = ndig - expt;
861ebbad5ecSDavid Schultz 					if (prec < 0)
862ebbad5ecSDavid Schultz 						prec = 0;
8631f2a0cdfSDavid Schultz 				} else {
8641f2a0cdfSDavid Schultz 					/*
8651f2a0cdfSDavid Schultz 					 * Make %[gG] smell like %[eE], but
8661f2a0cdfSDavid Schultz 					 * trim trailing zeroes if no # flag.
8671f2a0cdfSDavid Schultz 					 */
8681f2a0cdfSDavid Schultz 					if (!(flags & ALT))
8691f2a0cdfSDavid Schultz 						prec = ndig;
87058f0484fSRodney W. Grimes 				}
871ebbad5ecSDavid Schultz 			}
872ebbad5ecSDavid Schultz 			if (expchar) {
873ebbad5ecSDavid Schultz 				expsize = exponent(expstr, expt - 1, expchar);
874ebbad5ecSDavid Schultz 				size = expsize + prec;
8753b204b7dSDavid Schultz 				if (prec > 1 || flags & ALT)
87658f0484fSRodney W. Grimes 					++size;
877ebbad5ecSDavid Schultz 			} else {
87881ae2e9aSDavid Schultz 				/* space for digits before decimal point */
87981ae2e9aSDavid Schultz 				if (expt > 0)
88058f0484fSRodney W. Grimes 					size = expt;
88181ae2e9aSDavid Schultz 				else	/* "0" */
88281ae2e9aSDavid Schultz 					size = 1;
88381ae2e9aSDavid Schultz 				/* space for decimal pt and following digits */
88458f0484fSRodney W. Grimes 				if (prec || flags & ALT)
88558f0484fSRodney W. Grimes 					size += prec + 1;
886ebbad5ecSDavid Schultz 				if (grouping && expt > 0) {
887ebbad5ecSDavid Schultz 					/* space for thousands' grouping */
888ebbad5ecSDavid Schultz 					nseps = nrepeats = 0;
889ebbad5ecSDavid Schultz 					lead = expt;
890ebbad5ecSDavid Schultz 					while (*grouping != CHAR_MAX) {
891ebbad5ecSDavid Schultz 						if (lead <= *grouping)
892ebbad5ecSDavid Schultz 							break;
893ebbad5ecSDavid Schultz 						lead -= *grouping;
894ebbad5ecSDavid Schultz 						if (*(grouping+1)) {
895ebbad5ecSDavid Schultz 							nseps++;
896ebbad5ecSDavid Schultz 							grouping++;
89758f0484fSRodney W. Grimes 						} else
898ebbad5ecSDavid Schultz 							nrepeats++;
899ebbad5ecSDavid Schultz 					}
900ebbad5ecSDavid Schultz 					size += nseps + nrepeats;
901ebbad5ecSDavid Schultz 				} else
902d890afb8SDavid Schultz 					lead = expt;
903ebbad5ecSDavid Schultz 			}
90458f0484fSRodney W. Grimes 			break;
9058de9e897SDavid Schultz #endif /* !NO_FLOATING_POINT */
90658f0484fSRodney W. Grimes 		case 'n':
9077735bb0fSBill Fenner 			/*
9087735bb0fSBill Fenner 			 * Assignment-like behavior is specified if the
9097735bb0fSBill Fenner 			 * value overflows or is otherwise unrepresentable.
9107735bb0fSBill Fenner 			 * C99 says to use `signed char' for %hhn conversions.
9117735bb0fSBill Fenner 			 */
9127735bb0fSBill Fenner 			if (flags & LLONGINT)
9137735bb0fSBill Fenner 				*GETARG(long long *) = ret;
9147735bb0fSBill Fenner 			else if (flags & SIZET)
9157735bb0fSBill Fenner 				*GETARG(ssize_t *) = (ssize_t)ret;
9167735bb0fSBill Fenner 			else if (flags & PTRDIFFT)
9177735bb0fSBill Fenner 				*GETARG(ptrdiff_t *) = ret;
9187735bb0fSBill Fenner 			else if (flags & INTMAXT)
9197735bb0fSBill Fenner 				*GETARG(intmax_t *) = ret;
92058f0484fSRodney W. Grimes 			else if (flags & LONGINT)
9216e690ad4SAndrey A. Chernov 				*GETARG(long *) = ret;
92258f0484fSRodney W. Grimes 			else if (flags & SHORTINT)
9236e690ad4SAndrey A. Chernov 				*GETARG(short *) = ret;
9247735bb0fSBill Fenner 			else if (flags & CHARINT)
9257735bb0fSBill Fenner 				*GETARG(signed char *) = ret;
92658f0484fSRodney W. Grimes 			else
9276e690ad4SAndrey A. Chernov 				*GETARG(int *) = ret;
92858f0484fSRodney W. Grimes 			continue;	/* no output */
92958f0484fSRodney W. Grimes 		case 'O':
93058f0484fSRodney W. Grimes 			flags |= LONGINT;
93158f0484fSRodney W. Grimes 			/*FALLTHROUGH*/
93258f0484fSRodney W. Grimes 		case 'o':
9337735bb0fSBill Fenner 			if (flags & INTMAX_SIZE)
9347735bb0fSBill Fenner 				ujval = UJARG();
93558f0484fSRodney W. Grimes 			else
93658f0484fSRodney W. Grimes 				ulval = UARG();
93758f0484fSRodney W. Grimes 			base = 8;
93858f0484fSRodney W. Grimes 			goto nosign;
93958f0484fSRodney W. Grimes 		case 'p':
9402e394b2fSAlexey Zelkin 			/*-
94158f0484fSRodney W. Grimes 			 * ``The argument shall be a pointer to void.  The
94258f0484fSRodney W. Grimes 			 * value of the pointer is converted to a sequence
94358f0484fSRodney W. Grimes 			 * of printable characters, in an implementation-
94458f0484fSRodney W. Grimes 			 * defined manner.''
94558f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
94658f0484fSRodney W. Grimes 			 */
9477735bb0fSBill Fenner 			ujval = (uintmax_t)(uintptr_t)GETARG(void *);
94858f0484fSRodney W. Grimes 			base = 16;
949ebbad5ecSDavid Schultz 			xdigs = xdigs_lower;
950ebbad5ecSDavid Schultz 			flags = flags | INTMAXT;
951ebbad5ecSDavid Schultz 			ox[1] = 'x';
95258f0484fSRodney W. Grimes 			goto nosign;
953927ecbf3STim J. Robbins 		case 'S':
954927ecbf3STim J. Robbins 			flags |= LONGINT;
955927ecbf3STim J. Robbins 			/*FALLTHROUGH*/
95658f0484fSRodney W. Grimes 		case 's':
957b9aac308STim J. Robbins 			if (flags & LONGINT) {
958b9aac308STim J. Robbins 				wchar_t *wcp;
959b9aac308STim J. Robbins 
960b9aac308STim J. Robbins 				if (convbuf != NULL)
961b9aac308STim J. Robbins 					free(convbuf);
962b9aac308STim J. Robbins 				if ((wcp = GETARG(wchar_t *)) == NULL)
963b9aac308STim J. Robbins 					cp = "(null)";
964b9aac308STim J. Robbins 				else {
965b9aac308STim J. Robbins 					convbuf = __wcsconv(wcp, prec);
9666180233fSTim J. Robbins 					if (convbuf == NULL) {
9676180233fSTim J. Robbins 						fp->_flags |= __SERR;
968b9aac308STim J. Robbins 						goto error;
9696180233fSTim J. Robbins 					}
970b9aac308STim J. Robbins 					cp = convbuf;
971b9aac308STim J. Robbins 				}
972b9aac308STim J. Robbins 			} else if ((cp = GETARG(char *)) == NULL)
97358f0484fSRodney W. Grimes 				cp = "(null)";
97458f0484fSRodney W. Grimes 			if (prec >= 0) {
97558f0484fSRodney W. Grimes 				/*
97658f0484fSRodney W. Grimes 				 * can't use strlen; can only look for the
97758f0484fSRodney W. Grimes 				 * NUL in the first `prec' characters, and
97858f0484fSRodney W. Grimes 				 * strlen() will go further.
97958f0484fSRodney W. Grimes 				 */
980ce51cf03SJames Raynard 				char *p = memchr(cp, 0, (size_t)prec);
98158f0484fSRodney W. Grimes 
98258f0484fSRodney W. Grimes 				if (p != NULL) {
98358f0484fSRodney W. Grimes 					size = p - cp;
98458f0484fSRodney W. Grimes 					if (size > prec)
98558f0484fSRodney W. Grimes 						size = prec;
98658f0484fSRodney W. Grimes 				} else
98758f0484fSRodney W. Grimes 					size = prec;
98858f0484fSRodney W. Grimes 			} else
98958f0484fSRodney W. Grimes 				size = strlen(cp);
99058f0484fSRodney W. Grimes 			sign = '\0';
99158f0484fSRodney W. Grimes 			break;
99258f0484fSRodney W. Grimes 		case 'U':
99358f0484fSRodney W. Grimes 			flags |= LONGINT;
99458f0484fSRodney W. Grimes 			/*FALLTHROUGH*/
99558f0484fSRodney W. Grimes 		case 'u':
9967735bb0fSBill Fenner 			if (flags & INTMAX_SIZE)
9977735bb0fSBill Fenner 				ujval = UJARG();
99858f0484fSRodney W. Grimes 			else
99958f0484fSRodney W. Grimes 				ulval = UARG();
100058f0484fSRodney W. Grimes 			base = 10;
100158f0484fSRodney W. Grimes 			goto nosign;
100258f0484fSRodney W. Grimes 		case 'X':
1003ebbad5ecSDavid Schultz 			xdigs = xdigs_upper;
100458f0484fSRodney W. Grimes 			goto hex;
100558f0484fSRodney W. Grimes 		case 'x':
1006ebbad5ecSDavid Schultz 			xdigs = xdigs_lower;
10077735bb0fSBill Fenner hex:
10087735bb0fSBill Fenner 			if (flags & INTMAX_SIZE)
10097735bb0fSBill Fenner 				ujval = UJARG();
101058f0484fSRodney W. Grimes 			else
101158f0484fSRodney W. Grimes 				ulval = UARG();
101258f0484fSRodney W. Grimes 			base = 16;
101358f0484fSRodney W. Grimes 			/* leading 0x/X only if non-zero */
101458f0484fSRodney W. Grimes 			if (flags & ALT &&
10157735bb0fSBill Fenner 			    (flags & INTMAX_SIZE ? ujval != 0 : ulval != 0))
1016ebbad5ecSDavid Schultz 				ox[1] = ch;
101758f0484fSRodney W. Grimes 
101898ee7635SAlexey Zelkin 			flags &= ~GROUPING;
101958f0484fSRodney W. Grimes 			/* unsigned conversions */
102058f0484fSRodney W. Grimes nosign:			sign = '\0';
10212e394b2fSAlexey Zelkin 			/*-
102258f0484fSRodney W. Grimes 			 * ``... diouXx conversions ... if a precision is
102358f0484fSRodney W. Grimes 			 * specified, the 0 flag will be ignored.''
102458f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
102558f0484fSRodney W. Grimes 			 */
102658f0484fSRodney W. Grimes number:			if ((dprec = prec) >= 0)
102758f0484fSRodney W. Grimes 				flags &= ~ZEROPAD;
102858f0484fSRodney W. Grimes 
10292e394b2fSAlexey Zelkin 			/*-
103058f0484fSRodney W. Grimes 			 * ``The result of converting a zero value with an
103158f0484fSRodney W. Grimes 			 * explicit precision of zero is no characters.''
103258f0484fSRodney W. Grimes 			 *	-- ANSI X3J11
10331be5319aSDavid Schultz 			 *
10341be5319aSDavid Schultz 			 * ``The C Standard is clear enough as is.  The call
10351be5319aSDavid Schultz 			 * printf("%#.0o", 0) should print 0.''
10361be5319aSDavid Schultz 			 *	-- Defect Report #151
103758f0484fSRodney W. Grimes 			 */
103858f0484fSRodney W. Grimes 			cp = buf + BUF;
10397735bb0fSBill Fenner 			if (flags & INTMAX_SIZE) {
10401be5319aSDavid Schultz 				if (ujval != 0 || prec != 0 ||
10411be5319aSDavid Schultz 				    (flags & ALT && base == 8))
10427735bb0fSBill Fenner 					cp = __ujtoa(ujval, cp, base,
104398ee7635SAlexey Zelkin 					    flags & ALT, xdigs,
104498ee7635SAlexey Zelkin 					    flags & GROUPING, thousands_sep,
104598ee7635SAlexey Zelkin 					    grouping);
104658f0484fSRodney W. Grimes 			} else {
10471be5319aSDavid Schultz 				if (ulval != 0 || prec != 0 ||
10481be5319aSDavid Schultz 				    (flags & ALT && base == 8))
104958f0484fSRodney W. Grimes 					cp = __ultoa(ulval, cp, base,
105098ee7635SAlexey Zelkin 					    flags & ALT, xdigs,
105198ee7635SAlexey Zelkin 					    flags & GROUPING, thousands_sep,
105298ee7635SAlexey Zelkin 					    grouping);
105358f0484fSRodney W. Grimes 			}
105458f0484fSRodney W. Grimes 			size = buf + BUF - cp;
105538cac8f8SDavid Schultz 			if (size > BUF)	/* should never happen */
105638cac8f8SDavid Schultz 				abort();
105758f0484fSRodney W. Grimes 			break;
105858f0484fSRodney W. Grimes 		default:	/* "%?" prints ?, unless ? is NUL */
105958f0484fSRodney W. Grimes 			if (ch == '\0')
106058f0484fSRodney W. Grimes 				goto done;
106158f0484fSRodney W. Grimes 			/* pretend it was %c with argument ch */
106258f0484fSRodney W. Grimes 			cp = buf;
106358f0484fSRodney W. Grimes 			*cp = ch;
106458f0484fSRodney W. Grimes 			size = 1;
106558f0484fSRodney W. Grimes 			sign = '\0';
106658f0484fSRodney W. Grimes 			break;
106758f0484fSRodney W. Grimes 		}
106858f0484fSRodney W. Grimes 
106958f0484fSRodney W. Grimes 		/*
107058f0484fSRodney W. Grimes 		 * All reasonable formats wind up here.  At this point, `cp'
107158f0484fSRodney W. Grimes 		 * points to a string which (if not flags&LADJUST) should be
107258f0484fSRodney W. Grimes 		 * padded out to `width' places.  If flags&ZEROPAD, it should
107358f0484fSRodney W. Grimes 		 * first be prefixed by any sign or other prefix; otherwise,
107458f0484fSRodney W. Grimes 		 * it should be blank padded before the prefix is emitted.
107558f0484fSRodney W. Grimes 		 * After any left-hand padding and prefixing, emit zeroes
107658f0484fSRodney W. Grimes 		 * required by a decimal [diouxX] precision, then print the
107758f0484fSRodney W. Grimes 		 * string proper, then emit zeroes required by any leftover
107858f0484fSRodney W. Grimes 		 * floating precision; finally, if LADJUST, pad with blanks.
107958f0484fSRodney W. Grimes 		 *
108058f0484fSRodney W. Grimes 		 * Compute actual size, so we know how much to pad.
1081261a532aSBill Fenner 		 * size excludes decimal prec; realsz includes it.
108258f0484fSRodney W. Grimes 		 */
1083261a532aSBill Fenner 		realsz = dprec > size ? dprec : size;
108458f0484fSRodney W. Grimes 		if (sign)
1085261a532aSBill Fenner 			realsz++;
1086904322a5SDavid Schultz 		if (ox[1])
1087261a532aSBill Fenner 			realsz += 2;
108858f0484fSRodney W. Grimes 
108992e88f87SAndrey A. Chernov 		prsize = width > realsz ? width : realsz;
1090b250f248SAndrey A. Chernov 		if ((unsigned)ret + prsize > INT_MAX) {
109192e88f87SAndrey A. Chernov 			ret = EOF;
109292e88f87SAndrey A. Chernov 			goto error;
109392e88f87SAndrey A. Chernov 		}
109492e88f87SAndrey A. Chernov 
109558f0484fSRodney W. Grimes 		/* right-adjusting blank padding */
109658f0484fSRodney W. Grimes 		if ((flags & (LADJUST|ZEROPAD)) == 0)
109758f0484fSRodney W. Grimes 			PAD(width - realsz, blanks);
109858f0484fSRodney W. Grimes 
109958f0484fSRodney W. Grimes 		/* prefix */
1100904322a5SDavid Schultz 		if (sign)
110158f0484fSRodney W. Grimes 			PRINT(&sign, 1);
1102904322a5SDavid Schultz 
1103904322a5SDavid Schultz 		if (ox[1]) {	/* ox[1] is either x, X, or \0 */
110458f0484fSRodney W. Grimes 			ox[0] = '0';
110558f0484fSRodney W. Grimes 			PRINT(ox, 2);
110658f0484fSRodney W. Grimes 		}
110758f0484fSRodney W. Grimes 
110858f0484fSRodney W. Grimes 		/* right-adjusting zero padding */
110958f0484fSRodney W. Grimes 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
111058f0484fSRodney W. Grimes 			PAD(width - realsz, zeroes);
111158f0484fSRodney W. Grimes 
111258f0484fSRodney W. Grimes 		/* leading zeroes from decimal precision */
1113261a532aSBill Fenner 		PAD(dprec - size, zeroes);
111458f0484fSRodney W. Grimes 
111558f0484fSRodney W. Grimes 		/* the string or number proper */
11168de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
111758f0484fSRodney W. Grimes 		if ((flags & FPT) == 0) {
111858f0484fSRodney W. Grimes 			PRINT(cp, size);
111958f0484fSRodney W. Grimes 		} else {	/* glue together f_p fragments */
1120ebbad5ecSDavid Schultz 			if (!expchar) {	/* %[fF] or sufficiently short %[gG] */
1121ebbad5ecSDavid Schultz 				if (expt <= 0) {
112281ae2e9aSDavid Schultz 					PRINT(zeroes, 1);
112381ae2e9aSDavid Schultz 					if (prec || flags & ALT)
112481ae2e9aSDavid Schultz 						PRINT(decimal_point, 1);
112558f0484fSRodney W. Grimes 					PAD(-expt, zeroes);
11263b204b7dSDavid Schultz 					/* already handled initial 0's */
11273b204b7dSDavid Schultz 					prec += expt;
112858f0484fSRodney W. Grimes 				} else {
11293b204b7dSDavid Schultz 					PRINTANDPAD(cp, dtoaend, lead, zeroes);
1130ebbad5ecSDavid Schultz 					cp += lead;
1131ebbad5ecSDavid Schultz 					if (grouping) {
1132ebbad5ecSDavid Schultz 						while (nseps>0 || nrepeats>0) {
1133ebbad5ecSDavid Schultz 							if (nrepeats > 0)
1134ebbad5ecSDavid Schultz 								nrepeats--;
1135ebbad5ecSDavid Schultz 							else {
1136ebbad5ecSDavid Schultz 								grouping--;
1137ebbad5ecSDavid Schultz 								nseps--;
113858f0484fSRodney W. Grimes 							}
1139ebbad5ecSDavid Schultz 							PRINT(&thousands_sep,
1140ebbad5ecSDavid Schultz 							    1);
11413b204b7dSDavid Schultz 							PRINTANDPAD(cp,dtoaend,
11423b204b7dSDavid Schultz 							    *grouping, zeroes);
1143ebbad5ecSDavid Schultz 							cp += *grouping;
1144ebbad5ecSDavid Schultz 						}
11453b204b7dSDavid Schultz 						if (cp > dtoaend)
11463b204b7dSDavid Schultz 							cp = dtoaend;
1147ebbad5ecSDavid Schultz 					}
1148ebbad5ecSDavid Schultz 					if (prec || flags & ALT)
1149ebbad5ecSDavid Schultz 						PRINT(decimal_point,1);
1150ebbad5ecSDavid Schultz 				}
11513b204b7dSDavid Schultz 				PRINTANDPAD(cp, dtoaend, prec, zeroes);
1152ebbad5ecSDavid Schultz 			} else {	/* %[eE] or sufficiently long %[gG] */
11533b204b7dSDavid Schultz 				if (prec > 1 || flags & ALT) {
1154ebbad5ecSDavid Schultz 					buf[0] = *cp++;
1155ebbad5ecSDavid Schultz 					buf[1] = *decimal_point;
1156ebbad5ecSDavid Schultz 					PRINT(buf, 2);
115758f0484fSRodney W. Grimes 					PRINT(cp, ndig-1);
1158ebbad5ecSDavid Schultz 					PAD(prec - ndig, zeroes);
115958f0484fSRodney W. Grimes 				} else	/* XeYYY */
116058f0484fSRodney W. Grimes 					PRINT(cp, 1);
116158f0484fSRodney W. Grimes 				PRINT(expstr, expsize);
116258f0484fSRodney W. Grimes 			}
116358f0484fSRodney W. Grimes 		}
116458f0484fSRodney W. Grimes #else
116558f0484fSRodney W. Grimes 		PRINT(cp, size);
116658f0484fSRodney W. Grimes #endif
116758f0484fSRodney W. Grimes 		/* left-adjusting padding (always blank) */
116858f0484fSRodney W. Grimes 		if (flags & LADJUST)
116958f0484fSRodney W. Grimes 			PAD(width - realsz, blanks);
117058f0484fSRodney W. Grimes 
117158f0484fSRodney W. Grimes 		/* finally, adjust ret */
117292e88f87SAndrey A. Chernov 		ret += prsize;
117358f0484fSRodney W. Grimes 
117458f0484fSRodney W. Grimes 		FLUSH();	/* copy out the I/O vectors */
117558f0484fSRodney W. Grimes 	}
117658f0484fSRodney W. Grimes done:
117758f0484fSRodney W. Grimes 	FLUSH();
117858f0484fSRodney W. Grimes error:
1179096ad104SDag-Erling Smørgrav 	va_end(orgap);
11808de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
11812ffc61baSTor Egge 	if (dtoaresult != NULL)
1182ebbad5ecSDavid Schultz 		freedtoa(dtoaresult);
11832ffc61baSTor Egge #endif
1184b9aac308STim J. Robbins 	if (convbuf != NULL)
1185b9aac308STim J. Robbins 		free(convbuf);
1186f70177e7SJulian Elischer 	if (__sferror(fp))
1187f70177e7SJulian Elischer 		ret = EOF;
1188efb7e53dSJordan K. Hubbard 	if ((argtable != NULL) && (argtable != statargtable))
1189efb7e53dSJordan K. Hubbard 		free (argtable);
1190f70177e7SJulian Elischer 	return (ret);
119158f0484fSRodney W. Grimes 	/* NOTREACHED */
119258f0484fSRodney W. Grimes }
119358f0484fSRodney W. Grimes 
1194efb7e53dSJordan K. Hubbard 
11958de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
119658f0484fSRodney W. Grimes 
119758f0484fSRodney W. Grimes static int
1198d201fe46SDaniel Eischen exponent(char *p0, int exp, int fmtch)
119958f0484fSRodney W. Grimes {
1200d201fe46SDaniel Eischen 	char *p, *t;
120138cac8f8SDavid Schultz 	char expbuf[MAXEXPDIG];
120258f0484fSRodney W. Grimes 
120358f0484fSRodney W. Grimes 	p = p0;
120458f0484fSRodney W. Grimes 	*p++ = fmtch;
120558f0484fSRodney W. Grimes 	if (exp < 0) {
120658f0484fSRodney W. Grimes 		exp = -exp;
120758f0484fSRodney W. Grimes 		*p++ = '-';
120858f0484fSRodney W. Grimes 	}
120958f0484fSRodney W. Grimes 	else
121058f0484fSRodney W. Grimes 		*p++ = '+';
121138cac8f8SDavid Schultz 	t = expbuf + MAXEXPDIG;
121258f0484fSRodney W. Grimes 	if (exp > 9) {
121358f0484fSRodney W. Grimes 		do {
121458f0484fSRodney W. Grimes 			*--t = to_char(exp % 10);
121558f0484fSRodney W. Grimes 		} while ((exp /= 10) > 9);
121658f0484fSRodney W. Grimes 		*--t = to_char(exp);
121738cac8f8SDavid Schultz 		for (; t < expbuf + MAXEXPDIG; *p++ = *t++);
121858f0484fSRodney W. Grimes 	}
121958f0484fSRodney W. Grimes 	else {
1220ebbad5ecSDavid Schultz 		/*
1221ebbad5ecSDavid Schultz 		 * Exponents for decimal floating point conversions
1222ebbad5ecSDavid Schultz 		 * (%[eEgG]) must be at least two characters long,
1223ebbad5ecSDavid Schultz 		 * whereas exponents for hexadecimal conversions can
1224ebbad5ecSDavid Schultz 		 * be only one character long.
1225ebbad5ecSDavid Schultz 		 */
1226ebbad5ecSDavid Schultz 		if (fmtch == 'e' || fmtch == 'E')
122758f0484fSRodney W. Grimes 			*p++ = '0';
122858f0484fSRodney W. Grimes 		*p++ = to_char(exp);
122958f0484fSRodney W. Grimes 	}
123058f0484fSRodney W. Grimes 	return (p - p0);
123158f0484fSRodney W. Grimes }
12328de9e897SDavid Schultz #endif /* !NO_FLOATING_POINT */
1233