xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34427)
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*34427Sbostic static char sccsid[] = "@(#)vfprintf.c	5.28 (Berkeley) 05/23/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 
29*34427Sbostic #define	PUTC(ch)	(void) putc(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 
3534331Sbostic #define	todigit(c)	((c) - '0')
3634331Sbostic #define	tochar(n)	((n) + '0')
3734331Sbostic 
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 */
46*34427Sbostic #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
47*34427Sbostic #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
4834318Sbostic 
4934323Sbostic _doprnt(fmt0, argp, fp)
5034323Sbostic 	u_char *fmt0;
5134233Sbostic 	va_list argp;
5234235Sbostic 	register FILE *fp;
5334226Sbostic {
54*34427Sbostic 	register u_char *fmt;	/* format string */
55*34427Sbostic 	register int ch;	/* character from fmt */
56*34427Sbostic 	register int cnt;	/* return value accumulator */
57*34427Sbostic 	register int n;		/* random handy integer */
58*34427Sbostic 	register char *t;	/* buffer pointer */
59*34427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
60*34427Sbostic 	u_long _ulong;		/* integer arguments %[diouxX] */
61*34427Sbostic 	int flags;		/* flags as above */
62*34427Sbostic 	int dprec;		/* decimal precision in [diouxX] */
63*34427Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
64*34427Sbostic 	int width;		/* width from format (%8d), or 0 */
65*34427Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
66*34427Sbostic 	int size;		/* size of converted field or string */
67*34427Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
68*34427Sbostic 	int realsz;		/* field size expanded by decimal precision */
69*34427Sbostic 	char sign;		/* sign prefix (+ - or \0) */
70*34427Sbostic 	int base;		/* base for [diouxX] conversion */
71*34427Sbostic 	char *digs;		/* digits for [diouxX] conversion */
72*34427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
73*34427Sbostic 	char *_cvt();		/* handles [eEfgG] formats */
7434226Sbostic 
7534323Sbostic 	fmt = fmt0;
7634243Sbostic 	digs = "0123456789abcdef";
7734322Sbostic 	for (cnt = 0;; ++fmt) {
7834318Sbostic 		n = fp->_cnt;
79*34427Sbostic 		for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
80*34427Sbostic 		     ++cnt, ++fmt)
8134318Sbostic 			if (--n < 0
8234318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
8334318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
8434318Sbostic #endif
85*34427Sbostic 			    || ch == '\n' && fp->_flag & _IOLBF) {
8634318Sbostic 				fp->_cnt = n;
87*34427Sbostic 				fp->_ptr = (u_char *)t;
88*34427Sbostic 				(void) _flsbuf((u_char)ch, fp);
8934318Sbostic 				n = fp->_cnt;
90*34427Sbostic 				t = (char *)fp->_ptr;
91*34427Sbostic 			} else
9234314Sbostic 				*t++ = ch;
9334318Sbostic 		fp->_cnt = n;
94*34427Sbostic 		fp->_ptr = (u_char *)t;
9534318Sbostic 		if (!ch)
96*34427Sbostic 			return (cnt);
9734314Sbostic 
98*34427Sbostic 		flags = dprec = fpprec = width = 0;
9934233Sbostic 		prec = -1;
10034318Sbostic 		sign = '\0';
10134226Sbostic 
10234318Sbostic rflag:		switch (*++fmt) {
10334318Sbostic 		case ' ':
10434318Sbostic 			sign = ' ';
10534318Sbostic 			goto rflag;
10634233Sbostic 		case '#':
10734318Sbostic 			flags |= ALT;
10834318Sbostic 			goto rflag;
10934233Sbostic 		case '*':
11034235Sbostic 			/*
11134235Sbostic 			 * ``A negative field width argument is taken as a
11234235Sbostic 			 * - flag followed by a  positive field width.''
11334235Sbostic 			 *	-- ANSI X3J11
11434235Sbostic 			 * They don't exclude field widths read from args.
11534235Sbostic 			 */
11634235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
11734318Sbostic 				goto rflag;
11834235Sbostic 			width = -width;
119*34427Sbostic 			/* FALLTHROUGH */
12034235Sbostic 		case '-':
12134318Sbostic 			flags |= LADJUST;
12234318Sbostic 			goto rflag;
12334233Sbostic 		case '+':
12434314Sbostic 			sign = '+';
12534318Sbostic 			goto rflag;
12634233Sbostic 		case '.':
12734235Sbostic 			if (*++fmt == '*')
12834318Sbostic 				n = va_arg(argp, int);
129*34427Sbostic 			else {
13034318Sbostic 				n = 0;
131*34427Sbostic 				while (isascii(*fmt) && isdigit(*fmt))
132*34427Sbostic 					n = 10 * n + todigit(*fmt++);
13334233Sbostic 				--fmt;
13434226Sbostic 			}
13534318Sbostic 			prec = n < 0 ? -1 : n;
13634318Sbostic 			goto rflag;
13734233Sbostic 		case '0':
138*34427Sbostic 			/*
139*34427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
140*34427Sbostic 			 * beginning of a field width.''
141*34427Sbostic 			 *	-- ANSI X3J11
142*34427Sbostic 			 */
143*34427Sbostic 			flags |= ZEROPAD;
144*34427Sbostic 			goto rflag;
14534233Sbostic 		case '1': case '2': case '3': case '4':
14634233Sbostic 		case '5': case '6': case '7': case '8': case '9':
14734318Sbostic 			n = 0;
14834233Sbostic 			do {
14934331Sbostic 				n = 10 * n + todigit(*fmt);
15034318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
15134318Sbostic 			width = n;
15234233Sbostic 			--fmt;
15334319Sbostic 			goto rflag;
15434235Sbostic 		case 'L':
15534329Sbostic 			flags |= LONGDBL;
15634318Sbostic 			goto rflag;
15734235Sbostic 		case 'h':
15834318Sbostic 			flags |= SHORTINT;
15934318Sbostic 			goto rflag;
16034233Sbostic 		case 'l':
16134318Sbostic 			flags |= LONGINT;
16234318Sbostic 			goto rflag;
16334314Sbostic 		case 'c':
164*34427Sbostic 			*(t = buf) = va_arg(argp, int);
16534314Sbostic 			size = 1;
166*34427Sbostic 			sign = '\0';
16734314Sbostic 			goto pforw;
16834314Sbostic 		case 'd':
16934318Sbostic 		case 'i':
17034318Sbostic 			ARG();
17134318Sbostic 			if ((long)_ulong < 0) {
17234318Sbostic 				_ulong = -_ulong;
17334314Sbostic 				sign = '-';
17434241Sbostic 			}
17534241Sbostic 			base = 10;
17634327Sbostic 			goto number;
17734261Sbostic 		case 'e':
17834236Sbostic 		case 'E':
17934235Sbostic 		case 'f':
18034261Sbostic 		case 'g':
18134243Sbostic 		case 'G':
18234243Sbostic 			_double = va_arg(argp, double);
18334328Sbostic 			/*
18434328Sbostic 			 * don't bother to do unrealistic precision; just
18534328Sbostic 			 * pad it with zeroes later.  This keeps buffer size
18634328Sbostic 			 * rational.
18734328Sbostic 			 */
18834328Sbostic 			if (prec > MAXFRACT) {
18934328Sbostic 				fpprec = prec - MAXFRACT;
19034328Sbostic 				prec = MAXFRACT;
19134328Sbostic 			}
19234319Sbostic 			t = buf;
193*34427Sbostic 			size = _cvt(_double, prec, flags, *fmt, &sign,
194*34427Sbostic 				    t, t + sizeof(buf)) - t;
19534314Sbostic 			goto pforw;
19634235Sbostic 		case 'n':
197*34427Sbostic 			if (flags & LONGINT)
19834318Sbostic 				*va_arg(argp, long *) = cnt;
199*34427Sbostic 			else if (flags & SHORTINT)
20034318Sbostic 				*va_arg(argp, short *) = cnt;
20134318Sbostic 			else
20234318Sbostic 				*va_arg(argp, int *) = cnt;
20334235Sbostic 			break;
20434226Sbostic 		case 'o':
20534318Sbostic 			ARG();
20634226Sbostic 			base = 8;
20734327Sbostic 			goto nosign;
20834235Sbostic 		case 'p':
20934320Sbostic 			/*
21034321Sbostic 			 * ``The argument shall be a pointer to void.  The
21134321Sbostic 			 * value of the pointer is converted to a sequence
21234321Sbostic 			 * of printable characters, in an implementation-
21334321Sbostic 			 * defined manner.''
21434321Sbostic 			 *	-- ANSI X3J11
21534320Sbostic 			 */
216*34427Sbostic 			/* NOSTRICT */
21734320Sbostic 			_ulong = (u_long)va_arg(argp, void *);
21834320Sbostic 			base = 16;
21934327Sbostic 			goto nosign;
22034226Sbostic 		case 's':
22134314Sbostic 			if (!(t = va_arg(argp, char *)))
22234314Sbostic 				t = "(null)";
22334321Sbostic 			if (prec >= 0) {
22434321Sbostic 				/*
22534321Sbostic 				 * can't use strlen; can only look for the
22634321Sbostic 				 * NUL in the first `prec' characters, and
22734321Sbostic 				 * strlen() will go further.
22834321Sbostic 				 */
22934321Sbostic 				char *p, *memchr();
23034321Sbostic 
23134321Sbostic 				if (p = memchr(t, 0, prec)) {
23234321Sbostic 					size = p - t;
23334321Sbostic 					if (size > prec)
23434321Sbostic 						size = prec;
235*34427Sbostic 				} else
23634321Sbostic 					size = prec;
237*34427Sbostic 			} else
23834321Sbostic 				size = strlen(t);
239*34427Sbostic 			sign = '\0';
24034328Sbostic 			goto pforw;
24134226Sbostic 		case 'u':
24234318Sbostic 			ARG();
24334226Sbostic 			base = 10;
24434327Sbostic 			goto nosign;
24534226Sbostic 		case 'X':
24634226Sbostic 			digs = "0123456789ABCDEF";
247*34427Sbostic 			/* FALLTHROUGH */
24834226Sbostic 		case 'x':
24934318Sbostic 			ARG();
25034314Sbostic 			base = 16;
25134326Sbostic 			/* leading 0x/X only if non-zero */
252*34427Sbostic 			if (flags & ALT && _ulong != 0)
253*34427Sbostic 				flags |= HEXPREFIX;
25434327Sbostic 
25534327Sbostic 			/* unsigned conversions */
256*34427Sbostic nosign:			sign = '\0';
25734326Sbostic 			/*
25834330Sbostic 			 * ``... diouXx conversions ... if a precision is
25934330Sbostic 			 * specified, the 0 flag will be ignored.''
26034330Sbostic 			 *	-- ANSI X3J11
26134330Sbostic 			 */
262*34427Sbostic number:			if ((dprec = prec) >= 0)
263*34427Sbostic 				flags &= ~ZEROPAD;
264*34427Sbostic 
26534330Sbostic 			/*
26634326Sbostic 			 * ``The result of converting a zero value with an
26734326Sbostic 			 * explicit precision of zero is no characters.''
26834326Sbostic 			 *	-- ANSI X3J11
26934326Sbostic 			 */
270*34427Sbostic 			t = buf + BUF;
271*34427Sbostic 			if (_ulong != 0 || prec != 0) {
272*34427Sbostic 				do {
273*34427Sbostic 					*--t = digs[_ulong % base];
274*34427Sbostic 					_ulong /= base;
275*34427Sbostic 				} while (_ulong);
276*34427Sbostic 				digs = "0123456789abcdef";
277*34427Sbostic 				if (flags & ALT && base == 8 && *t != '0')
278*34427Sbostic 					*--t = '0'; /* octal leading 0 */
27934330Sbostic 			}
280*34427Sbostic 			size = buf + BUF - t;
28134327Sbostic 
282*34427Sbostic pforw:
283*34427Sbostic 			/*
284*34427Sbostic 			 * All reasonable formats wind up here.  At this
285*34427Sbostic 			 * point, `t' points to a string which (if not
286*34427Sbostic 			 * flags&LADJUST) should be padded out to `width'
287*34427Sbostic 			 * places.  If flags&ZEROPAD, it should first be
288*34427Sbostic 			 * prefixed by any sign or other prefix; otherwise,
289*34427Sbostic 			 * it should be blank padded before the prefix is
290*34427Sbostic 			 * emitted.  After any left-hand padding and
291*34427Sbostic 			 * prefixing, emit zeroes required by a decimal
292*34427Sbostic 			 * [diouxX] precision, then print the string proper,
293*34427Sbostic 			 * then emit zeroes required by any leftover floating
294*34427Sbostic 			 * precision; finally, if LADJUST, pad with blanks.
295*34427Sbostic 			 */
29634327Sbostic 
297*34427Sbostic 			/* compute actual size, so we know how much to pad */
298*34427Sbostic 			/* this code is not terribly satisfactory */
299*34427Sbostic 			/* fieldsz excludes decimal prec; realsz includes it */
300*34427Sbostic 			fieldsz = size + fpprec;
301*34427Sbostic 			if (sign)
302*34427Sbostic 				fieldsz++;
303*34427Sbostic 			if (flags & HEXPREFIX)
304*34427Sbostic 				fieldsz += 2;
305*34427Sbostic 			realsz = dprec > fieldsz ? dprec : fieldsz;
30634327Sbostic 
307*34427Sbostic 			/* right-adjusting blank padding */
308*34427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
309*34427Sbostic 				for (n = realsz; n < width; n++)
310*34427Sbostic 					PUTC(' ');
311*34427Sbostic 			/* prefix */
312*34427Sbostic 			if (sign)
313*34427Sbostic 				PUTC(sign);
314*34427Sbostic 			if (flags & HEXPREFIX) {
315*34427Sbostic 				PUTC('0');
316*34427Sbostic 				PUTC((char)*fmt);
31734327Sbostic 			}
318*34427Sbostic 			/* right-adjusting zero padding */
319*34427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
320*34427Sbostic 				for (n = realsz; n < width; n++)
321*34427Sbostic 					PUTC('0');
322*34427Sbostic 			/* leading zeroes from decimal precision */
323*34427Sbostic 			for (n = fieldsz; n < dprec; n++)
324*34427Sbostic 				PUTC('0');
32534327Sbostic 
326*34427Sbostic 			/* the string or number proper */
327*34427Sbostic 			if (fp->_cnt - (n = size) >= 0 &&
328*34427Sbostic 			    (fp->_flag & _IOLBF) == 0) {
32934328Sbostic 				fp->_cnt -= n;
330*34427Sbostic 				bcopy(t, (char *)fp->_ptr, n);
33134328Sbostic 				fp->_ptr += n;
332*34427Sbostic 			} else
333*34427Sbostic 				while (--n >= 0)
334*34427Sbostic 					PUTC(*t++);
335*34427Sbostic 			/* trailing f.p. zeroes */
336*34427Sbostic 			while (--fpprec >= 0)
33734328Sbostic 				PUTC('0');
338*34427Sbostic 			/* left-adjusting padding (always blank) */
339*34427Sbostic 			if (flags & LADJUST)
340*34427Sbostic 				for (n = realsz; n < width; n++)
34134328Sbostic 					PUTC(' ');
342*34427Sbostic 
343*34427Sbostic 			/* finally, adjust cnt */
344*34427Sbostic 			cnt += width > realsz ? width : realsz;
34534226Sbostic 			break;
346*34427Sbostic 		case '\0':	/* "%?" prints ?, unless ? is NULL */
347*34427Sbostic 			return (cnt);
34834226Sbostic 		default:
349*34427Sbostic 			PUTC((char)*fmt);
350*34427Sbostic 			cnt++;
35134226Sbostic 		}
35234226Sbostic 	}
353*34427Sbostic 	/* NOTREACHED */
35434226Sbostic }
35534242Sbostic 
35634261Sbostic #define	EFORMAT	0x01
35734261Sbostic #define	FFORMAT	0x02
35834261Sbostic #define	GFORMAT	0x04
35934314Sbostic #define	DEFPREC	6
36034261Sbostic 
36134323Sbostic static char *
362*34427Sbostic _cvt(number, prec, flags, fmtch, sign, startp, endp)
36334242Sbostic 	double number;
36434261Sbostic 	register int prec;
36534323Sbostic 	int flags;
36634323Sbostic 	u_char fmtch;
367*34427Sbostic 	char *sign, *startp, *endp;
36834242Sbostic {
36934331Sbostic 	register char *p, *t;
37034261Sbostic 	register int expcnt, format;
37134248Sbostic 	double fract, integer, tmp, modf();
37234261Sbostic 	int decpt;
37334331Sbostic 	char *savep, exponent[MAXEXP];
37434242Sbostic 
37534314Sbostic 	if (prec == -1)
37634242Sbostic 		prec = DEFPREC;
37734242Sbostic 
37834314Sbostic 	if (number < 0) {
37934327Sbostic 		*sign = '-';
38034248Sbostic 		number = -number;
38134248Sbostic 	}
38234242Sbostic 
38334261Sbostic 	switch(fmtch) {
38434261Sbostic 	case 'e':
38534261Sbostic 	case 'E':
38634261Sbostic 		format = EFORMAT;
38734261Sbostic 		break;
38834261Sbostic 	case 'f':
38934261Sbostic 		format = FFORMAT;
39034261Sbostic 		break;
39134261Sbostic 	case 'g':
39234261Sbostic 	case 'G':
39334261Sbostic 		format = GFORMAT;
39434261Sbostic 		fmtch -= 2;
39534261Sbostic 	}
39634261Sbostic 
39734248Sbostic 	/*
39834248Sbostic 	 * if the alternate flag is set, or, at least one digit of precision
39934248Sbostic 	 * was requested, add a decimal point, unless it's the g/G format
40034314Sbostic 	 * in which case we require two digits of precision, as it counts
40134248Sbostic 	 * precision differently.
40234248Sbostic 	 */
40334318Sbostic 	decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
40434248Sbostic 
40534248Sbostic 	expcnt = 0;
40634323Sbostic 	p = endp - 1;
40734248Sbostic 	fract = modf(number, &integer);
40834248Sbostic 	if (integer) {
40934248Sbostic 		/* get integer part of number; count decimal places */
41034248Sbostic 		for (; integer; ++expcnt) {
41134248Sbostic 			tmp = modf(integer / 10, &integer);
41234331Sbostic 			*p-- = tochar((int)((tmp + .03) * 10));
41334242Sbostic 		}
41434248Sbostic 
41534248Sbostic 		/* copy, in reverse order, to start of buffer */
41634331Sbostic 		t = startp;
41734331Sbostic 		*t++ = *++p;
41834248Sbostic 
41934248Sbostic 		/*
42034248Sbostic 		 * if the format is g/G, and the resulting exponent will be
42134248Sbostic 		 * greater than the precision, use e/E format.  If e/E format,
42234248Sbostic 		 * put in a decimal point as needed, and decrement precision
42334248Sbostic 		 * count for each digit after the decimal point.
42434248Sbostic 		 */
42534248Sbostic 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
42634248Sbostic 			if (format&GFORMAT) {
42734248Sbostic 				format |= EFORMAT;
42834248Sbostic 
42934248Sbostic 				/* first digit is precision for g/G format */
43034248Sbostic 				if (prec)
43134248Sbostic 					--prec;
43234248Sbostic 			}
43334248Sbostic 			if (decpt)
43434331Sbostic 				*t++ = '.';
43534331Sbostic 			for (; ++p < endp && prec; --prec, *t++ = *p);
43634248Sbostic 
43734318Sbostic 			/* precision ran out, round */
43834248Sbostic 			if (p < endp) {
43934248Sbostic 				if (*p > '4') {
44034331Sbostic 					for (savep = t--;; *t-- = '0') {
44134331Sbostic 						if (*t == '.')
44234331Sbostic 							--t;
44334331Sbostic 						if (++*t <= '9')
44434248Sbostic 							break;
44534248Sbostic 					}
44634331Sbostic 					t = savep;
44734248Sbostic 				}
44834248Sbostic 				fract = 0;
44934248Sbostic 			}
45034242Sbostic 		}
45134248Sbostic 		/*
45234314Sbostic 		 * g/G in f format; if out of precision, replace digits with
45334314Sbostic 		 * zeroes, note, have to round first.
45434248Sbostic 		 */
45534248Sbostic 		else if (format&GFORMAT) {
45634331Sbostic 			for (; ++p < endp && prec; --prec, *t++ = *p);
45734248Sbostic 			/* precision ran out; round and then add zeroes */
45834248Sbostic 			if (p < endp) {
45934248Sbostic 				if (*p > '4') {
46034331Sbostic 					for (savep = t--; ++*t > '9';
46134331Sbostic 					    *t-- = '0');
46234331Sbostic 					t = savep;
46334248Sbostic 				}
46434248Sbostic 				do {
46534331Sbostic 					*t++ = '0';
46634248Sbostic 				} while (++p < endp);
46734248Sbostic 				fract = 0;
46834248Sbostic 			}
46934248Sbostic 			if (decpt)
47034331Sbostic 				*t++ = '.';
47134242Sbostic 		}
47234248Sbostic 		/* f format */
47334248Sbostic 		else {
47434331Sbostic 			for (; ++p < endp; *t++ = *p);
47534248Sbostic 			if (decpt)
47634331Sbostic 				*t++ = '.';
47734248Sbostic 		}
47834331Sbostic 		p = t;
47934248Sbostic 	}
48034248Sbostic 	/*
48134318Sbostic 	 * if no fraction, the number was zero, and if no precision, can't
48234318Sbostic 	 * show anything after the decimal point.
48334248Sbostic 	 */
48434248Sbostic 	else if (!fract || !prec) {
48534248Sbostic 		*startp++ = '0';
48634318Sbostic 		if (decpt && !(format&GFORMAT))
48734248Sbostic 			*startp++ = '.';
48834318Sbostic 		*startp = '\0';
48934323Sbostic 		return(startp);
49034248Sbostic 	}
49134248Sbostic 	/*
49234248Sbostic 	 * if the format is g/G, and the resulting exponent will be less than
49334248Sbostic 	 * -4 use e/E format.  If e/E format, compute exponent value.
49434248Sbostic 	 */
49534248Sbostic 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
49634248Sbostic 		format |= EFORMAT;
49734248Sbostic 		if (fract)
49834248Sbostic 			for (p = startp; fract;) {
49934248Sbostic 				fract = modf(fract * 10, &tmp);
50034248Sbostic 				if (!tmp) {
50134248Sbostic 					--expcnt;
50234248Sbostic 					continue;
50334248Sbostic 				}
50434331Sbostic 				*p++ = tochar((int)tmp);
50534248Sbostic 				break;
50634248Sbostic 			}
50734242Sbostic 		else
50834248Sbostic 			*p++ = '0';
50934248Sbostic 
51034248Sbostic 		/* g/G format, decrement precision for first digit */
51134248Sbostic 		if (format&GFORMAT && prec)
51234248Sbostic 			--prec;
51334248Sbostic 
51434248Sbostic 		/* add decimal after first non-zero digit */
51534248Sbostic 		if (decpt)
51634248Sbostic 			*p++ = '.';
51734242Sbostic 	}
51834248Sbostic 	/*
51934248Sbostic 	 * f format or g/G printed as f format; don't worry about decimal
52034248Sbostic 	 * point, if g/G format doesn't need it, will get stripped later.
52134248Sbostic 	 */
52234242Sbostic 	else {
52334248Sbostic 		p = startp;
52434248Sbostic 		*p++ = '0';
52534248Sbostic 		*p++ = '.';
52634248Sbostic 	}
52734248Sbostic 
52834319Sbostic 	/* finish out requested precision */
52934319Sbostic 	while (fract && prec-- > 0) {
53034319Sbostic 		fract = modf(fract * 10, &tmp);
53134331Sbostic 		*p++ = tochar((int)tmp);
53234319Sbostic 	}
53334319Sbostic 	while (prec-- > 0)
53434319Sbostic 		*p++ = '0';
53534248Sbostic 
53634248Sbostic 	/*
53734248Sbostic 	 * if any fractional value left, "round" it back up to the beginning
53834248Sbostic 	 * of the number, fixing the exponent as necessary, and avoiding the
53934248Sbostic 	 * decimal point.
54034248Sbostic 	 */
54134248Sbostic 	if (fract) {
54234248Sbostic 		(void)modf(fract * 10, &tmp);
54334248Sbostic 		if (tmp > 4) {
54434248Sbostic 			for (savep = p--;; *p-- = '0') {
54534248Sbostic 				if (*p == '.')
54634248Sbostic 					--p;
54734248Sbostic 				if (p == startp) {
54834248Sbostic 					*p = '1';
54934248Sbostic 					++expcnt;
55034248Sbostic 					break;
55134248Sbostic 				}
55234248Sbostic 				if (++*p <= '9')
55334248Sbostic 					break;
55434242Sbostic 			}
55534248Sbostic 			p = savep;
55634242Sbostic 		}
55734248Sbostic 	}
55834248Sbostic 
55934248Sbostic 	/*
56034248Sbostic 	 * if a g/G format and not alternate flag, lose trailing zeroes,
56134248Sbostic 	 * if e/E or g/G format, and last char is decimal point, lose it.
56234248Sbostic 	 */
56334318Sbostic 	if (!(flags&ALT)) {
56434248Sbostic 		if (format&GFORMAT)
56534248Sbostic 			for (; p[-1] == '0'; --p);
56634248Sbostic 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
56734248Sbostic 			--p;
56834248Sbostic 	}
56934248Sbostic 
57034248Sbostic 	/* if an e/E format, add exponent */
57134248Sbostic 	if (format&EFORMAT) {
57234248Sbostic 		*p++ = fmtch;
57334248Sbostic 		if (--expcnt < 0) {
57434248Sbostic 			expcnt = -expcnt;
57534248Sbostic 			*p++ = '-';
57634242Sbostic 		}
57734248Sbostic 		else
57834248Sbostic 			*p++ = '+';
57934331Sbostic 		t = exponent + MAXEXP;
58034331Sbostic 		if (expcnt > 9) {
58134331Sbostic 			do {
58234331Sbostic 				*--t = tochar(expcnt % 10);
58334331Sbostic 			} while ((expcnt /= 10) > 9);
58434331Sbostic 			*--t = tochar(expcnt);
58534331Sbostic 			for (; t < exponent + MAXEXP; *p++ = *t++);
58634331Sbostic 		}
58734331Sbostic 		else {
58834331Sbostic 			*p++ = '0';
58934331Sbostic 			*p++ = tochar(expcnt);
59034331Sbostic 		}
59134242Sbostic 	}
59234323Sbostic 	return(p);
59334242Sbostic }
594