xref: /csrg-svn/lib/libc/stdio/vfprintf.c (revision 46611)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Chris Torek.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 static char sccsid[] = "@(#)vfprintf.c	5.45 (Berkeley) 02/24/91";
13 #endif /* LIBC_SCCS and not lint */
14 
15 /*
16  * Actual printf innards.
17  *
18  * This code is large and complicated...
19  */
20 
21 #include <sys/types.h>
22 #include <math.h>
23 #include <stdio.h>
24 #include <string.h>
25 #if __STDC__
26 #include <stdarg.h>
27 #else
28 #include <varargs.h>
29 #endif
30 #include "local.h"
31 #include "fvwrite.h"
32 
33 /*
34  * Define FLOATING_POINT to get floating point.
35  * Define CSH to get a csh-specific version (grr).
36  */
37 #ifndef CSH
38 #define	FLOATING_POINT
39 #endif
40 
41 /* end of configuration stuff */
42 
43 
44 #ifdef CSH
45 /*
46  * C shell hacks.  Ick, gag.
47  */
48 #undef BUFSIZ
49 #include "sh.h"
50 
51 #if __STDC__
52 int
53 printf(const char *fmt, ...) {
54 	FILE f;
55 
56 	f._flags = __SWR;
57 	return (vfprintf(&f, fmt, (va_list)(&fmt + 1)));
58 }
59 #else
60 int
61 printf(fmt, args)
62 	char *fmt;
63 {
64 	FILE f;
65 
66 	f._flags = __SWR;
67 	f._write = NULL;
68 	return (vfprintf(&f, fmt, &args));
69 }
70 #endif
71 
72 int
73 __sprint(fp, uio)
74 	FILE *fp;
75 	register struct __suio *uio;
76 {
77 	register char *p;
78 	register int n, ch, iovcnt;
79 	register struct __siov *iov;
80 
81 	/* must allow sprintf to work, might as well allow others too */
82 	if (fp->_write || fp->_flags & __SSTR) {
83 		if (uio->uio_resid == 0) {
84 			uio->uio_iovcnt = 0;
85 			return (0);
86 		}
87 		n = __sfvwrite(fp, uio);
88 		uio->uio_resid = 0;
89 		uio->uio_iovcnt = 0;
90 		return (n);
91 	}
92 	iov = uio->uio_iov;
93 	for (iovcnt = uio->uio_iovcnt; --iovcnt >= 0; iov++) {
94 		for (p = iov->iov_base, n = iov->iov_len; --n >= 0;) {
95 #ifdef CSHPUTCHAR
96 			ch = *p++;
97 			CSHPUTCHAR;	/* this horrid macro uses `ch' */
98 #else
99 #undef putchar
100 			putchar(*p++);
101 #endif
102 		}
103 	}
104 	uio->uio_resid = 0;
105 	uio->uio_iovcnt = 0;
106 	return (0);
107 }
108 
109 #else /* CSH */
110 
111 /*
112  * Flush out all the vectors defined by the given uio,
113  * then reset it so that it can be reused.
114  */
115 static int
116 __sprint(fp, uio)
117 	FILE *fp;
118 	register struct __suio *uio;
119 {
120 	register int err;
121 
122 	if (uio->uio_resid == 0) {
123 		uio->uio_iovcnt = 0;
124 		return (0);
125 	}
126 	err = __sfvwrite(fp, uio);
127 	uio->uio_resid = 0;
128 	uio->uio_iovcnt = 0;
129 	return (err);
130 }
131 
132 /*
133  * Helper function for `fprintf to unbuffered unix file': creates a
134  * temporary buffer.  We only work on write-only files; this avoids
135  * worries about ungetc buffers and so forth.
136  */
137 static int
138 __sbprintf(fp, fmt, ap)
139 	register FILE *fp;
140 	const char *fmt;
141 	va_list ap;
142 {
143 	int ret;
144 	FILE fake;
145 	unsigned char buf[BUFSIZ];
146 
147 	/* copy the important variables */
148 	fake._flags = fp->_flags & ~__SNBF;
149 	fake._file = fp->_file;
150 	fake._cookie = fp->_cookie;
151 	fake._write = fp->_write;
152 
153 	/* set up the buffer */
154 	fake._bf._base = fake._p = buf;
155 	fake._bf._size = fake._w = sizeof(buf);
156 	fake._lbfsize = 0;	/* not actually used, but Just In Case */
157 
158 	/* do the work, then copy any error status */
159 	ret = vfprintf(&fake, fmt, ap);
160 	if (ret >= 0 && fflush(&fake))
161 		ret = EOF;
162 	if (fake._flags & __SERR)
163 		fp->_flags |= __SERR;
164 	return (ret);
165 }
166 
167 #endif /* CSH */
168 
169 
170 #ifdef FLOATING_POINT
171 #include "floatio.h"
172 
173 #define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */
174 #define	DEFPREC		6
175 
176 static int cvt();
177 #if defined(hp300) || defined(sparc)
178 static char *isspecial();
179 #endif
180 
181 #else /* no FLOATING_POINT */
182 
183 #define	BUF		40
184 
185 #endif /* FLOATING_POINT */
186 
187 
188 /*
189  * Macros for converting digits to letters and vice versa
190  */
191 #define	to_digit(c)	((c) - '0')
192 #define is_digit(c)	((unsigned)to_digit(c) <= 9)
193 #define	to_char(n)	((n) + '0')
194 
195 /*
196  * Flags used during conversion.
197  */
198 #define	LONGINT		0x01		/* long integer */
199 #define	LONGDBL		0x02		/* long double; unimplemented */
200 #define	SHORTINT	0x04		/* short integer */
201 #define	ALT		0x08		/* alternate form */
202 #define	LADJUST		0x10		/* left adjustment */
203 #define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
204 #define	HEXPREFIX	0x40		/* add 0x or 0X prefix */
205 
206 int
207 vfprintf(fp, fmt0, ap)
208 	FILE *fp;
209 	const char *fmt0;
210 #if tahoe
211  register /* technically illegal, since we do not know what type va_list is */
212 #endif
213 	va_list ap;
214 {
215 	register char *fmt;	/* format string */
216 	register int ch;	/* character from fmt */
217 	register int n;		/* handy integer (short term usage) */
218 	register char *cp;	/* handy char pointer (short term usage) */
219 	register struct __siov *iovp;/* for PRINT macro */
220 	register int flags;	/* flags as above */
221 	int ret;		/* return value accumulator */
222 	int width;		/* width from format (%8d), or 0 */
223 	int prec;		/* precision from format (%.3d), or -1 */
224 	char sign;		/* sign prefix (' ', '+', '-', or \0) */
225 #ifdef FLOATING_POINT
226 	char softsign;		/* temporary negative sign for floats */
227 	double _double;		/* double precision arguments %[eEfgG] */
228 	int fpprec;		/* `extra' floating precision in [eEfgG] */
229 #endif
230 	u_long _ulong;		/* integer arguments %[diouxX] */
231 	enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
232 	int dprec;		/* a copy of prec if [diouxX], 0 otherwise */
233 	int fieldsz;		/* field size expanded by sign, etc */
234 	int realsz;		/* field size expanded by dprec */
235 	int size;		/* size of converted field or string */
236 	char *xdigs;		/* digits for [xX] conversion */
237 #define NIOV 8
238 	struct __suio uio;	/* output information: summary */
239 	struct __siov iov[NIOV];/* ... and individual io vectors */
240 	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */
241 	char ox[2];		/* space for 0x hex-prefix */
242 
243 	/*
244 	 * Choose PADSIZE to trade efficiency vs size.  If larger
245 	 * printf fields occur frequently, increase PADSIZE (and make
246 	 * the initialisers below longer).
247 	 */
248 #define	PADSIZE	16		/* pad chunk size */
249 	static char blanks[PADSIZE] =
250 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
251 	static char zeroes[PADSIZE] =
252 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
253 
254 	/*
255 	 * BEWARE, these `goto error' on error, and PAD uses `n'.
256 	 */
257 #define	PRINT(ptr, len) { \
258 	iovp->iov_base = (ptr); \
259 	iovp->iov_len = (len); \
260 	uio.uio_resid += (len); \
261 	iovp++; \
262 	if (++uio.uio_iovcnt >= NIOV) { \
263 		if (__sprint(fp, &uio)) \
264 			goto error; \
265 		iovp = iov; \
266 	} \
267 }
268 #define	PAD(howmany, with) { \
269 	if ((n = (howmany)) > 0) { \
270 		while (n > PADSIZE) { \
271 			PRINT(with, PADSIZE); \
272 			n -= PADSIZE; \
273 		} \
274 		PRINT(with, n); \
275 	} \
276 }
277 #define	FLUSH() { \
278 	if (uio.uio_resid && __sprint(fp, &uio)) \
279 		goto error; \
280 	uio.uio_iovcnt = 0; \
281 	iovp = iov; \
282 }
283 
284 	/*
285 	 * To extend shorts properly, we need both signed and unsigned
286 	 * argument extraction methods.
287 	 */
288 #define	SARG() \
289 	(flags&LONGINT ? va_arg(ap, long) : \
290 	    flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
291 	    (long)va_arg(ap, int))
292 #define	UARG() \
293 	(flags&LONGINT ? va_arg(ap, u_long) : \
294 	    flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \
295 	    (u_long)va_arg(ap, u_int))
296 
297 #ifndef CSH
298 	/* sorry, fprintf(read_only_file, "") returns EOF, not 0 */
299 	if (cantwrite(fp))
300 		return (EOF);
301 
302 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
303 	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
304 	    fp->_file >= 0)
305 		return (__sbprintf(fp, fmt0, ap));
306 #endif /* CSH */
307 
308 	fmt = (char *)fmt0;
309 	uio.uio_iov = iovp = iov;
310 	uio.uio_resid = 0;
311 	uio.uio_iovcnt = 0;
312 	ret = 0;
313 
314 	/*
315 	 * Scan the format for conversions (`%' character).
316 	 */
317 	for (;;) {
318 		for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
319 			/* void */;
320 		if ((n = fmt - cp) != 0) {
321 			PRINT(cp, n);
322 			ret += n;
323 		}
324 		if (ch == '\0')
325 			goto done;
326 		fmt++;		/* skip over '%' */
327 
328 		flags = 0;
329 		dprec = 0;
330 #ifdef FLOATING_POINT
331 		fpprec = 0;
332 #endif
333 		width = 0;
334 		prec = -1;
335 		sign = '\0';
336 
337 rflag:		ch = *fmt++;
338 reswitch:	switch (ch) {
339 		case ' ':
340 			/*
341 			 * ``If the space and + flags both appear, the space
342 			 * flag will be ignored.''
343 			 *	-- ANSI X3J11
344 			 */
345 			if (!sign)
346 				sign = ' ';
347 			goto rflag;
348 		case '#':
349 			flags |= ALT;
350 			goto rflag;
351 		case '*':
352 			/*
353 			 * ``A negative field width argument is taken as a
354 			 * - flag followed by a positive field width.''
355 			 *	-- ANSI X3J11
356 			 * They don't exclude field widths read from args.
357 			 */
358 			if ((width = va_arg(ap, int)) >= 0)
359 				goto rflag;
360 			width = -width;
361 			/* FALLTHROUGH */
362 		case '-':
363 			flags |= LADJUST;
364 			goto rflag;
365 		case '+':
366 			sign = '+';
367 			goto rflag;
368 		case '.':
369 			if ((ch = *fmt++) == '*') {
370 				n = va_arg(ap, int);
371 				prec = n < 0 ? -1 : n;
372 				goto rflag;
373 			}
374 			n = 0;
375 			while (is_digit(ch)) {
376 				n = 10 * n + to_digit(ch);
377 				ch = *fmt++;
378 			}
379 			prec = n < 0 ? -1 : n;
380 			goto reswitch;
381 		case '0':
382 			/*
383 			 * ``Note that 0 is taken as a flag, not as the
384 			 * beginning of a field width.''
385 			 *	-- ANSI X3J11
386 			 */
387 			flags |= ZEROPAD;
388 			goto rflag;
389 		case '1': case '2': case '3': case '4':
390 		case '5': case '6': case '7': case '8': case '9':
391 			n = 0;
392 			do {
393 				n = 10 * n + to_digit(ch);
394 				ch = *fmt++;
395 			} while (is_digit(ch));
396 			width = n;
397 			goto reswitch;
398 #ifdef FLOATING_POINT
399 		case 'L':
400 			flags |= LONGDBL;
401 			goto rflag;
402 #endif
403 		case 'h':
404 			flags |= SHORTINT;
405 			goto rflag;
406 		case 'l':
407 			flags |= LONGINT;
408 			goto rflag;
409 		case 'c':
410 			*(cp = buf) = va_arg(ap, int);
411 			size = 1;
412 			sign = '\0';
413 			break;
414 		case 'D':
415 			flags |= LONGINT;
416 			/*FALLTHROUGH*/
417 		case 'd':
418 		case 'i':
419 			_ulong = SARG();
420 			if ((long)_ulong < 0) {
421 				_ulong = -_ulong;
422 				sign = '-';
423 			}
424 			base = DEC;
425 			goto number;
426 #ifdef FLOATING_POINT
427 		case 'e':
428 		case 'E':
429 		case 'f':
430 		case 'g':
431 		case 'G':
432 			_double = va_arg(ap, double);
433 #if defined(hp300) || defined(sparc)
434 			/* do this before tricky precision changes */
435 			if ((cp = isspecial(_double, &sign)) != NULL) {
436 				size = strlen(cp);
437 				break;
438 			}
439 #endif
440 			/*
441 			 * don't do unrealistic precision; just pad it with
442 			 * zeroes later, so buffer size stays rational.
443 			 */
444 			if (prec > MAXFRACT) {
445 				if (ch != 'g' && ch != 'G' || (flags&ALT))
446 					fpprec = prec - MAXFRACT;
447 				prec = MAXFRACT;
448 			} else if (prec == -1)
449 				prec = DEFPREC;
450 			/*
451 			 * cvt may have to round up before the "start" of
452 			 * its buffer, i.e. ``intf("%.2f", (double)9.999);'';
453 			 * if the first character is still NUL, it did.
454 			 * softsign avoids negative 0 if _double < 0 but
455 			 * no significant digits will be shown.
456 			 */
457 			cp = buf;
458 			*cp = '\0';
459 			size = cvt(_double, prec, flags, &softsign, ch,
460 			    cp, buf + sizeof(buf));
461 			if (softsign)
462 				sign = '-';
463 			if (*cp == '\0')
464 				cp++;
465 			break;
466 #endif /* FLOATING_POINT */
467 		case 'n':
468 			if (flags & LONGINT)
469 				*va_arg(ap, long *) = ret;
470 			else if (flags & SHORTINT)
471 				*va_arg(ap, short *) = ret;
472 			else
473 				*va_arg(ap, int *) = ret;
474 			continue;	/* no output */
475 		case 'O':
476 			flags |= LONGINT;
477 			/*FALLTHROUGH*/
478 		case 'o':
479 			_ulong = UARG();
480 			base = OCT;
481 			goto nosign;
482 		case 'p':
483 			/*
484 			 * ``The argument shall be a pointer to void.  The
485 			 * value of the pointer is converted to a sequence
486 			 * of printable characters, in an implementation-
487 			 * defined manner.''
488 			 *	-- ANSI X3J11
489 			 */
490 			/* NOSTRICT */
491 			_ulong = (u_long)va_arg(ap, void *);
492 			base = HEX;
493 			xdigs = "0123456789abcdef";
494 			flags |= HEXPREFIX;
495 			ch = 'x';
496 			goto nosign;
497 		case 's':
498 			if ((cp = va_arg(ap, char *)) == NULL)
499 				cp = "(null)";
500 			if (prec >= 0) {
501 				/*
502 				 * can't use strlen; can only look for the
503 				 * NUL in the first `prec' characters, and
504 				 * strlen() will go further.
505 				 */
506 				char *p = memchr(cp, 0, prec);
507 
508 				if (p != NULL) {
509 					size = p - cp;
510 					if (size > prec)
511 						size = prec;
512 				} else
513 					size = prec;
514 			} else
515 				size = strlen(cp);
516 			sign = '\0';
517 			break;
518 		case 'U':
519 			flags |= LONGINT;
520 			/*FALLTHROUGH*/
521 		case 'u':
522 			_ulong = UARG();
523 			base = DEC;
524 			goto nosign;
525 		case 'X':
526 			xdigs = "0123456789ABCDEF";
527 			goto hex;
528 		case 'x':
529 			xdigs = "0123456789abcdef";
530 hex:			_ulong = UARG();
531 			base = HEX;
532 			/* leading 0x/X only if non-zero */
533 			if (flags & ALT && _ulong != 0)
534 				flags |= HEXPREFIX;
535 
536 			/* unsigned conversions */
537 nosign:			sign = '\0';
538 			/*
539 			 * ``... diouXx conversions ... if a precision is
540 			 * specified, the 0 flag will be ignored.''
541 			 *	-- ANSI X3J11
542 			 */
543 number:			if ((dprec = prec) >= 0)
544 				flags &= ~ZEROPAD;
545 
546 			/*
547 			 * ``The result of converting a zero value with an
548 			 * explicit precision of zero is no characters.''
549 			 *	-- ANSI X3J11
550 			 */
551 			cp = buf + BUF;
552 			if (_ulong != 0 || prec != 0) {
553 				/*
554 				 * unsigned mod is hard, and unsigned mod
555 				 * by a constant is easier than that by
556 				 * a variable; hence this switch.
557 				 */
558 				switch (base) {
559 				case OCT:
560 					do {
561 						*--cp = to_char(_ulong & 7);
562 						_ulong >>= 3;
563 					} while (_ulong);
564 					/* handle octal leading 0 */
565 					if (flags & ALT && *cp != '0')
566 						*--cp = '0';
567 					break;
568 
569 				case DEC:
570 					/* many numbers are 1 digit */
571 					while (_ulong >= 10) {
572 						*--cp = to_char(_ulong % 10);
573 						_ulong /= 10;
574 					}
575 					*--cp = to_char(_ulong);
576 					break;
577 
578 				case HEX:
579 					do {
580 						*--cp = xdigs[_ulong & 15];
581 						_ulong >>= 4;
582 					} while (_ulong);
583 					break;
584 
585 				default:
586 					cp = "bug in vfprintf: bad base";
587 					size = strlen(cp);
588 					goto skipsize;
589 				}
590 			}
591 			size = buf + BUF - cp;
592 		skipsize:
593 			break;
594 		default:	/* "%?" prints ?, unless ? is NUL */
595 			if (ch == '\0')
596 				goto done;
597 			/* pretend it was %c with argument ch */
598 			cp = buf;
599 			*cp = ch;
600 			size = 1;
601 			sign = '\0';
602 			break;
603 		}
604 
605 		/*
606 		 * All reasonable formats wind up here.  At this point,
607 		 * `cp' points to a string which (if not flags&LADJUST)
608 		 * should be padded out to `width' places.  If
609 		 * flags&ZEROPAD, it should first be prefixed by any
610 		 * sign or other prefix; otherwise, it should be blank
611 		 * padded before the prefix is emitted.  After any
612 		 * left-hand padding and prefixing, emit zeroes
613 		 * required by a decimal [diouxX] precision, then print
614 		 * the string proper, then emit zeroes required by any
615 		 * leftover floating precision; finally, if LADJUST,
616 		 * pad with blanks.
617 		 */
618 
619 		/*
620 		 * compute actual size, so we know how much to pad.
621 		 * fieldsz excludes decimal prec; realsz includes it
622 		 */
623 #ifdef FLOATING_POINT
624 		fieldsz = size + fpprec;
625 #else
626 		fieldsz = size;
627 #endif
628 		if (sign)
629 			fieldsz++;
630 		else if (flags & HEXPREFIX)
631 			fieldsz += 2;
632 		realsz = dprec > fieldsz ? dprec : fieldsz;
633 
634 		/* right-adjusting blank padding */
635 		if ((flags & (LADJUST|ZEROPAD)) == 0)
636 			PAD(width - realsz, blanks);
637 
638 		/* prefix */
639 		if (sign) {
640 			PRINT(&sign, 1);
641 		} else if (flags & HEXPREFIX) {
642 			ox[0] = '0';
643 			ox[1] = ch;
644 			PRINT(ox, 2);
645 		}
646 
647 		/* right-adjusting zero padding */
648 		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
649 			PAD(width - realsz, zeroes);
650 
651 		/* leading zeroes from decimal precision */
652 		PAD(dprec - fieldsz, zeroes);
653 
654 		/* the string or number proper */
655 		PRINT(cp, size);
656 
657 #ifdef FLOATING_POINT
658 		/* trailing f.p. zeroes */
659 		PAD(fpprec, zeroes);
660 #endif
661 
662 		/* left-adjusting padding (always blank) */
663 		if (flags & LADJUST)
664 			PAD(width - realsz, blanks);
665 
666 		/* finally, adjust ret */
667 		ret += width > realsz ? width : realsz;
668 
669 		FLUSH();	/* copy out the I/O vectors */
670 	}
671 done:
672 	FLUSH();
673 error:
674 	return (__sferror(fp) ? EOF : ret);
675 	/* NOTREACHED */
676 }
677 
678 #ifdef FLOATING_POINT
679 #include <math.h>
680 
681 static char *exponent();
682 static char *round();
683 
684 #if defined(hp300) || defined(sparc)
685 /*
686  * Check for special IEEE format values (NaN, Inf).
687  */
688 static char *
689 isspecial(d, signp)
690 	double d;
691 	char *signp;
692 {
693 	register struct IEEEdp {
694 		unsigned sign:1;
695 		unsigned exp:11;
696 		unsigned manh:20;
697 		unsigned manl:32;
698 	} *ip = (struct IEEEdp *)&d;
699 
700 	if (ip->exp != 0x7ff)
701 		return (NULL);
702 	if (ip->sign)
703 		*signp = '-';
704 	return (ip->manh || ip->manl ? "NaN" : "Inf");
705 }
706 #endif /* hp300 or sparc */
707 
708 static int
709 cvt(number, prec, flags, signp, fmtch, startp, endp)
710 	double number;
711 	register int prec;
712 	int flags;
713 	char *signp;
714 	int fmtch;
715 	char *startp, *endp;
716 {
717 	register char *p, *t;
718 	register double fract;
719 	int dotrim, expcnt, gformat;
720 	double integer, tmp;
721 
722 	dotrim = expcnt = gformat = 0;
723 	if (number < 0) {
724 		number = -number;
725 		*signp = '-';
726 	} else
727 		*signp = 0;
728 
729 	fract = modf(number, &integer);
730 
731 	/* get an extra slot for rounding. */
732 	t = ++startp;
733 
734 	/*
735 	 * get integer portion of number; put into the end of the buffer; the
736 	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
737 	 */
738 	for (p = endp - 1; integer; ++expcnt) {
739 		tmp = modf(integer / 10, &integer);
740 		*p-- = to_char((int)((tmp + .01) * 10));
741 	}
742 	switch (fmtch) {
743 	case 'f':
744 		/* reverse integer into beginning of buffer */
745 		if (expcnt)
746 			for (; ++p < endp; *t++ = *p);
747 		else
748 			*t++ = '0';
749 		/*
750 		 * if precision required or alternate flag set, add in a
751 		 * decimal point.
752 		 */
753 		if (prec || flags&ALT)
754 			*t++ = '.';
755 		/* if requires more precision and some fraction left */
756 		if (fract) {
757 			if (prec)
758 				do {
759 					fract = modf(fract * 10, &tmp);
760 					*t++ = to_char((int)tmp);
761 				} while (--prec && fract);
762 			if (fract)
763 				startp = round(fract, (int *)NULL, startp,
764 				    t - 1, (char)0, signp);
765 		}
766 		for (; prec--; *t++ = '0');
767 		break;
768 	case 'e':
769 	case 'E':
770 eformat:	if (expcnt) {
771 			*t++ = *++p;
772 			if (prec || flags&ALT)
773 				*t++ = '.';
774 			/* if requires more precision and some integer left */
775 			for (; prec && ++p < endp; --prec)
776 				*t++ = *p;
777 			/*
778 			 * if done precision and more of the integer component,
779 			 * round using it; adjust fract so we don't re-round
780 			 * later.
781 			 */
782 			if (!prec && ++p < endp) {
783 				fract = 0;
784 				startp = round((double)0, &expcnt, startp,
785 				    t - 1, *p, signp);
786 			}
787 			/* adjust expcnt for digit in front of decimal */
788 			--expcnt;
789 		}
790 		/* until first fractional digit, decrement exponent */
791 		else if (fract) {
792 			/* adjust expcnt for digit in front of decimal */
793 			for (expcnt = -1;; --expcnt) {
794 				fract = modf(fract * 10, &tmp);
795 				if (tmp)
796 					break;
797 			}
798 			*t++ = to_char((int)tmp);
799 			if (prec || flags&ALT)
800 				*t++ = '.';
801 		}
802 		else {
803 			*t++ = '0';
804 			if (prec || flags&ALT)
805 				*t++ = '.';
806 		}
807 		/* if requires more precision and some fraction left */
808 		if (fract) {
809 			if (prec)
810 				do {
811 					fract = modf(fract * 10, &tmp);
812 					*t++ = to_char((int)tmp);
813 				} while (--prec && fract);
814 			if (fract)
815 				startp = round(fract, &expcnt, startp,
816 				    t - 1, (char)0, signp);
817 		}
818 		/* if requires more precision */
819 		for (; prec--; *t++ = '0');
820 
821 		/* unless alternate flag, trim any g/G format trailing 0's */
822 		if (gformat && !(flags&ALT)) {
823 			while (t > startp && *--t == '0');
824 			if (*t == '.')
825 				--t;
826 			++t;
827 		}
828 		t = exponent(t, expcnt, fmtch);
829 		break;
830 	case 'g':
831 	case 'G':
832 		/* a precision of 0 is treated as a precision of 1. */
833 		if (!prec)
834 			++prec;
835 		/*
836 		 * ``The style used depends on the value converted; style e
837 		 * will be used only if the exponent resulting from the
838 		 * conversion is less than -4 or greater than the precision.''
839 		 *	-- ANSI X3J11
840 		 */
841 		if (expcnt > prec || !expcnt && fract && fract < .0001) {
842 			/*
843 			 * g/G format counts "significant digits, not digits of
844 			 * precision; for the e/E format, this just causes an
845 			 * off-by-one problem, i.e. g/G considers the digit
846 			 * before the decimal point significant and e/E doesn't
847 			 * count it as precision.
848 			 */
849 			--prec;
850 			fmtch -= 2;		/* G->E, g->e */
851 			gformat = 1;
852 			goto eformat;
853 		}
854 		/*
855 		 * reverse integer into beginning of buffer,
856 		 * note, decrement precision
857 		 */
858 		if (expcnt)
859 			for (; ++p < endp; *t++ = *p, --prec);
860 		else
861 			*t++ = '0';
862 		/*
863 		 * if precision required or alternate flag set, add in a
864 		 * decimal point.  If no digits yet, add in leading 0.
865 		 */
866 		if (prec || flags&ALT) {
867 			dotrim = 1;
868 			*t++ = '.';
869 		}
870 		else
871 			dotrim = 0;
872 		/* if requires more precision and some fraction left */
873 		if (fract) {
874 			if (prec) {
875 				do {
876 					fract = modf(fract * 10, &tmp);
877 					*t++ = to_char((int)tmp);
878 				} while(!tmp);
879 				while (--prec && fract) {
880 					fract = modf(fract * 10, &tmp);
881 					*t++ = to_char((int)tmp);
882 				}
883 			}
884 			if (fract)
885 				startp = round(fract, (int *)NULL, startp,
886 				    t - 1, (char)0, signp);
887 		}
888 		/* alternate format, adds 0's for precision, else trim 0's */
889 		if (flags&ALT)
890 			for (; prec--; *t++ = '0');
891 		else if (dotrim) {
892 			while (t > startp && *--t == '0');
893 			if (*t != '.')
894 				++t;
895 		}
896 	}
897 	return (t - startp);
898 }
899 
900 static char *
901 round(fract, exp, start, end, ch, signp)
902 	double fract;
903 	int *exp;
904 	register char *start, *end;
905 	char ch, *signp;
906 {
907 	double tmp;
908 
909 	if (fract)
910 		(void)modf(fract * 10, &tmp);
911 	else
912 		tmp = to_digit(ch);
913 	if (tmp > 4)
914 		for (;; --end) {
915 			if (*end == '.')
916 				--end;
917 			if (++*end <= '9')
918 				break;
919 			*end = '0';
920 			if (end == start) {
921 				if (exp) {	/* e/E; increment exponent */
922 					*end = '1';
923 					++*exp;
924 				}
925 				else {		/* f; add extra digit */
926 				*--end = '1';
927 				--start;
928 				}
929 				break;
930 			}
931 		}
932 	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
933 	else if (*signp == '-')
934 		for (;; --end) {
935 			if (*end == '.')
936 				--end;
937 			if (*end != '0')
938 				break;
939 			if (end == start)
940 				*signp = 0;
941 		}
942 	return (start);
943 }
944 
945 static char *
946 exponent(p, exp, fmtch)
947 	register char *p;
948 	register int exp;
949 	int fmtch;
950 {
951 	register char *t;
952 	char expbuf[MAXEXP];
953 
954 	*p++ = fmtch;
955 	if (exp < 0) {
956 		exp = -exp;
957 		*p++ = '-';
958 	}
959 	else
960 		*p++ = '+';
961 	t = expbuf + MAXEXP;
962 	if (exp > 9) {
963 		do {
964 			*--t = to_char(exp % 10);
965 		} while ((exp /= 10) > 9);
966 		*--t = to_char(exp);
967 		for (; t < expbuf + MAXEXP; *p++ = *t++);
968 	}
969 	else {
970 		*p++ = '0';
971 		*p++ = to_char(exp);
972 	}
973 	return (p);
974 }
975 #endif /* FLOATING_POINT */
976