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