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