xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 46076)
1*46076Sbostic /*-
2*46076Sbostic  * Copyright (c) 1990 The Regents of the University of California.
334233Sbostic  * All rights reserved.
434226Sbostic  *
5*46076Sbostic  * This code is derived from software contributed to Berkeley by
6*46076Sbostic  * Chris Torek.
7*46076Sbostic  *
842631Sbostic  * %sccs.include.redist.c%
934226Sbostic  */
1034226Sbostic 
1134233Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*46076Sbostic static char sccsid[] = "@(#)vfprintf.c	5.40 (Berkeley) 01/20/91";
1334233Sbostic #endif /* LIBC_SCCS and not lint */
1434233Sbostic 
15*46076Sbostic /*
16*46076Sbostic  * Actual printf innards.
17*46076Sbostic  *
18*46076Sbostic  * This code is large and complicated...
19*46076Sbostic  */
20*46076Sbostic 
2134261Sbostic #include <sys/types.h>
22*46076Sbostic #include <stdio.h>
23*46076Sbostic #include <string.h>
24*46076Sbostic #if __STDC__
25*46076Sbostic #include <stdarg.h>
26*46076Sbostic #else
2734233Sbostic #include <varargs.h>
28*46076Sbostic #endif
29*46076Sbostic #include "local.h"
30*46076Sbostic #include "fvwrite.h"
3134226Sbostic 
32*46076Sbostic /*
33*46076Sbostic  * Define FLOATING_POINT to get floating point.
34*46076Sbostic  * Define CSH to get a csh-specific version (grr).
35*46076Sbostic  */
36*46076Sbostic #ifndef CSH
37*46076Sbostic #define	FLOATING_POINT
38*46076Sbostic #endif
3934226Sbostic 
40*46076Sbostic /* end of configuration stuff */
4134624Sbostic 
42*46076Sbostic 
43*46076Sbostic #ifdef CSH
44*46076Sbostic /*
45*46076Sbostic  * C shell hacks.  Ick, gag.
46*46076Sbostic  */
47*46076Sbostic #undef BUFSIZ
48*46076Sbostic #include "sh.h"
49*46076Sbostic 
50*46076Sbostic printf(fmt, args)
51*46076Sbostic 	char *fmt;
52*46076Sbostic {
53*46076Sbostic 	FILE f;
54*46076Sbostic 
55*46076Sbostic 	f._flags = __SWR;
56*46076Sbostic 	return (vfprintf(&f, fmt, &args));
57*46076Sbostic }
58*46076Sbostic 
59*46076Sbostic #define __sprint(fp, uio) cshprintv(uio)
60*46076Sbostic 
61*46076Sbostic cshprintv(uio)
62*46076Sbostic 	register struct __suio *uio;
63*46076Sbostic {
64*46076Sbostic 	register char *p;
65*46076Sbostic 	register int n, ch, iovcnt;
66*46076Sbostic 	register struct __siov *iov = uio->uio_iov;
67*46076Sbostic 
68*46076Sbostic 	for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) {
69*46076Sbostic 		for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) {
70*46076Sbostic #ifdef CSHPUTCHAR
71*46076Sbostic 			ch = *p++;
72*46076Sbostic 			CSHPUTCHAR;	/* this horrid macro uses `ch' */
73*46076Sbostic #else
74*46076Sbostic #undef putchar
75*46076Sbostic 			putchar(*p++);
76*46076Sbostic #endif
77*46076Sbostic 		}
78*46076Sbostic 	}
79*46076Sbostic 	uio->uio_resid = 0;
80*46076Sbostic 	uio->uio_iovcnt = 0;
81*46076Sbostic 	return (0);
82*46076Sbostic }
83*46076Sbostic 
84*46076Sbostic #else /* CSH */
85*46076Sbostic 
86*46076Sbostic /*
87*46076Sbostic  * Flush out all the vectors defined by the given uio,
88*46076Sbostic  * then reset it so that it can be reused.
89*46076Sbostic  */
90*46076Sbostic static
91*46076Sbostic __sprint(fp, uio)
92*46076Sbostic 	FILE *fp;
93*46076Sbostic 	register struct __suio *uio;
94*46076Sbostic {
95*46076Sbostic 	register int err;
96*46076Sbostic 
97*46076Sbostic 	if (uio->uio_resid == 0) {
98*46076Sbostic 		uio->uio_iovcnt = 0;
99*46076Sbostic 		return (0);
100*46076Sbostic 	}
101*46076Sbostic 	err = __sfvwrite(fp, uio);
102*46076Sbostic 	uio->uio_resid = 0;
103*46076Sbostic 	uio->uio_iovcnt = 0;
104*46076Sbostic 	return (err);
105*46076Sbostic }
106*46076Sbostic 
107*46076Sbostic /*
108*46076Sbostic  * Helper function for `fprintf to unbuffered unix file': creates a
109*46076Sbostic  * temporary buffer.  We only work on write-only files; this avoids
110*46076Sbostic  * worries about ungetc buffers and so forth.
111*46076Sbostic  */
112*46076Sbostic static
113*46076Sbostic __sbprintf(fp, fmt, ap)
114*46076Sbostic 	register FILE *fp;
115*46076Sbostic 	char *fmt;
116*46076Sbostic 	va_list ap;
117*46076Sbostic {
118*46076Sbostic 	int ret;
119*46076Sbostic 	FILE fake;
120*46076Sbostic 	unsigned char buf[BUFSIZ];
121*46076Sbostic 
122*46076Sbostic 	/* copy the important variables */
123*46076Sbostic 	fake._flags = fp->_flags & ~__SNBF;
124*46076Sbostic 	fake._file = fp->_file;
125*46076Sbostic 	fake._cookie = fp->_cookie;
126*46076Sbostic 	fake._write = fp->_write;
127*46076Sbostic 
128*46076Sbostic 	/* set up the buffer */
129*46076Sbostic 	fake._bf._base = fake._p = buf;
130*46076Sbostic 	fake._bf._size = fake._w = sizeof(buf);
131*46076Sbostic 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
132*46076Sbostic 
133*46076Sbostic 	/* do the work, then copy any error status */
134*46076Sbostic 	ret = vfprintf(&fake, fmt, ap);
135*46076Sbostic 	if (ret >= 0 && fflush(&fake))
136*46076Sbostic 		ret = EOF;
137*46076Sbostic 	if (fake._flags & __SERR)
138*46076Sbostic 		fp->_flags |= __SERR;
139*46076Sbostic 	return (ret);
140*46076Sbostic }
141*46076Sbostic 
142*46076Sbostic #endif /* CSH */
143*46076Sbostic 
144*46076Sbostic 
145*46076Sbostic #ifdef FLOATING_POINT
146*46076Sbostic 
147*46076Sbostic #include "floatio.h"
14834328Sbostic #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
149*46076Sbostic #define	DEFPREC		6
15034328Sbostic 
151*46076Sbostic static int cvt();
15234236Sbostic 
153*46076Sbostic #else /* no FLOATING_POINT */
15434235Sbostic 
155*46076Sbostic #define	BUF		40
15634331Sbostic 
157*46076Sbostic #endif /* FLOATING_POINT */
15834318Sbostic 
159*46076Sbostic 
160*46076Sbostic /*
161*46076Sbostic  * Macros for converting digits to letters and vice versa
162*46076Sbostic  */
163*46076Sbostic #define	to_digit(c)	((c) - '0')
164*46076Sbostic #define is_digit(c)	((unsigned)to_digit(c) <= 9)
165*46076Sbostic #define	to_char(n)	((n) + '0')
166*46076Sbostic 
167*46076Sbostic /*
168*46076Sbostic  * Flags used during conversion.
169*46076Sbostic  */
17034318Sbostic #define	LONGINT		0x01		/* long integer */
17134318Sbostic #define	LONGDBL		0x02		/* long double; unimplemented */
17234318Sbostic #define	SHORTINT	0x04		/* short integer */
17334318Sbostic #define	ALT		0x08		/* alternate form */
17434318Sbostic #define	LADJUST		0x10		/* left adjustment */
17534427Sbostic #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
17634427Sbostic #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
17734318Sbostic 
178*46076Sbostic vfprintf(fp, fmt0, ap)
179*46076Sbostic 	FILE *fp;
180*46076Sbostic 	char *fmt0;
181*46076Sbostic #if tahoe
182*46076Sbostic  register /* technically illegal, since we do not know what type va_list is */
183*46076Sbostic #endif
184*46076Sbostic 	va_list ap;
18534226Sbostic {
186*46076Sbostic 	register char *fmt;	/* format string */
18734427Sbostic 	register int ch;	/* character from fmt */
188*46076Sbostic 	register int n;		/* handy integer (short term usage) */
189*46076Sbostic 	register char *cp;	/* handy char pointer (short term usage) */
190*46076Sbostic 	register struct __siov *iovp;/* for PRINT macro */
191*46076Sbostic 	register int flags;	/* flags as above */
192*46076Sbostic 	int ret;		/* return value accumulator */
193*46076Sbostic 	int width;		/* width from format (%8d), or 0 */
194*46076Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
195*46076Sbostic 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
196*46076Sbostic #ifdef FLOATING_POINT
197*46076Sbostic 	char softsign;		/* temporary negative sign for floats */
19834427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
199*46076Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
200*46076Sbostic #endif
20134427Sbostic 	u_long _ulong;		/* integer arguments %[diouxX] */
202*46076Sbostic 	enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
203*46076Sbostic 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
20434624Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
205*46076Sbostic 	int realsz;		/* field size expanded by dprec */
20634427Sbostic 	int size;		/* size of converted field or string */
207*46076Sbostic 	char *xdigs;		/* digits for [xX] conversion */
208*46076Sbostic #define NIOV 8
209*46076Sbostic 	struct __suio uio;	/* output information: summary */
210*46076Sbostic 	struct __siov iov[NIOV];/* ... and individual io vectors */
21134427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
212*46076Sbostic 	char ox[2];		/* space for 0x hex-prefix */
21334226Sbostic 
214*46076Sbostic 	/*
215*46076Sbostic 	 * Choose PADSIZE to trade efficiency vs size.  If larger
216*46076Sbostic 	 * printf fields occur frequently, increase PADSIZE (and make
217*46076Sbostic 	 * the initialisers below longer).
218*46076Sbostic 	 */
219*46076Sbostic #define	PADSIZE	16		/* pad chunk size */
220*46076Sbostic 	static char blanks[PADSIZE] =
221*46076Sbostic 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
222*46076Sbostic 	static char zeroes[PADSIZE] =
223*46076Sbostic 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
224*46076Sbostic 
225*46076Sbostic 	/*
226*46076Sbostic 	 * BEWARE, these `goto error' on error, and PAD uses `n'.
227*46076Sbostic 	 */
228*46076Sbostic #define	PRINT(ptr, len) { \
229*46076Sbostic 	iovp->iov_base = (ptr); \
230*46076Sbostic 	iovp->iov_len = (len); \
231*46076Sbostic 	uio.uio_resid += (len); \
232*46076Sbostic 	iovp++; \
233*46076Sbostic 	if (++uio.uio_iovcnt >= NIOV) { \
234*46076Sbostic 		if (__sprint(fp, &uio)) \
235*46076Sbostic 			goto error; \
236*46076Sbostic 		iovp = iov; \
237*46076Sbostic 	} \
238*46076Sbostic }
239*46076Sbostic #define	PAD(howmany, with) { \
240*46076Sbostic 	if ((n = (howmany)) > 0) { \
241*46076Sbostic 		while (n > PADSIZE) { \
242*46076Sbostic 			PRINT(with, PADSIZE); \
243*46076Sbostic 			n -= PADSIZE; \
244*46076Sbostic 		} \
245*46076Sbostic 		PRINT(with, n); \
246*46076Sbostic 	} \
247*46076Sbostic }
248*46076Sbostic #define	FLUSH() { \
249*46076Sbostic 	if (uio.uio_resid && __sprint(fp, &uio)) \
250*46076Sbostic 		goto error; \
251*46076Sbostic 	uio.uio_iovcnt = 0; \
252*46076Sbostic 	iovp = iov; \
253*46076Sbostic }
254*46076Sbostic 
255*46076Sbostic 	/*
256*46076Sbostic 	 * To extend shorts properly, we need both signed and unsigned
257*46076Sbostic 	 * argument extraction methods.
258*46076Sbostic 	 */
259*46076Sbostic #define	SARG() \
260*46076Sbostic 	(flags&LONGINT ? va_arg(ap, long) : \
261*46076Sbostic 	    flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
262*46076Sbostic 	    (long)va_arg(ap, int))
263*46076Sbostic #define	UARG() \
264*46076Sbostic 	(flags&LONGINT ? va_arg(ap, u_long) : \
265*46076Sbostic 	    flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
266*46076Sbostic 	    (u_long)va_arg(ap, u_int))
267*46076Sbostic 
268*46076Sbostic #ifndef CSH
269*46076Sbostic 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
270*46076Sbostic 	if (cantwrite(fp))
27134428Sbostic 		return (EOF);
27234428Sbostic 
273*46076Sbostic 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
274*46076Sbostic 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
275*46076Sbostic 	    fp->_file >= 0)
276*46076Sbostic 		return (__sbprintf(fp, fmt0, ap));
277*46076Sbostic #endif /* CSH */
278*46076Sbostic 
279*46076Sbostic 	fmt = (char *)fmt0;
280*46076Sbostic 	uio.uio_iov = iovp = iov;
281*46076Sbostic 	uio.uio_resid = 0;
282*46076Sbostic 	uio.uio_iovcnt = 0;
283*46076Sbostic 	ret = 0;
284*46076Sbostic 
285*46076Sbostic 	/*
286*46076Sbostic 	 * Scan the format for conversions (`%' character).
287*46076Sbostic 	 */
288*46076Sbostic 	for (;;) {
289*46076Sbostic 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
290*46076Sbostic 			/* void */;
291*46076Sbostic 		if ((n = fmt - cp) != 0) {
292*46076Sbostic 			PRINT(cp, n);
293*46076Sbostic 			ret += n;
294*46076Sbostic 		}
295*46076Sbostic 		if (ch == '\0')
296*46076Sbostic 			goto done;
297*46076Sbostic 		fmt++;		/* skip over '%' */
298*46076Sbostic 
299*46076Sbostic 		flags = 0;
300*46076Sbostic 		dprec = 0;
301*46076Sbostic #ifdef FLOATING_POINT
302*46076Sbostic 		fpprec = 0;
30334318Sbostic #endif
304*46076Sbostic 		width = 0;
30534233Sbostic 		prec = -1;
30634318Sbostic 		sign = '\0';
30734226Sbostic 
308*46076Sbostic rflag:		ch = *fmt++;
309*46076Sbostic reswitch:	switch (ch) {
31034318Sbostic 		case ' ':
31134669Sbostic 			/*
31234669Sbostic 			 * ``If the space and + flags both appear, the space
31334669Sbostic 			 * flag will be ignored.''
31434669Sbostic 			 *	-- ANSI X3J11
31534669Sbostic 			 */
31634669Sbostic 			if (!sign)
31734669Sbostic 				sign = ' ';
31834318Sbostic 			goto rflag;
31934233Sbostic 		case '#':
32034318Sbostic 			flags |= ALT;
32134318Sbostic 			goto rflag;
32234233Sbostic 		case '*':
32334235Sbostic 			/*
32434235Sbostic 			 * ``A negative field width argument is taken as a
325*46076Sbostic 			 * - flag followed by a positive field width.''
32634235Sbostic 			 *	-- ANSI X3J11
32734235Sbostic 			 * They don't exclude field widths read from args.
32834235Sbostic 			 */
329*46076Sbostic 			if ((width = va_arg(ap, int)) >= 0)
33034318Sbostic 				goto rflag;
33134235Sbostic 			width = -width;
33234427Sbostic 			/* FALLTHROUGH */
33334235Sbostic 		case '-':
33434318Sbostic 			flags |= LADJUST;
33534318Sbostic 			goto rflag;
33634233Sbostic 		case '+':
33734314Sbostic 			sign = '+';
33834318Sbostic 			goto rflag;
33934233Sbostic 		case '.':
340*46076Sbostic 			if ((ch = *fmt++) == '*') {
341*46076Sbostic 				n = va_arg(ap, int);
342*46076Sbostic 				prec = n < 0 ? -1 : n;
343*46076Sbostic 				goto rflag;
34434226Sbostic 			}
345*46076Sbostic 			n = 0;
346*46076Sbostic 			while (is_digit(ch)) {
347*46076Sbostic 				n = 10 * n + to_digit(ch);
348*46076Sbostic 				ch = *fmt++;
349*46076Sbostic 			}
35034318Sbostic 			prec = n < 0 ? -1 : n;
351*46076Sbostic 			goto reswitch;
35234233Sbostic 		case '0':
35334427Sbostic 			/*
35434427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
35534427Sbostic 			 * beginning of a field width.''
35634427Sbostic 			 *	-- ANSI X3J11
35734427Sbostic 			 */
35834427Sbostic 			flags |= ZEROPAD;
35934427Sbostic 			goto rflag;
36034233Sbostic 		case '1': case '2': case '3': case '4':
36134233Sbostic 		case '5': case '6': case '7': case '8': case '9':
36234318Sbostic 			n = 0;
36334233Sbostic 			do {
364*46076Sbostic 				n = 10 * n + to_digit(ch);
365*46076Sbostic 				ch = *fmt++;
366*46076Sbostic 			} while (is_digit(ch));
36734318Sbostic 			width = n;
368*46076Sbostic 			goto reswitch;
369*46076Sbostic #ifdef FLOATING_POINT
37034235Sbostic 		case 'L':
37134329Sbostic 			flags |= LONGDBL;
37234318Sbostic 			goto rflag;
373*46076Sbostic #endif
37434235Sbostic 		case 'h':
37534318Sbostic 			flags |= SHORTINT;
37634318Sbostic 			goto rflag;
37734233Sbostic 		case 'l':
37834318Sbostic 			flags |= LONGINT;
37934318Sbostic 			goto rflag;
38034314Sbostic 		case 'c':
381*46076Sbostic 			*(cp = buf) = va_arg(ap, int);
38234314Sbostic 			size = 1;
38334427Sbostic 			sign = '\0';
384*46076Sbostic 			break;
38534624Sbostic 		case 'D':
38634624Sbostic 			flags |= LONGINT;
38734624Sbostic 			/*FALLTHROUGH*/
38834314Sbostic 		case 'd':
38934318Sbostic 		case 'i':
390*46076Sbostic 			_ulong = SARG();
39134318Sbostic 			if ((long)_ulong < 0) {
39234318Sbostic 				_ulong = -_ulong;
39334314Sbostic 				sign = '-';
39434241Sbostic 			}
395*46076Sbostic 			base = DEC;
39634327Sbostic 			goto number;
397*46076Sbostic #ifdef FLOATING_POINT
39834261Sbostic 		case 'e':
39934236Sbostic 		case 'E':
40034235Sbostic 		case 'f':
40134261Sbostic 		case 'g':
40234243Sbostic 		case 'G':
403*46076Sbostic 			_double = va_arg(ap, double);
40434328Sbostic 			/*
40534669Sbostic 			 * don't do unrealistic precision; just pad it with
40634669Sbostic 			 * zeroes later, so buffer size stays rational.
40734328Sbostic 			 */
40834328Sbostic 			if (prec > MAXFRACT) {
409*46076Sbostic 				if (ch != 'g' && ch != 'G' || (flags&ALT))
41034475Sbostic 					fpprec = prec - MAXFRACT;
41134328Sbostic 				prec = MAXFRACT;
412*46076Sbostic 			} else if (prec == -1)
41334624Sbostic 				prec = DEFPREC;
41434669Sbostic 			/*
415*46076Sbostic 			 * cvt may have to round up before the "start" of
416*46076Sbostic 			 * its buffer, i.e. ``intf("%.2f", (double)9.999);'';
417*46076Sbostic 			 * if the first character is still NUL, it did.
418*46076Sbostic 			 * softsign avoids negative 0 if _double < 0 but
419*46076Sbostic 			 * no significant digits will be shown.
42034669Sbostic 			 */
421*46076Sbostic 			cp = buf;
422*46076Sbostic 			*cp = '\0';
423*46076Sbostic 			size = cvt(_double, prec, flags, &softsign, ch,
424*46076Sbostic 			    cp, buf + sizeof(buf));
42534669Sbostic 			if (softsign)
42634669Sbostic 				sign = '-';
427*46076Sbostic 			if (*cp == '\0')
428*46076Sbostic 				cp++;
429*46076Sbostic 			break;
430*46076Sbostic #endif /* FLOATING_POINT */
43134235Sbostic 		case 'n':
43234427Sbostic 			if (flags & LONGINT)
433*46076Sbostic 				*va_arg(ap, long *) = ret;
43434427Sbostic 			else if (flags & SHORTINT)
435*46076Sbostic 				*va_arg(ap, short *) = ret;
43634318Sbostic 			else
437*46076Sbostic 				*va_arg(ap, int *) = ret;
438*46076Sbostic 			continue;	/* no output */
43934624Sbostic 		case 'O':
44034624Sbostic 			flags |= LONGINT;
44134624Sbostic 			/*FALLTHROUGH*/
44234226Sbostic 		case 'o':
443*46076Sbostic 			_ulong = UARG();
444*46076Sbostic 			base = OCT;
44534327Sbostic 			goto nosign;
44634235Sbostic 		case 'p':
44734320Sbostic 			/*
44834321Sbostic 			 * ``The argument shall be a pointer to void.  The
44934321Sbostic 			 * value of the pointer is converted to a sequence
45034321Sbostic 			 * of printable characters, in an implementation-
45134321Sbostic 			 * defined manner.''
45234321Sbostic 			 *	-- ANSI X3J11
45334320Sbostic 			 */
45434427Sbostic 			/* NOSTRICT */
455*46076Sbostic 			_ulong = (u_long)va_arg(ap, void *);
456*46076Sbostic 			base = HEX;
457*46076Sbostic 			xdigs = "0123456789abcdef";
458*46076Sbostic 			flags |= HEXPREFIX;
459*46076Sbostic 			ch = 'x';
46034327Sbostic 			goto nosign;
46134226Sbostic 		case 's':
462*46076Sbostic 			if ((cp = va_arg(ap, char *)) == NULL)
463*46076Sbostic 				cp = "(null)";
46434321Sbostic 			if (prec >= 0) {
46534321Sbostic 				/*
46634321Sbostic 				 * can't use strlen; can only look for the
46734321Sbostic 				 * NUL in the first `prec' characters, and
46834321Sbostic 				 * strlen() will go further.
46934321Sbostic 				 */
470*46076Sbostic 				char *p = memchr(cp, 0, prec);
47134321Sbostic 
472*46076Sbostic 				if (p != NULL) {
473*46076Sbostic 					size = p - cp;
47434321Sbostic 					if (size > prec)
47534321Sbostic 						size = prec;
47634427Sbostic 				} else
47734321Sbostic 					size = prec;
47834427Sbostic 			} else
479*46076Sbostic 				size = strlen(cp);
48034427Sbostic 			sign = '\0';
481*46076Sbostic 			break;
48234624Sbostic 		case 'U':
48334624Sbostic 			flags |= LONGINT;
48434624Sbostic 			/*FALLTHROUGH*/
48534226Sbostic 		case 'u':
486*46076Sbostic 			_ulong = UARG();
487*46076Sbostic 			base = DEC;
48834327Sbostic 			goto nosign;
48934226Sbostic 		case 'X':
490*46076Sbostic 			xdigs = "0123456789ABCDEF";
491*46076Sbostic 			goto hex;
49234226Sbostic 		case 'x':
493*46076Sbostic 			xdigs = "0123456789abcdef";
494*46076Sbostic hex:			_ulong = UARG();
495*46076Sbostic 			base = HEX;
49634326Sbostic 			/* leading 0x/X only if non-zero */
49734427Sbostic 			if (flags & ALT && _ulong != 0)
49834427Sbostic 				flags |= HEXPREFIX;
49934327Sbostic 
50034327Sbostic 			/* unsigned conversions */
50134427Sbostic nosign:			sign = '\0';
50234326Sbostic 			/*
50334330Sbostic 			 * ``... diouXx conversions ... if a precision is
50434330Sbostic 			 * specified, the 0 flag will be ignored.''
50534330Sbostic 			 *	-- ANSI X3J11
50634330Sbostic 			 */
50734427Sbostic number:			if ((dprec = prec) >= 0)
50834427Sbostic 				flags &= ~ZEROPAD;
50934427Sbostic 
51034330Sbostic 			/*
51134326Sbostic 			 * ``The result of converting a zero value with an
51234326Sbostic 			 * explicit precision of zero is no characters.''
51334326Sbostic 			 *	-- ANSI X3J11
51434326Sbostic 			 */
515*46076Sbostic 			cp = buf + BUF;
51634427Sbostic 			if (_ulong != 0 || prec != 0) {
517*46076Sbostic 				/*
518*46076Sbostic 				 * unsigned mod is hard, and unsigned mod
519*46076Sbostic 				 * by a constant is easier than that by
520*46076Sbostic 				 * a variable; hence this switch.
521*46076Sbostic 				 */
522*46076Sbostic 				switch (base) {
523*46076Sbostic 				case OCT:
524*46076Sbostic 					do {
525*46076Sbostic 						*--cp = to_char(_ulong & 7);
526*46076Sbostic 						_ulong >>= 3;
527*46076Sbostic 					} while (_ulong);
528*46076Sbostic 					/* handle octal leading 0 */
529*46076Sbostic 					if (flags & ALT && *cp != '0')
530*46076Sbostic 						*--cp = '0';
531*46076Sbostic 					break;
53234327Sbostic 
533*46076Sbostic 				case DEC:
534*46076Sbostic 					/* many numbers are 1 digit */
535*46076Sbostic 					while (_ulong >= 10) {
536*46076Sbostic 						*--cp = to_char(_ulong % 10);
537*46076Sbostic 						_ulong /= 10;
538*46076Sbostic 					}
539*46076Sbostic 					*--cp = to_char(_ulong);
540*46076Sbostic 					break;
54134327Sbostic 
542*46076Sbostic 				case HEX:
543*46076Sbostic 					do {
544*46076Sbostic 						*--cp = xdigs[_ulong & 15];
545*46076Sbostic 						_ulong >>= 4;
546*46076Sbostic 					} while (_ulong);
547*46076Sbostic 					break;
54834327Sbostic 
549*46076Sbostic 				default:
550*46076Sbostic 					cp = "bug in vfprintf: bad base";
551*46076Sbostic 					goto skipsize;
552*46076Sbostic 				}
55334327Sbostic 			}
554*46076Sbostic 			size = buf + BUF - cp;
555*46076Sbostic 		skipsize:
55634226Sbostic 			break;
557*46076Sbostic 		default:	/* "%?" prints ?, unless ? is NUL */
558*46076Sbostic 			if (ch == '\0')
559*46076Sbostic 				goto done;
560*46076Sbostic 			/* pretend it was %c with argument ch */
561*46076Sbostic 			cp = buf;
562*46076Sbostic 			*cp = ch;
563*46076Sbostic 			size = 1;
564*46076Sbostic 			sign = '\0';
565*46076Sbostic 			break;
56634226Sbostic 		}
567*46076Sbostic 
568*46076Sbostic 		/*
569*46076Sbostic 		 * All reasonable formats wind up here.  At this point,
570*46076Sbostic 		 * `cp' points to a string which (if not flags&LADJUST)
571*46076Sbostic 		 * should be padded out to `width' places.  If
572*46076Sbostic 		 * flags&ZEROPAD, it should first be prefixed by any
573*46076Sbostic 		 * sign or other prefix; otherwise, it should be blank
574*46076Sbostic 		 * padded before the prefix is emitted.  After any
575*46076Sbostic 		 * left-hand padding and prefixing, emit zeroes
576*46076Sbostic 		 * required by a decimal [diouxX] precision, then print
577*46076Sbostic 		 * the string proper, then emit zeroes required by any
578*46076Sbostic 		 * leftover floating precision; finally, if LADJUST,
579*46076Sbostic 		 * pad with blanks.
580*46076Sbostic 		 */
581*46076Sbostic 
582*46076Sbostic 		/*
583*46076Sbostic 		 * compute actual size, so we know how much to pad.
584*46076Sbostic 		 * fieldsz excludes decimal prec; realsz includes it
585*46076Sbostic 		 */
586*46076Sbostic #ifdef FLOATING_POINT
587*46076Sbostic 		fieldsz = size + fpprec;
588*46076Sbostic #else
589*46076Sbostic 		fieldsz = size;
590*46076Sbostic #endif
591*46076Sbostic 		if (sign)
592*46076Sbostic 			fieldsz++;
593*46076Sbostic 		else if (flags & HEXPREFIX)
594*46076Sbostic 			fieldsz += 2;
595*46076Sbostic 		realsz = dprec > fieldsz ? dprec : fieldsz;
596*46076Sbostic 
597*46076Sbostic 		/* right-adjusting blank padding */
598*46076Sbostic 		if ((flags & (LADJUST|ZEROPAD)) == 0)
599*46076Sbostic 			PAD(width - realsz, blanks);
600*46076Sbostic 
601*46076Sbostic 		/* prefix */
602*46076Sbostic 		if (sign) {
603*46076Sbostic 			PRINT(&sign, 1);
604*46076Sbostic 		} else if (flags & HEXPREFIX) {
605*46076Sbostic 			ox[0] = '0';
606*46076Sbostic 			ox[1] = ch;
607*46076Sbostic 			PRINT(ox, 2);
608*46076Sbostic 		}
609*46076Sbostic 
610*46076Sbostic 		/* right-adjusting zero padding */
611*46076Sbostic 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
612*46076Sbostic 			PAD(width - realsz, zeroes);
613*46076Sbostic 
614*46076Sbostic 		/* leading zeroes from decimal precision */
615*46076Sbostic 		PAD(dprec - fieldsz, zeroes);
616*46076Sbostic 
617*46076Sbostic 		/* the string or number proper */
618*46076Sbostic 		PRINT(cp, size);
619*46076Sbostic 
620*46076Sbostic #ifdef FLOATING_POINT
621*46076Sbostic 		/* trailing f.p. zeroes */
622*46076Sbostic 		PAD(fpprec, zeroes);
623*46076Sbostic #endif
624*46076Sbostic 
625*46076Sbostic 		/* left-adjusting padding (always blank) */
626*46076Sbostic 		if (flags & LADJUST)
627*46076Sbostic 			PAD(width - realsz, blanks);
628*46076Sbostic 
629*46076Sbostic 		/* finally, adjust ret */
630*46076Sbostic 		ret += width > realsz ? width : realsz;
631*46076Sbostic 
632*46076Sbostic 		FLUSH();	/* copy out the I/O vectors */
63334226Sbostic 	}
634*46076Sbostic done:
635*46076Sbostic 	FLUSH();
636*46076Sbostic error:
637*46076Sbostic 	return (__sferror(fp) ? EOF : ret);
63834427Sbostic 	/* NOTREACHED */
63934226Sbostic }
64034242Sbostic 
641*46076Sbostic #ifdef FLOATING_POINT
642*46076Sbostic static char *exponent();
643*46076Sbostic static char *round();
644*46076Sbostic 
64534624Sbostic static
64634669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp)
64734242Sbostic 	double number;
64834261Sbostic 	register int prec;
64934323Sbostic 	int flags;
650*46076Sbostic 	char *signp;
651*46076Sbostic 	int fmtch;
652*46076Sbostic 	char *startp, *endp;
65334242Sbostic {
65434331Sbostic 	register char *p, *t;
65534672Sbostic 	register double fract;
65634624Sbostic 	int dotrim, expcnt, gformat;
65734672Sbostic 	double integer, tmp, modf();
65834242Sbostic 
659*46076Sbostic 	dotrim = expcnt = gformat = 0;
660*46076Sbostic 	if (number < 0) {
661*46076Sbostic 		number = -number;
662*46076Sbostic 		*signp = '-';
663*46076Sbostic 	} else
664*46076Sbostic 		*signp = 0;
66544426Sbostic 
66634624Sbostic 	fract = modf(number, &integer);
66734242Sbostic 
66834624Sbostic 	/* get an extra slot for rounding. */
66934624Sbostic 	t = ++startp;
67034624Sbostic 
67134624Sbostic 	/*
67234624Sbostic 	 * get integer portion of number; put into the end of the buffer; the
67334624Sbostic 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
67434624Sbostic 	 */
67534624Sbostic 	for (p = endp - 1; integer; ++expcnt) {
67634624Sbostic 		tmp = modf(integer / 10, &integer);
677*46076Sbostic 		*p-- = to_char((int)((tmp + .01) * 10));
67834248Sbostic 	}
679*46076Sbostic 	switch (fmtch) {
68034261Sbostic 	case 'f':
68134624Sbostic 		/* reverse integer into beginning of buffer */
68234624Sbostic 		if (expcnt)
68334624Sbostic 			for (; ++p < endp; *t++ = *p);
68434624Sbostic 		else
68534624Sbostic 			*t++ = '0';
68634248Sbostic 		/*
68734624Sbostic 		 * if precision required or alternate flag set, add in a
68834624Sbostic 		 * decimal point.
68934248Sbostic 		 */
69034624Sbostic 		if (prec || flags&ALT)
69134624Sbostic 			*t++ = '.';
69234624Sbostic 		/* if requires more precision and some fraction left */
69334624Sbostic 		if (fract) {
69434624Sbostic 			if (prec)
69534624Sbostic 				do {
69634624Sbostic 					fract = modf(fract * 10, &tmp);
697*46076Sbostic 					*t++ = to_char((int)tmp);
69834624Sbostic 				} while (--prec && fract);
69934624Sbostic 			if (fract)
70034669Sbostic 				startp = round(fract, (int *)NULL, startp,
70134669Sbostic 				    t - 1, (char)0, signp);
70234624Sbostic 		}
70334624Sbostic 		for (; prec--; *t++ = '0');
70434624Sbostic 		break;
70534624Sbostic 	case 'e':
70634624Sbostic 	case 'E':
70734624Sbostic eformat:	if (expcnt) {
70834624Sbostic 			*t++ = *++p;
70934624Sbostic 			if (prec || flags&ALT)
71034331Sbostic 				*t++ = '.';
71134624Sbostic 			/* if requires more precision and some integer left */
71234624Sbostic 			for (; prec && ++p < endp; --prec)
71334624Sbostic 				*t++ = *p;
71434624Sbostic 			/*
71534624Sbostic 			 * if done precision and more of the integer component,
71634624Sbostic 			 * round using it; adjust fract so we don't re-round
71734624Sbostic 			 * later.
71834624Sbostic 			 */
71934624Sbostic 			if (!prec && ++p < endp) {
72034248Sbostic 				fract = 0;
72134669Sbostic 				startp = round((double)0, &expcnt, startp,
72234669Sbostic 				    t - 1, *p, signp);
72334248Sbostic 			}
72434624Sbostic 			/* adjust expcnt for digit in front of decimal */
72534624Sbostic 			--expcnt;
72634242Sbostic 		}
72734624Sbostic 		/* until first fractional digit, decrement exponent */
72834624Sbostic 		else if (fract) {
72934624Sbostic 			/* adjust expcnt for digit in front of decimal */
73034624Sbostic 			for (expcnt = -1;; --expcnt) {
73134624Sbostic 				fract = modf(fract * 10, &tmp);
73234624Sbostic 				if (tmp)
73334624Sbostic 					break;
73434248Sbostic 			}
735*46076Sbostic 			*t++ = to_char((int)tmp);
73634624Sbostic 			if (prec || flags&ALT)
73734331Sbostic 				*t++ = '.';
73834242Sbostic 		}
73934248Sbostic 		else {
74034624Sbostic 			*t++ = '0';
74134624Sbostic 			if (prec || flags&ALT)
74234331Sbostic 				*t++ = '.';
74334248Sbostic 		}
74434624Sbostic 		/* if requires more precision and some fraction left */
74534624Sbostic 		if (fract) {
74634624Sbostic 			if (prec)
74734624Sbostic 				do {
74834624Sbostic 					fract = modf(fract * 10, &tmp);
749*46076Sbostic 					*t++ = to_char((int)tmp);
75034624Sbostic 				} while (--prec && fract);
75134624Sbostic 			if (fract)
75234669Sbostic 				startp = round(fract, &expcnt, startp,
75334669Sbostic 				    t - 1, (char)0, signp);
75434584Sbostic 		}
75534624Sbostic 		/* if requires more precision */
75634624Sbostic 		for (; prec--; *t++ = '0');
75734624Sbostic 
75834624Sbostic 		/* unless alternate flag, trim any g/G format trailing 0's */
75934624Sbostic 		if (gformat && !(flags&ALT)) {
76034624Sbostic 			while (t > startp && *--t == '0');
76134624Sbostic 			if (*t == '.')
76234624Sbostic 				--t;
76334624Sbostic 			++t;
76434624Sbostic 		}
76534624Sbostic 		t = exponent(t, expcnt, fmtch);
76634624Sbostic 		break;
76734624Sbostic 	case 'g':
76834624Sbostic 	case 'G':
76934624Sbostic 		/* a precision of 0 is treated as a precision of 1. */
77034624Sbostic 		if (!prec)
77134624Sbostic 			++prec;
77234624Sbostic 		/*
77334624Sbostic 		 * ``The style used depends on the value converted; style e
77434624Sbostic 		 * will be used only if the exponent resulting from the
77534624Sbostic 		 * conversion is less than -4 or greater than the precision.''
77634624Sbostic 		 *	-- ANSI X3J11
77734624Sbostic 		 */
77834624Sbostic 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
77934624Sbostic 			/*
78034624Sbostic 			 * g/G format counts "significant digits, not digits of
78134624Sbostic 			 * precision; for the e/E format, this just causes an
78234624Sbostic 			 * off-by-one problem, i.e. g/G considers the digit
78334624Sbostic 			 * before the decimal point significant and e/E doesn't
78434624Sbostic 			 * count it as precision.
78534624Sbostic 			 */
78634624Sbostic 			--prec;
78734624Sbostic 			fmtch -= 2;		/* G->E, g->e */
78834624Sbostic 			gformat = 1;
78934624Sbostic 			goto eformat;
79034624Sbostic 		}
79134624Sbostic 		/*
79234624Sbostic 		 * reverse integer into beginning of buffer,
79334624Sbostic 		 * note, decrement precision
79434624Sbostic 		 */
79534624Sbostic 		if (expcnt)
79634624Sbostic 			for (; ++p < endp; *t++ = *p, --prec);
79734624Sbostic 		else
79834624Sbostic 			*t++ = '0';
79934624Sbostic 		/*
80034624Sbostic 		 * if precision required or alternate flag set, add in a
80134624Sbostic 		 * decimal point.  If no digits yet, add in leading 0.
80234624Sbostic 		 */
80334624Sbostic 		if (prec || flags&ALT) {
80434624Sbostic 			dotrim = 1;
80534624Sbostic 			*t++ = '.';
80634624Sbostic 		}
80734624Sbostic 		else
80834624Sbostic 			dotrim = 0;
80934624Sbostic 		/* if requires more precision and some fraction left */
81034624Sbostic 		if (fract) {
81134624Sbostic 			if (prec) {
81234624Sbostic 				do {
81334624Sbostic 					fract = modf(fract * 10, &tmp);
814*46076Sbostic 					*t++ = to_char((int)tmp);
81534624Sbostic 				} while(!tmp);
81634624Sbostic 				while (--prec && fract) {
81734624Sbostic 					fract = modf(fract * 10, &tmp);
818*46076Sbostic 					*t++ = to_char((int)tmp);
81934248Sbostic 				}
82034248Sbostic 			}
82134624Sbostic 			if (fract)
82234669Sbostic 				startp = round(fract, (int *)NULL, startp,
82334669Sbostic 				    t - 1, (char)0, signp);
82434624Sbostic 		}
82534624Sbostic 		/* alternate format, adds 0's for precision, else trim 0's */
82634624Sbostic 		if (flags&ALT)
82734624Sbostic 			for (; prec--; *t++ = '0');
82834624Sbostic 		else if (dotrim) {
82934624Sbostic 			while (t > startp && *--t == '0');
83034624Sbostic 			if (*t != '.')
83134624Sbostic 				++t;
83234624Sbostic 		}
83334624Sbostic 	}
834*46076Sbostic 	return (t - startp);
83534624Sbostic }
83634248Sbostic 
83734624Sbostic static char *
83834669Sbostic round(fract, exp, start, end, ch, signp)
83934624Sbostic 	double fract;
84034669Sbostic 	int *exp;
84134624Sbostic 	register char *start, *end;
84234669Sbostic 	char ch, *signp;
84334624Sbostic {
84434624Sbostic 	double tmp;
84534248Sbostic 
84634624Sbostic 	if (fract)
847*46076Sbostic 	(void)modf(fract * 10, &tmp);
84834624Sbostic 	else
849*46076Sbostic 		tmp = to_digit(ch);
85034624Sbostic 	if (tmp > 4)
85134624Sbostic 		for (;; --end) {
85234624Sbostic 			if (*end == '.')
85334624Sbostic 				--end;
85434624Sbostic 			if (++*end <= '9')
85534624Sbostic 				break;
85634624Sbostic 			*end = '0';
85734624Sbostic 			if (end == start) {
85834669Sbostic 				if (exp) {	/* e/E; increment exponent */
85934669Sbostic 					*end = '1';
86034669Sbostic 					++*exp;
86134669Sbostic 				}
86234669Sbostic 				else {		/* f; add extra digit */
863*46076Sbostic 				*--end = '1';
864*46076Sbostic 				--start;
86534669Sbostic 				}
86634624Sbostic 				break;
86734242Sbostic 			}
86834242Sbostic 		}
86934669Sbostic 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
87034669Sbostic 	else if (*signp == '-')
87134669Sbostic 		for (;; --end) {
87234669Sbostic 			if (*end == '.')
87334669Sbostic 				--end;
87434669Sbostic 			if (*end != '0')
87534669Sbostic 				break;
87634669Sbostic 			if (end == start)
87734669Sbostic 				*signp = 0;
87834669Sbostic 		}
879*46076Sbostic 	return (start);
88034624Sbostic }
88134248Sbostic 
88234624Sbostic static char *
88334624Sbostic exponent(p, exp, fmtch)
88434624Sbostic 	register char *p;
88534624Sbostic 	register int exp;
886*46076Sbostic 	int fmtch;
88734624Sbostic {
88834624Sbostic 	register char *t;
88934624Sbostic 	char expbuf[MAXEXP];
89034248Sbostic 
89134624Sbostic 	*p++ = fmtch;
89234624Sbostic 	if (exp < 0) {
89334624Sbostic 		exp = -exp;
89434624Sbostic 		*p++ = '-';
89534242Sbostic 	}
89634624Sbostic 	else
89734624Sbostic 		*p++ = '+';
89834624Sbostic 	t = expbuf + MAXEXP;
89934624Sbostic 	if (exp > 9) {
90034624Sbostic 		do {
901*46076Sbostic 			*--t = to_char(exp % 10);
90234624Sbostic 		} while ((exp /= 10) > 9);
903*46076Sbostic 		*--t = to_char(exp);
90434624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
90534624Sbostic 	}
90634624Sbostic 	else {
90734624Sbostic 		*p++ = '0';
908*46076Sbostic 		*p++ = to_char(exp);
90934624Sbostic 	}
910*46076Sbostic 	return (p);
91134242Sbostic }
912*46076Sbostic #endif /* FLOATING_POINT */
913