xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 53810)
146076Sbostic /*-
246076Sbostic  * Copyright (c) 1990 The Regents of the University of California.
334233Sbostic  * 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*53810Sbostic static char sccsid[] = "@(#)vfprintf.c	5.49 (Berkeley) 06/02/92";
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>
2246611Sbostic #include <math.h>
2346076Sbostic #include <stdio.h>
2446076Sbostic #include <string.h>
2546076Sbostic #if __STDC__
2646076Sbostic #include <stdarg.h>
2746076Sbostic #else
2834233Sbostic #include <varargs.h>
2946076Sbostic #endif
3046076Sbostic #include "local.h"
3146076Sbostic #include "fvwrite.h"
3234226Sbostic 
3350438Sbostic /* Define FLOATING_POINT to get floating point. */
3446076Sbostic #define	FLOATING_POINT
3534226Sbostic 
3646076Sbostic /*
3746076Sbostic  * Flush out all the vectors defined by the given uio,
3846076Sbostic  * then reset it so that it can be reused.
3946076Sbostic  */
4046180Storek static int
4146076Sbostic __sprint(fp, uio)
4246076Sbostic 	FILE *fp;
4346076Sbostic 	register struct __suio *uio;
4446076Sbostic {
4546076Sbostic 	register int err;
4646076Sbostic 
4746076Sbostic 	if (uio->uio_resid == 0) {
4846076Sbostic 		uio->uio_iovcnt = 0;
4946076Sbostic 		return (0);
5046076Sbostic 	}
5146076Sbostic 	err = __sfvwrite(fp, uio);
5246076Sbostic 	uio->uio_resid = 0;
5346076Sbostic 	uio->uio_iovcnt = 0;
5446076Sbostic 	return (err);
5546076Sbostic }
5646076Sbostic 
5746076Sbostic /*
5846076Sbostic  * Helper function for `fprintf to unbuffered unix file': creates a
5946076Sbostic  * temporary buffer.  We only work on write-only files; this avoids
6046076Sbostic  * worries about ungetc buffers and so forth.
6146076Sbostic  */
6246180Storek static int
6346076Sbostic __sbprintf(fp, fmt, ap)
6446076Sbostic 	register FILE *fp;
6546180Storek 	const char *fmt;
6646076Sbostic 	va_list ap;
6746076Sbostic {
6846076Sbostic 	int ret;
6946076Sbostic 	FILE fake;
7046076Sbostic 	unsigned char buf[BUFSIZ];
7146076Sbostic 
7246076Sbostic 	/* copy the important variables */
7346076Sbostic 	fake._flags = fp->_flags & ~__SNBF;
7446076Sbostic 	fake._file = fp->_file;
7546076Sbostic 	fake._cookie = fp->_cookie;
7646076Sbostic 	fake._write = fp->_write;
7746076Sbostic 
7846076Sbostic 	/* set up the buffer */
7946076Sbostic 	fake._bf._base = fake._p = buf;
8046076Sbostic 	fake._bf._size = fake._w = sizeof(buf);
8146076Sbostic 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
8246076Sbostic 
8346076Sbostic 	/* do the work, then copy any error status */
8446076Sbostic 	ret = vfprintf(&fake, fmt, ap);
8546076Sbostic 	if (ret >= 0 && fflush(&fake))
8646076Sbostic 		ret = EOF;
8746076Sbostic 	if (fake._flags & __SERR)
8846076Sbostic 		fp->_flags |= __SERR;
8946076Sbostic 	return (ret);
9046076Sbostic }
9146076Sbostic 
9246076Sbostic 
9346076Sbostic #ifdef FLOATING_POINT
9446180Storek #include "floatio.h"
9546076Sbostic 
9634328Sbostic #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
9746076Sbostic #define	DEFPREC		6
9834328Sbostic 
9946076Sbostic static int cvt();
10034236Sbostic 
10146076Sbostic #else /* no FLOATING_POINT */
10234235Sbostic 
10346076Sbostic #define	BUF		40
10434331Sbostic 
10546076Sbostic #endif /* FLOATING_POINT */
10634318Sbostic 
10746076Sbostic 
10846076Sbostic /*
10946076Sbostic  * Macros for converting digits to letters and vice versa
11046076Sbostic  */
11146076Sbostic #define	to_digit(c)	((c) - '0')
11246076Sbostic #define is_digit(c)	((unsigned)to_digit(c) <= 9)
11346076Sbostic #define	to_char(n)	((n) + '0')
11446076Sbostic 
11546076Sbostic /*
11646076Sbostic  * Flags used during conversion.
11746076Sbostic  */
118*53810Sbostic #define	ALT		0x001		/* alternate form */
119*53810Sbostic #define	HEXPREFIX	0x002		/* add 0x or 0X prefix */
120*53810Sbostic #define	LADJUST		0x004		/* left adjustment */
121*53810Sbostic #define	LONGDBL		0x008		/* long double; unimplemented */
122*53810Sbostic #define	LONGINT		0x010		/* long integer */
123*53810Sbostic #define	QUADINT		0x020		/* quad integer */
124*53810Sbostic #define	SHORTINT	0x040		/* short integer */
125*53810Sbostic #define	ZEROPAD		0x080		/* zero (as opposed to blank) pad */
12634318Sbostic 
12746180Storek int
12846076Sbostic vfprintf(fp, fmt0, ap)
12946076Sbostic 	FILE *fp;
13046180Storek 	const char *fmt0;
13146076Sbostic 	va_list ap;
13234226Sbostic {
13346076Sbostic 	register char *fmt;	/* format string */
13434427Sbostic 	register int ch;	/* character from fmt */
13546076Sbostic 	register int n;		/* handy integer (short term usage) */
13646076Sbostic 	register char *cp;	/* handy char pointer (short term usage) */
13746076Sbostic 	register struct __siov *iovp;/* for PRINT macro */
13846076Sbostic 	register int flags;	/* flags as above */
13946076Sbostic 	int ret;		/* return value accumulator */
14046076Sbostic 	int width;		/* width from format (%8d), or 0 */
14146076Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
14246076Sbostic 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
14346076Sbostic #ifdef FLOATING_POINT
14446076Sbostic 	char softsign;		/* temporary negative sign for floats */
14534427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
14646076Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
14746076Sbostic #endif
148*53810Sbostic 	u_quad_t _uquad;	/* integer arguments %[diouxX] */
14946076Sbostic 	enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
15046076Sbostic 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
15134624Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
15246076Sbostic 	int realsz;		/* field size expanded by dprec */
15334427Sbostic 	int size;		/* size of converted field or string */
15446076Sbostic 	char *xdigs;		/* digits for [xX] conversion */
15546076Sbostic #define NIOV 8
15646076Sbostic 	struct __suio uio;	/* output information: summary */
15746076Sbostic 	struct __siov iov[NIOV];/* ... and individual io vectors */
15834427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
15946076Sbostic 	char ox[2];		/* space for 0x hex-prefix */
16034226Sbostic 
16146076Sbostic 	/*
162*53810Sbostic 	 * Choose PADSIZE to trade efficiency vs. size.  If larger printf
163*53810Sbostic 	 * fields occur frequently, increase PADSIZE and make the initialisers
164*53810Sbostic 	 * below longer.
16546076Sbostic 	 */
16646076Sbostic #define	PADSIZE	16		/* pad chunk size */
16746076Sbostic 	static char blanks[PADSIZE] =
16846076Sbostic 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
16946076Sbostic 	static char zeroes[PADSIZE] =
17046076Sbostic 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
17146076Sbostic 
17246076Sbostic 	/*
17346076Sbostic 	 * BEWARE, these `goto error' on error, and PAD uses `n'.
17446076Sbostic 	 */
17546076Sbostic #define	PRINT(ptr, len) { \
17646076Sbostic 	iovp->iov_base = (ptr); \
17746076Sbostic 	iovp->iov_len = (len); \
17846076Sbostic 	uio.uio_resid += (len); \
17946076Sbostic 	iovp++; \
18046076Sbostic 	if (++uio.uio_iovcnt >= NIOV) { \
18146076Sbostic 		if (__sprint(fp, &uio)) \
18246076Sbostic 			goto error; \
18346076Sbostic 		iovp = iov; \
18446076Sbostic 	} \
18546076Sbostic }
18646076Sbostic #define	PAD(howmany, with) { \
18746076Sbostic 	if ((n = (howmany)) > 0) { \
18846076Sbostic 		while (n > PADSIZE) { \
18946076Sbostic 			PRINT(with, PADSIZE); \
19046076Sbostic 			n -= PADSIZE; \
19146076Sbostic 		} \
19246076Sbostic 		PRINT(with, n); \
19346076Sbostic 	} \
19446076Sbostic }
19546076Sbostic #define	FLUSH() { \
19646076Sbostic 	if (uio.uio_resid && __sprint(fp, &uio)) \
19746076Sbostic 		goto error; \
19846076Sbostic 	uio.uio_iovcnt = 0; \
19946076Sbostic 	iovp = iov; \
20046076Sbostic }
20146076Sbostic 
20246076Sbostic 	/*
20346076Sbostic 	 * To extend shorts properly, we need both signed and unsigned
20446076Sbostic 	 * argument extraction methods.
20546076Sbostic 	 */
20646076Sbostic #define	SARG() \
207*53810Sbostic 	(flags&QUADINT ? va_arg(ap, quad_t) : \
208*53810Sbostic 	    flags&LONGINT ? va_arg(ap, long) : \
20946076Sbostic 	    flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
21046076Sbostic 	    (long)va_arg(ap, int))
21146076Sbostic #define	UARG() \
212*53810Sbostic 	(flags&QUADINT ? va_arg(ap, u_quad_t) : \
213*53810Sbostic 	    flags&LONGINT ? va_arg(ap, u_long) : \
21446076Sbostic 	    flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
21546076Sbostic 	    (u_long)va_arg(ap, u_int))
21646076Sbostic 
21746076Sbostic 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
21846076Sbostic 	if (cantwrite(fp))
21934428Sbostic 		return (EOF);
22034428Sbostic 
22146076Sbostic 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
22246076Sbostic 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
22346076Sbostic 	    fp->_file >= 0)
22446076Sbostic 		return (__sbprintf(fp, fmt0, ap));
22546076Sbostic 
22646076Sbostic 	fmt = (char *)fmt0;
22746076Sbostic 	uio.uio_iov = iovp = iov;
22846076Sbostic 	uio.uio_resid = 0;
22946076Sbostic 	uio.uio_iovcnt = 0;
23046076Sbostic 	ret = 0;
23146076Sbostic 
23246076Sbostic 	/*
23346076Sbostic 	 * Scan the format for conversions (`%' character).
23446076Sbostic 	 */
23546076Sbostic 	for (;;) {
23646076Sbostic 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
23746076Sbostic 			/* void */;
23846076Sbostic 		if ((n = fmt - cp) != 0) {
23946076Sbostic 			PRINT(cp, n);
24046076Sbostic 			ret += n;
24146076Sbostic 		}
24246076Sbostic 		if (ch == '\0')
24346076Sbostic 			goto done;
24446076Sbostic 		fmt++;		/* skip over '%' */
24546076Sbostic 
24646076Sbostic 		flags = 0;
24746076Sbostic 		dprec = 0;
24846076Sbostic #ifdef FLOATING_POINT
24946076Sbostic 		fpprec = 0;
25034318Sbostic #endif
25146076Sbostic 		width = 0;
25234233Sbostic 		prec = -1;
25334318Sbostic 		sign = '\0';
25434226Sbostic 
25546076Sbostic rflag:		ch = *fmt++;
25646076Sbostic reswitch:	switch (ch) {
25734318Sbostic 		case ' ':
25834669Sbostic 			/*
25934669Sbostic 			 * ``If the space and + flags both appear, the space
26034669Sbostic 			 * flag will be ignored.''
26134669Sbostic 			 *	-- ANSI X3J11
26234669Sbostic 			 */
26334669Sbostic 			if (!sign)
26434669Sbostic 				sign = ' ';
26534318Sbostic 			goto rflag;
26634233Sbostic 		case '#':
26734318Sbostic 			flags |= ALT;
26834318Sbostic 			goto rflag;
26934233Sbostic 		case '*':
27034235Sbostic 			/*
27134235Sbostic 			 * ``A negative field width argument is taken as a
27246076Sbostic 			 * - flag followed by a positive field width.''
27334235Sbostic 			 *	-- ANSI X3J11
27434235Sbostic 			 * They don't exclude field widths read from args.
27534235Sbostic 			 */
27646076Sbostic 			if ((width = va_arg(ap, int)) >= 0)
27734318Sbostic 				goto rflag;
27834235Sbostic 			width = -width;
27934427Sbostic 			/* FALLTHROUGH */
28034235Sbostic 		case '-':
28134318Sbostic 			flags |= LADJUST;
28234318Sbostic 			goto rflag;
28334233Sbostic 		case '+':
28434314Sbostic 			sign = '+';
28534318Sbostic 			goto rflag;
28634233Sbostic 		case '.':
28746076Sbostic 			if ((ch = *fmt++) == '*') {
28846076Sbostic 				n = va_arg(ap, int);
28946076Sbostic 				prec = n < 0 ? -1 : n;
29046076Sbostic 				goto rflag;
29134226Sbostic 			}
29246076Sbostic 			n = 0;
29346076Sbostic 			while (is_digit(ch)) {
29446076Sbostic 				n = 10 * n + to_digit(ch);
29546076Sbostic 				ch = *fmt++;
29646076Sbostic 			}
29734318Sbostic 			prec = n < 0 ? -1 : n;
29846076Sbostic 			goto reswitch;
29934233Sbostic 		case '0':
30034427Sbostic 			/*
30134427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
30234427Sbostic 			 * beginning of a field width.''
30334427Sbostic 			 *	-- ANSI X3J11
30434427Sbostic 			 */
30534427Sbostic 			flags |= ZEROPAD;
30634427Sbostic 			goto rflag;
30734233Sbostic 		case '1': case '2': case '3': case '4':
30834233Sbostic 		case '5': case '6': case '7': case '8': case '9':
30934318Sbostic 			n = 0;
31034233Sbostic 			do {
31146076Sbostic 				n = 10 * n + to_digit(ch);
31246076Sbostic 				ch = *fmt++;
31346076Sbostic 			} while (is_digit(ch));
31434318Sbostic 			width = n;
31546076Sbostic 			goto reswitch;
31646076Sbostic #ifdef FLOATING_POINT
31734235Sbostic 		case 'L':
31834329Sbostic 			flags |= LONGDBL;
31934318Sbostic 			goto rflag;
32046076Sbostic #endif
32134235Sbostic 		case 'h':
32234318Sbostic 			flags |= SHORTINT;
32334318Sbostic 			goto rflag;
32434233Sbostic 		case 'l':
32534318Sbostic 			flags |= LONGINT;
32634318Sbostic 			goto rflag;
327*53810Sbostic 		case 'q':
328*53810Sbostic 			flags |= QUADINT;
329*53810Sbostic 			goto rflag;
33034314Sbostic 		case 'c':
33146076Sbostic 			*(cp = buf) = va_arg(ap, int);
33234314Sbostic 			size = 1;
33334427Sbostic 			sign = '\0';
33446076Sbostic 			break;
33534624Sbostic 		case 'D':
33634624Sbostic 			flags |= LONGINT;
33734624Sbostic 			/*FALLTHROUGH*/
33834314Sbostic 		case 'd':
33934318Sbostic 		case 'i':
340*53810Sbostic 			_uquad = SARG();
341*53810Sbostic 			if ((quad_t)_uquad < 0) {
342*53810Sbostic 				_uquad = -_uquad;
34334314Sbostic 				sign = '-';
34434241Sbostic 			}
34546076Sbostic 			base = DEC;
34634327Sbostic 			goto number;
34746076Sbostic #ifdef FLOATING_POINT
34834261Sbostic 		case 'e':
34934236Sbostic 		case 'E':
35034235Sbostic 		case 'f':
35134261Sbostic 		case 'g':
35234243Sbostic 		case 'G':
35346076Sbostic 			_double = va_arg(ap, double);
35446126Storek 			/* do this before tricky precision changes */
35547607Sbostic 			if (isinf(_double)) {
35647607Sbostic 				if (_double < 0)
35747607Sbostic 					sign = '-';
35847607Sbostic 				cp = "Inf";
35947607Sbostic 				size = 3;
36046126Storek 				break;
36146126Storek 			}
36247607Sbostic 			if (isnan(_double)) {
36347607Sbostic 				cp = "NaN";
36447607Sbostic 				size = 3;
36547607Sbostic 				break;
36647607Sbostic 			}
36734328Sbostic 			/*
36834669Sbostic 			 * don't do unrealistic precision; just pad it with
36934669Sbostic 			 * zeroes later, so buffer size stays rational.
37034328Sbostic 			 */
37134328Sbostic 			if (prec > MAXFRACT) {
37246076Sbostic 				if (ch != 'g' && ch != 'G' || (flags&ALT))
37334475Sbostic 					fpprec = prec - MAXFRACT;
37434328Sbostic 				prec = MAXFRACT;
37546076Sbostic 			} else if (prec == -1)
37634624Sbostic 				prec = DEFPREC;
37734669Sbostic 			/*
37846076Sbostic 			 * cvt may have to round up before the "start" of
37946076Sbostic 			 * its buffer, i.e. ``intf("%.2f", (double)9.999);'';
38046076Sbostic 			 * if the first character is still NUL, it did.
38146076Sbostic 			 * softsign avoids negative 0 if _double < 0 but
38246076Sbostic 			 * no significant digits will be shown.
38334669Sbostic 			 */
38446076Sbostic 			cp = buf;
38546076Sbostic 			*cp = '\0';
38646076Sbostic 			size = cvt(_double, prec, flags, &softsign, ch,
38746076Sbostic 			    cp, buf + sizeof(buf));
38834669Sbostic 			if (softsign)
38934669Sbostic 				sign = '-';
39046076Sbostic 			if (*cp == '\0')
39146076Sbostic 				cp++;
39246076Sbostic 			break;
39346076Sbostic #endif /* FLOATING_POINT */
39434235Sbostic 		case 'n':
395*53810Sbostic 			if (flags & QUADINT)
396*53810Sbostic 				*va_arg(ap, quad_t *) = ret;
397*53810Sbostic 			else if (flags & LONGINT)
39846076Sbostic 				*va_arg(ap, long *) = ret;
39934427Sbostic 			else if (flags & SHORTINT)
40046076Sbostic 				*va_arg(ap, short *) = ret;
40134318Sbostic 			else
40246076Sbostic 				*va_arg(ap, int *) = ret;
40346076Sbostic 			continue;	/* no output */
40434624Sbostic 		case 'O':
40534624Sbostic 			flags |= LONGINT;
40634624Sbostic 			/*FALLTHROUGH*/
40734226Sbostic 		case 'o':
408*53810Sbostic 			_uquad = UARG();
40946076Sbostic 			base = OCT;
41034327Sbostic 			goto nosign;
41134235Sbostic 		case 'p':
41234320Sbostic 			/*
41334321Sbostic 			 * ``The argument shall be a pointer to void.  The
41434321Sbostic 			 * value of the pointer is converted to a sequence
41534321Sbostic 			 * of printable characters, in an implementation-
41634321Sbostic 			 * defined manner.''
41734321Sbostic 			 *	-- ANSI X3J11
41834320Sbostic 			 */
41934427Sbostic 			/* NOSTRICT */
420*53810Sbostic 			_uquad = (u_quad_t)va_arg(ap, void *);
42146076Sbostic 			base = HEX;
42246076Sbostic 			xdigs = "0123456789abcdef";
42346076Sbostic 			flags |= HEXPREFIX;
42446076Sbostic 			ch = 'x';
42534327Sbostic 			goto nosign;
42634226Sbostic 		case 's':
42746076Sbostic 			if ((cp = va_arg(ap, char *)) == NULL)
42846076Sbostic 				cp = "(null)";
42934321Sbostic 			if (prec >= 0) {
43034321Sbostic 				/*
43134321Sbostic 				 * can't use strlen; can only look for the
43234321Sbostic 				 * NUL in the first `prec' characters, and
43334321Sbostic 				 * strlen() will go further.
43434321Sbostic 				 */
43546076Sbostic 				char *p = memchr(cp, 0, prec);
43634321Sbostic 
43746076Sbostic 				if (p != NULL) {
43846076Sbostic 					size = p - cp;
43934321Sbostic 					if (size > prec)
44034321Sbostic 						size = prec;
44134427Sbostic 				} else
44234321Sbostic 					size = prec;
44334427Sbostic 			} else
44446076Sbostic 				size = strlen(cp);
44534427Sbostic 			sign = '\0';
44646076Sbostic 			break;
44734624Sbostic 		case 'U':
44834624Sbostic 			flags |= LONGINT;
44934624Sbostic 			/*FALLTHROUGH*/
45034226Sbostic 		case 'u':
451*53810Sbostic 			_uquad = UARG();
45246076Sbostic 			base = DEC;
45334327Sbostic 			goto nosign;
45434226Sbostic 		case 'X':
45546076Sbostic 			xdigs = "0123456789ABCDEF";
45646076Sbostic 			goto hex;
45734226Sbostic 		case 'x':
45846076Sbostic 			xdigs = "0123456789abcdef";
459*53810Sbostic hex:			_uquad = UARG();
46046076Sbostic 			base = HEX;
46134326Sbostic 			/* leading 0x/X only if non-zero */
462*53810Sbostic 			if (flags & ALT && _uquad != 0)
46334427Sbostic 				flags |= HEXPREFIX;
46434327Sbostic 
46534327Sbostic 			/* unsigned conversions */
46634427Sbostic nosign:			sign = '\0';
46734326Sbostic 			/*
46834330Sbostic 			 * ``... diouXx conversions ... if a precision is
46934330Sbostic 			 * specified, the 0 flag will be ignored.''
47034330Sbostic 			 *	-- ANSI X3J11
47134330Sbostic 			 */
47234427Sbostic number:			if ((dprec = prec) >= 0)
47334427Sbostic 				flags &= ~ZEROPAD;
47434427Sbostic 
47534330Sbostic 			/*
47634326Sbostic 			 * ``The result of converting a zero value with an
47734326Sbostic 			 * explicit precision of zero is no characters.''
47834326Sbostic 			 *	-- ANSI X3J11
47934326Sbostic 			 */
48046076Sbostic 			cp = buf + BUF;
481*53810Sbostic 			if (_uquad != 0 || prec != 0) {
48246076Sbostic 				/*
483*53810Sbostic 				 * Unsigned mod is hard, and unsigned mod
48446076Sbostic 				 * by a constant is easier than that by
48546076Sbostic 				 * a variable; hence this switch.
48646076Sbostic 				 */
48746076Sbostic 				switch (base) {
48846076Sbostic 				case OCT:
48946076Sbostic 					do {
490*53810Sbostic 						*--cp = to_char(_uquad & 7);
491*53810Sbostic 						_uquad >>= 3;
492*53810Sbostic 					} while (_uquad);
49346076Sbostic 					/* handle octal leading 0 */
49446076Sbostic 					if (flags & ALT && *cp != '0')
49546076Sbostic 						*--cp = '0';
49646076Sbostic 					break;
49734327Sbostic 
49846076Sbostic 				case DEC:
49946076Sbostic 					/* many numbers are 1 digit */
500*53810Sbostic 					while (_uquad >= 10) {
501*53810Sbostic 						*--cp = to_char(_uquad % 10);
502*53810Sbostic 						_uquad /= 10;
50346076Sbostic 					}
504*53810Sbostic 					*--cp = to_char(_uquad);
50546076Sbostic 					break;
50634327Sbostic 
50746076Sbostic 				case HEX:
50846076Sbostic 					do {
509*53810Sbostic 						*--cp = xdigs[_uquad & 15];
510*53810Sbostic 						_uquad >>= 4;
511*53810Sbostic 					} while (_uquad);
51246076Sbostic 					break;
51334327Sbostic 
51446076Sbostic 				default:
51546076Sbostic 					cp = "bug in vfprintf: bad base";
51646180Storek 					size = strlen(cp);
51746076Sbostic 					goto skipsize;
51846076Sbostic 				}
51934327Sbostic 			}
52046076Sbostic 			size = buf + BUF - cp;
52146076Sbostic 		skipsize:
52234226Sbostic 			break;
52346076Sbostic 		default:	/* "%?" prints ?, unless ? is NUL */
52446076Sbostic 			if (ch == '\0')
52546076Sbostic 				goto done;
52646076Sbostic 			/* pretend it was %c with argument ch */
52746076Sbostic 			cp = buf;
52846076Sbostic 			*cp = ch;
52946076Sbostic 			size = 1;
53046076Sbostic 			sign = '\0';
53146076Sbostic 			break;
53234226Sbostic 		}
53346076Sbostic 
53446076Sbostic 		/*
53546076Sbostic 		 * All reasonable formats wind up here.  At this point,
53646076Sbostic 		 * `cp' points to a string which (if not flags&LADJUST)
53746076Sbostic 		 * should be padded out to `width' places.  If
53846076Sbostic 		 * flags&ZEROPAD, it should first be prefixed by any
53946076Sbostic 		 * sign or other prefix; otherwise, it should be blank
54046076Sbostic 		 * padded before the prefix is emitted.  After any
54146076Sbostic 		 * left-hand padding and prefixing, emit zeroes
54246076Sbostic 		 * required by a decimal [diouxX] precision, then print
54346076Sbostic 		 * the string proper, then emit zeroes required by any
54446076Sbostic 		 * leftover floating precision; finally, if LADJUST,
54546076Sbostic 		 * pad with blanks.
54646076Sbostic 		 */
54746076Sbostic 
54846076Sbostic 		/*
54946076Sbostic 		 * compute actual size, so we know how much to pad.
55046076Sbostic 		 * fieldsz excludes decimal prec; realsz includes it
55146076Sbostic 		 */
55246076Sbostic #ifdef FLOATING_POINT
55346076Sbostic 		fieldsz = size + fpprec;
55446076Sbostic #else
55546076Sbostic 		fieldsz = size;
55646076Sbostic #endif
55746076Sbostic 		if (sign)
55846076Sbostic 			fieldsz++;
55946076Sbostic 		else if (flags & HEXPREFIX)
56046076Sbostic 			fieldsz += 2;
56146076Sbostic 		realsz = dprec > fieldsz ? dprec : fieldsz;
56246076Sbostic 
56346076Sbostic 		/* right-adjusting blank padding */
56446076Sbostic 		if ((flags & (LADJUST|ZEROPAD)) == 0)
56546076Sbostic 			PAD(width - realsz, blanks);
56646076Sbostic 
56746076Sbostic 		/* prefix */
56846076Sbostic 		if (sign) {
56946076Sbostic 			PRINT(&sign, 1);
57046076Sbostic 		} else if (flags & HEXPREFIX) {
57146076Sbostic 			ox[0] = '0';
57246076Sbostic 			ox[1] = ch;
57346076Sbostic 			PRINT(ox, 2);
57446076Sbostic 		}
57546076Sbostic 
57646076Sbostic 		/* right-adjusting zero padding */
57746076Sbostic 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
57846076Sbostic 			PAD(width - realsz, zeroes);
57946076Sbostic 
58046076Sbostic 		/* leading zeroes from decimal precision */
58146076Sbostic 		PAD(dprec - fieldsz, zeroes);
58246076Sbostic 
58346076Sbostic 		/* the string or number proper */
58446076Sbostic 		PRINT(cp, size);
58546076Sbostic 
58646076Sbostic #ifdef FLOATING_POINT
58746076Sbostic 		/* trailing f.p. zeroes */
58846076Sbostic 		PAD(fpprec, zeroes);
58946076Sbostic #endif
59046076Sbostic 
59146076Sbostic 		/* left-adjusting padding (always blank) */
59246076Sbostic 		if (flags & LADJUST)
59346076Sbostic 			PAD(width - realsz, blanks);
59446076Sbostic 
59546076Sbostic 		/* finally, adjust ret */
59646076Sbostic 		ret += width > realsz ? width : realsz;
59746076Sbostic 
59846076Sbostic 		FLUSH();	/* copy out the I/O vectors */
59934226Sbostic 	}
60046076Sbostic done:
60146076Sbostic 	FLUSH();
60246076Sbostic error:
60346076Sbostic 	return (__sferror(fp) ? EOF : ret);
60434427Sbostic 	/* NOTREACHED */
60534226Sbostic }
60634242Sbostic 
60746076Sbostic #ifdef FLOATING_POINT
60846180Storek #include <math.h>
60946180Storek 
61046076Sbostic static char *exponent();
61146076Sbostic static char *round();
61246076Sbostic 
61346180Storek static int
61434669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp)
61534242Sbostic 	double number;
61634261Sbostic 	register int prec;
61734323Sbostic 	int flags;
61846076Sbostic 	char *signp;
61946076Sbostic 	int fmtch;
62046076Sbostic 	char *startp, *endp;
62134242Sbostic {
62234331Sbostic 	register char *p, *t;
62334672Sbostic 	register double fract;
62434624Sbostic 	int dotrim, expcnt, gformat;
62546180Storek 	double integer, tmp;
62634242Sbostic 
62746076Sbostic 	dotrim = expcnt = gformat = 0;
62846076Sbostic 	if (number < 0) {
62946076Sbostic 		number = -number;
63046076Sbostic 		*signp = '-';
63146076Sbostic 	} else
63246076Sbostic 		*signp = 0;
63344426Sbostic 
63434624Sbostic 	fract = modf(number, &integer);
63534242Sbostic 
63634624Sbostic 	/* get an extra slot for rounding. */
63734624Sbostic 	t = ++startp;
63834624Sbostic 
63934624Sbostic 	/*
64034624Sbostic 	 * get integer portion of number; put into the end of the buffer; the
64134624Sbostic 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
64234624Sbostic 	 */
64334624Sbostic 	for (p = endp - 1; integer; ++expcnt) {
64434624Sbostic 		tmp = modf(integer / 10, &integer);
64546076Sbostic 		*p-- = to_char((int)((tmp + .01) * 10));
64634248Sbostic 	}
64746076Sbostic 	switch (fmtch) {
64834261Sbostic 	case 'f':
64934624Sbostic 		/* reverse integer into beginning of buffer */
65034624Sbostic 		if (expcnt)
65134624Sbostic 			for (; ++p < endp; *t++ = *p);
65234624Sbostic 		else
65334624Sbostic 			*t++ = '0';
65434248Sbostic 		/*
65534624Sbostic 		 * if precision required or alternate flag set, add in a
65634624Sbostic 		 * decimal point.
65734248Sbostic 		 */
65834624Sbostic 		if (prec || flags&ALT)
65934624Sbostic 			*t++ = '.';
66034624Sbostic 		/* if requires more precision and some fraction left */
66134624Sbostic 		if (fract) {
66234624Sbostic 			if (prec)
66334624Sbostic 				do {
66434624Sbostic 					fract = modf(fract * 10, &tmp);
66546076Sbostic 					*t++ = to_char((int)tmp);
66634624Sbostic 				} while (--prec && fract);
66734624Sbostic 			if (fract)
66834669Sbostic 				startp = round(fract, (int *)NULL, startp,
66934669Sbostic 				    t - 1, (char)0, signp);
67034624Sbostic 		}
67134624Sbostic 		for (; prec--; *t++ = '0');
67234624Sbostic 		break;
67334624Sbostic 	case 'e':
67434624Sbostic 	case 'E':
67534624Sbostic eformat:	if (expcnt) {
67634624Sbostic 			*t++ = *++p;
67734624Sbostic 			if (prec || flags&ALT)
67834331Sbostic 				*t++ = '.';
67934624Sbostic 			/* if requires more precision and some integer left */
68034624Sbostic 			for (; prec && ++p < endp; --prec)
68134624Sbostic 				*t++ = *p;
68234624Sbostic 			/*
68334624Sbostic 			 * if done precision and more of the integer component,
68434624Sbostic 			 * round using it; adjust fract so we don't re-round
68534624Sbostic 			 * later.
68634624Sbostic 			 */
68734624Sbostic 			if (!prec && ++p < endp) {
68834248Sbostic 				fract = 0;
68934669Sbostic 				startp = round((double)0, &expcnt, startp,
69034669Sbostic 				    t - 1, *p, signp);
69134248Sbostic 			}
69234624Sbostic 			/* adjust expcnt for digit in front of decimal */
69334624Sbostic 			--expcnt;
69434242Sbostic 		}
69534624Sbostic 		/* until first fractional digit, decrement exponent */
69634624Sbostic 		else if (fract) {
69734624Sbostic 			/* adjust expcnt for digit in front of decimal */
69834624Sbostic 			for (expcnt = -1;; --expcnt) {
69934624Sbostic 				fract = modf(fract * 10, &tmp);
70034624Sbostic 				if (tmp)
70134624Sbostic 					break;
70234248Sbostic 			}
70346076Sbostic 			*t++ = to_char((int)tmp);
70434624Sbostic 			if (prec || flags&ALT)
70534331Sbostic 				*t++ = '.';
70634242Sbostic 		}
70734248Sbostic 		else {
70834624Sbostic 			*t++ = '0';
70934624Sbostic 			if (prec || flags&ALT)
71034331Sbostic 				*t++ = '.';
71134248Sbostic 		}
71234624Sbostic 		/* if requires more precision and some fraction left */
71334624Sbostic 		if (fract) {
71434624Sbostic 			if (prec)
71534624Sbostic 				do {
71634624Sbostic 					fract = modf(fract * 10, &tmp);
71746076Sbostic 					*t++ = to_char((int)tmp);
71834624Sbostic 				} while (--prec && fract);
71934624Sbostic 			if (fract)
72034669Sbostic 				startp = round(fract, &expcnt, startp,
72134669Sbostic 				    t - 1, (char)0, signp);
72234584Sbostic 		}
72334624Sbostic 		/* if requires more precision */
72434624Sbostic 		for (; prec--; *t++ = '0');
72534624Sbostic 
72634624Sbostic 		/* unless alternate flag, trim any g/G format trailing 0's */
72734624Sbostic 		if (gformat && !(flags&ALT)) {
72834624Sbostic 			while (t > startp && *--t == '0');
72934624Sbostic 			if (*t == '.')
73034624Sbostic 				--t;
73134624Sbostic 			++t;
73234624Sbostic 		}
73334624Sbostic 		t = exponent(t, expcnt, fmtch);
73434624Sbostic 		break;
73534624Sbostic 	case 'g':
73634624Sbostic 	case 'G':
73734624Sbostic 		/* a precision of 0 is treated as a precision of 1. */
73834624Sbostic 		if (!prec)
73934624Sbostic 			++prec;
74034624Sbostic 		/*
74134624Sbostic 		 * ``The style used depends on the value converted; style e
74234624Sbostic 		 * will be used only if the exponent resulting from the
74334624Sbostic 		 * conversion is less than -4 or greater than the precision.''
74434624Sbostic 		 *	-- ANSI X3J11
74534624Sbostic 		 */
74634624Sbostic 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
74734624Sbostic 			/*
74834624Sbostic 			 * g/G format counts "significant digits, not digits of
74934624Sbostic 			 * precision; for the e/E format, this just causes an
75034624Sbostic 			 * off-by-one problem, i.e. g/G considers the digit
75134624Sbostic 			 * before the decimal point significant and e/E doesn't
75234624Sbostic 			 * count it as precision.
75334624Sbostic 			 */
75434624Sbostic 			--prec;
75534624Sbostic 			fmtch -= 2;		/* G->E, g->e */
75634624Sbostic 			gformat = 1;
75734624Sbostic 			goto eformat;
75834624Sbostic 		}
75934624Sbostic 		/*
76034624Sbostic 		 * reverse integer into beginning of buffer,
76134624Sbostic 		 * note, decrement precision
76234624Sbostic 		 */
76334624Sbostic 		if (expcnt)
76434624Sbostic 			for (; ++p < endp; *t++ = *p, --prec);
76534624Sbostic 		else
76634624Sbostic 			*t++ = '0';
76734624Sbostic 		/*
76834624Sbostic 		 * if precision required or alternate flag set, add in a
76934624Sbostic 		 * decimal point.  If no digits yet, add in leading 0.
77034624Sbostic 		 */
77134624Sbostic 		if (prec || flags&ALT) {
77234624Sbostic 			dotrim = 1;
77334624Sbostic 			*t++ = '.';
77434624Sbostic 		}
77534624Sbostic 		else
77634624Sbostic 			dotrim = 0;
77734624Sbostic 		/* if requires more precision and some fraction left */
77834624Sbostic 		if (fract) {
77934624Sbostic 			if (prec) {
78034624Sbostic 				do {
78134624Sbostic 					fract = modf(fract * 10, &tmp);
78246076Sbostic 					*t++ = to_char((int)tmp);
78334624Sbostic 				} while(!tmp);
78434624Sbostic 				while (--prec && fract) {
78534624Sbostic 					fract = modf(fract * 10, &tmp);
78646076Sbostic 					*t++ = to_char((int)tmp);
78734248Sbostic 				}
78834248Sbostic 			}
78934624Sbostic 			if (fract)
79034669Sbostic 				startp = round(fract, (int *)NULL, startp,
79134669Sbostic 				    t - 1, (char)0, signp);
79234624Sbostic 		}
79334624Sbostic 		/* alternate format, adds 0's for precision, else trim 0's */
79434624Sbostic 		if (flags&ALT)
79534624Sbostic 			for (; prec--; *t++ = '0');
79634624Sbostic 		else if (dotrim) {
79734624Sbostic 			while (t > startp && *--t == '0');
79834624Sbostic 			if (*t != '.')
79934624Sbostic 				++t;
80034624Sbostic 		}
80134624Sbostic 	}
80246076Sbostic 	return (t - startp);
80334624Sbostic }
80434248Sbostic 
80534624Sbostic static char *
80634669Sbostic round(fract, exp, start, end, ch, signp)
80734624Sbostic 	double fract;
80834669Sbostic 	int *exp;
80934624Sbostic 	register char *start, *end;
81034669Sbostic 	char ch, *signp;
81134624Sbostic {
81234624Sbostic 	double tmp;
81334248Sbostic 
81434624Sbostic 	if (fract)
81546180Storek 		(void)modf(fract * 10, &tmp);
81634624Sbostic 	else
81746076Sbostic 		tmp = to_digit(ch);
81834624Sbostic 	if (tmp > 4)
81934624Sbostic 		for (;; --end) {
82034624Sbostic 			if (*end == '.')
82134624Sbostic 				--end;
82234624Sbostic 			if (++*end <= '9')
82334624Sbostic 				break;
82434624Sbostic 			*end = '0';
82534624Sbostic 			if (end == start) {
82634669Sbostic 				if (exp) {	/* e/E; increment exponent */
82734669Sbostic 					*end = '1';
82834669Sbostic 					++*exp;
82934669Sbostic 				}
83034669Sbostic 				else {		/* f; add extra digit */
83146076Sbostic 				*--end = '1';
83246076Sbostic 				--start;
83334669Sbostic 				}
83434624Sbostic 				break;
83534242Sbostic 			}
83634242Sbostic 		}
83734669Sbostic 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
83834669Sbostic 	else if (*signp == '-')
83934669Sbostic 		for (;; --end) {
84034669Sbostic 			if (*end == '.')
84134669Sbostic 				--end;
84234669Sbostic 			if (*end != '0')
84334669Sbostic 				break;
84434669Sbostic 			if (end == start)
84534669Sbostic 				*signp = 0;
84634669Sbostic 		}
84746076Sbostic 	return (start);
84834624Sbostic }
84934248Sbostic 
85034624Sbostic static char *
85134624Sbostic exponent(p, exp, fmtch)
85234624Sbostic 	register char *p;
85334624Sbostic 	register int exp;
85446076Sbostic 	int fmtch;
85534624Sbostic {
85634624Sbostic 	register char *t;
85734624Sbostic 	char expbuf[MAXEXP];
85834248Sbostic 
85934624Sbostic 	*p++ = fmtch;
86034624Sbostic 	if (exp < 0) {
86134624Sbostic 		exp = -exp;
86234624Sbostic 		*p++ = '-';
86334242Sbostic 	}
86434624Sbostic 	else
86534624Sbostic 		*p++ = '+';
86634624Sbostic 	t = expbuf + MAXEXP;
86734624Sbostic 	if (exp > 9) {
86834624Sbostic 		do {
86946076Sbostic 			*--t = to_char(exp % 10);
87034624Sbostic 		} while ((exp /= 10) > 9);
87146076Sbostic 		*--t = to_char(exp);
87234624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
87334624Sbostic 	}
87434624Sbostic 	else {
87534624Sbostic 		*p++ = '0';
87646076Sbostic 		*p++ = to_char(exp);
87734624Sbostic 	}
87846076Sbostic 	return (p);
87934242Sbostic }
88046076Sbostic #endif /* FLOATING_POINT */
881