xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34331)
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*34331Sbostic static char sccsid[] = "@(#)vfprintf.c	5.27 (Berkeley) 05/19/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 
2734328Sbostic #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
2834328Sbostic 
2934323Sbostic #define	PUTC(ch)	{++cnt; putc((char)ch, fp);}
3034236Sbostic 
3134318Sbostic #define	ARG() \
3234318Sbostic 	_ulong = flags&LONGINT ? va_arg(argp, long) : \
3334318Sbostic 	    flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int);
3434235Sbostic 
35*34331Sbostic #define	todigit(c)	((c) - '0')
36*34331Sbostic #define	tochar(n)	((n) + '0')
37*34331Sbostic 
3834318Sbostic /* have to deal with the negative buffer count kludge */
3934318Sbostic #define	NEGATIVE_COUNT_KLUDGE
4034318Sbostic 
4134318Sbostic #define	LONGINT		0x01		/* long integer */
4234318Sbostic #define	LONGDBL		0x02		/* long double; unimplemented */
4334318Sbostic #define	SHORTINT	0x04		/* short integer */
4434318Sbostic #define	ALT		0x08		/* alternate form */
4534318Sbostic #define	LADJUST		0x10		/* left adjustment */
4634318Sbostic 
4734323Sbostic _doprnt(fmt0, argp, fp)
4834323Sbostic 	u_char *fmt0;
4934233Sbostic 	va_list argp;
5034235Sbostic 	register FILE *fp;
5134226Sbostic {
5234323Sbostic 	register u_char *fmt;
5334323Sbostic 	register int ch, cnt, n;
5434323Sbostic 	register char *t;
5534235Sbostic 	double _double;
5634314Sbostic 	u_long _ulong;
5734328Sbostic 	int base, flags, fpprec, prec, size, width;
5834328Sbostic 	char padc, sign, *digs, buf[BUF], *_cvt();
5934226Sbostic 
6034323Sbostic 	fmt = fmt0;
6134243Sbostic 	digs = "0123456789abcdef";
6234322Sbostic 	for (cnt = 0;; ++fmt) {
6334318Sbostic 		n = fp->_cnt;
6434318Sbostic 		for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
6534318Sbostic 			if (--n < 0
6634318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
6734318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
6834318Sbostic #endif
6934318Sbostic 			    || ch == '\n' && fp->_flag&_IOLBF) {
7034318Sbostic 				fp->_cnt = n;
7134318Sbostic 				fp->_ptr = t;
7234318Sbostic 				(void)_flsbuf(ch, fp);
7334318Sbostic 				n = fp->_cnt;
7434318Sbostic 				t = fp->_ptr;
7534318Sbostic 			}
7634318Sbostic 			else
7734314Sbostic 				*t++ = ch;
7834318Sbostic 		fp->_cnt = n;
7934318Sbostic 		fp->_ptr = t;
8034318Sbostic 		if (!ch)
8134314Sbostic 			return(cnt);
8234314Sbostic 
8334328Sbostic 		flags = fpprec = width = 0;
8434233Sbostic 		prec = -1;
8534233Sbostic 		padc = ' ';
8634318Sbostic 		sign = '\0';
8734226Sbostic 
8834318Sbostic rflag:		switch (*++fmt) {
8934318Sbostic 		case ' ':
9034318Sbostic 			sign = ' ';
9134318Sbostic 			goto rflag;
9234233Sbostic 		case '#':
9334318Sbostic 			flags |= ALT;
9434318Sbostic 			goto rflag;
9534233Sbostic 		case '*':
9634235Sbostic 			/*
9734235Sbostic 			 * ``A negative field width argument is taken as a
9834235Sbostic 			 * - flag followed by a  positive field width.''
9934235Sbostic 			 *	-- ANSI X3J11
10034235Sbostic 			 * They don't exclude field widths read from args.
10134235Sbostic 			 */
10234235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
10334318Sbostic 				goto rflag;
10434235Sbostic 			width = -width;
10534235Sbostic 			/*FALLTHROUGH*/
10634235Sbostic 		case '-':
10734318Sbostic 			flags |= LADJUST;
10834318Sbostic 			goto rflag;
10934233Sbostic 		case '+':
11034314Sbostic 			sign = '+';
11134318Sbostic 			goto rflag;
11234233Sbostic 		case '.':
11334235Sbostic 			if (*++fmt == '*')
11434318Sbostic 				n = va_arg(argp, int);
11534318Sbostic 			else if (isascii(*fmt) && isdigit(*fmt)) {
11634318Sbostic 				n = 0;
11734233Sbostic 				do {
118*34331Sbostic 					n = 10 * n + todigit(*fmt);
11934318Sbostic 				} while (isascii(*++fmt) && isdigit(*fmt));
12034233Sbostic 				--fmt;
12134226Sbostic 			}
12234235Sbostic 			else {
12334318Sbostic 				--fmt;
12434235Sbostic 				prec = 0;
12534318Sbostic 				goto rflag;
12634235Sbostic 			}
12734318Sbostic 			prec = n < 0 ? -1 : n;
12834318Sbostic 			goto rflag;
12934233Sbostic 		case '0':
13034233Sbostic 			padc = '0';
13134235Sbostic 			/*FALLTHROUGH*/
13234233Sbostic 		case '1': case '2': case '3': case '4':
13334233Sbostic 		case '5': case '6': case '7': case '8': case '9':
13434318Sbostic 			n = 0;
13534233Sbostic 			do {
136*34331Sbostic 				n = 10 * n + todigit(*fmt);
13734318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
13834318Sbostic 			width = n;
13934233Sbostic 			--fmt;
14034319Sbostic 			goto rflag;
14134235Sbostic 		case 'L':
14234329Sbostic 			flags |= LONGDBL;
14334318Sbostic 			goto rflag;
14434235Sbostic 		case 'h':
14534318Sbostic 			flags |= SHORTINT;
14634318Sbostic 			goto rflag;
14734233Sbostic 		case 'l':
14834318Sbostic 			flags |= LONGINT;
14934318Sbostic 			goto rflag;
15034314Sbostic 		case 'c':
15134319Sbostic 			buf[0] = va_arg(argp, int);
15234314Sbostic 			size = 1;
15334319Sbostic 			t = buf;
15434314Sbostic 			goto pforw;
15534314Sbostic 		case 'd':
15634318Sbostic 		case 'i':
15734318Sbostic 			ARG();
15834318Sbostic 			if ((long)_ulong < 0) {
15934318Sbostic 				_ulong = -_ulong;
16034314Sbostic 				sign = '-';
16134241Sbostic 			}
16234241Sbostic 			base = 10;
16334327Sbostic 			goto number;
16434261Sbostic 		case 'e':
16534236Sbostic 		case 'E':
16634235Sbostic 		case 'f':
16734261Sbostic 		case 'g':
16834243Sbostic 		case 'G':
16934243Sbostic 			_double = va_arg(argp, double);
17034328Sbostic 			/*
17134328Sbostic 			 * don't bother to do unrealistic precision; just
17234328Sbostic 			 * pad it with zeroes later.  This keeps buffer size
17334328Sbostic 			 * rational.
17434328Sbostic 			 */
17534328Sbostic 			if (prec > MAXFRACT) {
17634328Sbostic 				fpprec = prec - MAXFRACT;
17734328Sbostic 				prec = MAXFRACT;
17834328Sbostic 			}
17934327Sbostic 			size = _cvt(_double, prec, flags, *fmt, padc, &sign,
18034323Sbostic 			    buf, buf + sizeof(buf)) - buf;
18134319Sbostic 			t = buf;
18234327Sbostic 			/*
18334327Sbostic 			 * zero-padded sign put out here; blank padded sign
18434327Sbostic 			 * placed in number in _cvt().
18534327Sbostic 			 */
18634327Sbostic 			if (sign && padc == '0') {
18734327Sbostic 				PUTC(sign);
18834327Sbostic 				--width;
18934327Sbostic 			}
19034314Sbostic 			goto pforw;
19134235Sbostic 		case 'n':
19234329Sbostic 			if (flags&LONGINT)
19334318Sbostic 				*va_arg(argp, long *) = cnt;
19434318Sbostic 			else if (flags&SHORTINT)
19534318Sbostic 				*va_arg(argp, short *) = cnt;
19634318Sbostic 			else
19734318Sbostic 				*va_arg(argp, int *) = cnt;
19834235Sbostic 			break;
19934226Sbostic 		case 'o':
20034318Sbostic 			ARG();
20134226Sbostic 			base = 8;
20234327Sbostic 			goto nosign;
20334235Sbostic 		case 'p':
20434320Sbostic 			/*
20534321Sbostic 			 * ``The argument shall be a pointer to void.  The
20634321Sbostic 			 * value of the pointer is converted to a sequence
20734321Sbostic 			 * of printable characters, in an implementation-
20834321Sbostic 			 * defined manner.''
20934321Sbostic 			 *	-- ANSI X3J11
21034320Sbostic 			 */
21134327Sbostic 			/*NOSTRICT*/
21234320Sbostic 			_ulong = (u_long)va_arg(argp, void *);
21334320Sbostic 			base = 16;
21434327Sbostic 			goto nosign;
21534226Sbostic 		case 's':
21634314Sbostic 			if (!(t = va_arg(argp, char *)))
21734314Sbostic 				t = "(null)";
21834321Sbostic 			if (prec >= 0) {
21934321Sbostic 				/*
22034321Sbostic 				 * can't use strlen; can only look for the
22134321Sbostic 				 * NUL in the first `prec' characters, and
22234321Sbostic 				 * strlen() will go further.
22334321Sbostic 				 */
22434321Sbostic 				char *p, *memchr();
22534321Sbostic 
22634321Sbostic 				if (p = memchr(t, 0, prec)) {
22734321Sbostic 					size = p - t;
22834321Sbostic 					if (size > prec)
22934321Sbostic 						size = prec;
23034321Sbostic 				}
23134321Sbostic 				else
23234321Sbostic 					size = prec;
23334321Sbostic 			}
23434321Sbostic 			else
23534321Sbostic 				size = strlen(t);
23634328Sbostic 			goto pforw;
23734226Sbostic 		case 'u':
23834318Sbostic 			ARG();
23934226Sbostic 			base = 10;
24034327Sbostic 			goto nosign;
24134226Sbostic 		case 'X':
24234226Sbostic 			digs = "0123456789ABCDEF";
24334233Sbostic 			/*FALLTHROUGH*/
24434226Sbostic 		case 'x':
24534318Sbostic 			ARG();
24634314Sbostic 			base = 16;
24734326Sbostic 			/* leading 0x/X only if non-zero */
24834326Sbostic 			if (!_ulong)
24934324Sbostic 				flags &= ~ALT;
25034327Sbostic 
25134327Sbostic 			/* unsigned conversions */
25234327Sbostic nosign:			sign = NULL;
25334326Sbostic 			/*
25434330Sbostic 			 * ``... diouXx conversions ... if a precision is
25534330Sbostic 			 * specified, the 0 flag will be ignored.''
25634330Sbostic 			 *	-- ANSI X3J11
25734330Sbostic 			 */
25834330Sbostic number:			if (prec >= 0)
25934330Sbostic 				padc = ' ';
26034330Sbostic 			/*
26134326Sbostic 			 * ``The result of converting a zero value with an
26234326Sbostic 			 * explicit precision of zero is no characters.''
26334326Sbostic 			 *	-- ANSI X3J11
26434326Sbostic 			 */
26534330Sbostic 			if (!_ulong && !prec) {
26634330Sbostic 				size = 0;
26734330Sbostic 				goto pforw;
26834330Sbostic 			}
26934327Sbostic 
27034328Sbostic 			t = buf + BUF - 1;
27134233Sbostic 			do {
27234314Sbostic 				*t-- = digs[_ulong % base];
27334314Sbostic 				_ulong /= base;
27434314Sbostic 			} while(_ulong);
27534328Sbostic 			for (size = buf + BUF - 1 - t; size < prec; ++size)
27634324Sbostic 				*t-- = '0';
27734328Sbostic 			digs = "0123456789abcdef";
27834327Sbostic 
27934327Sbostic 			/* alternate mode for hex and octal numbers */
28034324Sbostic 			if (flags&ALT)
28134324Sbostic 				switch (base) {
28234324Sbostic 				case 16:
28334324Sbostic 					/* avoid "00000x35" */
28434327Sbostic 					if (padc == ' ') {
28534327Sbostic 						*t-- = *fmt;
28634327Sbostic 						*t-- = '0';
28734328Sbostic 						size += 2;
28834327Sbostic 					}
28934327Sbostic 					else {
29034324Sbostic 						PUTC('0');
29134324Sbostic 						PUTC(*fmt);
29234328Sbostic 						width -= 2;
29334324Sbostic 					}
29434324Sbostic 					break;
29534324Sbostic 				case 8:
29634324Sbostic 					if (t[1] != '0') {
29734324Sbostic 						*t-- = '0';
29834328Sbostic 						++size;
29934324Sbostic 					}
30034324Sbostic 					break;
30134314Sbostic 				}
30234327Sbostic 
30334327Sbostic 			if (sign) {
30434327Sbostic 				/* avoid "0000-3" */
30534328Sbostic 				if (padc == ' ') {
30634327Sbostic 					*t-- = sign;
30734328Sbostic 					++size;
30834328Sbostic 				}
30934328Sbostic 				else {
31034327Sbostic 					PUTC(sign);
31134328Sbostic 					--width;
31234328Sbostic 				}
31334327Sbostic 			}
31434328Sbostic 			++t;
31534327Sbostic 
31634328Sbostic pforw:			if (!(flags&LADJUST) && width)
31734328Sbostic 				for (n = size + fpprec; n++ < width;)
31834263Sbostic 					PUTC(padc);
31934328Sbostic 			if (fp->_cnt - (n = size) >= 0) {
32034328Sbostic 				cnt += n;
32134328Sbostic 				fp->_cnt -= n;
32234328Sbostic 				bcopy(t, fp->_ptr, n);
32334328Sbostic 				fp->_ptr += n;
32434328Sbostic 			}
32534328Sbostic 			else for (; n--; ++t)
32634314Sbostic 				PUTC(*t);
32734328Sbostic 			while (fpprec--)
32834328Sbostic 				PUTC('0');
32934328Sbostic 			if (flags&LADJUST)
33034328Sbostic 				for (n = size + fpprec; ++n < width;)
33134328Sbostic 					PUTC(' ');
33234226Sbostic 			break;
33334233Sbostic 		case '\0':		/* "%?" prints ?, unless ? is NULL */
33434314Sbostic 			return(cnt);
33534226Sbostic 		default:
33634263Sbostic 			PUTC(*fmt);
33734226Sbostic 		}
33834226Sbostic 	}
33934314Sbostic 	/*NOTREACHED*/
34034226Sbostic }
34134242Sbostic 
34234261Sbostic #define	EFORMAT	0x01
34334261Sbostic #define	FFORMAT	0x02
34434261Sbostic #define	GFORMAT	0x04
34534314Sbostic #define	DEFPREC	6
34634261Sbostic 
34734323Sbostic static char *
34834327Sbostic _cvt(number, prec, flags, fmtch, padc, sign, startp, endp)
34934242Sbostic 	double number;
35034261Sbostic 	register int prec;
35134323Sbostic 	int flags;
35234323Sbostic 	u_char fmtch;
35334327Sbostic 	char padc, *sign, *startp, *endp;
35434242Sbostic {
355*34331Sbostic 	register char *p, *t;
35634261Sbostic 	register int expcnt, format;
35734248Sbostic 	double fract, integer, tmp, modf();
35834261Sbostic 	int decpt;
359*34331Sbostic 	char *savep, exponent[MAXEXP];
36034242Sbostic 
36134314Sbostic 	if (prec == -1)
36234242Sbostic 		prec = DEFPREC;
36334242Sbostic 
36434314Sbostic 	if (number < 0) {
36534327Sbostic 		*sign = '-';
36634248Sbostic 		number = -number;
36734248Sbostic 	}
36834242Sbostic 
36934327Sbostic 	/* if blank padded, add sign in as part of the number */
37034327Sbostic 	if (*sign && padc == ' ')
37134327Sbostic 		*startp++ = *sign;
37234327Sbostic 
37334261Sbostic 	switch(fmtch) {
37434261Sbostic 	case 'e':
37534261Sbostic 	case 'E':
37634261Sbostic 		format = EFORMAT;
37734261Sbostic 		break;
37834261Sbostic 	case 'f':
37934261Sbostic 		format = FFORMAT;
38034261Sbostic 		break;
38134261Sbostic 	case 'g':
38234261Sbostic 	case 'G':
38334261Sbostic 		format = GFORMAT;
38434261Sbostic 		fmtch -= 2;
38534261Sbostic 	}
38634261Sbostic 
38734248Sbostic 	/*
38834248Sbostic 	 * if the alternate flag is set, or, at least one digit of precision
38934248Sbostic 	 * was requested, add a decimal point, unless it's the g/G format
39034314Sbostic 	 * in which case we require two digits of precision, as it counts
39134248Sbostic 	 * precision differently.
39234248Sbostic 	 */
39334318Sbostic 	decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
39434248Sbostic 
39534248Sbostic 	expcnt = 0;
39634323Sbostic 	p = endp - 1;
39734248Sbostic 	fract = modf(number, &integer);
39834248Sbostic 	if (integer) {
39934248Sbostic 		/* get integer part of number; count decimal places */
40034248Sbostic 		for (; integer; ++expcnt) {
40134248Sbostic 			tmp = modf(integer / 10, &integer);
402*34331Sbostic 			*p-- = tochar((int)((tmp + .03) * 10));
40334242Sbostic 		}
40434248Sbostic 
40534248Sbostic 		/* copy, in reverse order, to start of buffer */
406*34331Sbostic 		t = startp;
407*34331Sbostic 		*t++ = *++p;
40834248Sbostic 
40934248Sbostic 		/*
41034248Sbostic 		 * if the format is g/G, and the resulting exponent will be
41134248Sbostic 		 * greater than the precision, use e/E format.  If e/E format,
41234248Sbostic 		 * put in a decimal point as needed, and decrement precision
41334248Sbostic 		 * count for each digit after the decimal point.
41434248Sbostic 		 */
41534248Sbostic 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
41634248Sbostic 			if (format&GFORMAT) {
41734248Sbostic 				format |= EFORMAT;
41834248Sbostic 
41934248Sbostic 				/* first digit is precision for g/G format */
42034248Sbostic 				if (prec)
42134248Sbostic 					--prec;
42234248Sbostic 			}
42334248Sbostic 			if (decpt)
424*34331Sbostic 				*t++ = '.';
425*34331Sbostic 			for (; ++p < endp && prec; --prec, *t++ = *p);
42634248Sbostic 
42734318Sbostic 			/* precision ran out, round */
42834248Sbostic 			if (p < endp) {
42934248Sbostic 				if (*p > '4') {
430*34331Sbostic 					for (savep = t--;; *t-- = '0') {
431*34331Sbostic 						if (*t == '.')
432*34331Sbostic 							--t;
433*34331Sbostic 						if (++*t <= '9')
43434248Sbostic 							break;
43534248Sbostic 					}
436*34331Sbostic 					t = savep;
43734248Sbostic 				}
43834248Sbostic 				fract = 0;
43934248Sbostic 			}
44034242Sbostic 		}
44134248Sbostic 		/*
44234314Sbostic 		 * g/G in f format; if out of precision, replace digits with
44334314Sbostic 		 * zeroes, note, have to round first.
44434248Sbostic 		 */
44534248Sbostic 		else if (format&GFORMAT) {
446*34331Sbostic 			for (; ++p < endp && prec; --prec, *t++ = *p);
44734248Sbostic 			/* precision ran out; round and then add zeroes */
44834248Sbostic 			if (p < endp) {
44934248Sbostic 				if (*p > '4') {
450*34331Sbostic 					for (savep = t--; ++*t > '9';
451*34331Sbostic 					    *t-- = '0');
452*34331Sbostic 					t = savep;
45334248Sbostic 				}
45434248Sbostic 				do {
455*34331Sbostic 					*t++ = '0';
45634248Sbostic 				} while (++p < endp);
45734248Sbostic 				fract = 0;
45834248Sbostic 			}
45934248Sbostic 			if (decpt)
460*34331Sbostic 				*t++ = '.';
46134242Sbostic 		}
46234248Sbostic 		/* f format */
46334248Sbostic 		else {
464*34331Sbostic 			for (; ++p < endp; *t++ = *p);
46534248Sbostic 			if (decpt)
466*34331Sbostic 				*t++ = '.';
46734248Sbostic 		}
468*34331Sbostic 		p = t;
46934248Sbostic 	}
47034248Sbostic 	/*
47134318Sbostic 	 * if no fraction, the number was zero, and if no precision, can't
47234318Sbostic 	 * show anything after the decimal point.
47334248Sbostic 	 */
47434248Sbostic 	else if (!fract || !prec) {
47534248Sbostic 		*startp++ = '0';
47634318Sbostic 		if (decpt && !(format&GFORMAT))
47734248Sbostic 			*startp++ = '.';
47834318Sbostic 		*startp = '\0';
47934323Sbostic 		return(startp);
48034248Sbostic 	}
48134248Sbostic 	/*
48234248Sbostic 	 * if the format is g/G, and the resulting exponent will be less than
48334248Sbostic 	 * -4 use e/E format.  If e/E format, compute exponent value.
48434248Sbostic 	 */
48534248Sbostic 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
48634248Sbostic 		format |= EFORMAT;
48734248Sbostic 		if (fract)
48834248Sbostic 			for (p = startp; fract;) {
48934248Sbostic 				fract = modf(fract * 10, &tmp);
49034248Sbostic 				if (!tmp) {
49134248Sbostic 					--expcnt;
49234248Sbostic 					continue;
49334248Sbostic 				}
494*34331Sbostic 				*p++ = tochar((int)tmp);
49534248Sbostic 				break;
49634248Sbostic 			}
49734242Sbostic 		else
49834248Sbostic 			*p++ = '0';
49934248Sbostic 
50034248Sbostic 		/* g/G format, decrement precision for first digit */
50134248Sbostic 		if (format&GFORMAT && prec)
50234248Sbostic 			--prec;
50334248Sbostic 
50434248Sbostic 		/* add decimal after first non-zero digit */
50534248Sbostic 		if (decpt)
50634248Sbostic 			*p++ = '.';
50734242Sbostic 	}
50834248Sbostic 	/*
50934248Sbostic 	 * f format or g/G printed as f format; don't worry about decimal
51034248Sbostic 	 * point, if g/G format doesn't need it, will get stripped later.
51134248Sbostic 	 */
51234242Sbostic 	else {
51334248Sbostic 		p = startp;
51434248Sbostic 		*p++ = '0';
51534248Sbostic 		*p++ = '.';
51634248Sbostic 	}
51734248Sbostic 
51834319Sbostic 	/* finish out requested precision */
51934319Sbostic 	while (fract && prec-- > 0) {
52034319Sbostic 		fract = modf(fract * 10, &tmp);
521*34331Sbostic 		*p++ = tochar((int)tmp);
52234319Sbostic 	}
52334319Sbostic 	while (prec-- > 0)
52434319Sbostic 		*p++ = '0';
52534248Sbostic 
52634248Sbostic 	/*
52734248Sbostic 	 * if any fractional value left, "round" it back up to the beginning
52834248Sbostic 	 * of the number, fixing the exponent as necessary, and avoiding the
52934248Sbostic 	 * decimal point.
53034248Sbostic 	 */
53134248Sbostic 	if (fract) {
53234248Sbostic 		(void)modf(fract * 10, &tmp);
53334248Sbostic 		if (tmp > 4) {
53434248Sbostic 			for (savep = p--;; *p-- = '0') {
53534248Sbostic 				if (*p == '.')
53634248Sbostic 					--p;
53734248Sbostic 				if (p == startp) {
53834248Sbostic 					*p = '1';
53934248Sbostic 					++expcnt;
54034248Sbostic 					break;
54134248Sbostic 				}
54234248Sbostic 				if (++*p <= '9')
54334248Sbostic 					break;
54434242Sbostic 			}
54534248Sbostic 			p = savep;
54634242Sbostic 		}
54734248Sbostic 	}
54834248Sbostic 
54934248Sbostic 	/*
55034248Sbostic 	 * if a g/G format and not alternate flag, lose trailing zeroes,
55134248Sbostic 	 * if e/E or g/G format, and last char is decimal point, lose it.
55234248Sbostic 	 */
55334318Sbostic 	if (!(flags&ALT)) {
55434248Sbostic 		if (format&GFORMAT)
55534248Sbostic 			for (; p[-1] == '0'; --p);
55634248Sbostic 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
55734248Sbostic 			--p;
55834248Sbostic 	}
55934248Sbostic 
56034248Sbostic 	/* if an e/E format, add exponent */
56134248Sbostic 	if (format&EFORMAT) {
56234248Sbostic 		*p++ = fmtch;
56334248Sbostic 		if (--expcnt < 0) {
56434248Sbostic 			expcnt = -expcnt;
56534248Sbostic 			*p++ = '-';
56634242Sbostic 		}
56734248Sbostic 		else
56834248Sbostic 			*p++ = '+';
569*34331Sbostic 		t = exponent + MAXEXP;
570*34331Sbostic 		if (expcnt > 9) {
571*34331Sbostic 			do {
572*34331Sbostic 				*--t = tochar(expcnt % 10);
573*34331Sbostic 			} while ((expcnt /= 10) > 9);
574*34331Sbostic 			*--t = tochar(expcnt);
575*34331Sbostic 			for (; t < exponent + MAXEXP; *p++ = *t++);
576*34331Sbostic 		}
577*34331Sbostic 		else {
578*34331Sbostic 			*p++ = '0';
579*34331Sbostic 			*p++ = tochar(expcnt);
580*34331Sbostic 		}
58134242Sbostic 	}
58234323Sbostic 	return(p);
58334242Sbostic }
584