xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 46646)
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*46646Sdonn static char sccsid[] = "@(#)vfprintf.c	5.46 (Berkeley) 02/24/91";
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 
3346076Sbostic /*
3446076Sbostic  * Define FLOATING_POINT to get floating point.
3546076Sbostic  * Define CSH to get a csh-specific version (grr).
3646076Sbostic  */
3746076Sbostic #ifndef CSH
3846076Sbostic #define	FLOATING_POINT
3946076Sbostic #endif
4034226Sbostic 
4146076Sbostic /* end of configuration stuff */
4234624Sbostic 
4346076Sbostic 
4446076Sbostic #ifdef CSH
4546076Sbostic /*
4646076Sbostic  * C shell hacks.  Ick, gag.
4746076Sbostic  */
4846076Sbostic #undef BUFSIZ
4946076Sbostic #include "sh.h"
5046076Sbostic 
5146180Storek #if __STDC__
5246180Storek int
5346180Storek printf(const char *fmt, ...) {
5446180Storek 	FILE f;
55*46646Sdonn 	va_list ap;
56*46646Sdonn 	int ret;
5746180Storek 
58*46646Sdonn 	va_start(ap, fmt);
5946180Storek 	f._flags = __SWR;
60*46646Sdonn 	f._write = NULL;
61*46646Sdonn 	ret = vfprintf(&f, fmt, ap);
62*46646Sdonn 	va_end(ap);
63*46646Sdonn 	return ret;
6446180Storek }
6546180Storek #else
6646180Storek int
6746076Sbostic printf(fmt, args)
6846076Sbostic 	char *fmt;
6946076Sbostic {
7046076Sbostic 	FILE f;
7146076Sbostic 
7246076Sbostic 	f._flags = __SWR;
7346442Storek 	f._write = NULL;
7446076Sbostic 	return (vfprintf(&f, fmt, &args));
7546076Sbostic }
7646180Storek #endif
7746076Sbostic 
7846180Storek int
7946442Storek __sprint(fp, uio)
8046442Storek 	FILE *fp;
8146076Sbostic 	register struct __suio *uio;
8246076Sbostic {
8346076Sbostic 	register char *p;
8446076Sbostic 	register int n, ch, iovcnt;
8546442Storek 	register struct __siov *iov;
8646076Sbostic 
8746442Storek 	/* must allow sprintf to work, might as well allow others too */
8846442Storek 	if (fp->_write || fp->_flags & __SSTR) {
8946442Storek 		if (uio->uio_resid == 0) {
9046442Storek 			uio->uio_iovcnt = 0;
9146442Storek 			return (0);
9246442Storek 		}
9346442Storek 		n = __sfvwrite(fp, uio);
9446442Storek 		uio->uio_resid = 0;
9546442Storek 		uio->uio_iovcnt = 0;
9646442Storek 		return (n);
9746442Storek 	}
9846442Storek 	iov = uio->uio_iov;
9946076Sbostic 	for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) {
10046076Sbostic 		for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) {
10146076Sbostic #ifdef CSHPUTCHAR
10246076Sbostic 			ch = *p++;
10346076Sbostic 			CSHPUTCHAR;	/* this horrid macro uses `ch' */
10446076Sbostic #else
10546076Sbostic #undef putchar
10646076Sbostic 			putchar(*p++);
10746076Sbostic #endif
10846076Sbostic 		}
10946076Sbostic 	}
11046076Sbostic 	uio->uio_resid = 0;
11146076Sbostic 	uio->uio_iovcnt = 0;
11246076Sbostic 	return (0);
11346076Sbostic }
11446076Sbostic 
11546076Sbostic #else /* CSH */
11646076Sbostic 
11746076Sbostic /*
11846076Sbostic  * Flush out all the vectors defined by the given uio,
11946076Sbostic  * then reset it so that it can be reused.
12046076Sbostic  */
12146180Storek static int
12246076Sbostic __sprint(fp, uio)
12346076Sbostic 	FILE *fp;
12446076Sbostic 	register struct __suio *uio;
12546076Sbostic {
12646076Sbostic 	register int err;
12746076Sbostic 
12846076Sbostic 	if (uio->uio_resid == 0) {
12946076Sbostic 		uio->uio_iovcnt = 0;
13046076Sbostic 		return (0);
13146076Sbostic 	}
13246076Sbostic 	err = __sfvwrite(fp, uio);
13346076Sbostic 	uio->uio_resid = 0;
13446076Sbostic 	uio->uio_iovcnt = 0;
13546076Sbostic 	return (err);
13646076Sbostic }
13746076Sbostic 
13846076Sbostic /*
13946076Sbostic  * Helper function for `fprintf to unbuffered unix file': creates a
14046076Sbostic  * temporary buffer.  We only work on write-only files; this avoids
14146076Sbostic  * worries about ungetc buffers and so forth.
14246076Sbostic  */
14346180Storek static int
14446076Sbostic __sbprintf(fp, fmt, ap)
14546076Sbostic 	register FILE *fp;
14646180Storek 	const char *fmt;
14746076Sbostic 	va_list ap;
14846076Sbostic {
14946076Sbostic 	int ret;
15046076Sbostic 	FILE fake;
15146076Sbostic 	unsigned char buf[BUFSIZ];
15246076Sbostic 
15346076Sbostic 	/* copy the important variables */
15446076Sbostic 	fake._flags = fp->_flags & ~__SNBF;
15546076Sbostic 	fake._file = fp->_file;
15646076Sbostic 	fake._cookie = fp->_cookie;
15746076Sbostic 	fake._write = fp->_write;
15846076Sbostic 
15946076Sbostic 	/* set up the buffer */
16046076Sbostic 	fake._bf._base = fake._p = buf;
16146076Sbostic 	fake._bf._size = fake._w = sizeof(buf);
16246076Sbostic 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
16346076Sbostic 
16446076Sbostic 	/* do the work, then copy any error status */
16546076Sbostic 	ret = vfprintf(&fake, fmt, ap);
16646076Sbostic 	if (ret >= 0 && fflush(&fake))
16746076Sbostic 		ret = EOF;
16846076Sbostic 	if (fake._flags & __SERR)
16946076Sbostic 		fp->_flags |= __SERR;
17046076Sbostic 	return (ret);
17146076Sbostic }
17246076Sbostic 
17346076Sbostic #endif /* CSH */
17446076Sbostic 
17546076Sbostic 
17646076Sbostic #ifdef FLOATING_POINT
17746180Storek #include "floatio.h"
17846076Sbostic 
17934328Sbostic #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
18046076Sbostic #define	DEFPREC		6
18134328Sbostic 
18246076Sbostic static int cvt();
18346126Storek #if defined(hp300) || defined(sparc)
18446126Storek static char *isspecial();
18546126Storek #endif
18634236Sbostic 
18746076Sbostic #else /* no FLOATING_POINT */
18834235Sbostic 
18946076Sbostic #define	BUF		40
19034331Sbostic 
19146076Sbostic #endif /* FLOATING_POINT */
19234318Sbostic 
19346076Sbostic 
19446076Sbostic /*
19546076Sbostic  * Macros for converting digits to letters and vice versa
19646076Sbostic  */
19746076Sbostic #define	to_digit(c)	((c) - '0')
19846076Sbostic #define is_digit(c)	((unsigned)to_digit(c) <= 9)
19946076Sbostic #define	to_char(n)	((n) + '0')
20046076Sbostic 
20146076Sbostic /*
20246076Sbostic  * Flags used during conversion.
20346076Sbostic  */
20434318Sbostic #define	LONGINT		0x01		/* long integer */
20534318Sbostic #define	LONGDBL		0x02		/* long double; unimplemented */
20634318Sbostic #define	SHORTINT	0x04		/* short integer */
20734318Sbostic #define	ALT		0x08		/* alternate form */
20834318Sbostic #define	LADJUST		0x10		/* left adjustment */
20934427Sbostic #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
21034427Sbostic #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
21134318Sbostic 
21246180Storek int
21346076Sbostic vfprintf(fp, fmt0, ap)
21446076Sbostic 	FILE *fp;
21546180Storek 	const char *fmt0;
21646076Sbostic #if tahoe
21746076Sbostic  register /* technically illegal, since we do not know what type va_list is */
21846076Sbostic #endif
21946076Sbostic 	va_list ap;
22034226Sbostic {
22146076Sbostic 	register char *fmt;	/* format string */
22234427Sbostic 	register int ch;	/* character from fmt */
22346076Sbostic 	register int n;		/* handy integer (short term usage) */
22446076Sbostic 	register char *cp;	/* handy char pointer (short term usage) */
22546076Sbostic 	register struct __siov *iovp;/* for PRINT macro */
22646076Sbostic 	register int flags;	/* flags as above */
22746076Sbostic 	int ret;		/* return value accumulator */
22846076Sbostic 	int width;		/* width from format (%8d), or 0 */
22946076Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
23046076Sbostic 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
23146076Sbostic #ifdef FLOATING_POINT
23246076Sbostic 	char softsign;		/* temporary negative sign for floats */
23334427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
23446076Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
23546076Sbostic #endif
23634427Sbostic 	u_long _ulong;		/* integer arguments %[diouxX] */
23746076Sbostic 	enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
23846076Sbostic 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
23934624Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
24046076Sbostic 	int realsz;		/* field size expanded by dprec */
24134427Sbostic 	int size;		/* size of converted field or string */
24246076Sbostic 	char *xdigs;		/* digits for [xX] conversion */
24346076Sbostic #define NIOV 8
24446076Sbostic 	struct __suio uio;	/* output information: summary */
24546076Sbostic 	struct __siov iov[NIOV];/* ... and individual io vectors */
24634427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
24746076Sbostic 	char ox[2];		/* space for 0x hex-prefix */
24834226Sbostic 
24946076Sbostic 	/*
25046076Sbostic 	 * Choose PADSIZE to trade efficiency vs size.  If larger
25146076Sbostic 	 * printf fields occur frequently, increase PADSIZE (and make
25246076Sbostic 	 * the initialisers below longer).
25346076Sbostic 	 */
25446076Sbostic #define	PADSIZE	16		/* pad chunk size */
25546076Sbostic 	static char blanks[PADSIZE] =
25646076Sbostic 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
25746076Sbostic 	static char zeroes[PADSIZE] =
25846076Sbostic 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
25946076Sbostic 
26046076Sbostic 	/*
26146076Sbostic 	 * BEWARE, these `goto error' on error, and PAD uses `n'.
26246076Sbostic 	 */
26346076Sbostic #define	PRINT(ptr, len) { \
26446076Sbostic 	iovp->iov_base = (ptr); \
26546076Sbostic 	iovp->iov_len = (len); \
26646076Sbostic 	uio.uio_resid += (len); \
26746076Sbostic 	iovp++; \
26846076Sbostic 	if (++uio.uio_iovcnt >= NIOV) { \
26946076Sbostic 		if (__sprint(fp, &uio)) \
27046076Sbostic 			goto error; \
27146076Sbostic 		iovp = iov; \
27246076Sbostic 	} \
27346076Sbostic }
27446076Sbostic #define	PAD(howmany, with) { \
27546076Sbostic 	if ((n = (howmany)) > 0) { \
27646076Sbostic 		while (n > PADSIZE) { \
27746076Sbostic 			PRINT(with, PADSIZE); \
27846076Sbostic 			n -= PADSIZE; \
27946076Sbostic 		} \
28046076Sbostic 		PRINT(with, n); \
28146076Sbostic 	} \
28246076Sbostic }
28346076Sbostic #define	FLUSH() { \
28446076Sbostic 	if (uio.uio_resid && __sprint(fp, &uio)) \
28546076Sbostic 		goto error; \
28646076Sbostic 	uio.uio_iovcnt = 0; \
28746076Sbostic 	iovp = iov; \
28846076Sbostic }
28946076Sbostic 
29046076Sbostic 	/*
29146076Sbostic 	 * To extend shorts properly, we need both signed and unsigned
29246076Sbostic 	 * argument extraction methods.
29346076Sbostic 	 */
29446076Sbostic #define	SARG() \
29546076Sbostic 	(flags&LONGINT ? va_arg(ap, long) : \
29646076Sbostic 	    flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
29746076Sbostic 	    (long)va_arg(ap, int))
29846076Sbostic #define	UARG() \
29946076Sbostic 	(flags&LONGINT ? va_arg(ap, u_long) : \
30046076Sbostic 	    flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
30146076Sbostic 	    (u_long)va_arg(ap, u_int))
30246076Sbostic 
30346076Sbostic #ifndef CSH
30446076Sbostic 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
30546076Sbostic 	if (cantwrite(fp))
30634428Sbostic 		return (EOF);
30734428Sbostic 
30846076Sbostic 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
30946076Sbostic 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
31046076Sbostic 	    fp->_file >= 0)
31146076Sbostic 		return (__sbprintf(fp, fmt0, ap));
31246076Sbostic #endif /* CSH */
31346076Sbostic 
31446076Sbostic 	fmt = (char *)fmt0;
31546076Sbostic 	uio.uio_iov = iovp = iov;
31646076Sbostic 	uio.uio_resid = 0;
31746076Sbostic 	uio.uio_iovcnt = 0;
31846076Sbostic 	ret = 0;
31946076Sbostic 
32046076Sbostic 	/*
32146076Sbostic 	 * Scan the format for conversions (`%' character).
32246076Sbostic 	 */
32346076Sbostic 	for (;;) {
32446076Sbostic 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
32546076Sbostic 			/* void */;
32646076Sbostic 		if ((n = fmt - cp) != 0) {
32746076Sbostic 			PRINT(cp, n);
32846076Sbostic 			ret += n;
32946076Sbostic 		}
33046076Sbostic 		if (ch == '\0')
33146076Sbostic 			goto done;
33246076Sbostic 		fmt++;		/* skip over '%' */
33346076Sbostic 
33446076Sbostic 		flags = 0;
33546076Sbostic 		dprec = 0;
33646076Sbostic #ifdef FLOATING_POINT
33746076Sbostic 		fpprec = 0;
33834318Sbostic #endif
33946076Sbostic 		width = 0;
34034233Sbostic 		prec = -1;
34134318Sbostic 		sign = '\0';
34234226Sbostic 
34346076Sbostic rflag:		ch = *fmt++;
34446076Sbostic reswitch:	switch (ch) {
34534318Sbostic 		case ' ':
34634669Sbostic 			/*
34734669Sbostic 			 * ``If the space and + flags both appear, the space
34834669Sbostic 			 * flag will be ignored.''
34934669Sbostic 			 *	-- ANSI X3J11
35034669Sbostic 			 */
35134669Sbostic 			if (!sign)
35234669Sbostic 				sign = ' ';
35334318Sbostic 			goto rflag;
35434233Sbostic 		case '#':
35534318Sbostic 			flags |= ALT;
35634318Sbostic 			goto rflag;
35734233Sbostic 		case '*':
35834235Sbostic 			/*
35934235Sbostic 			 * ``A negative field width argument is taken as a
36046076Sbostic 			 * - flag followed by a positive field width.''
36134235Sbostic 			 *	-- ANSI X3J11
36234235Sbostic 			 * They don't exclude field widths read from args.
36334235Sbostic 			 */
36446076Sbostic 			if ((width = va_arg(ap, int)) >= 0)
36534318Sbostic 				goto rflag;
36634235Sbostic 			width = -width;
36734427Sbostic 			/* FALLTHROUGH */
36834235Sbostic 		case '-':
36934318Sbostic 			flags |= LADJUST;
37034318Sbostic 			goto rflag;
37134233Sbostic 		case '+':
37234314Sbostic 			sign = '+';
37334318Sbostic 			goto rflag;
37434233Sbostic 		case '.':
37546076Sbostic 			if ((ch = *fmt++) == '*') {
37646076Sbostic 				n = va_arg(ap, int);
37746076Sbostic 				prec = n < 0 ? -1 : n;
37846076Sbostic 				goto rflag;
37934226Sbostic 			}
38046076Sbostic 			n = 0;
38146076Sbostic 			while (is_digit(ch)) {
38246076Sbostic 				n = 10 * n + to_digit(ch);
38346076Sbostic 				ch = *fmt++;
38446076Sbostic 			}
38534318Sbostic 			prec = n < 0 ? -1 : n;
38646076Sbostic 			goto reswitch;
38734233Sbostic 		case '0':
38834427Sbostic 			/*
38934427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
39034427Sbostic 			 * beginning of a field width.''
39134427Sbostic 			 *	-- ANSI X3J11
39234427Sbostic 			 */
39334427Sbostic 			flags |= ZEROPAD;
39434427Sbostic 			goto rflag;
39534233Sbostic 		case '1': case '2': case '3': case '4':
39634233Sbostic 		case '5': case '6': case '7': case '8': case '9':
39734318Sbostic 			n = 0;
39834233Sbostic 			do {
39946076Sbostic 				n = 10 * n + to_digit(ch);
40046076Sbostic 				ch = *fmt++;
40146076Sbostic 			} while (is_digit(ch));
40234318Sbostic 			width = n;
40346076Sbostic 			goto reswitch;
40446076Sbostic #ifdef FLOATING_POINT
40534235Sbostic 		case 'L':
40634329Sbostic 			flags |= LONGDBL;
40734318Sbostic 			goto rflag;
40846076Sbostic #endif
40934235Sbostic 		case 'h':
41034318Sbostic 			flags |= SHORTINT;
41134318Sbostic 			goto rflag;
41234233Sbostic 		case 'l':
41334318Sbostic 			flags |= LONGINT;
41434318Sbostic 			goto rflag;
41534314Sbostic 		case 'c':
41646076Sbostic 			*(cp = buf) = va_arg(ap, int);
41734314Sbostic 			size = 1;
41834427Sbostic 			sign = '\0';
41946076Sbostic 			break;
42034624Sbostic 		case 'D':
42134624Sbostic 			flags |= LONGINT;
42234624Sbostic 			/*FALLTHROUGH*/
42334314Sbostic 		case 'd':
42434318Sbostic 		case 'i':
42546076Sbostic 			_ulong = SARG();
42634318Sbostic 			if ((long)_ulong < 0) {
42734318Sbostic 				_ulong = -_ulong;
42834314Sbostic 				sign = '-';
42934241Sbostic 			}
43046076Sbostic 			base = DEC;
43134327Sbostic 			goto number;
43246076Sbostic #ifdef FLOATING_POINT
43334261Sbostic 		case 'e':
43434236Sbostic 		case 'E':
43534235Sbostic 		case 'f':
43634261Sbostic 		case 'g':
43734243Sbostic 		case 'G':
43846076Sbostic 			_double = va_arg(ap, double);
43946126Storek #if defined(hp300) || defined(sparc)
44046126Storek 			/* do this before tricky precision changes */
44146126Storek 			if ((cp = isspecial(_double, &sign)) != NULL) {
44246126Storek 				size = strlen(cp);
44346126Storek 				break;
44446126Storek 			}
44546126Storek #endif
44634328Sbostic 			/*
44734669Sbostic 			 * don't do unrealistic precision; just pad it with
44834669Sbostic 			 * zeroes later, so buffer size stays rational.
44934328Sbostic 			 */
45034328Sbostic 			if (prec > MAXFRACT) {
45146076Sbostic 				if (ch != 'g' && ch != 'G' || (flags&ALT))
45234475Sbostic 					fpprec = prec - MAXFRACT;
45334328Sbostic 				prec = MAXFRACT;
45446076Sbostic 			} else if (prec == -1)
45534624Sbostic 				prec = DEFPREC;
45634669Sbostic 			/*
45746076Sbostic 			 * cvt may have to round up before the "start" of
45846076Sbostic 			 * its buffer, i.e. ``intf("%.2f", (double)9.999);'';
45946076Sbostic 			 * if the first character is still NUL, it did.
46046076Sbostic 			 * softsign avoids negative 0 if _double < 0 but
46146076Sbostic 			 * no significant digits will be shown.
46234669Sbostic 			 */
46346076Sbostic 			cp = buf;
46446076Sbostic 			*cp = '\0';
46546076Sbostic 			size = cvt(_double, prec, flags, &softsign, ch,
46646076Sbostic 			    cp, buf + sizeof(buf));
46734669Sbostic 			if (softsign)
46834669Sbostic 				sign = '-';
46946076Sbostic 			if (*cp == '\0')
47046076Sbostic 				cp++;
47146076Sbostic 			break;
47246076Sbostic #endif /* FLOATING_POINT */
47334235Sbostic 		case 'n':
47434427Sbostic 			if (flags & LONGINT)
47546076Sbostic 				*va_arg(ap, long *) = ret;
47634427Sbostic 			else if (flags & SHORTINT)
47746076Sbostic 				*va_arg(ap, short *) = ret;
47834318Sbostic 			else
47946076Sbostic 				*va_arg(ap, int *) = ret;
48046076Sbostic 			continue;	/* no output */
48134624Sbostic 		case 'O':
48234624Sbostic 			flags |= LONGINT;
48334624Sbostic 			/*FALLTHROUGH*/
48434226Sbostic 		case 'o':
48546076Sbostic 			_ulong = UARG();
48646076Sbostic 			base = OCT;
48734327Sbostic 			goto nosign;
48834235Sbostic 		case 'p':
48934320Sbostic 			/*
49034321Sbostic 			 * ``The argument shall be a pointer to void.  The
49134321Sbostic 			 * value of the pointer is converted to a sequence
49234321Sbostic 			 * of printable characters, in an implementation-
49334321Sbostic 			 * defined manner.''
49434321Sbostic 			 *	-- ANSI X3J11
49534320Sbostic 			 */
49634427Sbostic 			/* NOSTRICT */
49746076Sbostic 			_ulong = (u_long)va_arg(ap, void *);
49846076Sbostic 			base = HEX;
49946076Sbostic 			xdigs = "0123456789abcdef";
50046076Sbostic 			flags |= HEXPREFIX;
50146076Sbostic 			ch = 'x';
50234327Sbostic 			goto nosign;
50334226Sbostic 		case 's':
50446076Sbostic 			if ((cp = va_arg(ap, char *)) == NULL)
50546076Sbostic 				cp = "(null)";
50634321Sbostic 			if (prec >= 0) {
50734321Sbostic 				/*
50834321Sbostic 				 * can't use strlen; can only look for the
50934321Sbostic 				 * NUL in the first `prec' characters, and
51034321Sbostic 				 * strlen() will go further.
51134321Sbostic 				 */
51246076Sbostic 				char *p = memchr(cp, 0, prec);
51334321Sbostic 
51446076Sbostic 				if (p != NULL) {
51546076Sbostic 					size = p - cp;
51634321Sbostic 					if (size > prec)
51734321Sbostic 						size = prec;
51834427Sbostic 				} else
51934321Sbostic 					size = prec;
52034427Sbostic 			} else
52146076Sbostic 				size = strlen(cp);
52234427Sbostic 			sign = '\0';
52346076Sbostic 			break;
52434624Sbostic 		case 'U':
52534624Sbostic 			flags |= LONGINT;
52634624Sbostic 			/*FALLTHROUGH*/
52734226Sbostic 		case 'u':
52846076Sbostic 			_ulong = UARG();
52946076Sbostic 			base = DEC;
53034327Sbostic 			goto nosign;
53134226Sbostic 		case 'X':
53246076Sbostic 			xdigs = "0123456789ABCDEF";
53346076Sbostic 			goto hex;
53434226Sbostic 		case 'x':
53546076Sbostic 			xdigs = "0123456789abcdef";
53646076Sbostic hex:			_ulong = UARG();
53746076Sbostic 			base = HEX;
53834326Sbostic 			/* leading 0x/X only if non-zero */
53934427Sbostic 			if (flags & ALT && _ulong != 0)
54034427Sbostic 				flags |= HEXPREFIX;
54134327Sbostic 
54234327Sbostic 			/* unsigned conversions */
54334427Sbostic nosign:			sign = '\0';
54434326Sbostic 			/*
54534330Sbostic 			 * ``... diouXx conversions ... if a precision is
54634330Sbostic 			 * specified, the 0 flag will be ignored.''
54734330Sbostic 			 *	-- ANSI X3J11
54834330Sbostic 			 */
54934427Sbostic number:			if ((dprec = prec) >= 0)
55034427Sbostic 				flags &= ~ZEROPAD;
55134427Sbostic 
55234330Sbostic 			/*
55334326Sbostic 			 * ``The result of converting a zero value with an
55434326Sbostic 			 * explicit precision of zero is no characters.''
55534326Sbostic 			 *	-- ANSI X3J11
55634326Sbostic 			 */
55746076Sbostic 			cp = buf + BUF;
55834427Sbostic 			if (_ulong != 0 || prec != 0) {
55946076Sbostic 				/*
56046076Sbostic 				 * unsigned mod is hard, and unsigned mod
56146076Sbostic 				 * by a constant is easier than that by
56246076Sbostic 				 * a variable; hence this switch.
56346076Sbostic 				 */
56446076Sbostic 				switch (base) {
56546076Sbostic 				case OCT:
56646076Sbostic 					do {
56746076Sbostic 						*--cp = to_char(_ulong & 7);
56846076Sbostic 						_ulong >>= 3;
56946076Sbostic 					} while (_ulong);
57046076Sbostic 					/* handle octal leading 0 */
57146076Sbostic 					if (flags & ALT && *cp != '0')
57246076Sbostic 						*--cp = '0';
57346076Sbostic 					break;
57434327Sbostic 
57546076Sbostic 				case DEC:
57646076Sbostic 					/* many numbers are 1 digit */
57746076Sbostic 					while (_ulong >= 10) {
57846076Sbostic 						*--cp = to_char(_ulong % 10);
57946076Sbostic 						_ulong /= 10;
58046076Sbostic 					}
58146076Sbostic 					*--cp = to_char(_ulong);
58246076Sbostic 					break;
58334327Sbostic 
58446076Sbostic 				case HEX:
58546076Sbostic 					do {
58646076Sbostic 						*--cp = xdigs[_ulong & 15];
58746076Sbostic 						_ulong >>= 4;
58846076Sbostic 					} while (_ulong);
58946076Sbostic 					break;
59034327Sbostic 
59146076Sbostic 				default:
59246076Sbostic 					cp = "bug in vfprintf: bad base";
59346180Storek 					size = strlen(cp);
59446076Sbostic 					goto skipsize;
59546076Sbostic 				}
59634327Sbostic 			}
59746076Sbostic 			size = buf + BUF - cp;
59846076Sbostic 		skipsize:
59934226Sbostic 			break;
60046076Sbostic 		default:	/* "%?" prints ?, unless ? is NUL */
60146076Sbostic 			if (ch == '\0')
60246076Sbostic 				goto done;
60346076Sbostic 			/* pretend it was %c with argument ch */
60446076Sbostic 			cp = buf;
60546076Sbostic 			*cp = ch;
60646076Sbostic 			size = 1;
60746076Sbostic 			sign = '\0';
60846076Sbostic 			break;
60934226Sbostic 		}
61046076Sbostic 
61146076Sbostic 		/*
61246076Sbostic 		 * All reasonable formats wind up here.  At this point,
61346076Sbostic 		 * `cp' points to a string which (if not flags&LADJUST)
61446076Sbostic 		 * should be padded out to `width' places.  If
61546076Sbostic 		 * flags&ZEROPAD, it should first be prefixed by any
61646076Sbostic 		 * sign or other prefix; otherwise, it should be blank
61746076Sbostic 		 * padded before the prefix is emitted.  After any
61846076Sbostic 		 * left-hand padding and prefixing, emit zeroes
61946076Sbostic 		 * required by a decimal [diouxX] precision, then print
62046076Sbostic 		 * the string proper, then emit zeroes required by any
62146076Sbostic 		 * leftover floating precision; finally, if LADJUST,
62246076Sbostic 		 * pad with blanks.
62346076Sbostic 		 */
62446076Sbostic 
62546076Sbostic 		/*
62646076Sbostic 		 * compute actual size, so we know how much to pad.
62746076Sbostic 		 * fieldsz excludes decimal prec; realsz includes it
62846076Sbostic 		 */
62946076Sbostic #ifdef FLOATING_POINT
63046076Sbostic 		fieldsz = size + fpprec;
63146076Sbostic #else
63246076Sbostic 		fieldsz = size;
63346076Sbostic #endif
63446076Sbostic 		if (sign)
63546076Sbostic 			fieldsz++;
63646076Sbostic 		else if (flags & HEXPREFIX)
63746076Sbostic 			fieldsz += 2;
63846076Sbostic 		realsz = dprec > fieldsz ? dprec : fieldsz;
63946076Sbostic 
64046076Sbostic 		/* right-adjusting blank padding */
64146076Sbostic 		if ((flags & (LADJUST|ZEROPAD)) == 0)
64246076Sbostic 			PAD(width - realsz, blanks);
64346076Sbostic 
64446076Sbostic 		/* prefix */
64546076Sbostic 		if (sign) {
64646076Sbostic 			PRINT(&sign, 1);
64746076Sbostic 		} else if (flags & HEXPREFIX) {
64846076Sbostic 			ox[0] = '0';
64946076Sbostic 			ox[1] = ch;
65046076Sbostic 			PRINT(ox, 2);
65146076Sbostic 		}
65246076Sbostic 
65346076Sbostic 		/* right-adjusting zero padding */
65446076Sbostic 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
65546076Sbostic 			PAD(width - realsz, zeroes);
65646076Sbostic 
65746076Sbostic 		/* leading zeroes from decimal precision */
65846076Sbostic 		PAD(dprec - fieldsz, zeroes);
65946076Sbostic 
66046076Sbostic 		/* the string or number proper */
66146076Sbostic 		PRINT(cp, size);
66246076Sbostic 
66346076Sbostic #ifdef FLOATING_POINT
66446076Sbostic 		/* trailing f.p. zeroes */
66546076Sbostic 		PAD(fpprec, zeroes);
66646076Sbostic #endif
66746076Sbostic 
66846076Sbostic 		/* left-adjusting padding (always blank) */
66946076Sbostic 		if (flags & LADJUST)
67046076Sbostic 			PAD(width - realsz, blanks);
67146076Sbostic 
67246076Sbostic 		/* finally, adjust ret */
67346076Sbostic 		ret += width > realsz ? width : realsz;
67446076Sbostic 
67546076Sbostic 		FLUSH();	/* copy out the I/O vectors */
67634226Sbostic 	}
67746076Sbostic done:
67846076Sbostic 	FLUSH();
67946076Sbostic error:
68046076Sbostic 	return (__sferror(fp) ? EOF : ret);
68134427Sbostic 	/* NOTREACHED */
68234226Sbostic }
68334242Sbostic 
68446076Sbostic #ifdef FLOATING_POINT
68546180Storek #include <math.h>
68646180Storek 
68746076Sbostic static char *exponent();
68846076Sbostic static char *round();
68946076Sbostic 
69046126Storek #if defined(hp300) || defined(sparc)
69146126Storek /*
69246126Storek  * Check for special IEEE format values (NaN, Inf).
69346126Storek  */
69446126Storek static char *
69546126Storek isspecial(d, signp)
69646126Storek 	double d;
69746161Storek 	char *signp;
69846126Storek {
69946126Storek 	register struct IEEEdp {
70046126Storek 		unsigned sign:1;
70146126Storek 		unsigned exp:11;
70246126Storek 		unsigned manh:20;
70346126Storek 		unsigned manl:32;
70446126Storek 	} *ip = (struct IEEEdp *)&d;
70546126Storek 
70646126Storek 	if (ip->exp != 0x7ff)
70746126Storek 		return (NULL);
70846126Storek 	if (ip->sign)
70946126Storek 		*signp = '-';
71046161Storek 	return (ip->manh || ip->manl ? "NaN" : "Inf");
71146126Storek }
71246126Storek #endif /* hp300 or sparc */
71346126Storek 
71446180Storek static int
71534669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp)
71634242Sbostic 	double number;
71734261Sbostic 	register int prec;
71834323Sbostic 	int flags;
71946076Sbostic 	char *signp;
72046076Sbostic 	int fmtch;
72146076Sbostic 	char *startp, *endp;
72234242Sbostic {
72334331Sbostic 	register char *p, *t;
72434672Sbostic 	register double fract;
72534624Sbostic 	int dotrim, expcnt, gformat;
72646180Storek 	double integer, tmp;
72734242Sbostic 
72846076Sbostic 	dotrim = expcnt = gformat = 0;
72946076Sbostic 	if (number < 0) {
73046076Sbostic 		number = -number;
73146076Sbostic 		*signp = '-';
73246076Sbostic 	} else
73346076Sbostic 		*signp = 0;
73444426Sbostic 
73534624Sbostic 	fract = modf(number, &integer);
73634242Sbostic 
73734624Sbostic 	/* get an extra slot for rounding. */
73834624Sbostic 	t = ++startp;
73934624Sbostic 
74034624Sbostic 	/*
74134624Sbostic 	 * get integer portion of number; put into the end of the buffer; the
74234624Sbostic 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
74334624Sbostic 	 */
74434624Sbostic 	for (p = endp - 1; integer; ++expcnt) {
74534624Sbostic 		tmp = modf(integer / 10, &integer);
74646076Sbostic 		*p-- = to_char((int)((tmp + .01) * 10));
74734248Sbostic 	}
74846076Sbostic 	switch (fmtch) {
74934261Sbostic 	case 'f':
75034624Sbostic 		/* reverse integer into beginning of buffer */
75134624Sbostic 		if (expcnt)
75234624Sbostic 			for (; ++p < endp; *t++ = *p);
75334624Sbostic 		else
75434624Sbostic 			*t++ = '0';
75534248Sbostic 		/*
75634624Sbostic 		 * if precision required or alternate flag set, add in a
75734624Sbostic 		 * decimal point.
75834248Sbostic 		 */
75934624Sbostic 		if (prec || flags&ALT)
76034624Sbostic 			*t++ = '.';
76134624Sbostic 		/* if requires more precision and some fraction left */
76234624Sbostic 		if (fract) {
76334624Sbostic 			if (prec)
76434624Sbostic 				do {
76534624Sbostic 					fract = modf(fract * 10, &tmp);
76646076Sbostic 					*t++ = to_char((int)tmp);
76734624Sbostic 				} while (--prec && fract);
76834624Sbostic 			if (fract)
76934669Sbostic 				startp = round(fract, (int *)NULL, startp,
77034669Sbostic 				    t - 1, (char)0, signp);
77134624Sbostic 		}
77234624Sbostic 		for (; prec--; *t++ = '0');
77334624Sbostic 		break;
77434624Sbostic 	case 'e':
77534624Sbostic 	case 'E':
77634624Sbostic eformat:	if (expcnt) {
77734624Sbostic 			*t++ = *++p;
77834624Sbostic 			if (prec || flags&ALT)
77934331Sbostic 				*t++ = '.';
78034624Sbostic 			/* if requires more precision and some integer left */
78134624Sbostic 			for (; prec && ++p < endp; --prec)
78234624Sbostic 				*t++ = *p;
78334624Sbostic 			/*
78434624Sbostic 			 * if done precision and more of the integer component,
78534624Sbostic 			 * round using it; adjust fract so we don't re-round
78634624Sbostic 			 * later.
78734624Sbostic 			 */
78834624Sbostic 			if (!prec && ++p < endp) {
78934248Sbostic 				fract = 0;
79034669Sbostic 				startp = round((double)0, &expcnt, startp,
79134669Sbostic 				    t - 1, *p, signp);
79234248Sbostic 			}
79334624Sbostic 			/* adjust expcnt for digit in front of decimal */
79434624Sbostic 			--expcnt;
79534242Sbostic 		}
79634624Sbostic 		/* until first fractional digit, decrement exponent */
79734624Sbostic 		else if (fract) {
79834624Sbostic 			/* adjust expcnt for digit in front of decimal */
79934624Sbostic 			for (expcnt = -1;; --expcnt) {
80034624Sbostic 				fract = modf(fract * 10, &tmp);
80134624Sbostic 				if (tmp)
80234624Sbostic 					break;
80334248Sbostic 			}
80446076Sbostic 			*t++ = to_char((int)tmp);
80534624Sbostic 			if (prec || flags&ALT)
80634331Sbostic 				*t++ = '.';
80734242Sbostic 		}
80834248Sbostic 		else {
80934624Sbostic 			*t++ = '0';
81034624Sbostic 			if (prec || flags&ALT)
81134331Sbostic 				*t++ = '.';
81234248Sbostic 		}
81334624Sbostic 		/* if requires more precision and some fraction left */
81434624Sbostic 		if (fract) {
81534624Sbostic 			if (prec)
81634624Sbostic 				do {
81734624Sbostic 					fract = modf(fract * 10, &tmp);
81846076Sbostic 					*t++ = to_char((int)tmp);
81934624Sbostic 				} while (--prec && fract);
82034624Sbostic 			if (fract)
82134669Sbostic 				startp = round(fract, &expcnt, startp,
82234669Sbostic 				    t - 1, (char)0, signp);
82334584Sbostic 		}
82434624Sbostic 		/* if requires more precision */
82534624Sbostic 		for (; prec--; *t++ = '0');
82634624Sbostic 
82734624Sbostic 		/* unless alternate flag, trim any g/G format trailing 0's */
82834624Sbostic 		if (gformat && !(flags&ALT)) {
82934624Sbostic 			while (t > startp && *--t == '0');
83034624Sbostic 			if (*t == '.')
83134624Sbostic 				--t;
83234624Sbostic 			++t;
83334624Sbostic 		}
83434624Sbostic 		t = exponent(t, expcnt, fmtch);
83534624Sbostic 		break;
83634624Sbostic 	case 'g':
83734624Sbostic 	case 'G':
83834624Sbostic 		/* a precision of 0 is treated as a precision of 1. */
83934624Sbostic 		if (!prec)
84034624Sbostic 			++prec;
84134624Sbostic 		/*
84234624Sbostic 		 * ``The style used depends on the value converted; style e
84334624Sbostic 		 * will be used only if the exponent resulting from the
84434624Sbostic 		 * conversion is less than -4 or greater than the precision.''
84534624Sbostic 		 *	-- ANSI X3J11
84634624Sbostic 		 */
84734624Sbostic 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
84834624Sbostic 			/*
84934624Sbostic 			 * g/G format counts "significant digits, not digits of
85034624Sbostic 			 * precision; for the e/E format, this just causes an
85134624Sbostic 			 * off-by-one problem, i.e. g/G considers the digit
85234624Sbostic 			 * before the decimal point significant and e/E doesn't
85334624Sbostic 			 * count it as precision.
85434624Sbostic 			 */
85534624Sbostic 			--prec;
85634624Sbostic 			fmtch -= 2;		/* G->E, g->e */
85734624Sbostic 			gformat = 1;
85834624Sbostic 			goto eformat;
85934624Sbostic 		}
86034624Sbostic 		/*
86134624Sbostic 		 * reverse integer into beginning of buffer,
86234624Sbostic 		 * note, decrement precision
86334624Sbostic 		 */
86434624Sbostic 		if (expcnt)
86534624Sbostic 			for (; ++p < endp; *t++ = *p, --prec);
86634624Sbostic 		else
86734624Sbostic 			*t++ = '0';
86834624Sbostic 		/*
86934624Sbostic 		 * if precision required or alternate flag set, add in a
87034624Sbostic 		 * decimal point.  If no digits yet, add in leading 0.
87134624Sbostic 		 */
87234624Sbostic 		if (prec || flags&ALT) {
87334624Sbostic 			dotrim = 1;
87434624Sbostic 			*t++ = '.';
87534624Sbostic 		}
87634624Sbostic 		else
87734624Sbostic 			dotrim = 0;
87834624Sbostic 		/* if requires more precision and some fraction left */
87934624Sbostic 		if (fract) {
88034624Sbostic 			if (prec) {
88134624Sbostic 				do {
88234624Sbostic 					fract = modf(fract * 10, &tmp);
88346076Sbostic 					*t++ = to_char((int)tmp);
88434624Sbostic 				} while(!tmp);
88534624Sbostic 				while (--prec && fract) {
88634624Sbostic 					fract = modf(fract * 10, &tmp);
88746076Sbostic 					*t++ = to_char((int)tmp);
88834248Sbostic 				}
88934248Sbostic 			}
89034624Sbostic 			if (fract)
89134669Sbostic 				startp = round(fract, (int *)NULL, startp,
89234669Sbostic 				    t - 1, (char)0, signp);
89334624Sbostic 		}
89434624Sbostic 		/* alternate format, adds 0's for precision, else trim 0's */
89534624Sbostic 		if (flags&ALT)
89634624Sbostic 			for (; prec--; *t++ = '0');
89734624Sbostic 		else if (dotrim) {
89834624Sbostic 			while (t > startp && *--t == '0');
89934624Sbostic 			if (*t != '.')
90034624Sbostic 				++t;
90134624Sbostic 		}
90234624Sbostic 	}
90346076Sbostic 	return (t - startp);
90434624Sbostic }
90534248Sbostic 
90634624Sbostic static char *
90734669Sbostic round(fract, exp, start, end, ch, signp)
90834624Sbostic 	double fract;
90934669Sbostic 	int *exp;
91034624Sbostic 	register char *start, *end;
91134669Sbostic 	char ch, *signp;
91234624Sbostic {
91334624Sbostic 	double tmp;
91434248Sbostic 
91534624Sbostic 	if (fract)
91646180Storek 		(void)modf(fract * 10, &tmp);
91734624Sbostic 	else
91846076Sbostic 		tmp = to_digit(ch);
91934624Sbostic 	if (tmp > 4)
92034624Sbostic 		for (;; --end) {
92134624Sbostic 			if (*end == '.')
92234624Sbostic 				--end;
92334624Sbostic 			if (++*end <= '9')
92434624Sbostic 				break;
92534624Sbostic 			*end = '0';
92634624Sbostic 			if (end == start) {
92734669Sbostic 				if (exp) {	/* e/E; increment exponent */
92834669Sbostic 					*end = '1';
92934669Sbostic 					++*exp;
93034669Sbostic 				}
93134669Sbostic 				else {		/* f; add extra digit */
93246076Sbostic 				*--end = '1';
93346076Sbostic 				--start;
93434669Sbostic 				}
93534624Sbostic 				break;
93634242Sbostic 			}
93734242Sbostic 		}
93834669Sbostic 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
93934669Sbostic 	else if (*signp == '-')
94034669Sbostic 		for (;; --end) {
94134669Sbostic 			if (*end == '.')
94234669Sbostic 				--end;
94334669Sbostic 			if (*end != '0')
94434669Sbostic 				break;
94534669Sbostic 			if (end == start)
94634669Sbostic 				*signp = 0;
94734669Sbostic 		}
94846076Sbostic 	return (start);
94934624Sbostic }
95034248Sbostic 
95134624Sbostic static char *
95234624Sbostic exponent(p, exp, fmtch)
95334624Sbostic 	register char *p;
95434624Sbostic 	register int exp;
95546076Sbostic 	int fmtch;
95634624Sbostic {
95734624Sbostic 	register char *t;
95834624Sbostic 	char expbuf[MAXEXP];
95934248Sbostic 
96034624Sbostic 	*p++ = fmtch;
96134624Sbostic 	if (exp < 0) {
96234624Sbostic 		exp = -exp;
96334624Sbostic 		*p++ = '-';
96434242Sbostic 	}
96534624Sbostic 	else
96634624Sbostic 		*p++ = '+';
96734624Sbostic 	t = expbuf + MAXEXP;
96834624Sbostic 	if (exp > 9) {
96934624Sbostic 		do {
97046076Sbostic 			*--t = to_char(exp % 10);
97134624Sbostic 		} while ((exp /= 10) > 9);
97246076Sbostic 		*--t = to_char(exp);
97334624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
97434624Sbostic 	}
97534624Sbostic 	else {
97634624Sbostic 		*p++ = '0';
97746076Sbostic 		*p++ = to_char(exp);
97834624Sbostic 	}
97946076Sbostic 	return (p);
98034242Sbostic }
98146076Sbostic #endif /* FLOATING_POINT */
982