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