vfprintf.c revision 1.1 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