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