xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 37234)
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
634825Sbostic  * provided that the above copyright notice and this paragraph are
734825Sbostic  * duplicated in all such forms and that any documentation,
834825Sbostic  * advertising materials, and other materials related to such
934825Sbostic  * distribution and use acknowledge that the software was developed
1034825Sbostic  * by the University of California, Berkeley.  The name of the
1134825Sbostic  * University may not be used to endorse or promote products derived
1234825Sbostic  * from this software without specific prior written permission.
1334825Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
1434825Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
1534825Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1634226Sbostic  */
1734226Sbostic 
1834233Sbostic #if defined(LIBC_SCCS) && !defined(lint)
19*37234Sbostic static char sccsid[] = "@(#)vfprintf.c	5.37 (Berkeley) 03/26/89";
2034233Sbostic #endif /* LIBC_SCCS and not lint */
2134233Sbostic 
2234261Sbostic #include <sys/types.h>
2334233Sbostic #include <varargs.h>
2434226Sbostic #include <stdio.h>
2534233Sbostic #include <ctype.h>
2634226Sbostic 
2734328Sbostic /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
2834328Sbostic #define	MAXEXP		308
2934328Sbostic /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
3034328Sbostic #define	MAXFRACT	39
3134226Sbostic 
3234624Sbostic #define	DEFPREC		6
3334624Sbostic 
3434328Sbostic #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
3534328Sbostic 
3634427Sbostic #define	PUTC(ch)	(void) putc(ch, fp)
3734236Sbostic 
3836090Sbostic #define ARG(basetype) \
3936090Sbostic 	_ulong = flags&LONGINT ? va_arg(argp, long basetype) : \
4036090Sbostic 	    flags&SHORTINT ? (short basetype)va_arg(argp, int) : \
4136090Sbostic 	    va_arg(argp, int)
4234235Sbostic 
4334331Sbostic #define	todigit(c)	((c) - '0')
4434331Sbostic #define	tochar(n)	((n) + '0')
4534331Sbostic 
4634318Sbostic /* have to deal with the negative buffer count kludge */
4734318Sbostic #define	NEGATIVE_COUNT_KLUDGE
4834318Sbostic 
4934318Sbostic #define	LONGINT		0x01		/* long integer */
5034318Sbostic #define	LONGDBL		0x02		/* long double; unimplemented */
5134318Sbostic #define	SHORTINT	0x04		/* short integer */
5234318Sbostic #define	ALT		0x08		/* alternate form */
5334318Sbostic #define	LADJUST		0x10		/* left adjustment */
5434427Sbostic #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
5534427Sbostic #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
5634318Sbostic 
5734323Sbostic _doprnt(fmt0, argp, fp)
5834323Sbostic 	u_char *fmt0;
5934233Sbostic 	va_list argp;
6034235Sbostic 	register FILE *fp;
6134226Sbostic {
6234427Sbostic 	register u_char *fmt;	/* format string */
6334427Sbostic 	register int ch;	/* character from fmt */
6434427Sbostic 	register int cnt;	/* return value accumulator */
6534427Sbostic 	register int n;		/* random handy integer */
6634427Sbostic 	register char *t;	/* buffer pointer */
6734427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
6834427Sbostic 	u_long _ulong;		/* integer arguments %[diouxX] */
6934624Sbostic 	int base;		/* base for [diouxX] conversion */
7034624Sbostic 	int dprec;		/* decimal precision in [diouxX] */
7134624Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
7234427Sbostic 	int flags;		/* flags as above */
7334427Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
7434427Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
7534624Sbostic 	int realsz;		/* field size expanded by decimal precision */
7634427Sbostic 	int size;		/* size of converted field or string */
7734624Sbostic 	int width;		/* width from format (%8d), or 0 */
7834669Sbostic 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
7934669Sbostic 	char softsign;		/* temporary negative sign for floats */
8034427Sbostic 	char *digs;		/* digits for [diouxX] conversion */
8134427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
8234226Sbostic 
8334428Sbostic 	if (fp->_flag & _IORW) {
8434428Sbostic 		fp->_flag |= _IOWRT;
8534428Sbostic 		fp->_flag &= ~(_IOEOF|_IOREAD);
8634428Sbostic 	}
8734428Sbostic 	if ((fp->_flag & _IOWRT) == 0)
8834428Sbostic 		return (EOF);
8934428Sbostic 
9034323Sbostic 	fmt = fmt0;
9134243Sbostic 	digs = "0123456789abcdef";
9234322Sbostic 	for (cnt = 0;; ++fmt) {
9334318Sbostic 		n = fp->_cnt;
9434427Sbostic 		for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
9534427Sbostic 		     ++cnt, ++fmt)
9634318Sbostic 			if (--n < 0
9734318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
9834318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
9934318Sbostic #endif
10034427Sbostic 			    || ch == '\n' && fp->_flag & _IOLBF) {
10134318Sbostic 				fp->_cnt = n;
10234475Sbostic 				fp->_ptr = t;
10334427Sbostic 				(void) _flsbuf((u_char)ch, fp);
10434318Sbostic 				n = fp->_cnt;
10534427Sbostic 				t = (char *)fp->_ptr;
10634427Sbostic 			} else
10734314Sbostic 				*t++ = ch;
10834318Sbostic 		fp->_cnt = n;
10934475Sbostic 		fp->_ptr = t;
11034318Sbostic 		if (!ch)
11134427Sbostic 			return (cnt);
11234314Sbostic 
11334672Sbostic 		flags = 0; dprec = 0; fpprec = 0; width = 0;
11434233Sbostic 		prec = -1;
11534318Sbostic 		sign = '\0';
11634226Sbostic 
11734318Sbostic rflag:		switch (*++fmt) {
11834318Sbostic 		case ' ':
11934669Sbostic 			/*
12034669Sbostic 			 * ``If the space and + flags both appear, the space
12134669Sbostic 			 * flag will be ignored.''
12234669Sbostic 			 *	-- ANSI X3J11
12334669Sbostic 			 */
12434669Sbostic 			if (!sign)
12534669Sbostic 				sign = ' ';
12634318Sbostic 			goto rflag;
12734233Sbostic 		case '#':
12834318Sbostic 			flags |= ALT;
12934318Sbostic 			goto rflag;
13034233Sbostic 		case '*':
13134235Sbostic 			/*
13234235Sbostic 			 * ``A negative field width argument is taken as a
13334235Sbostic 			 * - flag followed by a  positive field width.''
13434235Sbostic 			 *	-- ANSI X3J11
13534235Sbostic 			 * They don't exclude field widths read from args.
13634235Sbostic 			 */
13734235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
13834318Sbostic 				goto rflag;
13934235Sbostic 			width = -width;
14034427Sbostic 			/* FALLTHROUGH */
14134235Sbostic 		case '-':
14234318Sbostic 			flags |= LADJUST;
14334318Sbostic 			goto rflag;
14434233Sbostic 		case '+':
14534314Sbostic 			sign = '+';
14634318Sbostic 			goto rflag;
14734233Sbostic 		case '.':
14834235Sbostic 			if (*++fmt == '*')
14934318Sbostic 				n = va_arg(argp, int);
15034427Sbostic 			else {
15134318Sbostic 				n = 0;
15234427Sbostic 				while (isascii(*fmt) && isdigit(*fmt))
15334427Sbostic 					n = 10 * n + todigit(*fmt++);
15434233Sbostic 				--fmt;
15534226Sbostic 			}
15634318Sbostic 			prec = n < 0 ? -1 : n;
15734318Sbostic 			goto rflag;
15834233Sbostic 		case '0':
15934427Sbostic 			/*
16034427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
16134427Sbostic 			 * beginning of a field width.''
16234427Sbostic 			 *	-- ANSI X3J11
16334427Sbostic 			 */
16434427Sbostic 			flags |= ZEROPAD;
16534427Sbostic 			goto rflag;
16634233Sbostic 		case '1': case '2': case '3': case '4':
16734233Sbostic 		case '5': case '6': case '7': case '8': case '9':
16834318Sbostic 			n = 0;
16934233Sbostic 			do {
17034331Sbostic 				n = 10 * n + todigit(*fmt);
17134318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
17234318Sbostic 			width = n;
17334233Sbostic 			--fmt;
17434319Sbostic 			goto rflag;
17534235Sbostic 		case 'L':
17634329Sbostic 			flags |= LONGDBL;
17734318Sbostic 			goto rflag;
17834235Sbostic 		case 'h':
17934318Sbostic 			flags |= SHORTINT;
18034318Sbostic 			goto rflag;
18134233Sbostic 		case 'l':
18234318Sbostic 			flags |= LONGINT;
18334318Sbostic 			goto rflag;
18434314Sbostic 		case 'c':
18534427Sbostic 			*(t = buf) = va_arg(argp, int);
18634314Sbostic 			size = 1;
18734427Sbostic 			sign = '\0';
18834314Sbostic 			goto pforw;
18934624Sbostic 		case 'D':
19034624Sbostic 			flags |= LONGINT;
19134624Sbostic 			/*FALLTHROUGH*/
19234314Sbostic 		case 'd':
19334318Sbostic 		case 'i':
19436090Sbostic 			ARG(int);
19534318Sbostic 			if ((long)_ulong < 0) {
19634318Sbostic 				_ulong = -_ulong;
19734314Sbostic 				sign = '-';
19834241Sbostic 			}
19934241Sbostic 			base = 10;
20034327Sbostic 			goto number;
20134261Sbostic 		case 'e':
20234236Sbostic 		case 'E':
20334235Sbostic 		case 'f':
20434261Sbostic 		case 'g':
20534243Sbostic 		case 'G':
20634243Sbostic 			_double = va_arg(argp, double);
20734328Sbostic 			/*
20834669Sbostic 			 * don't do unrealistic precision; just pad it with
20934669Sbostic 			 * zeroes later, so buffer size stays rational.
21034328Sbostic 			 */
21134328Sbostic 			if (prec > MAXFRACT) {
21234475Sbostic 				if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
21334475Sbostic 					fpprec = prec - MAXFRACT;
21434328Sbostic 				prec = MAXFRACT;
21534328Sbostic 			}
21634624Sbostic 			else if (prec == -1)
21734624Sbostic 				prec = DEFPREC;
21834669Sbostic 			/*
21934669Sbostic 			 * softsign avoids negative 0 if _double is < 0 and
22034669Sbostic 			 * no significant digits will be shown
22134669Sbostic 			 */
22234624Sbostic 			if (_double < 0) {
22334669Sbostic 				softsign = '-';
22434624Sbostic 				_double = -_double;
22534624Sbostic 			}
22634669Sbostic 			else
22734669Sbostic 				softsign = 0;
22834624Sbostic 			/*
22934669Sbostic 			 * cvt may have to round up past the "start" of the
23034624Sbostic 			 * buffer, i.e. ``intf("%.2f", (double)9.999);'';
23134624Sbostic 			 * if the first char isn't NULL, it did.
23234624Sbostic 			 */
23334624Sbostic 			*buf = NULL;
23434669Sbostic 			size = cvt(_double, prec, flags, &softsign, *fmt, buf,
23534624Sbostic 			    buf + sizeof(buf));
23634669Sbostic 			if (softsign)
23734669Sbostic 				sign = '-';
23834624Sbostic 			t = *buf ? buf : buf + 1;
23934314Sbostic 			goto pforw;
24034235Sbostic 		case 'n':
24134427Sbostic 			if (flags & LONGINT)
24234318Sbostic 				*va_arg(argp, long *) = cnt;
24334427Sbostic 			else if (flags & SHORTINT)
24434318Sbostic 				*va_arg(argp, short *) = cnt;
24534318Sbostic 			else
24634318Sbostic 				*va_arg(argp, int *) = cnt;
24734235Sbostic 			break;
24834624Sbostic 		case 'O':
24934624Sbostic 			flags |= LONGINT;
25034624Sbostic 			/*FALLTHROUGH*/
25134226Sbostic 		case 'o':
25236090Sbostic 			ARG(unsigned);
25334226Sbostic 			base = 8;
25434327Sbostic 			goto nosign;
25534235Sbostic 		case 'p':
25634320Sbostic 			/*
25734321Sbostic 			 * ``The argument shall be a pointer to void.  The
25834321Sbostic 			 * value of the pointer is converted to a sequence
25934321Sbostic 			 * of printable characters, in an implementation-
26034321Sbostic 			 * defined manner.''
26134321Sbostic 			 *	-- ANSI X3J11
26234320Sbostic 			 */
26334427Sbostic 			/* NOSTRICT */
26434320Sbostic 			_ulong = (u_long)va_arg(argp, void *);
26534320Sbostic 			base = 16;
26634327Sbostic 			goto nosign;
26734226Sbostic 		case 's':
26834314Sbostic 			if (!(t = va_arg(argp, char *)))
26934314Sbostic 				t = "(null)";
27034321Sbostic 			if (prec >= 0) {
27134321Sbostic 				/*
27234321Sbostic 				 * can't use strlen; can only look for the
27334321Sbostic 				 * NUL in the first `prec' characters, and
27434321Sbostic 				 * strlen() will go further.
27534321Sbostic 				 */
27634321Sbostic 				char *p, *memchr();
27734321Sbostic 
27834321Sbostic 				if (p = memchr(t, 0, prec)) {
27934321Sbostic 					size = p - t;
28034321Sbostic 					if (size > prec)
28134321Sbostic 						size = prec;
28234427Sbostic 				} else
28334321Sbostic 					size = prec;
28434427Sbostic 			} else
28534321Sbostic 				size = strlen(t);
28634427Sbostic 			sign = '\0';
28734328Sbostic 			goto pforw;
28834624Sbostic 		case 'U':
28934624Sbostic 			flags |= LONGINT;
29034624Sbostic 			/*FALLTHROUGH*/
29134226Sbostic 		case 'u':
29236090Sbostic 			ARG(unsigned);
29334226Sbostic 			base = 10;
29434327Sbostic 			goto nosign;
29534226Sbostic 		case 'X':
29634226Sbostic 			digs = "0123456789ABCDEF";
29734427Sbostic 			/* FALLTHROUGH */
29834226Sbostic 		case 'x':
29936090Sbostic 			ARG(unsigned);
30034314Sbostic 			base = 16;
30134326Sbostic 			/* leading 0x/X only if non-zero */
30234427Sbostic 			if (flags & ALT && _ulong != 0)
30334427Sbostic 				flags |= HEXPREFIX;
30434327Sbostic 
30534327Sbostic 			/* unsigned conversions */
30634427Sbostic nosign:			sign = '\0';
30734326Sbostic 			/*
30834330Sbostic 			 * ``... diouXx conversions ... if a precision is
30934330Sbostic 			 * specified, the 0 flag will be ignored.''
31034330Sbostic 			 *	-- ANSI X3J11
31134330Sbostic 			 */
31234427Sbostic number:			if ((dprec = prec) >= 0)
31334427Sbostic 				flags &= ~ZEROPAD;
31434427Sbostic 
31534330Sbostic 			/*
31634326Sbostic 			 * ``The result of converting a zero value with an
31734326Sbostic 			 * explicit precision of zero is no characters.''
31834326Sbostic 			 *	-- ANSI X3J11
31934326Sbostic 			 */
32034427Sbostic 			t = buf + BUF;
32134427Sbostic 			if (_ulong != 0 || prec != 0) {
32234427Sbostic 				do {
32334427Sbostic 					*--t = digs[_ulong % base];
32434427Sbostic 					_ulong /= base;
32534427Sbostic 				} while (_ulong);
32634427Sbostic 				digs = "0123456789abcdef";
32734427Sbostic 				if (flags & ALT && base == 8 && *t != '0')
32834427Sbostic 					*--t = '0'; /* octal leading 0 */
32934330Sbostic 			}
33034427Sbostic 			size = buf + BUF - t;
33134327Sbostic 
33234427Sbostic pforw:
33334427Sbostic 			/*
33434624Sbostic 			 * All reasonable formats wind up here.  At this point,
33534624Sbostic 			 * `t' points to a string which (if not flags&LADJUST)
33634624Sbostic 			 * should be padded out to `width' places.  If
33734624Sbostic 			 * flags&ZEROPAD, it should first be prefixed by any
33834624Sbostic 			 * sign or other prefix; otherwise, it should be blank
33934624Sbostic 			 * padded before the prefix is emitted.  After any
34034624Sbostic 			 * left-hand padding and prefixing, emit zeroes
34134624Sbostic 			 * required by a decimal [diouxX] precision, then print
34234624Sbostic 			 * the string proper, then emit zeroes required by any
34334624Sbostic 			 * leftover floating precision; finally, if LADJUST,
34434624Sbostic 			 * pad with blanks.
34534427Sbostic 			 */
34634327Sbostic 
34734624Sbostic 			/*
34834624Sbostic 			 * compute actual size, so we know how much to pad
34934624Sbostic 			 * fieldsz excludes decimal prec; realsz includes it
35034624Sbostic 			 */
35134427Sbostic 			fieldsz = size + fpprec;
35234427Sbostic 			if (sign)
35334427Sbostic 				fieldsz++;
35434427Sbostic 			if (flags & HEXPREFIX)
35534427Sbostic 				fieldsz += 2;
35634427Sbostic 			realsz = dprec > fieldsz ? dprec : fieldsz;
35734327Sbostic 
35834427Sbostic 			/* right-adjusting blank padding */
35934427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
36034427Sbostic 				for (n = realsz; n < width; n++)
36134427Sbostic 					PUTC(' ');
36234427Sbostic 			/* prefix */
36334427Sbostic 			if (sign)
36434427Sbostic 				PUTC(sign);
36534427Sbostic 			if (flags & HEXPREFIX) {
36634427Sbostic 				PUTC('0');
36734427Sbostic 				PUTC((char)*fmt);
36834327Sbostic 			}
36934427Sbostic 			/* right-adjusting zero padding */
37034427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
37134427Sbostic 				for (n = realsz; n < width; n++)
37234427Sbostic 					PUTC('0');
37334427Sbostic 			/* leading zeroes from decimal precision */
37434427Sbostic 			for (n = fieldsz; n < dprec; n++)
37534427Sbostic 				PUTC('0');
37634327Sbostic 
37734427Sbostic 			/* the string or number proper */
378*37234Sbostic 			n = size;
379*37234Sbostic 			if (fp->_cnt - n >= 0 && (fp->_flag & _IOLBF) == 0) {
38034328Sbostic 				fp->_cnt -= n;
38134427Sbostic 				bcopy(t, (char *)fp->_ptr, n);
38234328Sbostic 				fp->_ptr += n;
38334427Sbostic 			} else
38434427Sbostic 				while (--n >= 0)
38534427Sbostic 					PUTC(*t++);
38634427Sbostic 			/* trailing f.p. zeroes */
38734427Sbostic 			while (--fpprec >= 0)
38834328Sbostic 				PUTC('0');
38934427Sbostic 			/* left-adjusting padding (always blank) */
39034427Sbostic 			if (flags & LADJUST)
39134427Sbostic 				for (n = realsz; n < width; n++)
39234328Sbostic 					PUTC(' ');
39334427Sbostic 			/* finally, adjust cnt */
39434427Sbostic 			cnt += width > realsz ? width : realsz;
39534226Sbostic 			break;
39634427Sbostic 		case '\0':	/* "%?" prints ?, unless ? is NULL */
39734427Sbostic 			return (cnt);
39834226Sbostic 		default:
39934427Sbostic 			PUTC((char)*fmt);
40034427Sbostic 			cnt++;
40134226Sbostic 		}
40234226Sbostic 	}
40334427Sbostic 	/* NOTREACHED */
40434226Sbostic }
40534242Sbostic 
40634624Sbostic static
40734669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp)
40834242Sbostic 	double number;
40934261Sbostic 	register int prec;
41034323Sbostic 	int flags;
41134323Sbostic 	u_char fmtch;
41234669Sbostic 	char *signp, *startp, *endp;
41334242Sbostic {
41434331Sbostic 	register char *p, *t;
41534672Sbostic 	register double fract;
41634624Sbostic 	int dotrim, expcnt, gformat;
41734672Sbostic 	double integer, tmp, modf();
41834669Sbostic 	char *exponent(), *round();
41934242Sbostic 
42034624Sbostic 	dotrim = expcnt = gformat = 0;
42134624Sbostic 	fract = modf(number, &integer);
42234242Sbostic 
42334624Sbostic 	/* get an extra slot for rounding. */
42434624Sbostic 	t = ++startp;
42534624Sbostic 
42634624Sbostic 	/*
42734624Sbostic 	 * get integer portion of number; put into the end of the buffer; the
42834624Sbostic 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
42934624Sbostic 	 */
43034624Sbostic 	for (p = endp - 1; integer; ++expcnt) {
43134624Sbostic 		tmp = modf(integer / 10, &integer);
43234624Sbostic 		*p-- = tochar((int)((tmp + .01) * 10));
43334248Sbostic 	}
43434261Sbostic 	switch(fmtch) {
43534261Sbostic 	case 'f':
43634624Sbostic 		/* reverse integer into beginning of buffer */
43734624Sbostic 		if (expcnt)
43834624Sbostic 			for (; ++p < endp; *t++ = *p);
43934624Sbostic 		else
44034624Sbostic 			*t++ = '0';
44134248Sbostic 		/*
44234624Sbostic 		 * if precision required or alternate flag set, add in a
44334624Sbostic 		 * decimal point.
44434248Sbostic 		 */
44534624Sbostic 		if (prec || flags&ALT)
44634624Sbostic 			*t++ = '.';
44734624Sbostic 		/* if requires more precision and some fraction left */
44834624Sbostic 		if (fract) {
44934624Sbostic 			if (prec)
45034624Sbostic 				do {
45134624Sbostic 					fract = modf(fract * 10, &tmp);
45234624Sbostic 					*t++ = tochar((int)tmp);
45334624Sbostic 				} while (--prec && fract);
45434624Sbostic 			if (fract)
45534669Sbostic 				startp = round(fract, (int *)NULL, startp,
45634669Sbostic 				    t - 1, (char)0, signp);
45734624Sbostic 		}
45834624Sbostic 		for (; prec--; *t++ = '0');
45934624Sbostic 		break;
46034624Sbostic 	case 'e':
46134624Sbostic 	case 'E':
46234624Sbostic eformat:	if (expcnt) {
46334624Sbostic 			*t++ = *++p;
46434624Sbostic 			if (prec || flags&ALT)
46534331Sbostic 				*t++ = '.';
46634624Sbostic 			/* if requires more precision and some integer left */
46734624Sbostic 			for (; prec && ++p < endp; --prec)
46834624Sbostic 				*t++ = *p;
46934624Sbostic 			/*
47034624Sbostic 			 * if done precision and more of the integer component,
47134624Sbostic 			 * round using it; adjust fract so we don't re-round
47234624Sbostic 			 * later.
47334624Sbostic 			 */
47434624Sbostic 			if (!prec && ++p < endp) {
47534248Sbostic 				fract = 0;
47634669Sbostic 				startp = round((double)0, &expcnt, startp,
47734669Sbostic 				    t - 1, *p, signp);
47834248Sbostic 			}
47934624Sbostic 			/* adjust expcnt for digit in front of decimal */
48034624Sbostic 			--expcnt;
48134242Sbostic 		}
48234624Sbostic 		/* until first fractional digit, decrement exponent */
48334624Sbostic 		else if (fract) {
48434624Sbostic 			/* adjust expcnt for digit in front of decimal */
48534624Sbostic 			for (expcnt = -1;; --expcnt) {
48634624Sbostic 				fract = modf(fract * 10, &tmp);
48734624Sbostic 				if (tmp)
48834624Sbostic 					break;
48934248Sbostic 			}
49034624Sbostic 			*t++ = tochar((int)tmp);
49134624Sbostic 			if (prec || flags&ALT)
49234331Sbostic 				*t++ = '.';
49334242Sbostic 		}
49434248Sbostic 		else {
49534624Sbostic 			*t++ = '0';
49634624Sbostic 			if (prec || flags&ALT)
49734331Sbostic 				*t++ = '.';
49834248Sbostic 		}
49934624Sbostic 		/* if requires more precision and some fraction left */
50034624Sbostic 		if (fract) {
50134624Sbostic 			if (prec)
50234624Sbostic 				do {
50334624Sbostic 					fract = modf(fract * 10, &tmp);
50434624Sbostic 					*t++ = tochar((int)tmp);
50534624Sbostic 				} while (--prec && fract);
50634624Sbostic 			if (fract)
50734669Sbostic 				startp = round(fract, &expcnt, startp,
50834669Sbostic 				    t - 1, (char)0, signp);
50934584Sbostic 		}
51034624Sbostic 		/* if requires more precision */
51134624Sbostic 		for (; prec--; *t++ = '0');
51234624Sbostic 
51334624Sbostic 		/* unless alternate flag, trim any g/G format trailing 0's */
51434624Sbostic 		if (gformat && !(flags&ALT)) {
51534624Sbostic 			while (t > startp && *--t == '0');
51634624Sbostic 			if (*t == '.')
51734624Sbostic 				--t;
51834624Sbostic 			++t;
51934624Sbostic 		}
52034624Sbostic 		t = exponent(t, expcnt, fmtch);
52134624Sbostic 		break;
52234624Sbostic 	case 'g':
52334624Sbostic 	case 'G':
52434624Sbostic 		/* a precision of 0 is treated as a precision of 1. */
52534624Sbostic 		if (!prec)
52634624Sbostic 			++prec;
52734624Sbostic 		/*
52834624Sbostic 		 * ``The style used depends on the value converted; style e
52934624Sbostic 		 * will be used only if the exponent resulting from the
53034624Sbostic 		 * conversion is less than -4 or greater than the precision.''
53134624Sbostic 		 *	-- ANSI X3J11
53234624Sbostic 		 */
53334624Sbostic 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
53434624Sbostic 			/*
53534624Sbostic 			 * g/G format counts "significant digits, not digits of
53634624Sbostic 			 * precision; for the e/E format, this just causes an
53734624Sbostic 			 * off-by-one problem, i.e. g/G considers the digit
53834624Sbostic 			 * before the decimal point significant and e/E doesn't
53934624Sbostic 			 * count it as precision.
54034624Sbostic 			 */
54134624Sbostic 			--prec;
54234624Sbostic 			fmtch -= 2;		/* G->E, g->e */
54334624Sbostic 			gformat = 1;
54434624Sbostic 			goto eformat;
54534624Sbostic 		}
54634624Sbostic 		/*
54734624Sbostic 		 * reverse integer into beginning of buffer,
54834624Sbostic 		 * note, decrement precision
54934624Sbostic 		 */
55034624Sbostic 		if (expcnt)
55134624Sbostic 			for (; ++p < endp; *t++ = *p, --prec);
55234624Sbostic 		else
55334624Sbostic 			*t++ = '0';
55434624Sbostic 		/*
55534624Sbostic 		 * if precision required or alternate flag set, add in a
55634624Sbostic 		 * decimal point.  If no digits yet, add in leading 0.
55734624Sbostic 		 */
55834624Sbostic 		if (prec || flags&ALT) {
55934624Sbostic 			dotrim = 1;
56034624Sbostic 			*t++ = '.';
56134624Sbostic 		}
56234624Sbostic 		else
56334624Sbostic 			dotrim = 0;
56434624Sbostic 		/* if requires more precision and some fraction left */
56534624Sbostic 		if (fract) {
56634624Sbostic 			if (prec) {
56734624Sbostic 				do {
56834624Sbostic 					fract = modf(fract * 10, &tmp);
56934624Sbostic 					*t++ = tochar((int)tmp);
57034624Sbostic 				} while(!tmp);
57134624Sbostic 				while (--prec && fract) {
57234624Sbostic 					fract = modf(fract * 10, &tmp);
57334624Sbostic 					*t++ = tochar((int)tmp);
57434248Sbostic 				}
57534248Sbostic 			}
57634624Sbostic 			if (fract)
57734669Sbostic 				startp = round(fract, (int *)NULL, startp,
57834669Sbostic 				    t - 1, (char)0, signp);
57934624Sbostic 		}
58034624Sbostic 		/* alternate format, adds 0's for precision, else trim 0's */
58134624Sbostic 		if (flags&ALT)
58234624Sbostic 			for (; prec--; *t++ = '0');
58334624Sbostic 		else if (dotrim) {
58434624Sbostic 			while (t > startp && *--t == '0');
58534624Sbostic 			if (*t != '.')
58634624Sbostic 				++t;
58734624Sbostic 		}
58834624Sbostic 	}
58934624Sbostic 	return(t - startp);
59034624Sbostic }
59134248Sbostic 
59234624Sbostic static char *
59334669Sbostic round(fract, exp, start, end, ch, signp)
59434624Sbostic 	double fract;
59534669Sbostic 	int *exp;
59634624Sbostic 	register char *start, *end;
59734669Sbostic 	char ch, *signp;
59834624Sbostic {
59934624Sbostic 	double tmp;
60034248Sbostic 
60134624Sbostic 	if (fract)
60234248Sbostic 		(void)modf(fract * 10, &tmp);
60334624Sbostic 	else
60434624Sbostic 		tmp = todigit(ch);
60534624Sbostic 	if (tmp > 4)
60634624Sbostic 		for (;; --end) {
60734624Sbostic 			if (*end == '.')
60834624Sbostic 				--end;
60934624Sbostic 			if (++*end <= '9')
61034624Sbostic 				break;
61134624Sbostic 			*end = '0';
61234624Sbostic 			if (end == start) {
61334669Sbostic 				if (exp) {	/* e/E; increment exponent */
61434669Sbostic 					*end = '1';
61534669Sbostic 					++*exp;
61634669Sbostic 				}
61734669Sbostic 				else {		/* f; add extra digit */
61834669Sbostic 					*--end = '1';
61934669Sbostic 					--start;
62034669Sbostic 				}
62134624Sbostic 				break;
62234242Sbostic 			}
62334242Sbostic 		}
62434669Sbostic 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
62534669Sbostic 	else if (*signp == '-')
62634669Sbostic 		for (;; --end) {
62734669Sbostic 			if (*end == '.')
62834669Sbostic 				--end;
62934669Sbostic 			if (*end != '0')
63034669Sbostic 				break;
63134669Sbostic 			if (end == start)
63234669Sbostic 				*signp = 0;
63334669Sbostic 		}
63434624Sbostic 	return(start);
63534624Sbostic }
63634248Sbostic 
63734624Sbostic static char *
63834624Sbostic exponent(p, exp, fmtch)
63934624Sbostic 	register char *p;
64034624Sbostic 	register int exp;
64134624Sbostic 	u_char fmtch;
64234624Sbostic {
64334624Sbostic 	register char *t;
64434624Sbostic 	char expbuf[MAXEXP];
64534248Sbostic 
64634624Sbostic 	*p++ = fmtch;
64734624Sbostic 	if (exp < 0) {
64834624Sbostic 		exp = -exp;
64934624Sbostic 		*p++ = '-';
65034242Sbostic 	}
65134624Sbostic 	else
65234624Sbostic 		*p++ = '+';
65334624Sbostic 	t = expbuf + MAXEXP;
65434624Sbostic 	if (exp > 9) {
65534624Sbostic 		do {
65634624Sbostic 			*--t = tochar(exp % 10);
65734624Sbostic 		} while ((exp /= 10) > 9);
65834624Sbostic 		*--t = tochar(exp);
65934624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
66034624Sbostic 	}
66134624Sbostic 	else {
66234624Sbostic 		*p++ = '0';
66334624Sbostic 		*p++ = tochar(exp);
66434624Sbostic 	}
66534323Sbostic 	return(p);
66634242Sbostic }
667