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