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