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