xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34624)
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*34624Sbostic static char sccsid[] = "@(#)vfprintf.c	5.32 (Berkeley) 06/03/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 
27*34624Sbostic #define	DEFPREC		6
28*34624Sbostic 
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] */
63*34624Sbostic 	int base;		/* base for [diouxX] conversion */
64*34624Sbostic 	int dprec;		/* decimal precision in [diouxX] */
65*34624Sbostic 	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 */
69*34624Sbostic 	int realsz;		/* field size expanded by decimal precision */
7034427Sbostic 	int size;		/* size of converted field or string */
71*34624Sbostic 	int width;		/* width from format (%8d), or 0 */
7234427Sbostic 	char sign;		/* sign prefix (+ - or \0) */
7334427Sbostic 	char *digs;		/* digits for [diouxX] conversion */
7434427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
7534226Sbostic 
7634428Sbostic 	if (fp->_flag & _IORW) {
7734428Sbostic 		fp->_flag |= _IOWRT;
7834428Sbostic 		fp->_flag &= ~(_IOEOF|_IOREAD);
7934428Sbostic 	}
8034428Sbostic 	if ((fp->_flag & _IOWRT) == 0)
8134428Sbostic 		return (EOF);
8234428Sbostic 
8334323Sbostic 	fmt = fmt0;
8434243Sbostic 	digs = "0123456789abcdef";
8534322Sbostic 	for (cnt = 0;; ++fmt) {
8634318Sbostic 		n = fp->_cnt;
8734427Sbostic 		for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
8834427Sbostic 		     ++cnt, ++fmt)
8934318Sbostic 			if (--n < 0
9034318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
9134318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
9234318Sbostic #endif
9334427Sbostic 			    || ch == '\n' && fp->_flag & _IOLBF) {
9434318Sbostic 				fp->_cnt = n;
9534475Sbostic 				fp->_ptr = t;
9634427Sbostic 				(void) _flsbuf((u_char)ch, fp);
9734318Sbostic 				n = fp->_cnt;
9834427Sbostic 				t = (char *)fp->_ptr;
9934427Sbostic 			} else
10034314Sbostic 				*t++ = ch;
10134318Sbostic 		fp->_cnt = n;
10234475Sbostic 		fp->_ptr = t;
10334318Sbostic 		if (!ch)
10434427Sbostic 			return (cnt);
10534314Sbostic 
10634427Sbostic 		flags = dprec = fpprec = width = 0;
10734233Sbostic 		prec = -1;
10834318Sbostic 		sign = '\0';
10934226Sbostic 
11034318Sbostic rflag:		switch (*++fmt) {
11134318Sbostic 		case ' ':
11234318Sbostic 			sign = ' ';
11334318Sbostic 			goto rflag;
11434233Sbostic 		case '#':
11534318Sbostic 			flags |= ALT;
11634318Sbostic 			goto rflag;
11734233Sbostic 		case '*':
11834235Sbostic 			/*
11934235Sbostic 			 * ``A negative field width argument is taken as a
12034235Sbostic 			 * - flag followed by a  positive field width.''
12134235Sbostic 			 *	-- ANSI X3J11
12234235Sbostic 			 * They don't exclude field widths read from args.
12334235Sbostic 			 */
12434235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
12534318Sbostic 				goto rflag;
12634235Sbostic 			width = -width;
12734427Sbostic 			/* FALLTHROUGH */
12834235Sbostic 		case '-':
12934318Sbostic 			flags |= LADJUST;
13034318Sbostic 			goto rflag;
13134233Sbostic 		case '+':
13234314Sbostic 			sign = '+';
13334318Sbostic 			goto rflag;
13434233Sbostic 		case '.':
13534235Sbostic 			if (*++fmt == '*')
13634318Sbostic 				n = va_arg(argp, int);
13734427Sbostic 			else {
13834318Sbostic 				n = 0;
13934427Sbostic 				while (isascii(*fmt) && isdigit(*fmt))
14034427Sbostic 					n = 10 * n + todigit(*fmt++);
14134233Sbostic 				--fmt;
14234226Sbostic 			}
14334318Sbostic 			prec = n < 0 ? -1 : n;
14434318Sbostic 			goto rflag;
14534233Sbostic 		case '0':
14634427Sbostic 			/*
14734427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
14834427Sbostic 			 * beginning of a field width.''
14934427Sbostic 			 *	-- ANSI X3J11
15034427Sbostic 			 */
15134427Sbostic 			flags |= ZEROPAD;
15234427Sbostic 			goto rflag;
15334233Sbostic 		case '1': case '2': case '3': case '4':
15434233Sbostic 		case '5': case '6': case '7': case '8': case '9':
15534318Sbostic 			n = 0;
15634233Sbostic 			do {
15734331Sbostic 				n = 10 * n + todigit(*fmt);
15834318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
15934318Sbostic 			width = n;
16034233Sbostic 			--fmt;
16134319Sbostic 			goto rflag;
16234235Sbostic 		case 'L':
16334329Sbostic 			flags |= LONGDBL;
16434318Sbostic 			goto rflag;
16534235Sbostic 		case 'h':
16634318Sbostic 			flags |= SHORTINT;
16734318Sbostic 			goto rflag;
16834233Sbostic 		case 'l':
16934318Sbostic 			flags |= LONGINT;
17034318Sbostic 			goto rflag;
17134314Sbostic 		case 'c':
17234427Sbostic 			*(t = buf) = va_arg(argp, int);
17334314Sbostic 			size = 1;
17434427Sbostic 			sign = '\0';
17534314Sbostic 			goto pforw;
176*34624Sbostic 		case 'D':
177*34624Sbostic 			flags |= LONGINT;
178*34624Sbostic 			/*FALLTHROUGH*/
17934314Sbostic 		case 'd':
18034318Sbostic 		case 'i':
18134318Sbostic 			ARG();
18234318Sbostic 			if ((long)_ulong < 0) {
18334318Sbostic 				_ulong = -_ulong;
18434314Sbostic 				sign = '-';
18534241Sbostic 			}
18634241Sbostic 			base = 10;
18734327Sbostic 			goto number;
18834261Sbostic 		case 'e':
18934236Sbostic 		case 'E':
19034235Sbostic 		case 'f':
19134261Sbostic 		case 'g':
19234243Sbostic 		case 'G':
19334243Sbostic 			_double = va_arg(argp, double);
19434328Sbostic 			/*
19534328Sbostic 			 * don't bother to do unrealistic precision; just
19634328Sbostic 			 * pad it with zeroes later.  This keeps buffer size
19734328Sbostic 			 * rational.
19834328Sbostic 			 */
19934328Sbostic 			if (prec > MAXFRACT) {
20034475Sbostic 				if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
20134475Sbostic 					fpprec = prec - MAXFRACT;
20234328Sbostic 				prec = MAXFRACT;
20334328Sbostic 			}
204*34624Sbostic 			else if (prec == -1)
205*34624Sbostic 				prec = DEFPREC;
206*34624Sbostic 			if (_double < 0) {
207*34624Sbostic 				sign = '-';
208*34624Sbostic 				_double = -_double;
209*34624Sbostic 			}
210*34624Sbostic 			/*
211*34624Sbostic 			 * _cvt may have to round up past the "start" of the
212*34624Sbostic 			 * buffer, i.e. ``intf("%.2f", (double)9.999);'';
213*34624Sbostic 			 * if the first char isn't NULL, it did.
214*34624Sbostic 			 */
215*34624Sbostic 			*buf = NULL;
216*34624Sbostic 			size = _cvt(_double, prec, flags, *fmt, buf,
217*34624Sbostic 			    buf + sizeof(buf));
218*34624Sbostic 			t = *buf ? buf : buf + 1;
21934314Sbostic 			goto pforw;
22034235Sbostic 		case 'n':
22134427Sbostic 			if (flags & LONGINT)
22234318Sbostic 				*va_arg(argp, long *) = cnt;
22334427Sbostic 			else if (flags & SHORTINT)
22434318Sbostic 				*va_arg(argp, short *) = cnt;
22534318Sbostic 			else
22634318Sbostic 				*va_arg(argp, int *) = cnt;
22734235Sbostic 			break;
228*34624Sbostic 		case 'O':
229*34624Sbostic 			flags |= LONGINT;
230*34624Sbostic 			/*FALLTHROUGH*/
23134226Sbostic 		case 'o':
23234318Sbostic 			ARG();
23334226Sbostic 			base = 8;
23434327Sbostic 			goto nosign;
23534235Sbostic 		case 'p':
23634320Sbostic 			/*
23734321Sbostic 			 * ``The argument shall be a pointer to void.  The
23834321Sbostic 			 * value of the pointer is converted to a sequence
23934321Sbostic 			 * of printable characters, in an implementation-
24034321Sbostic 			 * defined manner.''
24134321Sbostic 			 *	-- ANSI X3J11
24234320Sbostic 			 */
24334427Sbostic 			/* NOSTRICT */
24434320Sbostic 			_ulong = (u_long)va_arg(argp, void *);
24534320Sbostic 			base = 16;
24634327Sbostic 			goto nosign;
24734226Sbostic 		case 's':
24834314Sbostic 			if (!(t = va_arg(argp, char *)))
24934314Sbostic 				t = "(null)";
25034321Sbostic 			if (prec >= 0) {
25134321Sbostic 				/*
25234321Sbostic 				 * can't use strlen; can only look for the
25334321Sbostic 				 * NUL in the first `prec' characters, and
25434321Sbostic 				 * strlen() will go further.
25534321Sbostic 				 */
25634321Sbostic 				char *p, *memchr();
25734321Sbostic 
25834321Sbostic 				if (p = memchr(t, 0, prec)) {
25934321Sbostic 					size = p - t;
26034321Sbostic 					if (size > prec)
26134321Sbostic 						size = prec;
26234427Sbostic 				} else
26334321Sbostic 					size = prec;
26434427Sbostic 			} else
26534321Sbostic 				size = strlen(t);
26634427Sbostic 			sign = '\0';
26734328Sbostic 			goto pforw;
268*34624Sbostic 		case 'U':
269*34624Sbostic 			flags |= LONGINT;
270*34624Sbostic 			/*FALLTHROUGH*/
27134226Sbostic 		case 'u':
27234318Sbostic 			ARG();
27334226Sbostic 			base = 10;
27434327Sbostic 			goto nosign;
27534226Sbostic 		case 'X':
27634226Sbostic 			digs = "0123456789ABCDEF";
27734427Sbostic 			/* FALLTHROUGH */
27834226Sbostic 		case 'x':
27934318Sbostic 			ARG();
28034314Sbostic 			base = 16;
28134326Sbostic 			/* leading 0x/X only if non-zero */
28234427Sbostic 			if (flags & ALT && _ulong != 0)
28334427Sbostic 				flags |= HEXPREFIX;
28434327Sbostic 
28534327Sbostic 			/* unsigned conversions */
28634427Sbostic nosign:			sign = '\0';
28734326Sbostic 			/*
28834330Sbostic 			 * ``... diouXx conversions ... if a precision is
28934330Sbostic 			 * specified, the 0 flag will be ignored.''
29034330Sbostic 			 *	-- ANSI X3J11
29134330Sbostic 			 */
29234427Sbostic number:			if ((dprec = prec) >= 0)
29334427Sbostic 				flags &= ~ZEROPAD;
29434427Sbostic 
29534330Sbostic 			/*
29634326Sbostic 			 * ``The result of converting a zero value with an
29734326Sbostic 			 * explicit precision of zero is no characters.''
29834326Sbostic 			 *	-- ANSI X3J11
29934326Sbostic 			 */
30034427Sbostic 			t = buf + BUF;
30134427Sbostic 			if (_ulong != 0 || prec != 0) {
30234427Sbostic 				do {
30334427Sbostic 					*--t = digs[_ulong % base];
30434427Sbostic 					_ulong /= base;
30534427Sbostic 				} while (_ulong);
30634427Sbostic 				digs = "0123456789abcdef";
30734427Sbostic 				if (flags & ALT && base == 8 && *t != '0')
30834427Sbostic 					*--t = '0'; /* octal leading 0 */
30934330Sbostic 			}
31034427Sbostic 			size = buf + BUF - t;
31134327Sbostic 
31234427Sbostic pforw:
31334427Sbostic 			/*
314*34624Sbostic 			 * All reasonable formats wind up here.  At this point,
315*34624Sbostic 			 * `t' points to a string which (if not flags&LADJUST)
316*34624Sbostic 			 * should be padded out to `width' places.  If
317*34624Sbostic 			 * flags&ZEROPAD, it should first be prefixed by any
318*34624Sbostic 			 * sign or other prefix; otherwise, it should be blank
319*34624Sbostic 			 * padded before the prefix is emitted.  After any
320*34624Sbostic 			 * left-hand padding and prefixing, emit zeroes
321*34624Sbostic 			 * required by a decimal [diouxX] precision, then print
322*34624Sbostic 			 * the string proper, then emit zeroes required by any
323*34624Sbostic 			 * leftover floating precision; finally, if LADJUST,
324*34624Sbostic 			 * pad with blanks.
32534427Sbostic 			 */
32634327Sbostic 
327*34624Sbostic 			/*
328*34624Sbostic 			 * compute actual size, so we know how much to pad
329*34624Sbostic 			 * fieldsz excludes decimal prec; realsz includes it
330*34624Sbostic 			 */
33134427Sbostic 			fieldsz = size + fpprec;
33234427Sbostic 			if (sign)
33334427Sbostic 				fieldsz++;
33434427Sbostic 			if (flags & HEXPREFIX)
33534427Sbostic 				fieldsz += 2;
33634427Sbostic 			realsz = dprec > fieldsz ? dprec : fieldsz;
33734327Sbostic 
33834427Sbostic 			/* right-adjusting blank padding */
33934427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
34034427Sbostic 				for (n = realsz; n < width; n++)
34134427Sbostic 					PUTC(' ');
34234427Sbostic 			/* prefix */
34334427Sbostic 			if (sign)
34434427Sbostic 				PUTC(sign);
34534427Sbostic 			if (flags & HEXPREFIX) {
34634427Sbostic 				PUTC('0');
34734427Sbostic 				PUTC((char)*fmt);
34834327Sbostic 			}
34934427Sbostic 			/* right-adjusting zero padding */
35034427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
35134427Sbostic 				for (n = realsz; n < width; n++)
35234427Sbostic 					PUTC('0');
35334427Sbostic 			/* leading zeroes from decimal precision */
35434427Sbostic 			for (n = fieldsz; n < dprec; n++)
35534427Sbostic 				PUTC('0');
35634327Sbostic 
35734427Sbostic 			/* the string or number proper */
35834427Sbostic 			if (fp->_cnt - (n = size) >= 0 &&
35934427Sbostic 			    (fp->_flag & _IOLBF) == 0) {
36034328Sbostic 				fp->_cnt -= n;
36134427Sbostic 				bcopy(t, (char *)fp->_ptr, n);
36234328Sbostic 				fp->_ptr += n;
36334427Sbostic 			} else
36434427Sbostic 				while (--n >= 0)
36534427Sbostic 					PUTC(*t++);
36634427Sbostic 			/* trailing f.p. zeroes */
36734427Sbostic 			while (--fpprec >= 0)
36834328Sbostic 				PUTC('0');
36934427Sbostic 			/* left-adjusting padding (always blank) */
37034427Sbostic 			if (flags & LADJUST)
37134427Sbostic 				for (n = realsz; n < width; n++)
37234328Sbostic 					PUTC(' ');
37334427Sbostic 			/* finally, adjust cnt */
37434427Sbostic 			cnt += width > realsz ? width : realsz;
37534226Sbostic 			break;
37634427Sbostic 		case '\0':	/* "%?" prints ?, unless ? is NULL */
37734427Sbostic 			return (cnt);
37834226Sbostic 		default:
37934427Sbostic 			PUTC((char)*fmt);
38034427Sbostic 			cnt++;
38134226Sbostic 		}
38234226Sbostic 	}
38334427Sbostic 	/* NOTREACHED */
38434226Sbostic }
38534242Sbostic 
386*34624Sbostic static
387*34624Sbostic _cvt(number, prec, flags, fmtch, startp, endp)
38834242Sbostic 	double number;
38934261Sbostic 	register int prec;
39034323Sbostic 	int flags;
39134323Sbostic 	u_char fmtch;
392*34624Sbostic 	char *startp, *endp;
39334242Sbostic {
39434331Sbostic 	register char *p, *t;
39534248Sbostic 	double fract, integer, tmp, modf();
396*34624Sbostic 	int dotrim, expcnt, gformat;
397*34624Sbostic 	char *exponent(), *eround(), *fround();
39834242Sbostic 
399*34624Sbostic 	dotrim = expcnt = gformat = 0;
400*34624Sbostic 	fract = modf(number, &integer);
40134242Sbostic 
402*34624Sbostic 	/* get an extra slot for rounding. */
403*34624Sbostic 	t = ++startp;
404*34624Sbostic 
405*34624Sbostic 	/*
406*34624Sbostic 	 * get integer portion of number; put into the end of the buffer; the
407*34624Sbostic 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
408*34624Sbostic 	 */
409*34624Sbostic 	for (p = endp - 1; integer; ++expcnt) {
410*34624Sbostic 		tmp = modf(integer / 10, &integer);
411*34624Sbostic 		*p-- = tochar((int)((tmp + .01) * 10));
41234248Sbostic 	}
41334261Sbostic 	switch(fmtch) {
41434261Sbostic 	case 'f':
415*34624Sbostic 		/* reverse integer into beginning of buffer */
416*34624Sbostic 		if (expcnt)
417*34624Sbostic 			for (; ++p < endp; *t++ = *p);
418*34624Sbostic 		else
419*34624Sbostic 			*t++ = '0';
42034248Sbostic 		/*
421*34624Sbostic 		 * if precision required or alternate flag set, add in a
422*34624Sbostic 		 * decimal point.
42334248Sbostic 		 */
424*34624Sbostic 		if (prec || flags&ALT)
425*34624Sbostic 			*t++ = '.';
426*34624Sbostic 		/* if requires more precision and some fraction left */
427*34624Sbostic 		if (fract) {
428*34624Sbostic 			if (prec)
429*34624Sbostic 				do {
430*34624Sbostic 					fract = modf(fract * 10, &tmp);
431*34624Sbostic 					*t++ = tochar((int)tmp);
432*34624Sbostic 				} while (--prec && fract);
433*34624Sbostic 			if (fract)
434*34624Sbostic 				startp = fround(startp, t - 1, fract);
435*34624Sbostic 		}
436*34624Sbostic 		for (; prec--; *t++ = '0');
437*34624Sbostic 		break;
438*34624Sbostic 	case 'e':
439*34624Sbostic 	case 'E':
440*34624Sbostic eformat:	if (expcnt) {
441*34624Sbostic 			*t++ = *++p;
442*34624Sbostic 			if (prec || flags&ALT)
44334331Sbostic 				*t++ = '.';
444*34624Sbostic 			/* if requires more precision and some integer left */
445*34624Sbostic 			for (; prec && ++p < endp; --prec)
446*34624Sbostic 				*t++ = *p;
447*34624Sbostic 			/*
448*34624Sbostic 			 * if done precision and more of the integer component,
449*34624Sbostic 			 * round using it; adjust fract so we don't re-round
450*34624Sbostic 			 * later.
451*34624Sbostic 			 */
452*34624Sbostic 			if (!prec && ++p < endp) {
453*34624Sbostic 				startp = eround(startp, t - 1, (double)0,
454*34624Sbostic 				    *p, &expcnt);
45534248Sbostic 				fract = 0;
45634248Sbostic 			}
457*34624Sbostic 			/* adjust expcnt for digit in front of decimal */
458*34624Sbostic 			--expcnt;
45934242Sbostic 		}
460*34624Sbostic 		/* until first fractional digit, decrement exponent */
461*34624Sbostic 		else if (fract) {
462*34624Sbostic 			/* adjust expcnt for digit in front of decimal */
463*34624Sbostic 			for (expcnt = -1;; --expcnt) {
464*34624Sbostic 				fract = modf(fract * 10, &tmp);
465*34624Sbostic 				if (tmp)
466*34624Sbostic 					break;
46734248Sbostic 			}
468*34624Sbostic 			*t++ = tochar((int)tmp);
469*34624Sbostic 			if (prec || flags&ALT)
47034331Sbostic 				*t++ = '.';
47134242Sbostic 		}
47234248Sbostic 		else {
473*34624Sbostic 			*t++ = '0';
474*34624Sbostic 			if (prec || flags&ALT)
47534331Sbostic 				*t++ = '.';
47634248Sbostic 		}
477*34624Sbostic 		/* if requires more precision and some fraction left */
478*34624Sbostic 		if (fract) {
479*34624Sbostic 			if (prec)
480*34624Sbostic 				do {
481*34624Sbostic 					fract = modf(fract * 10, &tmp);
482*34624Sbostic 					*t++ = tochar((int)tmp);
483*34624Sbostic 				} while (--prec && fract);
484*34624Sbostic 			if (fract)
485*34624Sbostic 				startp = eround(startp, t - 1, fract,
486*34624Sbostic 				    (char)0, &expcnt);
48734584Sbostic 		}
488*34624Sbostic 		/* if requires more precision */
489*34624Sbostic 		for (; prec--; *t++ = '0');
490*34624Sbostic 
491*34624Sbostic 		/* unless alternate flag, trim any g/G format trailing 0's */
492*34624Sbostic 		if (gformat && !(flags&ALT)) {
493*34624Sbostic 			while (t > startp && *--t == '0');
494*34624Sbostic 			if (*t == '.')
495*34624Sbostic 				--t;
496*34624Sbostic 			++t;
497*34624Sbostic 		}
498*34624Sbostic 		t = exponent(t, expcnt, fmtch);
499*34624Sbostic 		break;
500*34624Sbostic 	case 'g':
501*34624Sbostic 	case 'G':
502*34624Sbostic 		/* a precision of 0 is treated as a precision of 1. */
503*34624Sbostic 		if (!prec)
504*34624Sbostic 			++prec;
505*34624Sbostic 		/*
506*34624Sbostic 		 * ``The style used depends on the value converted; style e
507*34624Sbostic 		 * will be used only if the exponent resulting from the
508*34624Sbostic 		 * conversion is less than -4 or greater than the precision.''
509*34624Sbostic 		 *	-- ANSI X3J11
510*34624Sbostic 		 */
511*34624Sbostic 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
512*34624Sbostic 			/*
513*34624Sbostic 			 * g/G format counts "significant digits, not digits of
514*34624Sbostic 			 * precision; for the e/E format, this just causes an
515*34624Sbostic 			 * off-by-one problem, i.e. g/G considers the digit
516*34624Sbostic 			 * before the decimal point significant and e/E doesn't
517*34624Sbostic 			 * count it as precision.
518*34624Sbostic 			 */
519*34624Sbostic 			--prec;
520*34624Sbostic 			fmtch -= 2;		/* G->E, g->e */
521*34624Sbostic 			gformat = 1;
522*34624Sbostic 			goto eformat;
523*34624Sbostic 		}
524*34624Sbostic 		/*
525*34624Sbostic 		 * reverse integer into beginning of buffer,
526*34624Sbostic 		 * note, decrement precision
527*34624Sbostic 		 */
528*34624Sbostic 		if (expcnt)
529*34624Sbostic 			for (; ++p < endp; *t++ = *p, --prec);
530*34624Sbostic 		else
531*34624Sbostic 			*t++ = '0';
532*34624Sbostic 		/*
533*34624Sbostic 		 * if precision required or alternate flag set, add in a
534*34624Sbostic 		 * decimal point.  If no digits yet, add in leading 0.
535*34624Sbostic 		 */
536*34624Sbostic 		if (prec || flags&ALT) {
537*34624Sbostic 			dotrim = 1;
538*34624Sbostic 			*t++ = '.';
539*34624Sbostic 		}
540*34624Sbostic 		else
541*34624Sbostic 			dotrim = 0;
542*34624Sbostic 		/* if requires more precision and some fraction left */
543*34624Sbostic 		if (fract) {
544*34624Sbostic 			if (prec) {
545*34624Sbostic 				do {
546*34624Sbostic 					fract = modf(fract * 10, &tmp);
547*34624Sbostic 					*t++ = tochar((int)tmp);
548*34624Sbostic 				} while(!tmp);
549*34624Sbostic 				while (--prec && fract) {
550*34624Sbostic 					fract = modf(fract * 10, &tmp);
551*34624Sbostic 					*t++ = tochar((int)tmp);
55234248Sbostic 				}
55334248Sbostic 			}
554*34624Sbostic 			if (fract)
555*34624Sbostic 				startp = fround(startp, t - 1, fract);
556*34624Sbostic 		}
557*34624Sbostic 		/* alternate format, adds 0's for precision, else trim 0's */
558*34624Sbostic 		if (flags&ALT)
559*34624Sbostic 			for (; prec--; *t++ = '0');
560*34624Sbostic 		else if (dotrim) {
561*34624Sbostic 			while (t > startp && *--t == '0');
562*34624Sbostic 			if (*t != '.')
563*34624Sbostic 				++t;
564*34624Sbostic 		}
565*34624Sbostic 	}
566*34624Sbostic 	return(t - startp);
567*34624Sbostic }
56834248Sbostic 
569*34624Sbostic static char *
570*34624Sbostic fround(start, end, fract)
571*34624Sbostic 	register char *start, *end;
572*34624Sbostic 	double fract;
573*34624Sbostic {
574*34624Sbostic 	double tmp;
57534248Sbostic 
576*34624Sbostic 	(void)modf(fract * 10, &tmp);
577*34624Sbostic 	if (tmp > 4)
578*34624Sbostic 		for (;; --end) {
579*34624Sbostic 			if (*end == '.')
580*34624Sbostic 				--end;
581*34624Sbostic 			if (++*end <= '9')
582*34624Sbostic 				break;
583*34624Sbostic 			*end = '0';
584*34624Sbostic 			/* add extra digit to round past buffer beginning */
585*34624Sbostic 			if (end == start) {
586*34624Sbostic 				*--end = '1';
587*34624Sbostic 				--start;
588*34624Sbostic 				break;
589*34624Sbostic 			}
590*34624Sbostic 		}
591*34624Sbostic 	return(start);
592*34624Sbostic }
59334248Sbostic 
594*34624Sbostic static char *
595*34624Sbostic eround(start, end, fract, ch, exp)
596*34624Sbostic 	register char *start, *end;
597*34624Sbostic 	double fract;
598*34624Sbostic 	char ch;
599*34624Sbostic 	int *exp;
600*34624Sbostic {
601*34624Sbostic 	double tmp;
60234248Sbostic 
603*34624Sbostic 	if (fract)
60434248Sbostic 		(void)modf(fract * 10, &tmp);
605*34624Sbostic 	else
606*34624Sbostic 		tmp = todigit(ch);
607*34624Sbostic 	if (tmp > 4)
608*34624Sbostic 		for (;; --end) {
609*34624Sbostic 			if (*end == '.')
610*34624Sbostic 				--end;
611*34624Sbostic 			if (++*end <= '9')
612*34624Sbostic 				break;
613*34624Sbostic 			*end = '0';
614*34624Sbostic 			/* increment exponent to round past buffer beginning */
615*34624Sbostic 			if (end == start) {
616*34624Sbostic 				*end = '1';
617*34624Sbostic 				++*exp;
618*34624Sbostic 				break;
61934242Sbostic 			}
62034242Sbostic 		}
621*34624Sbostic 	return(start);
622*34624Sbostic }
62334248Sbostic 
624*34624Sbostic static char *
625*34624Sbostic exponent(p, exp, fmtch)
626*34624Sbostic 	register char *p;
627*34624Sbostic 	register int exp;
628*34624Sbostic 	u_char fmtch;
629*34624Sbostic {
630*34624Sbostic 	register char *t;
631*34624Sbostic 	char expbuf[MAXEXP];
63234248Sbostic 
633*34624Sbostic 	*p++ = fmtch;
634*34624Sbostic 	if (exp < 0) {
635*34624Sbostic 		exp = -exp;
636*34624Sbostic 		*p++ = '-';
63734242Sbostic 	}
638*34624Sbostic 	else
639*34624Sbostic 		*p++ = '+';
640*34624Sbostic 	t = expbuf + MAXEXP;
641*34624Sbostic 	if (exp > 9) {
642*34624Sbostic 		do {
643*34624Sbostic 			*--t = tochar(exp % 10);
644*34624Sbostic 		} while ((exp /= 10) > 9);
645*34624Sbostic 		*--t = tochar(exp);
646*34624Sbostic 		for (; t < expbuf + MAXEXP; *p++ = *t++);
647*34624Sbostic 	}
648*34624Sbostic 	else {
649*34624Sbostic 		*p++ = '0';
650*34624Sbostic 		*p++ = tochar(exp);
651*34624Sbostic 	}
65234323Sbostic 	return(p);
65334242Sbostic }
654