xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 61180)
146076Sbostic /*-
2*61180Sbostic  * Copyright (c) 1990, 1993
3*61180Sbostic  *	The Regents of the University of California.  All rights reserved.
434226Sbostic  *
546076Sbostic  * This code is derived from software contributed to Berkeley by
646076Sbostic  * Chris Torek.
746076Sbostic  *
842631Sbostic  * %sccs.include.redist.c%
934226Sbostic  */
1034226Sbostic 
1134233Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*61180Sbostic static char sccsid[] = "@(#)vfprintf.c	8.1 (Berkeley) 06/04/93";
1334233Sbostic #endif /* LIBC_SCCS and not lint */
1434233Sbostic 
1546076Sbostic /*
1646076Sbostic  * Actual printf innards.
1746076Sbostic  *
1846076Sbostic  * This code is large and complicated...
1946076Sbostic  */
2046076Sbostic 
2134261Sbostic #include <sys/types.h>
2257154Sbostic 
2361032Sbostic #include <limits.h>
2446076Sbostic #include <stdio.h>
2557154Sbostic #include <stdlib.h>
2646076Sbostic #include <string.h>
2757154Sbostic 
2846076Sbostic #if __STDC__
2946076Sbostic #include <stdarg.h>
3046076Sbostic #else
3134233Sbostic #include <varargs.h>
3246076Sbostic #endif
3357154Sbostic 
3446076Sbostic #include "local.h"
3546076Sbostic #include "fvwrite.h"
3634226Sbostic 
3750438Sbostic /* Define FLOATING_POINT to get floating point. */
3846076Sbostic #define	FLOATING_POINT
3934226Sbostic 
4046076Sbostic /*
4146076Sbostic  * Flush out all the vectors defined by the given uio,
4246076Sbostic  * then reset it so that it can be reused.
4346076Sbostic  */
4446180Storek static int
__sprint(fp,uio)4546076Sbostic __sprint(fp, uio)
4646076Sbostic 	FILE *fp;
4746076Sbostic 	register struct __suio *uio;
4846076Sbostic {
4946076Sbostic 	register int err;
5046076Sbostic 
5146076Sbostic 	if (uio->uio_resid == 0) {
5246076Sbostic 		uio->uio_iovcnt = 0;
5346076Sbostic 		return (0);
5446076Sbostic 	}
5546076Sbostic 	err = __sfvwrite(fp, uio);
5646076Sbostic 	uio->uio_resid = 0;
5746076Sbostic 	uio->uio_iovcnt = 0;
5846076Sbostic 	return (err);
5946076Sbostic }
6046076Sbostic 
6146076Sbostic /*
6246076Sbostic  * Helper function for `fprintf to unbuffered unix file': creates a
6346076Sbostic  * temporary buffer.  We only work on write-only files; this avoids
6446076Sbostic  * worries about ungetc buffers and so forth.
6546076Sbostic  */
6646180Storek static int
__sbprintf(fp,fmt,ap)6746076Sbostic __sbprintf(fp, fmt, ap)
6846076Sbostic 	register FILE *fp;
6946180Storek 	const char *fmt;
7046076Sbostic 	va_list ap;
7146076Sbostic {
7246076Sbostic 	int ret;
7346076Sbostic 	FILE fake;
7446076Sbostic 	unsigned char buf[BUFSIZ];
7546076Sbostic 
7646076Sbostic 	/* copy the important variables */
7746076Sbostic 	fake._flags = fp->_flags & ~__SNBF;
7846076Sbostic 	fake._file = fp->_file;
7946076Sbostic 	fake._cookie = fp->_cookie;
8046076Sbostic 	fake._write = fp->_write;
8146076Sbostic 
8246076Sbostic 	/* set up the buffer */
8346076Sbostic 	fake._bf._base = fake._p = buf;
8446076Sbostic 	fake._bf._size = fake._w = sizeof(buf);
8546076Sbostic 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
8646076Sbostic 
8746076Sbostic 	/* do the work, then copy any error status */
8846076Sbostic 	ret = vfprintf(&fake, fmt, ap);
8946076Sbostic 	if (ret >= 0 && fflush(&fake))
9046076Sbostic 		ret = EOF;
9146076Sbostic 	if (fake._flags & __SERR)
9246076Sbostic 		fp->_flags |= __SERR;
9346076Sbostic 	return (ret);
9446076Sbostic }
9546076Sbostic 
9661032Sbostic /*
9761032Sbostic  * Macros for converting digits to letters and vice versa
9861032Sbostic  */
9961032Sbostic #define	to_digit(c)	((c) - '0')
10061032Sbostic #define is_digit(c)	((unsigned)to_digit(c) <= 9)
10161032Sbostic #define	to_char(n)	((n) + '0')
10246076Sbostic 
10361032Sbostic /*
10461032Sbostic  * Convert an unsigned long to ASCII for printf purposes, returning
10561032Sbostic  * a pointer to the first character of the string representation.
10661032Sbostic  * Octal numbers can be forced to have a leading zero; hex numbers
10761032Sbostic  * use the given digits.
10861032Sbostic  */
10961032Sbostic static char *
__ultoa(val,endp,base,octzero,xdigs)11061032Sbostic __ultoa(val, endp, base, octzero, xdigs)
11161032Sbostic 	register u_long val;
11261032Sbostic 	char *endp;
11361032Sbostic 	int base, octzero;
11461032Sbostic 	char *xdigs;
11561032Sbostic {
11661032Sbostic 	register char *cp = endp;
11761032Sbostic 	register long sval;
11861032Sbostic 
11961032Sbostic 	/*
12061032Sbostic 	 * Handle the three cases separately, in the hope of getting
12161032Sbostic 	 * better/faster code.
12261032Sbostic 	 */
12361032Sbostic 	switch (base) {
12461032Sbostic 	case 10:
12561032Sbostic 		if (val < 10) {	/* many numbers are 1 digit */
12661032Sbostic 			*--cp = to_char(val);
12761032Sbostic 			return (cp);
12861032Sbostic 		}
12961032Sbostic 		/*
13061032Sbostic 		 * On many machines, unsigned arithmetic is harder than
13161032Sbostic 		 * signed arithmetic, so we do at most one unsigned mod and
13261032Sbostic 		 * divide; this is sufficient to reduce the range of
13361032Sbostic 		 * the incoming value to where signed arithmetic works.
13461032Sbostic 		 */
13561032Sbostic 		if (val > LONG_MAX) {
13661032Sbostic 			*--cp = to_char(val % 10);
13761032Sbostic 			sval = val / 10;
13861032Sbostic 		} else
13961032Sbostic 			sval = val;
14061032Sbostic 		do {
14161032Sbostic 			*--cp = to_char(sval % 10);
14261032Sbostic 			sval /= 10;
14361032Sbostic 		} while (sval != 0);
14461032Sbostic 		break;
14561032Sbostic 
14661032Sbostic 	case 8:
14761032Sbostic 		do {
14861032Sbostic 			*--cp = to_char(val & 7);
14961032Sbostic 			val >>= 3;
15061032Sbostic 		} while (val);
15161032Sbostic 		if (octzero && *cp != '0')
15261032Sbostic 			*--cp = '0';
15361032Sbostic 		break;
15461032Sbostic 
15561032Sbostic 	case 16:
15661032Sbostic 		do {
15761032Sbostic 			*--cp = xdigs[val & 15];
15861032Sbostic 			val >>= 4;
15961032Sbostic 		} while (val);
16061032Sbostic 		break;
16161032Sbostic 
16261032Sbostic 	default:			/* oops */
16361032Sbostic 		abort();
16461032Sbostic 	}
16561032Sbostic 	return (cp);
16661032Sbostic }
16761032Sbostic 
16861032Sbostic /* Identical to __ultoa, but for quads. */
16961032Sbostic static char *
__uqtoa(val,endp,base,octzero,xdigs)17061032Sbostic __uqtoa(val, endp, base, octzero, xdigs)
17161032Sbostic 	register u_quad_t val;
17261032Sbostic 	char *endp;
17361032Sbostic 	int base, octzero;
17461032Sbostic 	char *xdigs;
17561032Sbostic {
17661032Sbostic 	register char *cp = endp;
17761032Sbostic 	register quad_t sval;
17861032Sbostic 
17961032Sbostic 	/* quick test for small values; __ultoa is typically much faster */
18061032Sbostic 	/* (perhaps instead we should run until small, then call __ultoa?) */
18161032Sbostic 	if (val <= ULONG_MAX)
18261032Sbostic 		return (__ultoa((u_long)val, endp, base, octzero, xdigs));
18361032Sbostic 	switch (base) {
18461032Sbostic 	case 10:
18561032Sbostic 		if (val < 10) {
18661032Sbostic 			*--cp = to_char(val % 10);
18761032Sbostic 			return (cp);
18861032Sbostic 		}
18961032Sbostic 		if (val > QUAD_MAX) {
19061032Sbostic 			*--cp = to_char(val % 10);
19161032Sbostic 			sval = val / 10;
19261032Sbostic 		} else
19361032Sbostic 			sval = val;
19461032Sbostic 		do {
19561032Sbostic 			*--cp = to_char(sval % 10);
19661032Sbostic 			sval /= 10;
19761032Sbostic 		} while (sval != 0);
19861032Sbostic 		break;
19961032Sbostic 
20061032Sbostic 	case 8:
20161032Sbostic 		do {
20261032Sbostic 			*--cp = to_char(val & 7);
20361032Sbostic 			val >>= 3;
20461032Sbostic 		} while (val);
20561032Sbostic 		if (octzero && *cp != '0')
20661032Sbostic 			*--cp = '0';
20761032Sbostic 		break;
20861032Sbostic 
20961032Sbostic 	case 16:
21061032Sbostic 		do {
21161032Sbostic 			*--cp = xdigs[val & 15];
21261032Sbostic 			val >>= 4;
21361032Sbostic 		} while (val);
21461032Sbostic 		break;
21561032Sbostic 
21661032Sbostic 	default:
21761032Sbostic 		abort();
21861032Sbostic 	}
21961032Sbostic 	return (cp);
22061032Sbostic }
22161032Sbostic 
22246076Sbostic #ifdef FLOATING_POINT
22357154Sbostic #include <math.h>
22446180Storek #include "floatio.h"
22546076Sbostic 
22634328Sbostic #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
22746076Sbostic #define	DEFPREC		6
22834328Sbostic 
22957154Sbostic static char *cvt __P((double, int, int, char *, int *, int, int *));
23057154Sbostic static int exponent __P((char *, int, int));
23134236Sbostic 
23246076Sbostic #else /* no FLOATING_POINT */
23334235Sbostic 
23461032Sbostic #define	BUF		68
23534331Sbostic 
23646076Sbostic #endif /* FLOATING_POINT */
23734318Sbostic 
23846076Sbostic 
23946076Sbostic /*
24046076Sbostic  * Flags used during conversion.
24146076Sbostic  */
24253810Sbostic #define	ALT		0x001		/* alternate form */
24353810Sbostic #define	HEXPREFIX	0x002		/* add 0x or 0X prefix */
24453810Sbostic #define	LADJUST		0x004		/* left adjustment */
24553810Sbostic #define	LONGDBL		0x008		/* long double; unimplemented */
24653810Sbostic #define	LONGINT		0x010		/* long integer */
24753810Sbostic #define	QUADINT		0x020		/* quad integer */
24853810Sbostic #define	SHORTINT	0x040		/* short integer */
24953810Sbostic #define	ZEROPAD		0x080		/* zero (as opposed to blank) pad */
25057154Sbostic #define FPT		0x100		/* Floating point number */
25146180Storek int
vfprintf(fp,fmt0,ap)25246076Sbostic vfprintf(fp, fmt0, ap)
25346076Sbostic 	FILE *fp;
25446180Storek 	const char *fmt0;
25546076Sbostic 	va_list ap;
25634226Sbostic {
25746076Sbostic 	register char *fmt;	/* format string */
25834427Sbostic 	register int ch;	/* character from fmt */
25946076Sbostic 	register int n;		/* handy integer (short term usage) */
26046076Sbostic 	register char *cp;	/* handy char pointer (short term usage) */
26146076Sbostic 	register struct __siov *iovp;/* for PRINT macro */
26246076Sbostic 	register int flags;	/* flags as above */
26346076Sbostic 	int ret;		/* return value accumulator */
26446076Sbostic 	int width;		/* width from format (%8d), or 0 */
26546076Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
26646076Sbostic 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
26746076Sbostic #ifdef FLOATING_POINT
26846076Sbostic 	char softsign;		/* temporary negative sign for floats */
26934427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
27057154Sbostic 	int expt;		/* integer value of exponent */
27157154Sbostic 	int expsize;		/* character count for expstr */
27257154Sbostic 	int ndig;		/* actual number of digits returned by cvt */
27357154Sbostic 	char expstr[7];		/* buffer for exponent string */
27446076Sbostic #endif
27561032Sbostic 	u_long	ulval;		/* integer arguments %[diouxX] */
27661032Sbostic 	u_quad_t uqval;		/* %q integers */
27761032Sbostic 	int base;		/* base for [diouxX] conversion */
27846076Sbostic 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
27934624Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
28046076Sbostic 	int realsz;		/* field size expanded by dprec */
28134427Sbostic 	int size;		/* size of converted field or string */
28246076Sbostic 	char *xdigs;		/* digits for [xX] conversion */
28346076Sbostic #define NIOV 8
28446076Sbostic 	struct __suio uio;	/* output information: summary */
28546076Sbostic 	struct __siov iov[NIOV];/* ... and individual io vectors */
28634427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
28746076Sbostic 	char ox[2];		/* space for 0x hex-prefix */
28834226Sbostic 
28946076Sbostic 	/*
29053810Sbostic 	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
29153810Sbostic 	 * fields occur frequently, increase PADSIZE and make the initialisers
29253810Sbostic 	 * below longer.
29346076Sbostic 	 */
29446076Sbostic #define	PADSIZE	16		/* pad chunk size */
29546076Sbostic 	static char blanks[PADSIZE] =
29646076Sbostic 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
29746076Sbostic 	static char zeroes[PADSIZE] =
29846076Sbostic 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
29946076Sbostic 
30046076Sbostic 	/*
30146076Sbostic 	 * BEWARE, these `goto error' on error, and PAD uses `n'.
30246076Sbostic 	 */
30346076Sbostic #define	PRINT(ptr, len) { \
30446076Sbostic 	iovp->iov_base = (ptr); \
30546076Sbostic 	iovp->iov_len = (len); \
30646076Sbostic 	uio.uio_resid += (len); \
30746076Sbostic 	iovp++; \
30846076Sbostic 	if (++uio.uio_iovcnt >= NIOV) { \
30946076Sbostic 		if (__sprint(fp, &uio)) \
31046076Sbostic 			goto error; \
31146076Sbostic 		iovp = iov; \
31246076Sbostic 	} \
31346076Sbostic }
31446076Sbostic #define	PAD(howmany, with) { \
31546076Sbostic 	if ((n = (howmany)) > 0) { \
31646076Sbostic 		while (n > PADSIZE) { \
31746076Sbostic 			PRINT(with, PADSIZE); \
31846076Sbostic 			n -= PADSIZE; \
31946076Sbostic 		} \
32046076Sbostic 		PRINT(with, n); \
32146076Sbostic 	} \
32246076Sbostic }
32346076Sbostic #define	FLUSH() { \
32446076Sbostic 	if (uio.uio_resid && __sprint(fp, &uio)) \
32546076Sbostic 		goto error; \
32646076Sbostic 	uio.uio_iovcnt = 0; \
32746076Sbostic 	iovp = iov; \
32846076Sbostic }
32946076Sbostic 
33046076Sbostic 	/*
33146076Sbostic 	 * To extend shorts properly, we need both signed and unsigned
33246076Sbostic 	 * argument extraction methods.
33346076Sbostic 	 */
33446076Sbostic #define	SARG() \
33561032Sbostic 	(flags&LONGINT ? va_arg(ap, long) : \
33646076Sbostic 	    flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
33746076Sbostic 	    (long)va_arg(ap, int))
33846076Sbostic #define	UARG() \
33961032Sbostic 	(flags&LONGINT ? va_arg(ap, u_long) : \
34046076Sbostic 	    flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
34146076Sbostic 	    (u_long)va_arg(ap, u_int))
34246076Sbostic 
34346076Sbostic 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
34446076Sbostic 	if (cantwrite(fp))
34534428Sbostic 		return (EOF);
34634428Sbostic 
34746076Sbostic 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
34846076Sbostic 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
34946076Sbostic 	    fp->_file >= 0)
35046076Sbostic 		return (__sbprintf(fp, fmt0, ap));
35146076Sbostic 
35246076Sbostic 	fmt = (char *)fmt0;
35346076Sbostic 	uio.uio_iov = iovp = iov;
35446076Sbostic 	uio.uio_resid = 0;
35546076Sbostic 	uio.uio_iovcnt = 0;
35646076Sbostic 	ret = 0;
35746076Sbostic 
35846076Sbostic 	/*
35946076Sbostic 	 * Scan the format for conversions (`%' character).
36046076Sbostic 	 */
36146076Sbostic 	for (;;) {
36246076Sbostic 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
36346076Sbostic 			/* void */;
36446076Sbostic 		if ((n = fmt - cp) != 0) {
36546076Sbostic 			PRINT(cp, n);
36646076Sbostic 			ret += n;
36746076Sbostic 		}
36846076Sbostic 		if (ch == '\0')
36946076Sbostic 			goto done;
37046076Sbostic 		fmt++;		/* skip over '%' */
37146076Sbostic 
37246076Sbostic 		flags = 0;
37346076Sbostic 		dprec = 0;
37446076Sbostic 		width = 0;
37534233Sbostic 		prec = -1;
37634318Sbostic 		sign = '\0';
37734226Sbostic 
37846076Sbostic rflag:		ch = *fmt++;
37946076Sbostic reswitch:	switch (ch) {
38034318Sbostic 		case ' ':
38134669Sbostic 			/*
38234669Sbostic 			 * ``If the space and + flags both appear, the space
38334669Sbostic 			 * flag will be ignored.''
38434669Sbostic 			 *	-- ANSI X3J11
38534669Sbostic 			 */
38634669Sbostic 			if (!sign)
38734669Sbostic 				sign = ' ';
38834318Sbostic 			goto rflag;
38934233Sbostic 		case '#':
39034318Sbostic 			flags |= ALT;
39134318Sbostic 			goto rflag;
39234233Sbostic 		case '*':
39334235Sbostic 			/*
39434235Sbostic 			 * ``A negative field width argument is taken as a
39546076Sbostic 			 * - flag followed by a positive field width.''
39634235Sbostic 			 *	-- ANSI X3J11
39734235Sbostic 			 * They don't exclude field widths read from args.
39834235Sbostic 			 */
39946076Sbostic 			if ((width = va_arg(ap, int)) >= 0)
40034318Sbostic 				goto rflag;
40134235Sbostic 			width = -width;
40234427Sbostic 			/* FALLTHROUGH */
40334235Sbostic 		case '-':
40434318Sbostic 			flags |= LADJUST;
40534318Sbostic 			goto rflag;
40634233Sbostic 		case '+':
40734314Sbostic 			sign = '+';
40834318Sbostic 			goto rflag;
40934233Sbostic 		case '.':
41046076Sbostic 			if ((ch = *fmt++) == '*') {
41146076Sbostic 				n = va_arg(ap, int);
41246076Sbostic 				prec = n < 0 ? -1 : n;
41346076Sbostic 				goto rflag;
41434226Sbostic 			}
41546076Sbostic 			n = 0;
41646076Sbostic 			while (is_digit(ch)) {
41746076Sbostic 				n = 10 * n + to_digit(ch);
41846076Sbostic 				ch = *fmt++;
41946076Sbostic 			}
42034318Sbostic 			prec = n < 0 ? -1 : n;
42146076Sbostic 			goto reswitch;
42234233Sbostic 		case '0':
42334427Sbostic 			/*
42434427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
42534427Sbostic 			 * beginning of a field width.''
42634427Sbostic 			 *	-- ANSI X3J11
42734427Sbostic 			 */
42834427Sbostic 			flags |= ZEROPAD;
42934427Sbostic 			goto rflag;
43034233Sbostic 		case '1': case '2': case '3': case '4':
43134233Sbostic 		case '5': case '6': case '7': case '8': case '9':
43234318Sbostic 			n = 0;
43334233Sbostic 			do {
43446076Sbostic 				n = 10 * n + to_digit(ch);
43546076Sbostic 				ch = *fmt++;
43646076Sbostic 			} while (is_digit(ch));
43734318Sbostic 			width = n;
43846076Sbostic 			goto reswitch;
43946076Sbostic #ifdef FLOATING_POINT
44034235Sbostic 		case 'L':
44134329Sbostic 			flags |= LONGDBL;
44234318Sbostic 			goto rflag;
44346076Sbostic #endif
44434235Sbostic 		case 'h':
44534318Sbostic 			flags |= SHORTINT;
44634318Sbostic 			goto rflag;
44734233Sbostic 		case 'l':
44834318Sbostic 			flags |= LONGINT;
44934318Sbostic 			goto rflag;
45053810Sbostic 		case 'q':
45153810Sbostic 			flags |= QUADINT;
45253810Sbostic 			goto rflag;
45334314Sbostic 		case 'c':
45446076Sbostic 			*(cp = buf) = va_arg(ap, int);
45534314Sbostic 			size = 1;
45634427Sbostic 			sign = '\0';
45746076Sbostic 			break;
45834624Sbostic 		case 'D':
45934624Sbostic 			flags |= LONGINT;
46034624Sbostic 			/*FALLTHROUGH*/
46134314Sbostic 		case 'd':
46234318Sbostic 		case 'i':
46361032Sbostic 			if (flags & QUADINT) {
46461032Sbostic 				uqval = va_arg(ap, quad_t);
46561032Sbostic 				if ((quad_t)uqval < 0) {
46661032Sbostic 					uqval = -uqval;
46761032Sbostic 					sign = '-';
46861032Sbostic 				}
46961032Sbostic 			} else {
47061032Sbostic 				ulval = SARG();
47161032Sbostic 				if ((long)ulval < 0) {
47261032Sbostic 					ulval = -ulval;
47361032Sbostic 					sign = '-';
47461032Sbostic 				}
47534241Sbostic 			}
47661032Sbostic 			base = 10;
47734327Sbostic 			goto number;
47846076Sbostic #ifdef FLOATING_POINT
47957154Sbostic 		case 'e':		/* anomalous precision */
48034236Sbostic 		case 'E':
48157154Sbostic 			prec = (prec == -1) ?
48257154Sbostic 				DEFPREC + 1 : prec + 1;
48357154Sbostic 			/* FALLTHROUGH */
48457154Sbostic 			goto fp_begin;
48557154Sbostic 		case 'f':		/* always print trailing zeroes */
48657154Sbostic 			if (prec != 0)
48757154Sbostic 				flags |= ALT;
48834261Sbostic 		case 'g':
48934243Sbostic 		case 'G':
49057154Sbostic 			if (prec == -1)
49157154Sbostic 				prec = DEFPREC;
49257154Sbostic fp_begin:		_double = va_arg(ap, double);
49346126Storek 			/* do this before tricky precision changes */
49447607Sbostic 			if (isinf(_double)) {
49547607Sbostic 				if (_double < 0)
49647607Sbostic 					sign = '-';
49747607Sbostic 				cp = "Inf";
49847607Sbostic 				size = 3;
49946126Storek 				break;
50046126Storek 			}
50147607Sbostic 			if (isnan(_double)) {
50247607Sbostic 				cp = "NaN";
50347607Sbostic 				size = 3;
50447607Sbostic 				break;
50547607Sbostic 			}
50657154Sbostic 			flags |= FPT;
50757154Sbostic 			cp = cvt(_double, prec, flags, &softsign,
50857154Sbostic 				&expt, ch, &ndig);
50957154Sbostic 			if (ch == 'g' || ch == 'G') {
51057154Sbostic 				if (expt <= -4 || expt > prec)
51157154Sbostic 					ch = (ch == 'g') ? 'e' : 'E';
51257154Sbostic 				else
51357154Sbostic 					ch = 'g';
51457154Sbostic 			}
51557154Sbostic 			if (ch <= 'e') {	/* 'e' or 'E' fmt */
51657154Sbostic 				--expt;
51757154Sbostic 				expsize = exponent(expstr, expt, ch);
51857154Sbostic 				size = expsize + ndig;
51957154Sbostic 				if (ndig > 1 || flags & ALT)
52057154Sbostic 					++size;
52157154Sbostic 			} else if (ch == 'f') {		/* f fmt */
52257154Sbostic 				if (expt > 0) {
52357154Sbostic 					size = expt;
52457154Sbostic 					if (prec || flags & ALT)
52557154Sbostic 						size += prec + 1;
52657154Sbostic 				} else	/* "0.X" */
52757154Sbostic 					size = prec + 2;
52857154Sbostic 			} else if (expt >= ndig) {	/* fixed g fmt */
52957154Sbostic 				size = expt;
53057154Sbostic 				if (flags & ALT)
53157154Sbostic 					++size;
53257154Sbostic 			} else
53357154Sbostic 				size = ndig + (expt > 0 ?
53457154Sbostic 					1 : 2 - expt);
53557154Sbostic 
53634669Sbostic 			if (softsign)
53734669Sbostic 				sign = '-';
53846076Sbostic 			break;
53946076Sbostic #endif /* FLOATING_POINT */
54034235Sbostic 		case 'n':
54153810Sbostic 			if (flags & QUADINT)
54253810Sbostic 				*va_arg(ap, quad_t *) = ret;
54353810Sbostic 			else if (flags & LONGINT)
54446076Sbostic 				*va_arg(ap, long *) = ret;
54534427Sbostic 			else if (flags & SHORTINT)
54646076Sbostic 				*va_arg(ap, short *) = ret;
54734318Sbostic 			else
54846076Sbostic 				*va_arg(ap, int *) = ret;
54946076Sbostic 			continue;	/* no output */
55034624Sbostic 		case 'O':
55134624Sbostic 			flags |= LONGINT;
55234624Sbostic 			/*FALLTHROUGH*/
55334226Sbostic 		case 'o':
55461032Sbostic 			if (flags & QUADINT)
55561032Sbostic 				uqval = va_arg(ap, u_quad_t);
55661032Sbostic 			else
55761032Sbostic 				ulval = UARG();
55861032Sbostic 			base = 8;
55934327Sbostic 			goto nosign;
56034235Sbostic 		case 'p':
56134320Sbostic 			/*
56234321Sbostic 			 * ``The argument shall be a pointer to void.  The
56334321Sbostic 			 * value of the pointer is converted to a sequence
56434321Sbostic 			 * of printable characters, in an implementation-
56534321Sbostic 			 * defined manner.''
56634321Sbostic 			 *	-- ANSI X3J11
56734320Sbostic 			 */
56861032Sbostic 			ulval = (u_long)va_arg(ap, void *);
56961032Sbostic 			base = 16;
57046076Sbostic 			xdigs = "0123456789abcdef";
57161032Sbostic 			flags = (flags & ~QUADINT) | HEXPREFIX;
57246076Sbostic 			ch = 'x';
57334327Sbostic 			goto nosign;
57434226Sbostic 		case 's':
57546076Sbostic 			if ((cp = va_arg(ap, char *)) == NULL)
57646076Sbostic 				cp = "(null)";
57734321Sbostic 			if (prec >= 0) {
57834321Sbostic 				/*
57934321Sbostic 				 * can't use strlen; can only look for the
58034321Sbostic 				 * NUL in the first `prec' characters, and
58134321Sbostic 				 * strlen() will go further.
58234321Sbostic 				 */
58346076Sbostic 				char *p = memchr(cp, 0, prec);
58434321Sbostic 
58546076Sbostic 				if (p != NULL) {
58646076Sbostic 					size = p - cp;
58734321Sbostic 					if (size > prec)
58834321Sbostic 						size = prec;
58934427Sbostic 				} else
59034321Sbostic 					size = prec;
59134427Sbostic 			} else
59246076Sbostic 				size = strlen(cp);
59334427Sbostic 			sign = '\0';
59446076Sbostic 			break;
59534624Sbostic 		case 'U':
59634624Sbostic 			flags |= LONGINT;
59734624Sbostic 			/*FALLTHROUGH*/
59834226Sbostic 		case 'u':
59961032Sbostic 			if (flags & QUADINT)
60061032Sbostic 				uqval = va_arg(ap, u_quad_t);
60161032Sbostic 			else
60261032Sbostic 				ulval = UARG();
60361032Sbostic 			base = 10;
60434327Sbostic 			goto nosign;
60534226Sbostic 		case 'X':
60646076Sbostic 			xdigs = "0123456789ABCDEF";
60746076Sbostic 			goto hex;
60834226Sbostic 		case 'x':
60946076Sbostic 			xdigs = "0123456789abcdef";
61061032Sbostic hex:			if (flags & QUADINT)
61161032Sbostic 				uqval = va_arg(ap, u_quad_t);
61261032Sbostic 			else
61361032Sbostic 				ulval = UARG();
61461032Sbostic 			base = 16;
61534326Sbostic 			/* leading 0x/X only if non-zero */
61661032Sbostic 			if (flags & ALT &&
61761032Sbostic 			    (flags & QUADINT ? uqval != 0 : ulval != 0))
61834427Sbostic 				flags |= HEXPREFIX;
61934327Sbostic 
62034327Sbostic 			/* unsigned conversions */
62134427Sbostic nosign:			sign = '\0';
62234326Sbostic 			/*
62334330Sbostic 			 * ``... diouXx conversions ... if a precision is
62434330Sbostic 			 * specified, the 0 flag will be ignored.''
62534330Sbostic 			 *	-- ANSI X3J11
62634330Sbostic 			 */
62734427Sbostic number:			if ((dprec = prec) >= 0)
62834427Sbostic 				flags &= ~ZEROPAD;
62934427Sbostic 
63034330Sbostic 			/*
63134326Sbostic 			 * ``The result of converting a zero value with an
63234326Sbostic 			 * explicit precision of zero is no characters.''
63334326Sbostic 			 *	-- ANSI X3J11
63434326Sbostic 			 */
63546076Sbostic 			cp = buf + BUF;
63661032Sbostic 			if (flags & QUADINT) {
63761032Sbostic 				if (uqval != 0 || prec != 0)
63861032Sbostic 					cp = __uqtoa(uqval, cp, base,
63961032Sbostic 					    flags & ALT, xdigs);
64061032Sbostic 			} else {
64161032Sbostic 				if (ulval != 0 || prec != 0)
64261032Sbostic 					cp = __ultoa(ulval, cp, base,
64361032Sbostic 					    flags & ALT, xdigs);
64434327Sbostic 			}
64546076Sbostic 			size = buf + BUF - cp;
64634226Sbostic 			break;
64746076Sbostic 		default:	/* "%?" prints ?, unless ? is NUL */
64846076Sbostic 			if (ch == '\0')
64946076Sbostic 				goto done;
65046076Sbostic 			/* pretend it was %c with argument ch */
65146076Sbostic 			cp = buf;
65246076Sbostic 			*cp = ch;
65346076Sbostic 			size = 1;
65446076Sbostic 			sign = '\0';
65546076Sbostic 			break;
65634226Sbostic 		}
65746076Sbostic 
65846076Sbostic 		/*
65957154Sbostic 		 * All reasonable formats wind up here.  At this point, `cp'
66057154Sbostic 		 * points to a string which (if not flags&LADJUST) should be
66157154Sbostic 		 * padded out to `width' places.  If flags&ZEROPAD, it should
66257154Sbostic 		 * first be prefixed by any sign or other prefix; otherwise,
66357154Sbostic 		 * it should be blank padded before the prefix is emitted.
66457154Sbostic 		 * After any left-hand padding and prefixing, emit zeroes
66557154Sbostic 		 * required by a decimal [diouxX] precision, then print the
66657154Sbostic 		 * string proper, then emit zeroes required by any leftover
66757154Sbostic 		 * floating precision; finally, if LADJUST, pad with blanks.
66857154Sbostic 		 *
66957154Sbostic 		 * Compute actual size, so we know how much to pad.
67057154Sbostic 		 * fieldsz excludes decimal prec; realsz includes it.
67146076Sbostic 		 */
67246076Sbostic 		fieldsz = size;
67346076Sbostic 		if (sign)
67446076Sbostic 			fieldsz++;
67546076Sbostic 		else if (flags & HEXPREFIX)
67646076Sbostic 			fieldsz += 2;
67746076Sbostic 		realsz = dprec > fieldsz ? dprec : fieldsz;
67846076Sbostic 
67946076Sbostic 		/* right-adjusting blank padding */
68046076Sbostic 		if ((flags & (LADJUST|ZEROPAD)) == 0)
68146076Sbostic 			PAD(width - realsz, blanks);
68246076Sbostic 
68346076Sbostic 		/* prefix */
68446076Sbostic 		if (sign) {
68546076Sbostic 			PRINT(&sign, 1);
68646076Sbostic 		} else if (flags & HEXPREFIX) {
68746076Sbostic 			ox[0] = '0';
68846076Sbostic 			ox[1] = ch;
68946076Sbostic 			PRINT(ox, 2);
69046076Sbostic 		}
69146076Sbostic 
69246076Sbostic 		/* right-adjusting zero padding */
69346076Sbostic 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
69446076Sbostic 			PAD(width - realsz, zeroes);
69546076Sbostic 
69646076Sbostic 		/* leading zeroes from decimal precision */
69746076Sbostic 		PAD(dprec - fieldsz, zeroes);
69846076Sbostic 
69946076Sbostic 		/* the string or number proper */
70057154Sbostic #ifdef FLOATING_POINT
70157154Sbostic 		if ((flags & FPT) == 0) {
70257154Sbostic 			PRINT(cp, size);
70357154Sbostic 		} else {	/* glue together f_p fragments */
70457154Sbostic 			if (ch >= 'f') {	/* 'f' or 'g' */
70557154Sbostic 				if (_double == 0) {
70657154Sbostic 				/* kludge for __dtoa irregularity */
70757154Sbostic 					if (prec == 0 ||
70857154Sbostic 					    (flags & ALT) == 0) {
70957154Sbostic 						PRINT("0", 1);
71057154Sbostic 					} else {
71157154Sbostic 						PRINT("0.", 2);
71257154Sbostic 						PAD(ndig - 1, zeroes);
71357154Sbostic 					}
71457154Sbostic 				} else if (expt <= 0) {
71557154Sbostic 					PRINT("0.", 2);
71657154Sbostic 					PAD(-expt, zeroes);
71757154Sbostic 					PRINT(cp, ndig);
71857154Sbostic 				} else if (expt >= ndig) {
71957154Sbostic 					PRINT(cp, ndig);
72057154Sbostic 					PAD(expt - ndig, zeroes);
72157154Sbostic 					if (flags & ALT)
72257154Sbostic 						PRINT(".", 1);
72357154Sbostic 				} else {
72457154Sbostic 					PRINT(cp, expt);
72557154Sbostic 					cp += expt;
72657154Sbostic 					PRINT(".", 1);
72757154Sbostic 					PRINT(cp, ndig-expt);
72857154Sbostic 				}
72957154Sbostic 			} else {	/* 'e' or 'E' */
73057154Sbostic 				if (ndig > 1 || flags & ALT) {
73157154Sbostic 					ox[0] = *cp++;
73257154Sbostic 					ox[1] = '.';
73357154Sbostic 					PRINT(ox, 2);
73457154Sbostic 					if (_double || flags & ALT == 0) {
73557154Sbostic 						PRINT(cp, ndig-1);
73657154Sbostic 					} else	/* 0.[0..] */
73757154Sbostic 						/* __dtoa irregularity */
73857154Sbostic 						PAD(ndig - 1, zeroes);
73957154Sbostic 				} else	/* XeYYY */
74057154Sbostic 					PRINT(cp, 1);
74157154Sbostic 				PRINT(expstr, expsize);
74257154Sbostic 			}
74357154Sbostic 		}
74457154Sbostic #else
74546076Sbostic 		PRINT(cp, size);
74646076Sbostic #endif
74746076Sbostic 		/* left-adjusting padding (always blank) */
74846076Sbostic 		if (flags & LADJUST)
74946076Sbostic 			PAD(width - realsz, blanks);
75046076Sbostic 
75146076Sbostic 		/* finally, adjust ret */
75246076Sbostic 		ret += width > realsz ? width : realsz;
75346076Sbostic 
75446076Sbostic 		FLUSH();	/* copy out the I/O vectors */
75534226Sbostic 	}
75646076Sbostic done:
75746076Sbostic 	FLUSH();
75846076Sbostic error:
75946076Sbostic 	return (__sferror(fp) ? EOF : ret);
76034427Sbostic 	/* NOTREACHED */
76134226Sbostic }
76234242Sbostic 
76346076Sbostic #ifdef FLOATING_POINT
76446180Storek 
76557154Sbostic extern char *__dtoa __P((double, int, int, int *, int *, char **));
76646076Sbostic 
76757154Sbostic static char *
cvt(value,ndigits,flags,sign,decpt,ch,length)76857154Sbostic cvt(value, ndigits, flags, sign, decpt, ch, length)
76957154Sbostic 	double value;
77057154Sbostic 	int ndigits, flags, *decpt, ch, *length;
77157154Sbostic 	char *sign;
77234242Sbostic {
77357154Sbostic 	int mode, dsgn;
77457154Sbostic 	char *digits, *bp, *rve;
77534242Sbostic 
77657154Sbostic 	if (ch == 'f')
77757154Sbostic 		mode = 3;
77857154Sbostic 	else {
77957154Sbostic 		mode = 2;
78057154Sbostic 	}
78157154Sbostic 	if (value < 0) {
78257154Sbostic 		value = -value;
78357154Sbostic 		*sign = '-';
78446076Sbostic 	} else
78557154Sbostic 		*sign = '\000';
78657154Sbostic 	digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve);
78757154Sbostic 	if (flags & ALT) {	/* Print trailing zeros */
78857154Sbostic 		bp = digits + ndigits;
78957154Sbostic 		if (ch == 'f') {
79057154Sbostic 			if (*digits == '0' && value)
79157154Sbostic 				*decpt = -ndigits + 1;
79257154Sbostic 			bp += *decpt;
79334624Sbostic 		}
79457154Sbostic 		if (value == 0)	/* kludge for __dtoa irregularity */
79557154Sbostic 			rve = bp;
79657154Sbostic 		while (rve < bp)
79757154Sbostic 			*rve++ = '0';
79834624Sbostic 	}
79957154Sbostic 	*length = rve - digits;
80057154Sbostic 	return (digits);
80134624Sbostic }
80234248Sbostic 
80357154Sbostic static int
exponent(p0,exp,fmtch)80457154Sbostic exponent(p0, exp, fmtch)
80557154Sbostic 	char *p0;
80657154Sbostic 	int exp, fmtch;
80734624Sbostic {
80857154Sbostic 	register char *p, *t;
80934624Sbostic 	char expbuf[MAXEXP];
81034248Sbostic 
81157154Sbostic 	p = p0;
81234624Sbostic 	*p++ = fmtch;
81334624Sbostic 	if (exp < 0) {
81434624Sbostic 		exp = -exp;
81534624Sbostic 		*p++ = '-';
81634242Sbostic 	}
81734624Sbostic 	else
81834624Sbostic 		*p++ = '+';
81934624Sbostic 	t = expbuf + MAXEXP;
82034624Sbostic 	if (exp > 9) {
82134624Sbostic 		do {
82246076Sbostic 			*--t = to_char(exp % 10);
82334624Sbostic 		} while ((exp /= 10) > 9);
82446076Sbostic 		*--t = to_char(exp);
82534624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
82634624Sbostic 	}
82734624Sbostic 	else {
82834624Sbostic 		*p++ = '0';
82946076Sbostic 		*p++ = to_char(exp);
83034624Sbostic 	}
83157154Sbostic 	return (p - p0);
83234242Sbostic }
83346076Sbostic #endif /* FLOATING_POINT */
834