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