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