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