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