xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34825)
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
6*34825Sbostic  * provided that the above copyright notice and this paragraph are
7*34825Sbostic  * duplicated in all such forms and that any documentation,
8*34825Sbostic  * advertising materials, and other materials related to such
9*34825Sbostic  * distribution and use acknowledge that the software was developed
10*34825Sbostic  * by the University of California, Berkeley.  The name of the
11*34825Sbostic  * University may not be used to endorse or promote products derived
12*34825Sbostic  * from this software without specific prior written permission.
13*34825Sbostic  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14*34825Sbostic  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15*34825Sbostic  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
1634226Sbostic  */
1734226Sbostic 
1834233Sbostic #if defined(LIBC_SCCS) && !defined(lint)
19*34825Sbostic static char sccsid[] = "@(#)vfprintf.c	5.35 (Berkeley) 06/27/88";
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 
3834318Sbostic #define	ARG() \
3934318Sbostic 	_ulong = flags&LONGINT ? va_arg(argp, long) : \
4034318Sbostic 	    flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
4134235Sbostic 
4234331Sbostic #define	todigit(c)	((c) - '0')
4334331Sbostic #define	tochar(n)	((n) + '0')
4434331Sbostic 
4534318Sbostic /* have to deal with the negative buffer count kludge */
4634318Sbostic #define	NEGATIVE_COUNT_KLUDGE
4734318Sbostic 
4834318Sbostic #define	LONGINT		0x01		/* long integer */
4934318Sbostic #define	LONGDBL		0x02		/* long double; unimplemented */
5034318Sbostic #define	SHORTINT	0x04		/* short integer */
5134318Sbostic #define	ALT		0x08		/* alternate form */
5234318Sbostic #define	LADJUST		0x10		/* left adjustment */
5334427Sbostic #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
5434427Sbostic #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
5534318Sbostic 
5634323Sbostic _doprnt(fmt0, argp, fp)
5734323Sbostic 	u_char *fmt0;
5834233Sbostic 	va_list argp;
5934235Sbostic 	register FILE *fp;
6034226Sbostic {
6134427Sbostic 	register u_char *fmt;	/* format string */
6234427Sbostic 	register int ch;	/* character from fmt */
6334427Sbostic 	register int cnt;	/* return value accumulator */
6434427Sbostic 	register int n;		/* random handy integer */
6534427Sbostic 	register char *t;	/* buffer pointer */
6634427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
6734427Sbostic 	u_long _ulong;		/* integer arguments %[diouxX] */
6834624Sbostic 	int base;		/* base for [diouxX] conversion */
6934624Sbostic 	int dprec;		/* decimal precision in [diouxX] */
7034624Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
7134427Sbostic 	int flags;		/* flags as above */
7234427Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
7334427Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
7434624Sbostic 	int realsz;		/* field size expanded by decimal precision */
7534427Sbostic 	int size;		/* size of converted field or string */
7634624Sbostic 	int width;		/* width from format (%8d), or 0 */
7734669Sbostic 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
7834669Sbostic 	char softsign;		/* temporary negative sign for floats */
7934427Sbostic 	char *digs;		/* digits for [diouxX] conversion */
8034427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
8134226Sbostic 
8234428Sbostic 	if (fp->_flag & _IORW) {
8334428Sbostic 		fp->_flag |= _IOWRT;
8434428Sbostic 		fp->_flag &= ~(_IOEOF|_IOREAD);
8534428Sbostic 	}
8634428Sbostic 	if ((fp->_flag & _IOWRT) == 0)
8734428Sbostic 		return (EOF);
8834428Sbostic 
8934323Sbostic 	fmt = fmt0;
9034243Sbostic 	digs = "0123456789abcdef";
9134322Sbostic 	for (cnt = 0;; ++fmt) {
9234318Sbostic 		n = fp->_cnt;
9334427Sbostic 		for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
9434427Sbostic 		     ++cnt, ++fmt)
9534318Sbostic 			if (--n < 0
9634318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
9734318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
9834318Sbostic #endif
9934427Sbostic 			    || ch == '\n' && fp->_flag & _IOLBF) {
10034318Sbostic 				fp->_cnt = n;
10134475Sbostic 				fp->_ptr = t;
10234427Sbostic 				(void) _flsbuf((u_char)ch, fp);
10334318Sbostic 				n = fp->_cnt;
10434427Sbostic 				t = (char *)fp->_ptr;
10534427Sbostic 			} else
10634314Sbostic 				*t++ = ch;
10734318Sbostic 		fp->_cnt = n;
10834475Sbostic 		fp->_ptr = t;
10934318Sbostic 		if (!ch)
11034427Sbostic 			return (cnt);
11134314Sbostic 
11234672Sbostic 		flags = 0; dprec = 0; fpprec = 0; width = 0;
11334233Sbostic 		prec = -1;
11434318Sbostic 		sign = '\0';
11534226Sbostic 
11634318Sbostic rflag:		switch (*++fmt) {
11734318Sbostic 		case ' ':
11834669Sbostic 			/*
11934669Sbostic 			 * ``If the space and + flags both appear, the space
12034669Sbostic 			 * flag will be ignored.''
12134669Sbostic 			 *	-- ANSI X3J11
12234669Sbostic 			 */
12334669Sbostic 			if (!sign)
12434669Sbostic 				sign = ' ';
12534318Sbostic 			goto rflag;
12634233Sbostic 		case '#':
12734318Sbostic 			flags |= ALT;
12834318Sbostic 			goto rflag;
12934233Sbostic 		case '*':
13034235Sbostic 			/*
13134235Sbostic 			 * ``A negative field width argument is taken as a
13234235Sbostic 			 * - flag followed by a  positive field width.''
13334235Sbostic 			 *	-- ANSI X3J11
13434235Sbostic 			 * They don't exclude field widths read from args.
13534235Sbostic 			 */
13634235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
13734318Sbostic 				goto rflag;
13834235Sbostic 			width = -width;
13934427Sbostic 			/* FALLTHROUGH */
14034235Sbostic 		case '-':
14134318Sbostic 			flags |= LADJUST;
14234318Sbostic 			goto rflag;
14334233Sbostic 		case '+':
14434314Sbostic 			sign = '+';
14534318Sbostic 			goto rflag;
14634233Sbostic 		case '.':
14734235Sbostic 			if (*++fmt == '*')
14834318Sbostic 				n = va_arg(argp, int);
14934427Sbostic 			else {
15034318Sbostic 				n = 0;
15134427Sbostic 				while (isascii(*fmt) && isdigit(*fmt))
15234427Sbostic 					n = 10 * n + todigit(*fmt++);
15334233Sbostic 				--fmt;
15434226Sbostic 			}
15534318Sbostic 			prec = n < 0 ? -1 : n;
15634318Sbostic 			goto rflag;
15734233Sbostic 		case '0':
15834427Sbostic 			/*
15934427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
16034427Sbostic 			 * beginning of a field width.''
16134427Sbostic 			 *	-- ANSI X3J11
16234427Sbostic 			 */
16334427Sbostic 			flags |= ZEROPAD;
16434427Sbostic 			goto rflag;
16534233Sbostic 		case '1': case '2': case '3': case '4':
16634233Sbostic 		case '5': case '6': case '7': case '8': case '9':
16734318Sbostic 			n = 0;
16834233Sbostic 			do {
16934331Sbostic 				n = 10 * n + todigit(*fmt);
17034318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
17134318Sbostic 			width = n;
17234233Sbostic 			--fmt;
17334319Sbostic 			goto rflag;
17434235Sbostic 		case 'L':
17534329Sbostic 			flags |= LONGDBL;
17634318Sbostic 			goto rflag;
17734235Sbostic 		case 'h':
17834318Sbostic 			flags |= SHORTINT;
17934318Sbostic 			goto rflag;
18034233Sbostic 		case 'l':
18134318Sbostic 			flags |= LONGINT;
18234318Sbostic 			goto rflag;
18334314Sbostic 		case 'c':
18434427Sbostic 			*(t = buf) = va_arg(argp, int);
18534314Sbostic 			size = 1;
18634427Sbostic 			sign = '\0';
18734314Sbostic 			goto pforw;
18834624Sbostic 		case 'D':
18934624Sbostic 			flags |= LONGINT;
19034624Sbostic 			/*FALLTHROUGH*/
19134314Sbostic 		case 'd':
19234318Sbostic 		case 'i':
19334318Sbostic 			ARG();
19434318Sbostic 			if ((long)_ulong < 0) {
19534318Sbostic 				_ulong = -_ulong;
19634314Sbostic 				sign = '-';
19734241Sbostic 			}
19834241Sbostic 			base = 10;
19934327Sbostic 			goto number;
20034261Sbostic 		case 'e':
20134236Sbostic 		case 'E':
20234235Sbostic 		case 'f':
20334261Sbostic 		case 'g':
20434243Sbostic 		case 'G':
20534243Sbostic 			_double = va_arg(argp, double);
20634328Sbostic 			/*
20734669Sbostic 			 * don't do unrealistic precision; just pad it with
20834669Sbostic 			 * zeroes later, so buffer size stays rational.
20934328Sbostic 			 */
21034328Sbostic 			if (prec > MAXFRACT) {
21134475Sbostic 				if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
21234475Sbostic 					fpprec = prec - MAXFRACT;
21334328Sbostic 				prec = MAXFRACT;
21434328Sbostic 			}
21534624Sbostic 			else if (prec == -1)
21634624Sbostic 				prec = DEFPREC;
21734669Sbostic 			/*
21834669Sbostic 			 * softsign avoids negative 0 if _double is < 0 and
21934669Sbostic 			 * no significant digits will be shown
22034669Sbostic 			 */
22134624Sbostic 			if (_double < 0) {
22234669Sbostic 				softsign = '-';
22334624Sbostic 				_double = -_double;
22434624Sbostic 			}
22534669Sbostic 			else
22634669Sbostic 				softsign = 0;
22734624Sbostic 			/*
22834669Sbostic 			 * cvt may have to round up past the "start" of the
22934624Sbostic 			 * buffer, i.e. ``intf("%.2f", (double)9.999);'';
23034624Sbostic 			 * if the first char isn't NULL, it did.
23134624Sbostic 			 */
23234624Sbostic 			*buf = NULL;
23334669Sbostic 			size = cvt(_double, prec, flags, &softsign, *fmt, buf,
23434624Sbostic 			    buf + sizeof(buf));
23534669Sbostic 			if (softsign)
23634669Sbostic 				sign = '-';
23734624Sbostic 			t = *buf ? buf : buf + 1;
23834314Sbostic 			goto pforw;
23934235Sbostic 		case 'n':
24034427Sbostic 			if (flags & LONGINT)
24134318Sbostic 				*va_arg(argp, long *) = cnt;
24234427Sbostic 			else if (flags & SHORTINT)
24334318Sbostic 				*va_arg(argp, short *) = cnt;
24434318Sbostic 			else
24534318Sbostic 				*va_arg(argp, int *) = cnt;
24634235Sbostic 			break;
24734624Sbostic 		case 'O':
24834624Sbostic 			flags |= LONGINT;
24934624Sbostic 			/*FALLTHROUGH*/
25034226Sbostic 		case 'o':
25134318Sbostic 			ARG();
25234226Sbostic 			base = 8;
25334327Sbostic 			goto nosign;
25434235Sbostic 		case 'p':
25534320Sbostic 			/*
25634321Sbostic 			 * ``The argument shall be a pointer to void.  The
25734321Sbostic 			 * value of the pointer is converted to a sequence
25834321Sbostic 			 * of printable characters, in an implementation-
25934321Sbostic 			 * defined manner.''
26034321Sbostic 			 *	-- ANSI X3J11
26134320Sbostic 			 */
26234427Sbostic 			/* NOSTRICT */
26334320Sbostic 			_ulong = (u_long)va_arg(argp, void *);
26434320Sbostic 			base = 16;
26534327Sbostic 			goto nosign;
26634226Sbostic 		case 's':
26734314Sbostic 			if (!(t = va_arg(argp, char *)))
26834314Sbostic 				t = "(null)";
26934321Sbostic 			if (prec >= 0) {
27034321Sbostic 				/*
27134321Sbostic 				 * can't use strlen; can only look for the
27234321Sbostic 				 * NUL in the first `prec' characters, and
27334321Sbostic 				 * strlen() will go further.
27434321Sbostic 				 */
27534321Sbostic 				char *p, *memchr();
27634321Sbostic 
27734321Sbostic 				if (p = memchr(t, 0, prec)) {
27834321Sbostic 					size = p - t;
27934321Sbostic 					if (size > prec)
28034321Sbostic 						size = prec;
28134427Sbostic 				} else
28234321Sbostic 					size = prec;
28334427Sbostic 			} else
28434321Sbostic 				size = strlen(t);
28534427Sbostic 			sign = '\0';
28634328Sbostic 			goto pforw;
28734624Sbostic 		case 'U':
28834624Sbostic 			flags |= LONGINT;
28934624Sbostic 			/*FALLTHROUGH*/
29034226Sbostic 		case 'u':
29134318Sbostic 			ARG();
29234226Sbostic 			base = 10;
29334327Sbostic 			goto nosign;
29434226Sbostic 		case 'X':
29534226Sbostic 			digs = "0123456789ABCDEF";
29634427Sbostic 			/* FALLTHROUGH */
29734226Sbostic 		case 'x':
29834318Sbostic 			ARG();
29934314Sbostic 			base = 16;
30034326Sbostic 			/* leading 0x/X only if non-zero */
30134427Sbostic 			if (flags & ALT && _ulong != 0)
30234427Sbostic 				flags |= HEXPREFIX;
30334327Sbostic 
30434327Sbostic 			/* unsigned conversions */
30534427Sbostic nosign:			sign = '\0';
30634326Sbostic 			/*
30734330Sbostic 			 * ``... diouXx conversions ... if a precision is
30834330Sbostic 			 * specified, the 0 flag will be ignored.''
30934330Sbostic 			 *	-- ANSI X3J11
31034330Sbostic 			 */
31134427Sbostic number:			if ((dprec = prec) >= 0)
31234427Sbostic 				flags &= ~ZEROPAD;
31334427Sbostic 
31434330Sbostic 			/*
31534326Sbostic 			 * ``The result of converting a zero value with an
31634326Sbostic 			 * explicit precision of zero is no characters.''
31734326Sbostic 			 *	-- ANSI X3J11
31834326Sbostic 			 */
31934427Sbostic 			t = buf + BUF;
32034427Sbostic 			if (_ulong != 0 || prec != 0) {
32134427Sbostic 				do {
32234427Sbostic 					*--t = digs[_ulong % base];
32334427Sbostic 					_ulong /= base;
32434427Sbostic 				} while (_ulong);
32534427Sbostic 				digs = "0123456789abcdef";
32634427Sbostic 				if (flags & ALT && base == 8 && *t != '0')
32734427Sbostic 					*--t = '0'; /* octal leading 0 */
32834330Sbostic 			}
32934427Sbostic 			size = buf + BUF - t;
33034327Sbostic 
33134427Sbostic pforw:
33234427Sbostic 			/*
33334624Sbostic 			 * All reasonable formats wind up here.  At this point,
33434624Sbostic 			 * `t' points to a string which (if not flags&LADJUST)
33534624Sbostic 			 * should be padded out to `width' places.  If
33634624Sbostic 			 * flags&ZEROPAD, it should first be prefixed by any
33734624Sbostic 			 * sign or other prefix; otherwise, it should be blank
33834624Sbostic 			 * padded before the prefix is emitted.  After any
33934624Sbostic 			 * left-hand padding and prefixing, emit zeroes
34034624Sbostic 			 * required by a decimal [diouxX] precision, then print
34134624Sbostic 			 * the string proper, then emit zeroes required by any
34234624Sbostic 			 * leftover floating precision; finally, if LADJUST,
34334624Sbostic 			 * pad with blanks.
34434427Sbostic 			 */
34534327Sbostic 
34634624Sbostic 			/*
34734624Sbostic 			 * compute actual size, so we know how much to pad
34834624Sbostic 			 * fieldsz excludes decimal prec; realsz includes it
34934624Sbostic 			 */
35034427Sbostic 			fieldsz = size + fpprec;
35134427Sbostic 			if (sign)
35234427Sbostic 				fieldsz++;
35334427Sbostic 			if (flags & HEXPREFIX)
35434427Sbostic 				fieldsz += 2;
35534427Sbostic 			realsz = dprec > fieldsz ? dprec : fieldsz;
35634327Sbostic 
35734427Sbostic 			/* right-adjusting blank padding */
35834427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
35934427Sbostic 				for (n = realsz; n < width; n++)
36034427Sbostic 					PUTC(' ');
36134427Sbostic 			/* prefix */
36234427Sbostic 			if (sign)
36334427Sbostic 				PUTC(sign);
36434427Sbostic 			if (flags & HEXPREFIX) {
36534427Sbostic 				PUTC('0');
36634427Sbostic 				PUTC((char)*fmt);
36734327Sbostic 			}
36834427Sbostic 			/* right-adjusting zero padding */
36934427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
37034427Sbostic 				for (n = realsz; n < width; n++)
37134427Sbostic 					PUTC('0');
37234427Sbostic 			/* leading zeroes from decimal precision */
37334427Sbostic 			for (n = fieldsz; n < dprec; n++)
37434427Sbostic 				PUTC('0');
37534327Sbostic 
37634427Sbostic 			/* the string or number proper */
37734427Sbostic 			if (fp->_cnt - (n = size) >= 0 &&
37834427Sbostic 			    (fp->_flag & _IOLBF) == 0) {
37934328Sbostic 				fp->_cnt -= n;
38034427Sbostic 				bcopy(t, (char *)fp->_ptr, n);
38134328Sbostic 				fp->_ptr += n;
38234427Sbostic 			} else
38334427Sbostic 				while (--n >= 0)
38434427Sbostic 					PUTC(*t++);
38534427Sbostic 			/* trailing f.p. zeroes */
38634427Sbostic 			while (--fpprec >= 0)
38734328Sbostic 				PUTC('0');
38834427Sbostic 			/* left-adjusting padding (always blank) */
38934427Sbostic 			if (flags & LADJUST)
39034427Sbostic 				for (n = realsz; n < width; n++)
39134328Sbostic 					PUTC(' ');
39234427Sbostic 			/* finally, adjust cnt */
39334427Sbostic 			cnt += width > realsz ? width : realsz;
39434226Sbostic 			break;
39534427Sbostic 		case '\0':	/* "%?" prints ?, unless ? is NULL */
39634427Sbostic 			return (cnt);
39734226Sbostic 		default:
39834427Sbostic 			PUTC((char)*fmt);
39934427Sbostic 			cnt++;
40034226Sbostic 		}
40134226Sbostic 	}
40234427Sbostic 	/* NOTREACHED */
40334226Sbostic }
40434242Sbostic 
40534624Sbostic static
40634669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp)
40734242Sbostic 	double number;
40834261Sbostic 	register int prec;
40934323Sbostic 	int flags;
41034323Sbostic 	u_char fmtch;
41134669Sbostic 	char *signp, *startp, *endp;
41234242Sbostic {
41334331Sbostic 	register char *p, *t;
41434672Sbostic 	register double fract;
41534624Sbostic 	int dotrim, expcnt, gformat;
41634672Sbostic 	double integer, tmp, modf();
41734669Sbostic 	char *exponent(), *round();
41834242Sbostic 
41934624Sbostic 	dotrim = expcnt = gformat = 0;
42034624Sbostic 	fract = modf(number, &integer);
42134242Sbostic 
42234624Sbostic 	/* get an extra slot for rounding. */
42334624Sbostic 	t = ++startp;
42434624Sbostic 
42534624Sbostic 	/*
42634624Sbostic 	 * get integer portion of number; put into the end of the buffer; the
42734624Sbostic 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
42834624Sbostic 	 */
42934624Sbostic 	for (p = endp - 1; integer; ++expcnt) {
43034624Sbostic 		tmp = modf(integer / 10, &integer);
43134624Sbostic 		*p-- = tochar((int)((tmp + .01) * 10));
43234248Sbostic 	}
43334261Sbostic 	switch(fmtch) {
43434261Sbostic 	case 'f':
43534624Sbostic 		/* reverse integer into beginning of buffer */
43634624Sbostic 		if (expcnt)
43734624Sbostic 			for (; ++p < endp; *t++ = *p);
43834624Sbostic 		else
43934624Sbostic 			*t++ = '0';
44034248Sbostic 		/*
44134624Sbostic 		 * if precision required or alternate flag set, add in a
44234624Sbostic 		 * decimal point.
44334248Sbostic 		 */
44434624Sbostic 		if (prec || flags&ALT)
44534624Sbostic 			*t++ = '.';
44634624Sbostic 		/* if requires more precision and some fraction left */
44734624Sbostic 		if (fract) {
44834624Sbostic 			if (prec)
44934624Sbostic 				do {
45034624Sbostic 					fract = modf(fract * 10, &tmp);
45134624Sbostic 					*t++ = tochar((int)tmp);
45234624Sbostic 				} while (--prec && fract);
45334624Sbostic 			if (fract)
45434669Sbostic 				startp = round(fract, (int *)NULL, startp,
45534669Sbostic 				    t - 1, (char)0, signp);
45634624Sbostic 		}
45734624Sbostic 		for (; prec--; *t++ = '0');
45834624Sbostic 		break;
45934624Sbostic 	case 'e':
46034624Sbostic 	case 'E':
46134624Sbostic eformat:	if (expcnt) {
46234624Sbostic 			*t++ = *++p;
46334624Sbostic 			if (prec || flags&ALT)
46434331Sbostic 				*t++ = '.';
46534624Sbostic 			/* if requires more precision and some integer left */
46634624Sbostic 			for (; prec && ++p < endp; --prec)
46734624Sbostic 				*t++ = *p;
46834624Sbostic 			/*
46934624Sbostic 			 * if done precision and more of the integer component,
47034624Sbostic 			 * round using it; adjust fract so we don't re-round
47134624Sbostic 			 * later.
47234624Sbostic 			 */
47334624Sbostic 			if (!prec && ++p < endp) {
47434248Sbostic 				fract = 0;
47534669Sbostic 				startp = round((double)0, &expcnt, startp,
47634669Sbostic 				    t - 1, *p, signp);
47734248Sbostic 			}
47834624Sbostic 			/* adjust expcnt for digit in front of decimal */
47934624Sbostic 			--expcnt;
48034242Sbostic 		}
48134624Sbostic 		/* until first fractional digit, decrement exponent */
48234624Sbostic 		else if (fract) {
48334624Sbostic 			/* adjust expcnt for digit in front of decimal */
48434624Sbostic 			for (expcnt = -1;; --expcnt) {
48534624Sbostic 				fract = modf(fract * 10, &tmp);
48634624Sbostic 				if (tmp)
48734624Sbostic 					break;
48834248Sbostic 			}
48934624Sbostic 			*t++ = tochar((int)tmp);
49034624Sbostic 			if (prec || flags&ALT)
49134331Sbostic 				*t++ = '.';
49234242Sbostic 		}
49334248Sbostic 		else {
49434624Sbostic 			*t++ = '0';
49534624Sbostic 			if (prec || flags&ALT)
49634331Sbostic 				*t++ = '.';
49734248Sbostic 		}
49834624Sbostic 		/* if requires more precision and some fraction left */
49934624Sbostic 		if (fract) {
50034624Sbostic 			if (prec)
50134624Sbostic 				do {
50234624Sbostic 					fract = modf(fract * 10, &tmp);
50334624Sbostic 					*t++ = tochar((int)tmp);
50434624Sbostic 				} while (--prec && fract);
50534624Sbostic 			if (fract)
50634669Sbostic 				startp = round(fract, &expcnt, startp,
50734669Sbostic 				    t - 1, (char)0, signp);
50834584Sbostic 		}
50934624Sbostic 		/* if requires more precision */
51034624Sbostic 		for (; prec--; *t++ = '0');
51134624Sbostic 
51234624Sbostic 		/* unless alternate flag, trim any g/G format trailing 0's */
51334624Sbostic 		if (gformat && !(flags&ALT)) {
51434624Sbostic 			while (t > startp && *--t == '0');
51534624Sbostic 			if (*t == '.')
51634624Sbostic 				--t;
51734624Sbostic 			++t;
51834624Sbostic 		}
51934624Sbostic 		t = exponent(t, expcnt, fmtch);
52034624Sbostic 		break;
52134624Sbostic 	case 'g':
52234624Sbostic 	case 'G':
52334624Sbostic 		/* a precision of 0 is treated as a precision of 1. */
52434624Sbostic 		if (!prec)
52534624Sbostic 			++prec;
52634624Sbostic 		/*
52734624Sbostic 		 * ``The style used depends on the value converted; style e
52834624Sbostic 		 * will be used only if the exponent resulting from the
52934624Sbostic 		 * conversion is less than -4 or greater than the precision.''
53034624Sbostic 		 *	-- ANSI X3J11
53134624Sbostic 		 */
53234624Sbostic 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
53334624Sbostic 			/*
53434624Sbostic 			 * g/G format counts "significant digits, not digits of
53534624Sbostic 			 * precision; for the e/E format, this just causes an
53634624Sbostic 			 * off-by-one problem, i.e. g/G considers the digit
53734624Sbostic 			 * before the decimal point significant and e/E doesn't
53834624Sbostic 			 * count it as precision.
53934624Sbostic 			 */
54034624Sbostic 			--prec;
54134624Sbostic 			fmtch -= 2;		/* G->E, g->e */
54234624Sbostic 			gformat = 1;
54334624Sbostic 			goto eformat;
54434624Sbostic 		}
54534624Sbostic 		/*
54634624Sbostic 		 * reverse integer into beginning of buffer,
54734624Sbostic 		 * note, decrement precision
54834624Sbostic 		 */
54934624Sbostic 		if (expcnt)
55034624Sbostic 			for (; ++p < endp; *t++ = *p, --prec);
55134624Sbostic 		else
55234624Sbostic 			*t++ = '0';
55334624Sbostic 		/*
55434624Sbostic 		 * if precision required or alternate flag set, add in a
55534624Sbostic 		 * decimal point.  If no digits yet, add in leading 0.
55634624Sbostic 		 */
55734624Sbostic 		if (prec || flags&ALT) {
55834624Sbostic 			dotrim = 1;
55934624Sbostic 			*t++ = '.';
56034624Sbostic 		}
56134624Sbostic 		else
56234624Sbostic 			dotrim = 0;
56334624Sbostic 		/* if requires more precision and some fraction left */
56434624Sbostic 		if (fract) {
56534624Sbostic 			if (prec) {
56634624Sbostic 				do {
56734624Sbostic 					fract = modf(fract * 10, &tmp);
56834624Sbostic 					*t++ = tochar((int)tmp);
56934624Sbostic 				} while(!tmp);
57034624Sbostic 				while (--prec && fract) {
57134624Sbostic 					fract = modf(fract * 10, &tmp);
57234624Sbostic 					*t++ = tochar((int)tmp);
57334248Sbostic 				}
57434248Sbostic 			}
57534624Sbostic 			if (fract)
57634669Sbostic 				startp = round(fract, (int *)NULL, startp,
57734669Sbostic 				    t - 1, (char)0, signp);
57834624Sbostic 		}
57934624Sbostic 		/* alternate format, adds 0's for precision, else trim 0's */
58034624Sbostic 		if (flags&ALT)
58134624Sbostic 			for (; prec--; *t++ = '0');
58234624Sbostic 		else if (dotrim) {
58334624Sbostic 			while (t > startp && *--t == '0');
58434624Sbostic 			if (*t != '.')
58534624Sbostic 				++t;
58634624Sbostic 		}
58734624Sbostic 	}
58834624Sbostic 	return(t - startp);
58934624Sbostic }
59034248Sbostic 
59134624Sbostic static char *
59234669Sbostic round(fract, exp, start, end, ch, signp)
59334624Sbostic 	double fract;
59434669Sbostic 	int *exp;
59534624Sbostic 	register char *start, *end;
59634669Sbostic 	char ch, *signp;
59734624Sbostic {
59834624Sbostic 	double tmp;
59934248Sbostic 
60034624Sbostic 	if (fract)
60134248Sbostic 		(void)modf(fract * 10, &tmp);
60234624Sbostic 	else
60334624Sbostic 		tmp = todigit(ch);
60434624Sbostic 	if (tmp > 4)
60534624Sbostic 		for (;; --end) {
60634624Sbostic 			if (*end == '.')
60734624Sbostic 				--end;
60834624Sbostic 			if (++*end <= '9')
60934624Sbostic 				break;
61034624Sbostic 			*end = '0';
61134624Sbostic 			if (end == start) {
61234669Sbostic 				if (exp) {	/* e/E; increment exponent */
61334669Sbostic 					*end = '1';
61434669Sbostic 					++*exp;
61534669Sbostic 				}
61634669Sbostic 				else {		/* f; add extra digit */
61734669Sbostic 					*--end = '1';
61834669Sbostic 					--start;
61934669Sbostic 				}
62034624Sbostic 				break;
62134242Sbostic 			}
62234242Sbostic 		}
62334669Sbostic 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
62434669Sbostic 	else if (*signp == '-')
62534669Sbostic 		for (;; --end) {
62634669Sbostic 			if (*end == '.')
62734669Sbostic 				--end;
62834669Sbostic 			if (*end != '0')
62934669Sbostic 				break;
63034669Sbostic 			if (end == start)
63134669Sbostic 				*signp = 0;
63234669Sbostic 		}
63334624Sbostic 	return(start);
63434624Sbostic }
63534248Sbostic 
63634624Sbostic static char *
63734624Sbostic exponent(p, exp, fmtch)
63834624Sbostic 	register char *p;
63934624Sbostic 	register int exp;
64034624Sbostic 	u_char fmtch;
64134624Sbostic {
64234624Sbostic 	register char *t;
64334624Sbostic 	char expbuf[MAXEXP];
64434248Sbostic 
64534624Sbostic 	*p++ = fmtch;
64634624Sbostic 	if (exp < 0) {
64734624Sbostic 		exp = -exp;
64834624Sbostic 		*p++ = '-';
64934242Sbostic 	}
65034624Sbostic 	else
65134624Sbostic 		*p++ = '+';
65234624Sbostic 	t = expbuf + MAXEXP;
65334624Sbostic 	if (exp > 9) {
65434624Sbostic 		do {
65534624Sbostic 			*--t = tochar(exp % 10);
65634624Sbostic 		} while ((exp /= 10) > 9);
65734624Sbostic 		*--t = tochar(exp);
65834624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
65934624Sbostic 	}
66034624Sbostic 	else {
66134624Sbostic 		*p++ = '0';
66234624Sbostic 		*p++ = tochar(exp);
66334624Sbostic 	}
66434323Sbostic 	return(p);
66534242Sbostic }
666