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