xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 42631)
134226Sbostic /*
234233Sbostic  * Copyright (c) 1988 Regents of the University of California.
334233Sbostic  * All rights reserved.
434226Sbostic  *
5*42631Sbostic  * %sccs.include.redist.c%
634226Sbostic  */
734226Sbostic 
834233Sbostic #if defined(LIBC_SCCS) && !defined(lint)
9*42631Sbostic static char sccsid[] = "@(#)vfprintf.c	5.38 (Berkeley) 06/01/90";
1034233Sbostic #endif /* LIBC_SCCS and not lint */
1134233Sbostic 
1234261Sbostic #include <sys/types.h>
1334233Sbostic #include <varargs.h>
1434226Sbostic #include <stdio.h>
1534233Sbostic #include <ctype.h>
1634226Sbostic 
1734328Sbostic /* 11-bit exponent (VAX G floating point) is 308 decimal digits */
1834328Sbostic #define	MAXEXP		308
1934328Sbostic /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
2034328Sbostic #define	MAXFRACT	39
2134226Sbostic 
2234624Sbostic #define	DEFPREC		6
2334624Sbostic 
2434328Sbostic #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
2534328Sbostic 
2634427Sbostic #define	PUTC(ch)	(void) putc(ch, fp)
2734236Sbostic 
2836090Sbostic #define ARG(basetype) \
2936090Sbostic 	_ulong = flags&LONGINT ? va_arg(argp, long basetype) : \
3036090Sbostic 	    flags&SHORTINT ? (short basetype)va_arg(argp, int) : \
3136090Sbostic 	    va_arg(argp, int)
3234235Sbostic 
3334331Sbostic #define	todigit(c)	((c) - '0')
3434331Sbostic #define	tochar(n)	((n) + '0')
3534331Sbostic 
3634318Sbostic /* have to deal with the negative buffer count kludge */
3734318Sbostic #define	NEGATIVE_COUNT_KLUDGE
3834318Sbostic 
3934318Sbostic #define	LONGINT		0x01		/* long integer */
4034318Sbostic #define	LONGDBL		0x02		/* long double; unimplemented */
4134318Sbostic #define	SHORTINT	0x04		/* short integer */
4234318Sbostic #define	ALT		0x08		/* alternate form */
4334318Sbostic #define	LADJUST		0x10		/* left adjustment */
4434427Sbostic #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
4534427Sbostic #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
4634318Sbostic 
4734323Sbostic _doprnt(fmt0, argp, fp)
4834323Sbostic 	u_char *fmt0;
4934233Sbostic 	va_list argp;
5034235Sbostic 	register FILE *fp;
5134226Sbostic {
5234427Sbostic 	register u_char *fmt;	/* format string */
5334427Sbostic 	register int ch;	/* character from fmt */
5434427Sbostic 	register int cnt;	/* return value accumulator */
5534427Sbostic 	register int n;		/* random handy integer */
5634427Sbostic 	register char *t;	/* buffer pointer */
5734427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
5834427Sbostic 	u_long _ulong;		/* integer arguments %[diouxX] */
5934624Sbostic 	int base;		/* base for [diouxX] conversion */
6034624Sbostic 	int dprec;		/* decimal precision in [diouxX] */
6134624Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
6234427Sbostic 	int flags;		/* flags as above */
6334427Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
6434427Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
6534624Sbostic 	int realsz;		/* field size expanded by decimal precision */
6634427Sbostic 	int size;		/* size of converted field or string */
6734624Sbostic 	int width;		/* width from format (%8d), or 0 */
6834669Sbostic 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
6934669Sbostic 	char softsign;		/* temporary negative sign for floats */
7034427Sbostic 	char *digs;		/* digits for [diouxX] conversion */
7134427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
7234226Sbostic 
7334428Sbostic 	if (fp->_flag & _IORW) {
7434428Sbostic 		fp->_flag |= _IOWRT;
7534428Sbostic 		fp->_flag &= ~(_IOEOF|_IOREAD);
7634428Sbostic 	}
7734428Sbostic 	if ((fp->_flag & _IOWRT) == 0)
7834428Sbostic 		return (EOF);
7934428Sbostic 
8034323Sbostic 	fmt = fmt0;
8134243Sbostic 	digs = "0123456789abcdef";
8234322Sbostic 	for (cnt = 0;; ++fmt) {
8334318Sbostic 		n = fp->_cnt;
8434427Sbostic 		for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
8534427Sbostic 		     ++cnt, ++fmt)
8634318Sbostic 			if (--n < 0
8734318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
8834318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
8934318Sbostic #endif
9034427Sbostic 			    || ch == '\n' && fp->_flag & _IOLBF) {
9134318Sbostic 				fp->_cnt = n;
9234475Sbostic 				fp->_ptr = t;
9334427Sbostic 				(void) _flsbuf((u_char)ch, fp);
9434318Sbostic 				n = fp->_cnt;
9534427Sbostic 				t = (char *)fp->_ptr;
9634427Sbostic 			} else
9734314Sbostic 				*t++ = ch;
9834318Sbostic 		fp->_cnt = n;
9934475Sbostic 		fp->_ptr = t;
10034318Sbostic 		if (!ch)
10134427Sbostic 			return (cnt);
10234314Sbostic 
10334672Sbostic 		flags = 0; dprec = 0; fpprec = 0; width = 0;
10434233Sbostic 		prec = -1;
10534318Sbostic 		sign = '\0';
10634226Sbostic 
10734318Sbostic rflag:		switch (*++fmt) {
10834318Sbostic 		case ' ':
10934669Sbostic 			/*
11034669Sbostic 			 * ``If the space and + flags both appear, the space
11134669Sbostic 			 * flag will be ignored.''
11234669Sbostic 			 *	-- ANSI X3J11
11334669Sbostic 			 */
11434669Sbostic 			if (!sign)
11534669Sbostic 				sign = ' ';
11634318Sbostic 			goto rflag;
11734233Sbostic 		case '#':
11834318Sbostic 			flags |= ALT;
11934318Sbostic 			goto rflag;
12034233Sbostic 		case '*':
12134235Sbostic 			/*
12234235Sbostic 			 * ``A negative field width argument is taken as a
12334235Sbostic 			 * - flag followed by a  positive field width.''
12434235Sbostic 			 *	-- ANSI X3J11
12534235Sbostic 			 * They don't exclude field widths read from args.
12634235Sbostic 			 */
12734235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
12834318Sbostic 				goto rflag;
12934235Sbostic 			width = -width;
13034427Sbostic 			/* FALLTHROUGH */
13134235Sbostic 		case '-':
13234318Sbostic 			flags |= LADJUST;
13334318Sbostic 			goto rflag;
13434233Sbostic 		case '+':
13534314Sbostic 			sign = '+';
13634318Sbostic 			goto rflag;
13734233Sbostic 		case '.':
13834235Sbostic 			if (*++fmt == '*')
13934318Sbostic 				n = va_arg(argp, int);
14034427Sbostic 			else {
14134318Sbostic 				n = 0;
14234427Sbostic 				while (isascii(*fmt) && isdigit(*fmt))
14334427Sbostic 					n = 10 * n + todigit(*fmt++);
14434233Sbostic 				--fmt;
14534226Sbostic 			}
14634318Sbostic 			prec = n < 0 ? -1 : n;
14734318Sbostic 			goto rflag;
14834233Sbostic 		case '0':
14934427Sbostic 			/*
15034427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
15134427Sbostic 			 * beginning of a field width.''
15234427Sbostic 			 *	-- ANSI X3J11
15334427Sbostic 			 */
15434427Sbostic 			flags |= ZEROPAD;
15534427Sbostic 			goto rflag;
15634233Sbostic 		case '1': case '2': case '3': case '4':
15734233Sbostic 		case '5': case '6': case '7': case '8': case '9':
15834318Sbostic 			n = 0;
15934233Sbostic 			do {
16034331Sbostic 				n = 10 * n + todigit(*fmt);
16134318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
16234318Sbostic 			width = n;
16334233Sbostic 			--fmt;
16434319Sbostic 			goto rflag;
16534235Sbostic 		case 'L':
16634329Sbostic 			flags |= LONGDBL;
16734318Sbostic 			goto rflag;
16834235Sbostic 		case 'h':
16934318Sbostic 			flags |= SHORTINT;
17034318Sbostic 			goto rflag;
17134233Sbostic 		case 'l':
17234318Sbostic 			flags |= LONGINT;
17334318Sbostic 			goto rflag;
17434314Sbostic 		case 'c':
17534427Sbostic 			*(t = buf) = va_arg(argp, int);
17634314Sbostic 			size = 1;
17734427Sbostic 			sign = '\0';
17834314Sbostic 			goto pforw;
17934624Sbostic 		case 'D':
18034624Sbostic 			flags |= LONGINT;
18134624Sbostic 			/*FALLTHROUGH*/
18234314Sbostic 		case 'd':
18334318Sbostic 		case 'i':
18436090Sbostic 			ARG(int);
18534318Sbostic 			if ((long)_ulong < 0) {
18634318Sbostic 				_ulong = -_ulong;
18734314Sbostic 				sign = '-';
18834241Sbostic 			}
18934241Sbostic 			base = 10;
19034327Sbostic 			goto number;
19134261Sbostic 		case 'e':
19234236Sbostic 		case 'E':
19334235Sbostic 		case 'f':
19434261Sbostic 		case 'g':
19534243Sbostic 		case 'G':
19634243Sbostic 			_double = va_arg(argp, double);
19734328Sbostic 			/*
19834669Sbostic 			 * don't do unrealistic precision; just pad it with
19934669Sbostic 			 * zeroes later, so buffer size stays rational.
20034328Sbostic 			 */
20134328Sbostic 			if (prec > MAXFRACT) {
20234475Sbostic 				if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
20334475Sbostic 					fpprec = prec - MAXFRACT;
20434328Sbostic 				prec = MAXFRACT;
20534328Sbostic 			}
20634624Sbostic 			else if (prec == -1)
20734624Sbostic 				prec = DEFPREC;
20834669Sbostic 			/*
20934669Sbostic 			 * softsign avoids negative 0 if _double is < 0 and
21034669Sbostic 			 * no significant digits will be shown
21134669Sbostic 			 */
21234624Sbostic 			if (_double < 0) {
21334669Sbostic 				softsign = '-';
21434624Sbostic 				_double = -_double;
21534624Sbostic 			}
21634669Sbostic 			else
21734669Sbostic 				softsign = 0;
21834624Sbostic 			/*
21934669Sbostic 			 * cvt may have to round up past the "start" of the
22034624Sbostic 			 * buffer, i.e. ``intf("%.2f", (double)9.999);'';
22134624Sbostic 			 * if the first char isn't NULL, it did.
22234624Sbostic 			 */
22334624Sbostic 			*buf = NULL;
22434669Sbostic 			size = cvt(_double, prec, flags, &softsign, *fmt, buf,
22534624Sbostic 			    buf + sizeof(buf));
22634669Sbostic 			if (softsign)
22734669Sbostic 				sign = '-';
22834624Sbostic 			t = *buf ? buf : buf + 1;
22934314Sbostic 			goto pforw;
23034235Sbostic 		case 'n':
23134427Sbostic 			if (flags & LONGINT)
23234318Sbostic 				*va_arg(argp, long *) = cnt;
23334427Sbostic 			else if (flags & SHORTINT)
23434318Sbostic 				*va_arg(argp, short *) = cnt;
23534318Sbostic 			else
23634318Sbostic 				*va_arg(argp, int *) = cnt;
23734235Sbostic 			break;
23834624Sbostic 		case 'O':
23934624Sbostic 			flags |= LONGINT;
24034624Sbostic 			/*FALLTHROUGH*/
24134226Sbostic 		case 'o':
24236090Sbostic 			ARG(unsigned);
24334226Sbostic 			base = 8;
24434327Sbostic 			goto nosign;
24534235Sbostic 		case 'p':
24634320Sbostic 			/*
24734321Sbostic 			 * ``The argument shall be a pointer to void.  The
24834321Sbostic 			 * value of the pointer is converted to a sequence
24934321Sbostic 			 * of printable characters, in an implementation-
25034321Sbostic 			 * defined manner.''
25134321Sbostic 			 *	-- ANSI X3J11
25234320Sbostic 			 */
25334427Sbostic 			/* NOSTRICT */
25434320Sbostic 			_ulong = (u_long)va_arg(argp, void *);
25534320Sbostic 			base = 16;
25634327Sbostic 			goto nosign;
25734226Sbostic 		case 's':
25834314Sbostic 			if (!(t = va_arg(argp, char *)))
25934314Sbostic 				t = "(null)";
26034321Sbostic 			if (prec >= 0) {
26134321Sbostic 				/*
26234321Sbostic 				 * can't use strlen; can only look for the
26334321Sbostic 				 * NUL in the first `prec' characters, and
26434321Sbostic 				 * strlen() will go further.
26534321Sbostic 				 */
26634321Sbostic 				char *p, *memchr();
26734321Sbostic 
26834321Sbostic 				if (p = memchr(t, 0, prec)) {
26934321Sbostic 					size = p - t;
27034321Sbostic 					if (size > prec)
27134321Sbostic 						size = prec;
27234427Sbostic 				} else
27334321Sbostic 					size = prec;
27434427Sbostic 			} else
27534321Sbostic 				size = strlen(t);
27634427Sbostic 			sign = '\0';
27734328Sbostic 			goto pforw;
27834624Sbostic 		case 'U':
27934624Sbostic 			flags |= LONGINT;
28034624Sbostic 			/*FALLTHROUGH*/
28134226Sbostic 		case 'u':
28236090Sbostic 			ARG(unsigned);
28334226Sbostic 			base = 10;
28434327Sbostic 			goto nosign;
28534226Sbostic 		case 'X':
28634226Sbostic 			digs = "0123456789ABCDEF";
28734427Sbostic 			/* FALLTHROUGH */
28834226Sbostic 		case 'x':
28936090Sbostic 			ARG(unsigned);
29034314Sbostic 			base = 16;
29134326Sbostic 			/* leading 0x/X only if non-zero */
29234427Sbostic 			if (flags & ALT && _ulong != 0)
29334427Sbostic 				flags |= HEXPREFIX;
29434327Sbostic 
29534327Sbostic 			/* unsigned conversions */
29634427Sbostic nosign:			sign = '\0';
29734326Sbostic 			/*
29834330Sbostic 			 * ``... diouXx conversions ... if a precision is
29934330Sbostic 			 * specified, the 0 flag will be ignored.''
30034330Sbostic 			 *	-- ANSI X3J11
30134330Sbostic 			 */
30234427Sbostic number:			if ((dprec = prec) >= 0)
30334427Sbostic 				flags &= ~ZEROPAD;
30434427Sbostic 
30534330Sbostic 			/*
30634326Sbostic 			 * ``The result of converting a zero value with an
30734326Sbostic 			 * explicit precision of zero is no characters.''
30834326Sbostic 			 *	-- ANSI X3J11
30934326Sbostic 			 */
31034427Sbostic 			t = buf + BUF;
31134427Sbostic 			if (_ulong != 0 || prec != 0) {
31234427Sbostic 				do {
31334427Sbostic 					*--t = digs[_ulong % base];
31434427Sbostic 					_ulong /= base;
31534427Sbostic 				} while (_ulong);
31634427Sbostic 				digs = "0123456789abcdef";
31734427Sbostic 				if (flags & ALT && base == 8 && *t != '0')
31834427Sbostic 					*--t = '0'; /* octal leading 0 */
31934330Sbostic 			}
32034427Sbostic 			size = buf + BUF - t;
32134327Sbostic 
32234427Sbostic pforw:
32334427Sbostic 			/*
32434624Sbostic 			 * All reasonable formats wind up here.  At this point,
32534624Sbostic 			 * `t' points to a string which (if not flags&LADJUST)
32634624Sbostic 			 * should be padded out to `width' places.  If
32734624Sbostic 			 * flags&ZEROPAD, it should first be prefixed by any
32834624Sbostic 			 * sign or other prefix; otherwise, it should be blank
32934624Sbostic 			 * padded before the prefix is emitted.  After any
33034624Sbostic 			 * left-hand padding and prefixing, emit zeroes
33134624Sbostic 			 * required by a decimal [diouxX] precision, then print
33234624Sbostic 			 * the string proper, then emit zeroes required by any
33334624Sbostic 			 * leftover floating precision; finally, if LADJUST,
33434624Sbostic 			 * pad with blanks.
33534427Sbostic 			 */
33634327Sbostic 
33734624Sbostic 			/*
33834624Sbostic 			 * compute actual size, so we know how much to pad
33934624Sbostic 			 * fieldsz excludes decimal prec; realsz includes it
34034624Sbostic 			 */
34134427Sbostic 			fieldsz = size + fpprec;
34234427Sbostic 			if (sign)
34334427Sbostic 				fieldsz++;
34434427Sbostic 			if (flags & HEXPREFIX)
34534427Sbostic 				fieldsz += 2;
34634427Sbostic 			realsz = dprec > fieldsz ? dprec : fieldsz;
34734327Sbostic 
34834427Sbostic 			/* right-adjusting blank padding */
34934427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
35034427Sbostic 				for (n = realsz; n < width; n++)
35134427Sbostic 					PUTC(' ');
35234427Sbostic 			/* prefix */
35334427Sbostic 			if (sign)
35434427Sbostic 				PUTC(sign);
35534427Sbostic 			if (flags & HEXPREFIX) {
35634427Sbostic 				PUTC('0');
35734427Sbostic 				PUTC((char)*fmt);
35834327Sbostic 			}
35934427Sbostic 			/* right-adjusting zero padding */
36034427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
36134427Sbostic 				for (n = realsz; n < width; n++)
36234427Sbostic 					PUTC('0');
36334427Sbostic 			/* leading zeroes from decimal precision */
36434427Sbostic 			for (n = fieldsz; n < dprec; n++)
36534427Sbostic 				PUTC('0');
36634327Sbostic 
36734427Sbostic 			/* the string or number proper */
36837234Sbostic 			n = size;
36937234Sbostic 			if (fp->_cnt - n >= 0 && (fp->_flag & _IOLBF) == 0) {
37034328Sbostic 				fp->_cnt -= n;
37134427Sbostic 				bcopy(t, (char *)fp->_ptr, n);
37234328Sbostic 				fp->_ptr += n;
37334427Sbostic 			} else
37434427Sbostic 				while (--n >= 0)
37534427Sbostic 					PUTC(*t++);
37634427Sbostic 			/* trailing f.p. zeroes */
37734427Sbostic 			while (--fpprec >= 0)
37834328Sbostic 				PUTC('0');
37934427Sbostic 			/* left-adjusting padding (always blank) */
38034427Sbostic 			if (flags & LADJUST)
38134427Sbostic 				for (n = realsz; n < width; n++)
38234328Sbostic 					PUTC(' ');
38334427Sbostic 			/* finally, adjust cnt */
38434427Sbostic 			cnt += width > realsz ? width : realsz;
38534226Sbostic 			break;
38634427Sbostic 		case '\0':	/* "%?" prints ?, unless ? is NULL */
38734427Sbostic 			return (cnt);
38834226Sbostic 		default:
38934427Sbostic 			PUTC((char)*fmt);
39034427Sbostic 			cnt++;
39134226Sbostic 		}
39234226Sbostic 	}
39334427Sbostic 	/* NOTREACHED */
39434226Sbostic }
39534242Sbostic 
39634624Sbostic static
39734669Sbostic cvt(number, prec, flags, signp, fmtch, startp, endp)
39834242Sbostic 	double number;
39934261Sbostic 	register int prec;
40034323Sbostic 	int flags;
40134323Sbostic 	u_char fmtch;
40234669Sbostic 	char *signp, *startp, *endp;
40334242Sbostic {
40434331Sbostic 	register char *p, *t;
40534672Sbostic 	register double fract;
40634624Sbostic 	int dotrim, expcnt, gformat;
40734672Sbostic 	double integer, tmp, modf();
40834669Sbostic 	char *exponent(), *round();
40934242Sbostic 
41034624Sbostic 	dotrim = expcnt = gformat = 0;
41134624Sbostic 	fract = modf(number, &integer);
41234242Sbostic 
41334624Sbostic 	/* get an extra slot for rounding. */
41434624Sbostic 	t = ++startp;
41534624Sbostic 
41634624Sbostic 	/*
41734624Sbostic 	 * get integer portion of number; put into the end of the buffer; the
41834624Sbostic 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
41934624Sbostic 	 */
42034624Sbostic 	for (p = endp - 1; integer; ++expcnt) {
42134624Sbostic 		tmp = modf(integer / 10, &integer);
42234624Sbostic 		*p-- = tochar((int)((tmp + .01) * 10));
42334248Sbostic 	}
42434261Sbostic 	switch(fmtch) {
42534261Sbostic 	case 'f':
42634624Sbostic 		/* reverse integer into beginning of buffer */
42734624Sbostic 		if (expcnt)
42834624Sbostic 			for (; ++p < endp; *t++ = *p);
42934624Sbostic 		else
43034624Sbostic 			*t++ = '0';
43134248Sbostic 		/*
43234624Sbostic 		 * if precision required or alternate flag set, add in a
43334624Sbostic 		 * decimal point.
43434248Sbostic 		 */
43534624Sbostic 		if (prec || flags&ALT)
43634624Sbostic 			*t++ = '.';
43734624Sbostic 		/* if requires more precision and some fraction left */
43834624Sbostic 		if (fract) {
43934624Sbostic 			if (prec)
44034624Sbostic 				do {
44134624Sbostic 					fract = modf(fract * 10, &tmp);
44234624Sbostic 					*t++ = tochar((int)tmp);
44334624Sbostic 				} while (--prec && fract);
44434624Sbostic 			if (fract)
44534669Sbostic 				startp = round(fract, (int *)NULL, startp,
44634669Sbostic 				    t - 1, (char)0, signp);
44734624Sbostic 		}
44834624Sbostic 		for (; prec--; *t++ = '0');
44934624Sbostic 		break;
45034624Sbostic 	case 'e':
45134624Sbostic 	case 'E':
45234624Sbostic eformat:	if (expcnt) {
45334624Sbostic 			*t++ = *++p;
45434624Sbostic 			if (prec || flags&ALT)
45534331Sbostic 				*t++ = '.';
45634624Sbostic 			/* if requires more precision and some integer left */
45734624Sbostic 			for (; prec && ++p < endp; --prec)
45834624Sbostic 				*t++ = *p;
45934624Sbostic 			/*
46034624Sbostic 			 * if done precision and more of the integer component,
46134624Sbostic 			 * round using it; adjust fract so we don't re-round
46234624Sbostic 			 * later.
46334624Sbostic 			 */
46434624Sbostic 			if (!prec && ++p < endp) {
46534248Sbostic 				fract = 0;
46634669Sbostic 				startp = round((double)0, &expcnt, startp,
46734669Sbostic 				    t - 1, *p, signp);
46834248Sbostic 			}
46934624Sbostic 			/* adjust expcnt for digit in front of decimal */
47034624Sbostic 			--expcnt;
47134242Sbostic 		}
47234624Sbostic 		/* until first fractional digit, decrement exponent */
47334624Sbostic 		else if (fract) {
47434624Sbostic 			/* adjust expcnt for digit in front of decimal */
47534624Sbostic 			for (expcnt = -1;; --expcnt) {
47634624Sbostic 				fract = modf(fract * 10, &tmp);
47734624Sbostic 				if (tmp)
47834624Sbostic 					break;
47934248Sbostic 			}
48034624Sbostic 			*t++ = tochar((int)tmp);
48134624Sbostic 			if (prec || flags&ALT)
48234331Sbostic 				*t++ = '.';
48334242Sbostic 		}
48434248Sbostic 		else {
48534624Sbostic 			*t++ = '0';
48634624Sbostic 			if (prec || flags&ALT)
48734331Sbostic 				*t++ = '.';
48834248Sbostic 		}
48934624Sbostic 		/* if requires more precision and some fraction left */
49034624Sbostic 		if (fract) {
49134624Sbostic 			if (prec)
49234624Sbostic 				do {
49334624Sbostic 					fract = modf(fract * 10, &tmp);
49434624Sbostic 					*t++ = tochar((int)tmp);
49534624Sbostic 				} while (--prec && fract);
49634624Sbostic 			if (fract)
49734669Sbostic 				startp = round(fract, &expcnt, startp,
49834669Sbostic 				    t - 1, (char)0, signp);
49934584Sbostic 		}
50034624Sbostic 		/* if requires more precision */
50134624Sbostic 		for (; prec--; *t++ = '0');
50234624Sbostic 
50334624Sbostic 		/* unless alternate flag, trim any g/G format trailing 0's */
50434624Sbostic 		if (gformat && !(flags&ALT)) {
50534624Sbostic 			while (t > startp && *--t == '0');
50634624Sbostic 			if (*t == '.')
50734624Sbostic 				--t;
50834624Sbostic 			++t;
50934624Sbostic 		}
51034624Sbostic 		t = exponent(t, expcnt, fmtch);
51134624Sbostic 		break;
51234624Sbostic 	case 'g':
51334624Sbostic 	case 'G':
51434624Sbostic 		/* a precision of 0 is treated as a precision of 1. */
51534624Sbostic 		if (!prec)
51634624Sbostic 			++prec;
51734624Sbostic 		/*
51834624Sbostic 		 * ``The style used depends on the value converted; style e
51934624Sbostic 		 * will be used only if the exponent resulting from the
52034624Sbostic 		 * conversion is less than -4 or greater than the precision.''
52134624Sbostic 		 *	-- ANSI X3J11
52234624Sbostic 		 */
52334624Sbostic 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
52434624Sbostic 			/*
52534624Sbostic 			 * g/G format counts "significant digits, not digits of
52634624Sbostic 			 * precision; for the e/E format, this just causes an
52734624Sbostic 			 * off-by-one problem, i.e. g/G considers the digit
52834624Sbostic 			 * before the decimal point significant and e/E doesn't
52934624Sbostic 			 * count it as precision.
53034624Sbostic 			 */
53134624Sbostic 			--prec;
53234624Sbostic 			fmtch -= 2;		/* G->E, g->e */
53334624Sbostic 			gformat = 1;
53434624Sbostic 			goto eformat;
53534624Sbostic 		}
53634624Sbostic 		/*
53734624Sbostic 		 * reverse integer into beginning of buffer,
53834624Sbostic 		 * note, decrement precision
53934624Sbostic 		 */
54034624Sbostic 		if (expcnt)
54134624Sbostic 			for (; ++p < endp; *t++ = *p, --prec);
54234624Sbostic 		else
54334624Sbostic 			*t++ = '0';
54434624Sbostic 		/*
54534624Sbostic 		 * if precision required or alternate flag set, add in a
54634624Sbostic 		 * decimal point.  If no digits yet, add in leading 0.
54734624Sbostic 		 */
54834624Sbostic 		if (prec || flags&ALT) {
54934624Sbostic 			dotrim = 1;
55034624Sbostic 			*t++ = '.';
55134624Sbostic 		}
55234624Sbostic 		else
55334624Sbostic 			dotrim = 0;
55434624Sbostic 		/* if requires more precision and some fraction left */
55534624Sbostic 		if (fract) {
55634624Sbostic 			if (prec) {
55734624Sbostic 				do {
55834624Sbostic 					fract = modf(fract * 10, &tmp);
55934624Sbostic 					*t++ = tochar((int)tmp);
56034624Sbostic 				} while(!tmp);
56134624Sbostic 				while (--prec && fract) {
56234624Sbostic 					fract = modf(fract * 10, &tmp);
56334624Sbostic 					*t++ = tochar((int)tmp);
56434248Sbostic 				}
56534248Sbostic 			}
56634624Sbostic 			if (fract)
56734669Sbostic 				startp = round(fract, (int *)NULL, startp,
56834669Sbostic 				    t - 1, (char)0, signp);
56934624Sbostic 		}
57034624Sbostic 		/* alternate format, adds 0's for precision, else trim 0's */
57134624Sbostic 		if (flags&ALT)
57234624Sbostic 			for (; prec--; *t++ = '0');
57334624Sbostic 		else if (dotrim) {
57434624Sbostic 			while (t > startp && *--t == '0');
57534624Sbostic 			if (*t != '.')
57634624Sbostic 				++t;
57734624Sbostic 		}
57834624Sbostic 	}
57934624Sbostic 	return(t - startp);
58034624Sbostic }
58134248Sbostic 
58234624Sbostic static char *
58334669Sbostic round(fract, exp, start, end, ch, signp)
58434624Sbostic 	double fract;
58534669Sbostic 	int *exp;
58634624Sbostic 	register char *start, *end;
58734669Sbostic 	char ch, *signp;
58834624Sbostic {
58934624Sbostic 	double tmp;
59034248Sbostic 
59134624Sbostic 	if (fract)
59234248Sbostic 		(void)modf(fract * 10, &tmp);
59334624Sbostic 	else
59434624Sbostic 		tmp = todigit(ch);
59534624Sbostic 	if (tmp > 4)
59634624Sbostic 		for (;; --end) {
59734624Sbostic 			if (*end == '.')
59834624Sbostic 				--end;
59934624Sbostic 			if (++*end <= '9')
60034624Sbostic 				break;
60134624Sbostic 			*end = '0';
60234624Sbostic 			if (end == start) {
60334669Sbostic 				if (exp) {	/* e/E; increment exponent */
60434669Sbostic 					*end = '1';
60534669Sbostic 					++*exp;
60634669Sbostic 				}
60734669Sbostic 				else {		/* f; add extra digit */
60834669Sbostic 					*--end = '1';
60934669Sbostic 					--start;
61034669Sbostic 				}
61134624Sbostic 				break;
61234242Sbostic 			}
61334242Sbostic 		}
61434669Sbostic 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
61534669Sbostic 	else if (*signp == '-')
61634669Sbostic 		for (;; --end) {
61734669Sbostic 			if (*end == '.')
61834669Sbostic 				--end;
61934669Sbostic 			if (*end != '0')
62034669Sbostic 				break;
62134669Sbostic 			if (end == start)
62234669Sbostic 				*signp = 0;
62334669Sbostic 		}
62434624Sbostic 	return(start);
62534624Sbostic }
62634248Sbostic 
62734624Sbostic static char *
62834624Sbostic exponent(p, exp, fmtch)
62934624Sbostic 	register char *p;
63034624Sbostic 	register int exp;
63134624Sbostic 	u_char fmtch;
63234624Sbostic {
63334624Sbostic 	register char *t;
63434624Sbostic 	char expbuf[MAXEXP];
63534248Sbostic 
63634624Sbostic 	*p++ = fmtch;
63734624Sbostic 	if (exp < 0) {
63834624Sbostic 		exp = -exp;
63934624Sbostic 		*p++ = '-';
64034242Sbostic 	}
64134624Sbostic 	else
64234624Sbostic 		*p++ = '+';
64334624Sbostic 	t = expbuf + MAXEXP;
64434624Sbostic 	if (exp > 9) {
64534624Sbostic 		do {
64634624Sbostic 			*--t = tochar(exp % 10);
64734624Sbostic 		} while ((exp /= 10) > 9);
64834624Sbostic 		*--t = tochar(exp);
64934624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
65034624Sbostic 	}
65134624Sbostic 	else {
65234624Sbostic 		*p++ = '0';
65334624Sbostic 		*p++ = tochar(exp);
65434624Sbostic 	}
65534323Sbostic 	return(p);
65634242Sbostic }
657