xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 34584)
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*34584Sbostic static char sccsid[] = "@(#)vfprintf.c	5.31 (Berkeley) 06/01/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 
2934427Sbostic #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 */
4634427Sbostic #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
4734427Sbostic #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 {
5434427Sbostic 	register u_char *fmt;	/* format string */
5534427Sbostic 	register int ch;	/* character from fmt */
5634427Sbostic 	register int cnt;	/* return value accumulator */
5734427Sbostic 	register int n;		/* random handy integer */
5834427Sbostic 	register char *t;	/* buffer pointer */
5934427Sbostic 	double _double;		/* double precision arguments %[eEfgG] */
6034427Sbostic 	u_long _ulong;		/* integer arguments %[diouxX] */
6134427Sbostic 	int flags;		/* flags as above */
6234427Sbostic 	int dprec;		/* decimal precision in [diouxX] */
6334427Sbostic 	int fpprec;		/* `extra' floating precision in [eEfgG] */
6434427Sbostic 	int width;		/* width from format (%8d), or 0 */
6534427Sbostic 	int prec;		/* precision from format (%.3d), or -1 */
6634427Sbostic 	int size;		/* size of converted field or string */
6734427Sbostic 	int fieldsz;		/* field size expanded by sign, etc */
6834427Sbostic 	int realsz;		/* field size expanded by decimal precision */
6934427Sbostic 	char sign;		/* sign prefix (+ - or \0) */
7034427Sbostic 	int base;		/* base for [diouxX] conversion */
7134427Sbostic 	char *digs;		/* digits for [diouxX] conversion */
7234427Sbostic 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
7334427Sbostic 	char *_cvt();		/* handles [eEfgG] formats */
7434226Sbostic 
7534428Sbostic 	if (fp->_flag & _IORW) {
7634428Sbostic 		fp->_flag |= _IOWRT;
7734428Sbostic 		fp->_flag &= ~(_IOEOF|_IOREAD);
7834428Sbostic 	}
7934428Sbostic 	if ((fp->_flag & _IOWRT) == 0)
8034428Sbostic 		return (EOF);
8134428Sbostic 
8234323Sbostic 	fmt = fmt0;
8334243Sbostic 	digs = "0123456789abcdef";
8434322Sbostic 	for (cnt = 0;; ++fmt) {
8534318Sbostic 		n = fp->_cnt;
8634427Sbostic 		for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%';
8734427Sbostic 		     ++cnt, ++fmt)
8834318Sbostic 			if (--n < 0
8934318Sbostic #ifdef NEGATIVE_COUNT_KLUDGE
9034318Sbostic 			    && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz)
9134318Sbostic #endif
9234427Sbostic 			    || ch == '\n' && fp->_flag & _IOLBF) {
9334318Sbostic 				fp->_cnt = n;
9434475Sbostic 				fp->_ptr = t;
9534427Sbostic 				(void) _flsbuf((u_char)ch, fp);
9634318Sbostic 				n = fp->_cnt;
9734427Sbostic 				t = (char *)fp->_ptr;
9834427Sbostic 			} else
9934314Sbostic 				*t++ = ch;
10034318Sbostic 		fp->_cnt = n;
10134475Sbostic 		fp->_ptr = t;
10234318Sbostic 		if (!ch)
10334427Sbostic 			return (cnt);
10434314Sbostic 
10534427Sbostic 		flags = dprec = fpprec = width = 0;
10634233Sbostic 		prec = -1;
10734318Sbostic 		sign = '\0';
10834226Sbostic 
10934318Sbostic rflag:		switch (*++fmt) {
11034318Sbostic 		case ' ':
11134318Sbostic 			sign = ' ';
11234318Sbostic 			goto rflag;
11334233Sbostic 		case '#':
11434318Sbostic 			flags |= ALT;
11534318Sbostic 			goto rflag;
11634233Sbostic 		case '*':
11734235Sbostic 			/*
11834235Sbostic 			 * ``A negative field width argument is taken as a
11934235Sbostic 			 * - flag followed by a  positive field width.''
12034235Sbostic 			 *	-- ANSI X3J11
12134235Sbostic 			 * They don't exclude field widths read from args.
12234235Sbostic 			 */
12334235Sbostic 			if ((width = va_arg(argp, int)) >= 0)
12434318Sbostic 				goto rflag;
12534235Sbostic 			width = -width;
12634427Sbostic 			/* FALLTHROUGH */
12734235Sbostic 		case '-':
12834318Sbostic 			flags |= LADJUST;
12934318Sbostic 			goto rflag;
13034233Sbostic 		case '+':
13134314Sbostic 			sign = '+';
13234318Sbostic 			goto rflag;
13334233Sbostic 		case '.':
13434235Sbostic 			if (*++fmt == '*')
13534318Sbostic 				n = va_arg(argp, int);
13634427Sbostic 			else {
13734318Sbostic 				n = 0;
13834427Sbostic 				while (isascii(*fmt) && isdigit(*fmt))
13934427Sbostic 					n = 10 * n + todigit(*fmt++);
14034233Sbostic 				--fmt;
14134226Sbostic 			}
14234318Sbostic 			prec = n < 0 ? -1 : n;
14334318Sbostic 			goto rflag;
14434233Sbostic 		case '0':
14534427Sbostic 			/*
14634427Sbostic 			 * ``Note that 0 is taken as a flag, not as the
14734427Sbostic 			 * beginning of a field width.''
14834427Sbostic 			 *	-- ANSI X3J11
14934427Sbostic 			 */
15034427Sbostic 			flags |= ZEROPAD;
15134427Sbostic 			goto rflag;
15234233Sbostic 		case '1': case '2': case '3': case '4':
15334233Sbostic 		case '5': case '6': case '7': case '8': case '9':
15434318Sbostic 			n = 0;
15534233Sbostic 			do {
15634331Sbostic 				n = 10 * n + todigit(*fmt);
15734318Sbostic 			} while (isascii(*++fmt) && isdigit(*fmt));
15834318Sbostic 			width = n;
15934233Sbostic 			--fmt;
16034319Sbostic 			goto rflag;
16134235Sbostic 		case 'L':
16234329Sbostic 			flags |= LONGDBL;
16334318Sbostic 			goto rflag;
16434235Sbostic 		case 'h':
16534318Sbostic 			flags |= SHORTINT;
16634318Sbostic 			goto rflag;
16734233Sbostic 		case 'l':
16834318Sbostic 			flags |= LONGINT;
16934318Sbostic 			goto rflag;
17034314Sbostic 		case 'c':
17134427Sbostic 			*(t = buf) = va_arg(argp, int);
17234314Sbostic 			size = 1;
17334427Sbostic 			sign = '\0';
17434314Sbostic 			goto pforw;
17534314Sbostic 		case 'd':
17634318Sbostic 		case 'i':
17734318Sbostic 			ARG();
17834318Sbostic 			if ((long)_ulong < 0) {
17934318Sbostic 				_ulong = -_ulong;
18034314Sbostic 				sign = '-';
18134241Sbostic 			}
18234241Sbostic 			base = 10;
18334327Sbostic 			goto number;
18434261Sbostic 		case 'e':
18534236Sbostic 		case 'E':
18634235Sbostic 		case 'f':
18734261Sbostic 		case 'g':
18834243Sbostic 		case 'G':
18934243Sbostic 			_double = va_arg(argp, double);
19034328Sbostic 			/*
19134328Sbostic 			 * don't bother to do unrealistic precision; just
19234328Sbostic 			 * pad it with zeroes later.  This keeps buffer size
19334328Sbostic 			 * rational.
19434328Sbostic 			 */
19534328Sbostic 			if (prec > MAXFRACT) {
19634475Sbostic 				if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
19734475Sbostic 					fpprec = prec - MAXFRACT;
19834328Sbostic 				prec = MAXFRACT;
19934328Sbostic 			}
20034319Sbostic 			t = buf;
20134427Sbostic 			size = _cvt(_double, prec, flags, *fmt, &sign,
20234427Sbostic 				    t, t + sizeof(buf)) - t;
20334314Sbostic 			goto pforw;
20434235Sbostic 		case 'n':
20534427Sbostic 			if (flags & LONGINT)
20634318Sbostic 				*va_arg(argp, long *) = cnt;
20734427Sbostic 			else if (flags & SHORTINT)
20834318Sbostic 				*va_arg(argp, short *) = cnt;
20934318Sbostic 			else
21034318Sbostic 				*va_arg(argp, int *) = cnt;
21134235Sbostic 			break;
21234226Sbostic 		case 'o':
21334318Sbostic 			ARG();
21434226Sbostic 			base = 8;
21534327Sbostic 			goto nosign;
21634235Sbostic 		case 'p':
21734320Sbostic 			/*
21834321Sbostic 			 * ``The argument shall be a pointer to void.  The
21934321Sbostic 			 * value of the pointer is converted to a sequence
22034321Sbostic 			 * of printable characters, in an implementation-
22134321Sbostic 			 * defined manner.''
22234321Sbostic 			 *	-- ANSI X3J11
22334320Sbostic 			 */
22434427Sbostic 			/* NOSTRICT */
22534320Sbostic 			_ulong = (u_long)va_arg(argp, void *);
22634320Sbostic 			base = 16;
22734327Sbostic 			goto nosign;
22834226Sbostic 		case 's':
22934314Sbostic 			if (!(t = va_arg(argp, char *)))
23034314Sbostic 				t = "(null)";
23134321Sbostic 			if (prec >= 0) {
23234321Sbostic 				/*
23334321Sbostic 				 * can't use strlen; can only look for the
23434321Sbostic 				 * NUL in the first `prec' characters, and
23534321Sbostic 				 * strlen() will go further.
23634321Sbostic 				 */
23734321Sbostic 				char *p, *memchr();
23834321Sbostic 
23934321Sbostic 				if (p = memchr(t, 0, prec)) {
24034321Sbostic 					size = p - t;
24134321Sbostic 					if (size > prec)
24234321Sbostic 						size = prec;
24334427Sbostic 				} else
24434321Sbostic 					size = prec;
24534427Sbostic 			} else
24634321Sbostic 				size = strlen(t);
24734427Sbostic 			sign = '\0';
24834328Sbostic 			goto pforw;
24934226Sbostic 		case 'u':
25034318Sbostic 			ARG();
25134226Sbostic 			base = 10;
25234327Sbostic 			goto nosign;
25334226Sbostic 		case 'X':
25434226Sbostic 			digs = "0123456789ABCDEF";
25534427Sbostic 			/* FALLTHROUGH */
25634226Sbostic 		case 'x':
25734318Sbostic 			ARG();
25834314Sbostic 			base = 16;
25934326Sbostic 			/* leading 0x/X only if non-zero */
26034427Sbostic 			if (flags & ALT && _ulong != 0)
26134427Sbostic 				flags |= HEXPREFIX;
26234327Sbostic 
26334327Sbostic 			/* unsigned conversions */
26434427Sbostic nosign:			sign = '\0';
26534326Sbostic 			/*
26634330Sbostic 			 * ``... diouXx conversions ... if a precision is
26734330Sbostic 			 * specified, the 0 flag will be ignored.''
26834330Sbostic 			 *	-- ANSI X3J11
26934330Sbostic 			 */
27034427Sbostic number:			if ((dprec = prec) >= 0)
27134427Sbostic 				flags &= ~ZEROPAD;
27234427Sbostic 
27334330Sbostic 			/*
27434326Sbostic 			 * ``The result of converting a zero value with an
27534326Sbostic 			 * explicit precision of zero is no characters.''
27634326Sbostic 			 *	-- ANSI X3J11
27734326Sbostic 			 */
27834427Sbostic 			t = buf + BUF;
27934427Sbostic 			if (_ulong != 0 || prec != 0) {
28034427Sbostic 				do {
28134427Sbostic 					*--t = digs[_ulong % base];
28234427Sbostic 					_ulong /= base;
28334427Sbostic 				} while (_ulong);
28434427Sbostic 				digs = "0123456789abcdef";
28534427Sbostic 				if (flags & ALT && base == 8 && *t != '0')
28634427Sbostic 					*--t = '0'; /* octal leading 0 */
28734330Sbostic 			}
28834427Sbostic 			size = buf + BUF - t;
28934327Sbostic 
29034427Sbostic pforw:
29134427Sbostic 			/*
29234427Sbostic 			 * All reasonable formats wind up here.  At this
29334427Sbostic 			 * point, `t' points to a string which (if not
29434427Sbostic 			 * flags&LADJUST) should be padded out to `width'
29534427Sbostic 			 * places.  If flags&ZEROPAD, it should first be
29634427Sbostic 			 * prefixed by any sign or other prefix; otherwise,
29734427Sbostic 			 * it should be blank padded before the prefix is
29834427Sbostic 			 * emitted.  After any left-hand padding and
29934427Sbostic 			 * prefixing, emit zeroes required by a decimal
30034427Sbostic 			 * [diouxX] precision, then print the string proper,
30134427Sbostic 			 * then emit zeroes required by any leftover floating
30234427Sbostic 			 * precision; finally, if LADJUST, pad with blanks.
30334427Sbostic 			 */
30434327Sbostic 
30534427Sbostic 			/* compute actual size, so we know how much to pad */
30634427Sbostic 			/* this code is not terribly satisfactory */
30734427Sbostic 			/* fieldsz excludes decimal prec; realsz includes it */
30834427Sbostic 			fieldsz = size + fpprec;
30934427Sbostic 			if (sign)
31034427Sbostic 				fieldsz++;
31134427Sbostic 			if (flags & HEXPREFIX)
31234427Sbostic 				fieldsz += 2;
31334427Sbostic 			realsz = dprec > fieldsz ? dprec : fieldsz;
31434327Sbostic 
31534427Sbostic 			/* right-adjusting blank padding */
31634427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
31734427Sbostic 				for (n = realsz; n < width; n++)
31834427Sbostic 					PUTC(' ');
31934427Sbostic 			/* prefix */
32034427Sbostic 			if (sign)
32134427Sbostic 				PUTC(sign);
32234427Sbostic 			if (flags & HEXPREFIX) {
32334427Sbostic 				PUTC('0');
32434427Sbostic 				PUTC((char)*fmt);
32534327Sbostic 			}
32634427Sbostic 			/* right-adjusting zero padding */
32734427Sbostic 			if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
32834427Sbostic 				for (n = realsz; n < width; n++)
32934427Sbostic 					PUTC('0');
33034427Sbostic 			/* leading zeroes from decimal precision */
33134427Sbostic 			for (n = fieldsz; n < dprec; n++)
33234427Sbostic 				PUTC('0');
33334327Sbostic 
33434427Sbostic 			/* the string or number proper */
33534427Sbostic 			if (fp->_cnt - (n = size) >= 0 &&
33634427Sbostic 			    (fp->_flag & _IOLBF) == 0) {
33734328Sbostic 				fp->_cnt -= n;
33834427Sbostic 				bcopy(t, (char *)fp->_ptr, n);
33934328Sbostic 				fp->_ptr += n;
34034427Sbostic 			} else
34134427Sbostic 				while (--n >= 0)
34234427Sbostic 					PUTC(*t++);
34334427Sbostic 			/* trailing f.p. zeroes */
34434427Sbostic 			while (--fpprec >= 0)
34534328Sbostic 				PUTC('0');
34634427Sbostic 			/* left-adjusting padding (always blank) */
34734427Sbostic 			if (flags & LADJUST)
34834427Sbostic 				for (n = realsz; n < width; n++)
34934328Sbostic 					PUTC(' ');
35034427Sbostic 
35134427Sbostic 			/* finally, adjust cnt */
35234427Sbostic 			cnt += width > realsz ? width : realsz;
35334226Sbostic 			break;
35434427Sbostic 		case '\0':	/* "%?" prints ?, unless ? is NULL */
35534427Sbostic 			return (cnt);
35634226Sbostic 		default:
35734427Sbostic 			PUTC((char)*fmt);
35834427Sbostic 			cnt++;
35934226Sbostic 		}
36034226Sbostic 	}
36134427Sbostic 	/* NOTREACHED */
36234226Sbostic }
36334242Sbostic 
36434261Sbostic #define	EFORMAT	0x01
36534261Sbostic #define	FFORMAT	0x02
36634261Sbostic #define	GFORMAT	0x04
36734314Sbostic #define	DEFPREC	6
36834261Sbostic 
36934323Sbostic static char *
37034427Sbostic _cvt(number, prec, flags, fmtch, sign, startp, endp)
37134242Sbostic 	double number;
37234261Sbostic 	register int prec;
37334323Sbostic 	int flags;
37434323Sbostic 	u_char fmtch;
37534427Sbostic 	char *sign, *startp, *endp;
37634242Sbostic {
37734331Sbostic 	register char *p, *t;
37834261Sbostic 	register int expcnt, format;
37934248Sbostic 	double fract, integer, tmp, modf();
38034261Sbostic 	int decpt;
38134331Sbostic 	char *savep, exponent[MAXEXP];
38234242Sbostic 
38334314Sbostic 	if (prec == -1)
38434242Sbostic 		prec = DEFPREC;
38534242Sbostic 
38634314Sbostic 	if (number < 0) {
38734327Sbostic 		*sign = '-';
38834248Sbostic 		number = -number;
38934248Sbostic 	}
39034242Sbostic 
39134261Sbostic 	switch(fmtch) {
39234261Sbostic 	case 'e':
39334261Sbostic 	case 'E':
39434261Sbostic 		format = EFORMAT;
39534261Sbostic 		break;
39634261Sbostic 	case 'f':
39734261Sbostic 		format = FFORMAT;
39834261Sbostic 		break;
39934261Sbostic 	case 'g':
40034261Sbostic 	case 'G':
40134261Sbostic 		format = GFORMAT;
40234261Sbostic 		fmtch -= 2;
40334261Sbostic 	}
40434261Sbostic 
40534248Sbostic 	/*
40634248Sbostic 	 * if the alternate flag is set, or, at least one digit of precision
40734248Sbostic 	 * was requested, add a decimal point, unless it's the g/G format
40834314Sbostic 	 * in which case we require two digits of precision, as it counts
40934248Sbostic 	 * precision differently.
41034248Sbostic 	 */
41134318Sbostic 	decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0);
41234248Sbostic 
41334248Sbostic 	expcnt = 0;
41434323Sbostic 	p = endp - 1;
41534248Sbostic 	fract = modf(number, &integer);
41634248Sbostic 	if (integer) {
41734248Sbostic 		/* get integer part of number; count decimal places */
41834248Sbostic 		for (; integer; ++expcnt) {
41934248Sbostic 			tmp = modf(integer / 10, &integer);
42034331Sbostic 			*p-- = tochar((int)((tmp + .03) * 10));
42134242Sbostic 		}
42234248Sbostic 
42334248Sbostic 		/* copy, in reverse order, to start of buffer */
42434331Sbostic 		t = startp;
42534331Sbostic 		*t++ = *++p;
42634248Sbostic 
42734248Sbostic 		/*
42834248Sbostic 		 * if the format is g/G, and the resulting exponent will be
42934248Sbostic 		 * greater than the precision, use e/E format.  If e/E format,
43034248Sbostic 		 * put in a decimal point as needed, and decrement precision
43134248Sbostic 		 * count for each digit after the decimal point.
43234248Sbostic 		 */
43334248Sbostic 		if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) {
43434248Sbostic 			if (format&GFORMAT) {
43534248Sbostic 				format |= EFORMAT;
43634248Sbostic 
43734248Sbostic 				/* first digit is precision for g/G format */
43834248Sbostic 				if (prec)
43934248Sbostic 					--prec;
44034248Sbostic 			}
44134248Sbostic 			if (decpt)
44234331Sbostic 				*t++ = '.';
44334331Sbostic 			for (; ++p < endp && prec; --prec, *t++ = *p);
44434248Sbostic 
44534318Sbostic 			/* precision ran out, round */
44634248Sbostic 			if (p < endp) {
44734248Sbostic 				if (*p > '4') {
44834331Sbostic 					for (savep = t--;; *t-- = '0') {
44934331Sbostic 						if (*t == '.')
45034331Sbostic 							--t;
45134331Sbostic 						if (++*t <= '9')
45234248Sbostic 							break;
45334248Sbostic 					}
45434331Sbostic 					t = savep;
45534248Sbostic 				}
45634248Sbostic 				fract = 0;
45734248Sbostic 			}
45834242Sbostic 		}
45934248Sbostic 		/*
46034314Sbostic 		 * g/G in f format; if out of precision, replace digits with
46134314Sbostic 		 * zeroes, note, have to round first.
46234248Sbostic 		 */
46334248Sbostic 		else if (format&GFORMAT) {
46434331Sbostic 			for (; ++p < endp && prec; --prec, *t++ = *p);
46534248Sbostic 			/* precision ran out; round and then add zeroes */
46634248Sbostic 			if (p < endp) {
46734248Sbostic 				if (*p > '4') {
46834331Sbostic 					for (savep = t--; ++*t > '9';
46934331Sbostic 					    *t-- = '0');
47034331Sbostic 					t = savep;
47134248Sbostic 				}
47234248Sbostic 				do {
47334331Sbostic 					*t++ = '0';
47434248Sbostic 				} while (++p < endp);
47534248Sbostic 				fract = 0;
47634248Sbostic 			}
47734248Sbostic 			if (decpt)
47834331Sbostic 				*t++ = '.';
47934242Sbostic 		}
48034248Sbostic 		/* f format */
48134248Sbostic 		else {
48234331Sbostic 			for (; ++p < endp; *t++ = *p);
48334248Sbostic 			if (decpt)
48434331Sbostic 				*t++ = '.';
48534248Sbostic 		}
48634331Sbostic 		p = t;
48734248Sbostic 	}
48834248Sbostic 	/*
48934318Sbostic 	 * if no fraction, the number was zero, and if no precision, can't
49034318Sbostic 	 * show anything after the decimal point.
49134248Sbostic 	 */
49234248Sbostic 	else if (!fract || !prec) {
49334248Sbostic 		*startp++ = '0';
494*34584Sbostic 		if (decpt && !(format&GFORMAT)) {
49534248Sbostic 			*startp++ = '.';
496*34584Sbostic 			while (prec-- > 0)
497*34584Sbostic 				*startp++ = '0';
498*34584Sbostic 		}
49934318Sbostic 		*startp = '\0';
50034323Sbostic 		return(startp);
50134248Sbostic 	}
50234248Sbostic 	/*
50334248Sbostic 	 * if the format is g/G, and the resulting exponent will be less than
50434248Sbostic 	 * -4 use e/E format.  If e/E format, compute exponent value.
50534248Sbostic 	 */
50634248Sbostic 	else if (format&GFORMAT && fract < .0001 || format&EFORMAT) {
50734248Sbostic 		format |= EFORMAT;
50834248Sbostic 		if (fract)
50934248Sbostic 			for (p = startp; fract;) {
51034248Sbostic 				fract = modf(fract * 10, &tmp);
51134248Sbostic 				if (!tmp) {
51234248Sbostic 					--expcnt;
51334248Sbostic 					continue;
51434248Sbostic 				}
51534331Sbostic 				*p++ = tochar((int)tmp);
51634248Sbostic 				break;
51734248Sbostic 			}
51834242Sbostic 		else
51934248Sbostic 			*p++ = '0';
52034248Sbostic 
52134248Sbostic 		/* g/G format, decrement precision for first digit */
52234248Sbostic 		if (format&GFORMAT && prec)
52334248Sbostic 			--prec;
52434248Sbostic 
52534248Sbostic 		/* add decimal after first non-zero digit */
52634248Sbostic 		if (decpt)
52734248Sbostic 			*p++ = '.';
52834242Sbostic 	}
52934248Sbostic 	/*
53034248Sbostic 	 * f format or g/G printed as f format; don't worry about decimal
53134248Sbostic 	 * point, if g/G format doesn't need it, will get stripped later.
53234248Sbostic 	 */
53334242Sbostic 	else {
53434248Sbostic 		p = startp;
53534248Sbostic 		*p++ = '0';
53634248Sbostic 		*p++ = '.';
53734248Sbostic 	}
53834248Sbostic 
53934319Sbostic 	/* finish out requested precision */
54034319Sbostic 	while (fract && prec-- > 0) {
54134319Sbostic 		fract = modf(fract * 10, &tmp);
54234331Sbostic 		*p++ = tochar((int)tmp);
54334319Sbostic 	}
54434319Sbostic 	while (prec-- > 0)
54534319Sbostic 		*p++ = '0';
54634248Sbostic 
54734248Sbostic 	/*
54834248Sbostic 	 * if any fractional value left, "round" it back up to the beginning
54934248Sbostic 	 * of the number, fixing the exponent as necessary, and avoiding the
55034248Sbostic 	 * decimal point.
55134248Sbostic 	 */
55234248Sbostic 	if (fract) {
55334248Sbostic 		(void)modf(fract * 10, &tmp);
55434248Sbostic 		if (tmp > 4) {
55534248Sbostic 			for (savep = p--;; *p-- = '0') {
55634248Sbostic 				if (*p == '.')
55734248Sbostic 					--p;
55834248Sbostic 				if (p == startp) {
55934248Sbostic 					*p = '1';
56034248Sbostic 					++expcnt;
56134248Sbostic 					break;
56234248Sbostic 				}
56334248Sbostic 				if (++*p <= '9')
56434248Sbostic 					break;
56534242Sbostic 			}
56634248Sbostic 			p = savep;
56734242Sbostic 		}
56834248Sbostic 	}
56934248Sbostic 
57034248Sbostic 	/*
57134248Sbostic 	 * if a g/G format and not alternate flag, lose trailing zeroes,
57234248Sbostic 	 * if e/E or g/G format, and last char is decimal point, lose it.
57334248Sbostic 	 */
57434318Sbostic 	if (!(flags&ALT)) {
57534248Sbostic 		if (format&GFORMAT)
57634248Sbostic 			for (; p[-1] == '0'; --p);
57734248Sbostic 		if (format&(GFORMAT|EFORMAT) && p[-1] == '.')
57834248Sbostic 			--p;
57934248Sbostic 	}
58034248Sbostic 
58134248Sbostic 	/* if an e/E format, add exponent */
58234248Sbostic 	if (format&EFORMAT) {
58334248Sbostic 		*p++ = fmtch;
58434248Sbostic 		if (--expcnt < 0) {
58534248Sbostic 			expcnt = -expcnt;
58634248Sbostic 			*p++ = '-';
58734242Sbostic 		}
58834248Sbostic 		else
58934248Sbostic 			*p++ = '+';
59034331Sbostic 		t = exponent + MAXEXP;
59134331Sbostic 		if (expcnt > 9) {
59234331Sbostic 			do {
59334331Sbostic 				*--t = tochar(expcnt % 10);
59434331Sbostic 			} while ((expcnt /= 10) > 9);
59534331Sbostic 			*--t = tochar(expcnt);
59634331Sbostic 			for (; t < exponent + MAXEXP; *p++ = *t++);
59734331Sbostic 		}
59834331Sbostic 		else {
59934331Sbostic 			*p++ = '0';
60034331Sbostic 			*p++ = tochar(expcnt);
60134331Sbostic 		}
60234242Sbostic 	}
60334323Sbostic 	return(p);
60434242Sbostic }
605