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