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