xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34669)
134226Sbostic /*
234233Sbostic  * Copyright (c) 1988 Regents of the University of California.
334233Sbostic  * All rights reserved.
434226Sbostic  *
534233Sbostic  * Redistribution and use in source and binary forms are permitted
634233Sbostic  * provided that this notice is preserved and that due credit is given
734233Sbostic  * to the University of California at Berkeley. The name of the University
834233Sbostic  * may not be used to endorse or promote products derived from this
934233Sbostic  * software without specific prior written permission. This software
1034233Sbostic  * is provided ``as is'' without express or implied warranty.
1134226Sbostic  */
1234226Sbostic 
1334233Sbostic #if defined(LIBC_SCCS) && !defined(lint)
14*34669Sbostic static char sccsid[] = "@(#)vfprintf.c	5.33 (Berkeley) 06/07/88";
1534233Sbostic #endif /* LIBC_SCCS and not lint */
1634233Sbostic 
1734261Sbostic #include <sys/types.h>
1834233Sbostic #include <varargs.h>
1934226Sbostic #include <stdio.h>
2034233Sbostic #include <ctype.h>
2134226Sbostic 
2234328Sbostic /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
2334328Sbostic #define	MAXEXP		308
2434328Sbostic /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
2534328Sbostic #define	MAXFRACT	39
2634226Sbostic 
2734624Sbostic #define	DEFPREC		6
2834624Sbostic 
2934328Sbostic #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
3034328Sbostic 
3134427Sbostic #define	PUTC(ch)	(void) putc(ch, fp)
3234236Sbostic 
3334318Sbostic #define	ARG() \
3434318Sbostic 	_ulong = flags&LONGINT ? va_arg(argp, long) : \
3534318Sbostic 	    flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
3634235Sbostic 
3734331Sbostic #define	todigit(c)	((c) - '0')
3834331Sbostic #define	tochar(n)	((n) + '0')
3934331Sbostic 
4034318Sbostic /* have to deal with the negative buffer count kludge */
4134318Sbostic #define	NEGATIVE_COUNT_KLUDGE
4234318Sbostic 
4334318Sbostic #define	LONGINT		0x01		/* long integer */
4434318Sbostic #define	LONGDBL		0x02		/* long double; unimplemented */
4534318Sbostic #define	SHORTINT	0x04		/* short integer */
4634318Sbostic #define	ALT		0x08		/* alternate form */
4734318Sbostic #define	LADJUST		0x10		/* left adjustment */
4834427Sbostic #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
4934427Sbostic #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
5034318Sbostic 
5134323Sbostic _doprnt(fmt0, argp, fp)
5234323Sbostic 	u_char *fmt0;
5334233Sbostic 	va_list argp;
5434235Sbostic 	register FILE *fp;
5534226Sbostic {
5634427Sbostic 	register u_char *fmt;	/* format string */
5734427Sbostic 	register int ch;	/* character from fmt */
5834427Sbostic 	register int cnt;	/* return value accumulator */
5934427Sbostic 	register int n;		/* random handy integer */
6034427Sbostic 	register char *t;	/* buffer pointer */
6134427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
6234427Sbostic 	u_long _ulong;		/* integer arguments %[diouxX] */
6334624Sbostic 	int base;		/* base for [diouxX] conversion */
6434624Sbostic 	int dprec;		/* decimal precision in [diouxX] */
6534624Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
6634427Sbostic 	int flags;		/* flags as above */
6734427Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
6834427Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
6934624Sbostic 	int realsz;		/* field size expanded by decimal precision */
7034427Sbostic 	int size;		/* size of converted field or string */
7134624Sbostic 	int width;		/* width from format (%8d), or 0 */
72*34669Sbostic 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
73*34669Sbostic 	char softsign;		/* temporary negative sign for floats */
7434427Sbostic 	char *digs;		/* digits for [diouxX] conversion */
7534427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
7634226Sbostic 
7734428Sbostic 	if (fp->_flag & _IORW) {
7834428Sbostic 		fp->_flag |= _IOWRT;
7934428Sbostic 		fp->_flag &= ~(_IOEOF|_IOREAD);
8034428Sbostic 	}
8134428Sbostic 	if ((fp->_flag & _IOWRT) == 0)
8234428Sbostic 		return (EOF);
8334428Sbostic 
8434323Sbostic 	fmt = fmt0;
8534243Sbostic 	digs = "0123456789abcdef";
8634322Sbostic 	for (cnt = 0;; ++fmt) {
8734318Sbostic 		n = fp->_cnt;
8834427Sbostic 		for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
8934427Sbostic 		     ++cnt, ++fmt)
9034318Sbostic 			if (--n < 0
9134318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
9234318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
9334318Sbostic #endif
9434427Sbostic 			    || ch == '\n' && fp->_flag & _IOLBF) {
9534318Sbostic 				fp->_cnt = n;
9634475Sbostic 				fp->_ptr = t;
9734427Sbostic 				(void) _flsbuf((u_char)ch, fp);
9834318Sbostic 				n = fp->_cnt;
9934427Sbostic 				t = (char *)fp->_ptr;
10034427Sbostic 			} else
10134314Sbostic 				*t++ = ch;
10234318Sbostic 		fp->_cnt = n;
10334475Sbostic 		fp->_ptr = t;
10434318Sbostic 		if (!ch)
10534427Sbostic 			return (cnt);
10634314Sbostic 
10734427Sbostic 		flags = dprec = fpprec = width = 0;
10834233Sbostic 		prec = -1;
10934318Sbostic 		sign = '\0';
11034226Sbostic 
11134318Sbostic rflag:		switch (*++fmt) {
11234318Sbostic 		case ' ':
113*34669Sbostic 			/*
114*34669Sbostic 			 * ``If the space and + flags both appear, the space
115*34669Sbostic 			 * flag will be ignored.''
116*34669Sbostic 			 *	-- ANSI X3J11
117*34669Sbostic 			 */
118*34669Sbostic 			if (!sign)
119*34669Sbostic 				sign = ' ';
12034318Sbostic 			goto rflag;
12134233Sbostic 		case '#':
12234318Sbostic 			flags |= ALT;
12334318Sbostic 			goto rflag;
12434233Sbostic 		case '*':
12534235Sbostic 			/*
12634235Sbostic 			 * ``A negative field width argument is taken as a
12734235Sbostic 			 * - flag followed by a  positive field width.''
12834235Sbostic 			 *	-- ANSI X3J11
12934235Sbostic 			 * They don't exclude field widths read from args.
13034235Sbostic 			 */
13134235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
13234318Sbostic 				goto rflag;
13334235Sbostic 			width = -width;
13434427Sbostic 			/* FALLTHROUGH */
13534235Sbostic 		case '-':
13634318Sbostic 			flags |= LADJUST;
13734318Sbostic 			goto rflag;
13834233Sbostic 		case '+':
13934314Sbostic 			sign = '+';
14034318Sbostic 			goto rflag;
14134233Sbostic 		case '.':
14234235Sbostic 			if (*++fmt == '*')
14334318Sbostic 				n = va_arg(argp, int);
14434427Sbostic 			else {
14534318Sbostic 				n = 0;
14634427Sbostic 				while (isascii(*fmt) && isdigit(*fmt))
14734427Sbostic 					n = 10 * n + todigit(*fmt++);
14834233Sbostic 				--fmt;
14934226Sbostic 			}
15034318Sbostic 			prec = n < 0 ? -1 : n;
15134318Sbostic 			goto rflag;
15234233Sbostic 		case '0':
15334427Sbostic 			/*
15434427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
15534427Sbostic 			 * beginning of a field width.''
15634427Sbostic 			 *	-- ANSI X3J11
15734427Sbostic 			 */
15834427Sbostic 			flags |= ZEROPAD;
15934427Sbostic 			goto rflag;
16034233Sbostic 		case '1': case '2': case '3': case '4':
16134233Sbostic 		case '5': case '6': case '7': case '8': case '9':
16234318Sbostic 			n = 0;
16334233Sbostic 			do {
16434331Sbostic 				n = 10 * n + todigit(*fmt);
16534318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
16634318Sbostic 			width = n;
16734233Sbostic 			--fmt;
16834319Sbostic 			goto rflag;
16934235Sbostic 		case 'L':
17034329Sbostic 			flags |= LONGDBL;
17134318Sbostic 			goto rflag;
17234235Sbostic 		case 'h':
17334318Sbostic 			flags |= SHORTINT;
17434318Sbostic 			goto rflag;
17534233Sbostic 		case 'l':
17634318Sbostic 			flags |= LONGINT;
17734318Sbostic 			goto rflag;
17834314Sbostic 		case 'c':
17934427Sbostic 			*(t = buf) = va_arg(argp, int);
18034314Sbostic 			size = 1;
18134427Sbostic 			sign = '\0';
18234314Sbostic 			goto pforw;
18334624Sbostic 		case 'D':
18434624Sbostic 			flags |= LONGINT;
18534624Sbostic 			/*FALLTHROUGH*/
18634314Sbostic 		case 'd':
18734318Sbostic 		case 'i':
18834318Sbostic 			ARG();
18934318Sbostic 			if ((long)_ulong < 0) {
19034318Sbostic 				_ulong = -_ulong;
19134314Sbostic 				sign = '-';
19234241Sbostic 			}
19334241Sbostic 			base = 10;
19434327Sbostic 			goto number;
19534261Sbostic 		case 'e':
19634236Sbostic 		case 'E':
19734235Sbostic 		case 'f':
19834261Sbostic 		case 'g':
19934243Sbostic 		case 'G':
20034243Sbostic 			_double = va_arg(argp, double);
20134328Sbostic 			/*
202*34669Sbostic 			 * don't do unrealistic precision; just pad it with
203*34669Sbostic 			 * zeroes later, so buffer size stays rational.
20434328Sbostic 			 */
20534328Sbostic 			if (prec > MAXFRACT) {
20634475Sbostic 				if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
20734475Sbostic 					fpprec = prec - MAXFRACT;
20834328Sbostic 				prec = MAXFRACT;
20934328Sbostic 			}
21034624Sbostic 			else if (prec == -1)
21134624Sbostic 				prec = DEFPREC;
212*34669Sbostic 			/*
213*34669Sbostic 			 * softsign avoids negative 0 if _double is < 0 and
214*34669Sbostic 			 * no significant digits will be shown
215*34669Sbostic 			 */
21634624Sbostic 			if (_double < 0) {
217*34669Sbostic 				softsign = '-';
21834624Sbostic 				_double = -_double;
21934624Sbostic 			}
220*34669Sbostic 			else
221*34669Sbostic 				softsign = 0;
22234624Sbostic 			/*
223*34669Sbostic 			 * cvt may have to round up past the "start" of the
22434624Sbostic 			 * buffer, i.e. ``intf("%.2f", (double)9.999);'';
22534624Sbostic 			 * if the first char isn't NULL, it did.
22634624Sbostic 			 */
22734624Sbostic 			*buf = NULL;
228*34669Sbostic 			size = cvt(_double, prec, flags, &softsign, *fmt, buf,
22934624Sbostic 			    buf + sizeof(buf));
230*34669Sbostic 			if (softsign)
231*34669Sbostic 				sign = '-';
23234624Sbostic 			t = *buf ? buf : buf + 1;
23334314Sbostic 			goto pforw;
23434235Sbostic 		case 'n':
23534427Sbostic 			if (flags & LONGINT)
23634318Sbostic 				*va_arg(argp, long *) = cnt;
23734427Sbostic 			else if (flags & SHORTINT)
23834318Sbostic 				*va_arg(argp, short *) = cnt;
23934318Sbostic 			else
24034318Sbostic 				*va_arg(argp, int *) = cnt;
24134235Sbostic 			break;
24234624Sbostic 		case 'O':
24334624Sbostic 			flags |= LONGINT;
24434624Sbostic 			/*FALLTHROUGH*/
24534226Sbostic 		case 'o':
24634318Sbostic 			ARG();
24734226Sbostic 			base = 8;
24834327Sbostic 			goto nosign;
24934235Sbostic 		case 'p':
25034320Sbostic 			/*
25134321Sbostic 			 * ``The argument shall be a pointer to void.  The
25234321Sbostic 			 * value of the pointer is converted to a sequence
25334321Sbostic 			 * of printable characters, in an implementation-
25434321Sbostic 			 * defined manner.''
25534321Sbostic 			 *	-- ANSI X3J11
25634320Sbostic 			 */
25734427Sbostic 			/* NOSTRICT */
25834320Sbostic 			_ulong = (u_long)va_arg(argp, void *);
25934320Sbostic 			base = 16;
26034327Sbostic 			goto nosign;
26134226Sbostic 		case 's':
26234314Sbostic 			if (!(t = va_arg(argp, char *)))
26334314Sbostic 				t = "(null)";
26434321Sbostic 			if (prec >= 0) {
26534321Sbostic 				/*
26634321Sbostic 				 * can't use strlen; can only look for the
26734321Sbostic 				 * NUL in the first `prec' characters, and
26834321Sbostic 				 * strlen() will go further.
26934321Sbostic 				 */
27034321Sbostic 				char *p, *memchr();
27134321Sbostic 
27234321Sbostic 				if (p = memchr(t, 0, prec)) {
27334321Sbostic 					size = p - t;
27434321Sbostic 					if (size > prec)
27534321Sbostic 						size = prec;
27634427Sbostic 				} else
27734321Sbostic 					size = prec;
27834427Sbostic 			} else
27934321Sbostic 				size = strlen(t);
28034427Sbostic 			sign = '\0';
28134328Sbostic 			goto pforw;
28234624Sbostic 		case 'U':
28334624Sbostic 			flags |= LONGINT;
28434624Sbostic 			/*FALLTHROUGH*/
28534226Sbostic 		case 'u':
28634318Sbostic 			ARG();
28734226Sbostic 			base = 10;
28834327Sbostic 			goto nosign;
28934226Sbostic 		case 'X':
29034226Sbostic 			digs = "0123456789ABCDEF";
29134427Sbostic 			/* FALLTHROUGH */
29234226Sbostic 		case 'x':
29334318Sbostic 			ARG();
29434314Sbostic 			base = 16;
29534326Sbostic 			/* leading 0x/X only if non-zero */
29634427Sbostic 			if (flags & ALT && _ulong != 0)
29734427Sbostic 				flags |= HEXPREFIX;
29834327Sbostic 
29934327Sbostic 			/* unsigned conversions */
30034427Sbostic nosign:			sign = '\0';
30134326Sbostic 			/*
30234330Sbostic 			 * ``... diouXx conversions ... if a precision is
30334330Sbostic 			 * specified, the 0 flag will be ignored.''
30434330Sbostic 			 *	-- ANSI X3J11
30534330Sbostic 			 */
30634427Sbostic number:			if ((dprec = prec) >= 0)
30734427Sbostic 				flags &= ~ZEROPAD;
30834427Sbostic 
30934330Sbostic 			/*
31034326Sbostic 			 * ``The result of converting a zero value with an
31134326Sbostic 			 * explicit precision of zero is no characters.''
31234326Sbostic 			 *	-- ANSI X3J11
31334326Sbostic 			 */
31434427Sbostic 			t = buf + BUF;
31534427Sbostic 			if (_ulong != 0 || prec != 0) {
31634427Sbostic 				do {
31734427Sbostic 					*--t = digs[_ulong % base];
31834427Sbostic 					_ulong /= base;
31934427Sbostic 				} while (_ulong);
32034427Sbostic 				digs = "0123456789abcdef";
32134427Sbostic 				if (flags & ALT && base == 8 && *t != '0')
32234427Sbostic 					*--t = '0'; /* octal leading 0 */
32334330Sbostic 			}
32434427Sbostic 			size = buf + BUF - t;
32534327Sbostic 
32634427Sbostic pforw:
32734427Sbostic 			/*
32834624Sbostic 			 * All reasonable formats wind up here.  At this point,
32934624Sbostic 			 * `t' points to a string which (if not flags&LADJUST)
33034624Sbostic 			 * should be padded out to `width' places.  If
33134624Sbostic 			 * flags&ZEROPAD, it should first be prefixed by any
33234624Sbostic 			 * sign or other prefix; otherwise, it should be blank
33334624Sbostic 			 * padded before the prefix is emitted.  After any
33434624Sbostic 			 * left-hand padding and prefixing, emit zeroes
33534624Sbostic 			 * required by a decimal [diouxX] precision, then print
33634624Sbostic 			 * the string proper, then emit zeroes required by any
33734624Sbostic 			 * leftover floating precision; finally, if LADJUST,
33834624Sbostic 			 * pad with blanks.
33934427Sbostic 			 */
34034327Sbostic 
34134624Sbostic 			/*
34234624Sbostic 			 * compute actual size, so we know how much to pad
34334624Sbostic 			 * fieldsz excludes decimal prec; realsz includes it
34434624Sbostic 			 */
34534427Sbostic 			fieldsz = size + fpprec;
34634427Sbostic 			if (sign)
34734427Sbostic 				fieldsz++;
34834427Sbostic 			if (flags & HEXPREFIX)
34934427Sbostic 				fieldsz += 2;
35034427Sbostic 			realsz = dprec > fieldsz ? dprec : fieldsz;
35134327Sbostic 
35234427Sbostic 			/* right-adjusting blank padding */
35334427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
35434427Sbostic 				for (n = realsz; n < width; n++)
35534427Sbostic 					PUTC(' ');
35634427Sbostic 			/* prefix */
35734427Sbostic 			if (sign)
35834427Sbostic 				PUTC(sign);
35934427Sbostic 			if (flags & HEXPREFIX) {
36034427Sbostic 				PUTC('0');
36134427Sbostic 				PUTC((char)*fmt);
36234327Sbostic 			}
36334427Sbostic 			/* right-adjusting zero padding */
36434427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
36534427Sbostic 				for (n = realsz; n < width; n++)
36634427Sbostic 					PUTC('0');
36734427Sbostic 			/* leading zeroes from decimal precision */
36834427Sbostic 			for (n = fieldsz; n < dprec; n++)
36934427Sbostic 				PUTC('0');
37034327Sbostic 
37134427Sbostic 			/* the string or number proper */
37234427Sbostic 			if (fp->_cnt - (n = size) >= 0 &&
37334427Sbostic 			    (fp->_flag & _IOLBF) == 0) {
37434328Sbostic 				fp->_cnt -= n;
37534427Sbostic 				bcopy(t, (char *)fp->_ptr, n);
37634328Sbostic 				fp->_ptr += n;
37734427Sbostic 			} else
37834427Sbostic 				while (--n >= 0)
37934427Sbostic 					PUTC(*t++);
38034427Sbostic 			/* trailing f.p. zeroes */
38134427Sbostic 			while (--fpprec >= 0)
38234328Sbostic 				PUTC('0');
38334427Sbostic 			/* left-adjusting padding (always blank) */
38434427Sbostic 			if (flags & LADJUST)
38534427Sbostic 				for (n = realsz; n < width; n++)
38634328Sbostic 					PUTC(' ');
38734427Sbostic 			/* finally, adjust cnt */
38834427Sbostic 			cnt += width > realsz ? width : realsz;
38934226Sbostic 			break;
39034427Sbostic 		case '\0':	/* "%?" prints ?, unless ? is NULL */
39134427Sbostic 			return (cnt);
39234226Sbostic 		default:
39334427Sbostic 			PUTC((char)*fmt);
39434427Sbostic 			cnt++;
39534226Sbostic 		}
39634226Sbostic 	}
39734427Sbostic 	/* NOTREACHED */
39834226Sbostic }
39934242Sbostic 
40034624Sbostic static
401*34669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp)
40234242Sbostic 	double number;
40334261Sbostic 	register int prec;
40434323Sbostic 	int flags;
40534323Sbostic 	u_char fmtch;
406*34669Sbostic 	char *signp, *startp, *endp;
40734242Sbostic {
40834331Sbostic 	register char *p, *t;
40934248Sbostic 	double fract, integer, tmp, modf();
41034624Sbostic 	int dotrim, expcnt, gformat;
411*34669Sbostic 	char *exponent(), *round();
41234242Sbostic 
41334624Sbostic 	dotrim = expcnt = gformat = 0;
41434624Sbostic 	fract = modf(number, &integer);
41534242Sbostic 
41634624Sbostic 	/* get an extra slot for rounding. */
41734624Sbostic 	t = ++startp;
41834624Sbostic 
41934624Sbostic 	/*
42034624Sbostic 	 * get integer portion of number; put into the end of the buffer; the
42134624Sbostic 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
42234624Sbostic 	 */
42334624Sbostic 	for (p = endp - 1; integer; ++expcnt) {
42434624Sbostic 		tmp = modf(integer / 10, &integer);
42534624Sbostic 		*p-- = tochar((int)((tmp + .01) * 10));
42634248Sbostic 	}
42734261Sbostic 	switch(fmtch) {
42834261Sbostic 	case 'f':
42934624Sbostic 		/* reverse integer into beginning of buffer */
43034624Sbostic 		if (expcnt)
43134624Sbostic 			for (; ++p < endp; *t++ = *p);
43234624Sbostic 		else
43334624Sbostic 			*t++ = '0';
43434248Sbostic 		/*
43534624Sbostic 		 * if precision required or alternate flag set, add in a
43634624Sbostic 		 * decimal point.
43734248Sbostic 		 */
43834624Sbostic 		if (prec || flags&ALT)
43934624Sbostic 			*t++ = '.';
44034624Sbostic 		/* if requires more precision and some fraction left */
44134624Sbostic 		if (fract) {
44234624Sbostic 			if (prec)
44334624Sbostic 				do {
44434624Sbostic 					fract = modf(fract * 10, &tmp);
44534624Sbostic 					*t++ = tochar((int)tmp);
44634624Sbostic 				} while (--prec && fract);
44734624Sbostic 			if (fract)
448*34669Sbostic 				startp = round(fract, (int *)NULL, startp,
449*34669Sbostic 				    t - 1, (char)0, signp);
45034624Sbostic 		}
45134624Sbostic 		for (; prec--; *t++ = '0');
45234624Sbostic 		break;
45334624Sbostic 	case 'e':
45434624Sbostic 	case 'E':
45534624Sbostic eformat:	if (expcnt) {
45634624Sbostic 			*t++ = *++p;
45734624Sbostic 			if (prec || flags&ALT)
45834331Sbostic 				*t++ = '.';
45934624Sbostic 			/* if requires more precision and some integer left */
46034624Sbostic 			for (; prec && ++p < endp; --prec)
46134624Sbostic 				*t++ = *p;
46234624Sbostic 			/*
46334624Sbostic 			 * if done precision and more of the integer component,
46434624Sbostic 			 * round using it; adjust fract so we don't re-round
46534624Sbostic 			 * later.
46634624Sbostic 			 */
46734624Sbostic 			if (!prec && ++p < endp) {
46834248Sbostic 				fract = 0;
469*34669Sbostic 				startp = round((double)0, &expcnt, startp,
470*34669Sbostic 				    t - 1, *p, signp);
47134248Sbostic 			}
47234624Sbostic 			/* adjust expcnt for digit in front of decimal */
47334624Sbostic 			--expcnt;
47434242Sbostic 		}
47534624Sbostic 		/* until first fractional digit, decrement exponent */
47634624Sbostic 		else if (fract) {
47734624Sbostic 			/* adjust expcnt for digit in front of decimal */
47834624Sbostic 			for (expcnt = -1;; --expcnt) {
47934624Sbostic 				fract = modf(fract * 10, &tmp);
48034624Sbostic 				if (tmp)
48134624Sbostic 					break;
48234248Sbostic 			}
48334624Sbostic 			*t++ = tochar((int)tmp);
48434624Sbostic 			if (prec || flags&ALT)
48534331Sbostic 				*t++ = '.';
48634242Sbostic 		}
48734248Sbostic 		else {
48834624Sbostic 			*t++ = '0';
48934624Sbostic 			if (prec || flags&ALT)
49034331Sbostic 				*t++ = '.';
49134248Sbostic 		}
49234624Sbostic 		/* if requires more precision and some fraction left */
49334624Sbostic 		if (fract) {
49434624Sbostic 			if (prec)
49534624Sbostic 				do {
49634624Sbostic 					fract = modf(fract * 10, &tmp);
49734624Sbostic 					*t++ = tochar((int)tmp);
49834624Sbostic 				} while (--prec && fract);
49934624Sbostic 			if (fract)
500*34669Sbostic 				startp = round(fract, &expcnt, startp,
501*34669Sbostic 				    t - 1, (char)0, signp);
50234584Sbostic 		}
50334624Sbostic 		/* if requires more precision */
50434624Sbostic 		for (; prec--; *t++ = '0');
50534624Sbostic 
50634624Sbostic 		/* unless alternate flag, trim any g/G format trailing 0's */
50734624Sbostic 		if (gformat && !(flags&ALT)) {
50834624Sbostic 			while (t > startp && *--t == '0');
50934624Sbostic 			if (*t == '.')
51034624Sbostic 				--t;
51134624Sbostic 			++t;
51234624Sbostic 		}
51334624Sbostic 		t = exponent(t, expcnt, fmtch);
51434624Sbostic 		break;
51534624Sbostic 	case 'g':
51634624Sbostic 	case 'G':
51734624Sbostic 		/* a precision of 0 is treated as a precision of 1. */
51834624Sbostic 		if (!prec)
51934624Sbostic 			++prec;
52034624Sbostic 		/*
52134624Sbostic 		 * ``The style used depends on the value converted; style e
52234624Sbostic 		 * will be used only if the exponent resulting from the
52334624Sbostic 		 * conversion is less than -4 or greater than the precision.''
52434624Sbostic 		 *	-- ANSI X3J11
52534624Sbostic 		 */
52634624Sbostic 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
52734624Sbostic 			/*
52834624Sbostic 			 * g/G format counts "significant digits, not digits of
52934624Sbostic 			 * precision; for the e/E format, this just causes an
53034624Sbostic 			 * off-by-one problem, i.e. g/G considers the digit
53134624Sbostic 			 * before the decimal point significant and e/E doesn't
53234624Sbostic 			 * count it as precision.
53334624Sbostic 			 */
53434624Sbostic 			--prec;
53534624Sbostic 			fmtch -= 2;		/* G->E, g->e */
53634624Sbostic 			gformat = 1;
53734624Sbostic 			goto eformat;
53834624Sbostic 		}
53934624Sbostic 		/*
54034624Sbostic 		 * reverse integer into beginning of buffer,
54134624Sbostic 		 * note, decrement precision
54234624Sbostic 		 */
54334624Sbostic 		if (expcnt)
54434624Sbostic 			for (; ++p < endp; *t++ = *p, --prec);
54534624Sbostic 		else
54634624Sbostic 			*t++ = '0';
54734624Sbostic 		/*
54834624Sbostic 		 * if precision required or alternate flag set, add in a
54934624Sbostic 		 * decimal point.  If no digits yet, add in leading 0.
55034624Sbostic 		 */
55134624Sbostic 		if (prec || flags&ALT) {
55234624Sbostic 			dotrim = 1;
55334624Sbostic 			*t++ = '.';
55434624Sbostic 		}
55534624Sbostic 		else
55634624Sbostic 			dotrim = 0;
55734624Sbostic 		/* if requires more precision and some fraction left */
55834624Sbostic 		if (fract) {
55934624Sbostic 			if (prec) {
56034624Sbostic 				do {
56134624Sbostic 					fract = modf(fract * 10, &tmp);
56234624Sbostic 					*t++ = tochar((int)tmp);
56334624Sbostic 				} while(!tmp);
56434624Sbostic 				while (--prec && fract) {
56534624Sbostic 					fract = modf(fract * 10, &tmp);
56634624Sbostic 					*t++ = tochar((int)tmp);
56734248Sbostic 				}
56834248Sbostic 			}
56934624Sbostic 			if (fract)
570*34669Sbostic 				startp = round(fract, (int *)NULL, startp,
571*34669Sbostic 				    t - 1, (char)0, signp);
57234624Sbostic 		}
57334624Sbostic 		/* alternate format, adds 0's for precision, else trim 0's */
57434624Sbostic 		if (flags&ALT)
57534624Sbostic 			for (; prec--; *t++ = '0');
57634624Sbostic 		else if (dotrim) {
57734624Sbostic 			while (t > startp && *--t == '0');
57834624Sbostic 			if (*t != '.')
57934624Sbostic 				++t;
58034624Sbostic 		}
58134624Sbostic 	}
58234624Sbostic 	return(t - startp);
58334624Sbostic }
58434248Sbostic 
58534624Sbostic static char *
586*34669Sbostic round(fract, exp, start, end, ch, signp)
58734624Sbostic 	double fract;
588*34669Sbostic 	int *exp;
58934624Sbostic 	register char *start, *end;
590*34669Sbostic 	char ch, *signp;
59134624Sbostic {
59234624Sbostic 	double tmp;
59334248Sbostic 
59434624Sbostic 	if (fract)
59534248Sbostic 		(void)modf(fract * 10, &tmp);
59634624Sbostic 	else
59734624Sbostic 		tmp = todigit(ch);
59834624Sbostic 	if (tmp > 4)
59934624Sbostic 		for (;; --end) {
60034624Sbostic 			if (*end == '.')
60134624Sbostic 				--end;
60234624Sbostic 			if (++*end <= '9')
60334624Sbostic 				break;
60434624Sbostic 			*end = '0';
60534624Sbostic 			if (end == start) {
606*34669Sbostic 				if (exp) {	/* e/E; increment exponent */
607*34669Sbostic 					*end = '1';
608*34669Sbostic 					++*exp;
609*34669Sbostic 				}
610*34669Sbostic 				else {		/* f; add extra digit */
611*34669Sbostic 					*--end = '1';
612*34669Sbostic 					--start;
613*34669Sbostic 				}
61434624Sbostic 				break;
61534242Sbostic 			}
61634242Sbostic 		}
617*34669Sbostic 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
618*34669Sbostic 	else if (*signp == '-')
619*34669Sbostic 		for (;; --end) {
620*34669Sbostic 			if (*end == '.')
621*34669Sbostic 				--end;
622*34669Sbostic 			if (*end != '0')
623*34669Sbostic 				break;
624*34669Sbostic 			if (end == start)
625*34669Sbostic 				*signp = 0;
626*34669Sbostic 		}
62734624Sbostic 	return(start);
62834624Sbostic }
62934248Sbostic 
63034624Sbostic static char *
63134624Sbostic exponent(p, exp, fmtch)
63234624Sbostic 	register char *p;
63334624Sbostic 	register int exp;
63434624Sbostic 	u_char fmtch;
63534624Sbostic {
63634624Sbostic 	register char *t;
63734624Sbostic 	char expbuf[MAXEXP];
63834248Sbostic 
63934624Sbostic 	*p++ = fmtch;
64034624Sbostic 	if (exp < 0) {
64134624Sbostic 		exp = -exp;
64234624Sbostic 		*p++ = '-';
64334242Sbostic 	}
64434624Sbostic 	else
64534624Sbostic 		*p++ = '+';
64634624Sbostic 	t = expbuf + MAXEXP;
64734624Sbostic 	if (exp > 9) {
64834624Sbostic 		do {
64934624Sbostic 			*--t = tochar(exp % 10);
65034624Sbostic 		} while ((exp /= 10) > 9);
65134624Sbostic 		*--t = tochar(exp);
65234624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
65334624Sbostic 	}
65434624Sbostic 	else {
65534624Sbostic 		*p++ = '0';
65634624Sbostic 		*p++ = tochar(exp);
65734624Sbostic 	}
65834323Sbostic 	return(p);
65934242Sbostic }
660