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