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