1 /* 2 * Copyright (c) 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #if defined(LIBC_SCCS) && !defined(lint) 14 static char sccsid[] = "@(#)vfprintf.c 5.33 (Berkeley) 06/07/88"; 15 #endif /* LIBC_SCCS and not lint */ 16 17 #include <sys/types.h> 18 #include <varargs.h> 19 #include <stdio.h> 20 #include <ctype.h> 21 22 /* 11-bit exponent (VAX G floating point) is 308 decimal digits */ 23 #define MAXEXP 308 24 /* 128 bit fraction takes up 39 decimal digits; max reasonable precision */ 25 #define MAXFRACT 39 26 27 #define DEFPREC 6 28 29 #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 30 31 #define PUTC(ch) (void) putc(ch, fp) 32 33 #define ARG() \ 34 _ulong = flags&LONGINT ? va_arg(argp, long) : \ 35 flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 36 37 #define todigit(c) ((c) - '0') 38 #define tochar(n) ((n) + '0') 39 40 /* have to deal with the negative buffer count kludge */ 41 #define NEGATIVE_COUNT_KLUDGE 42 43 #define LONGINT 0x01 /* long integer */ 44 #define LONGDBL 0x02 /* long double; unimplemented */ 45 #define SHORTINT 0x04 /* short integer */ 46 #define ALT 0x08 /* alternate form */ 47 #define LADJUST 0x10 /* left adjustment */ 48 #define ZEROPAD 0x20 /* zero (as opposed to blank) pad */ 49 #define HEXPREFIX 0x40 /* add 0x or 0X prefix */ 50 51 _doprnt(fmt0, argp, fp) 52 u_char *fmt0; 53 va_list argp; 54 register FILE *fp; 55 { 56 register u_char *fmt; /* format string */ 57 register int ch; /* character from fmt */ 58 register int cnt; /* return value accumulator */ 59 register int n; /* random handy integer */ 60 register char *t; /* buffer pointer */ 61 double _double; /* double precision arguments %[eEfgG] */ 62 u_long _ulong; /* integer arguments %[diouxX] */ 63 int base; /* base for [diouxX] conversion */ 64 int dprec; /* decimal precision in [diouxX] */ 65 int fieldsz; /* field size expanded by sign, etc */ 66 int flags; /* flags as above */ 67 int fpprec; /* `extra' floating precision in [eEfgG] */ 68 int prec; /* precision from format (%.3d), or -1 */ 69 int realsz; /* field size expanded by decimal precision */ 70 int size; /* size of converted field or string */ 71 int width; /* width from format (%8d), or 0 */ 72 char sign; /* sign prefix (' ', '+', '-', or \0) */ 73 char softsign; /* temporary negative sign for floats */ 74 char *digs; /* digits for [diouxX] conversion */ 75 char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 76 77 if (fp->_flag & _IORW) { 78 fp->_flag |= _IOWRT; 79 fp->_flag &= ~(_IOEOF|_IOREAD); 80 } 81 if ((fp->_flag & _IOWRT) == 0) 82 return (EOF); 83 84 fmt = fmt0; 85 digs = "0123456789abcdef"; 86 for (cnt = 0;; ++fmt) { 87 n = fp->_cnt; 88 for (t = (char *)fp->_ptr; (ch = *fmt) && ch != '%'; 89 ++cnt, ++fmt) 90 if (--n < 0 91 #ifdef NEGATIVE_COUNT_KLUDGE 92 && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 93 #endif 94 || ch == '\n' && fp->_flag & _IOLBF) { 95 fp->_cnt = n; 96 fp->_ptr = t; 97 (void) _flsbuf((u_char)ch, fp); 98 n = fp->_cnt; 99 t = (char *)fp->_ptr; 100 } else 101 *t++ = ch; 102 fp->_cnt = n; 103 fp->_ptr = t; 104 if (!ch) 105 return (cnt); 106 107 flags = dprec = fpprec = width = 0; 108 prec = -1; 109 sign = '\0'; 110 111 rflag: switch (*++fmt) { 112 case ' ': 113 /* 114 * ``If the space and + flags both appear, the space 115 * flag will be ignored.'' 116 * -- ANSI X3J11 117 */ 118 if (!sign) 119 sign = ' '; 120 goto rflag; 121 case '#': 122 flags |= ALT; 123 goto rflag; 124 case '*': 125 /* 126 * ``A negative field width argument is taken as a 127 * - flag followed by a positive field width.'' 128 * -- ANSI X3J11 129 * They don't exclude field widths read from args. 130 */ 131 if ((width = va_arg(argp, int)) >= 0) 132 goto rflag; 133 width = -width; 134 /* FALLTHROUGH */ 135 case '-': 136 flags |= LADJUST; 137 goto rflag; 138 case '+': 139 sign = '+'; 140 goto rflag; 141 case '.': 142 if (*++fmt == '*') 143 n = va_arg(argp, int); 144 else { 145 n = 0; 146 while (isascii(*fmt) && isdigit(*fmt)) 147 n = 10 * n + todigit(*fmt++); 148 --fmt; 149 } 150 prec = n < 0 ? -1 : n; 151 goto rflag; 152 case '0': 153 /* 154 * ``Note that 0 is taken as a flag, not as the 155 * beginning of a field width.'' 156 * -- ANSI X3J11 157 */ 158 flags |= ZEROPAD; 159 goto rflag; 160 case '1': case '2': case '3': case '4': 161 case '5': case '6': case '7': case '8': case '9': 162 n = 0; 163 do { 164 n = 10 * n + todigit(*fmt); 165 } while (isascii(*++fmt) && isdigit(*fmt)); 166 width = n; 167 --fmt; 168 goto rflag; 169 case 'L': 170 flags |= LONGDBL; 171 goto rflag; 172 case 'h': 173 flags |= SHORTINT; 174 goto rflag; 175 case 'l': 176 flags |= LONGINT; 177 goto rflag; 178 case 'c': 179 *(t = buf) = va_arg(argp, int); 180 size = 1; 181 sign = '\0'; 182 goto pforw; 183 case 'D': 184 flags |= LONGINT; 185 /*FALLTHROUGH*/ 186 case 'd': 187 case 'i': 188 ARG(); 189 if ((long)_ulong < 0) { 190 _ulong = -_ulong; 191 sign = '-'; 192 } 193 base = 10; 194 goto number; 195 case 'e': 196 case 'E': 197 case 'f': 198 case 'g': 199 case 'G': 200 _double = va_arg(argp, double); 201 /* 202 * don't do unrealistic precision; just pad it with 203 * zeroes later, so buffer size stays rational. 204 */ 205 if (prec > MAXFRACT) { 206 if (*fmt != 'g' && *fmt != 'G' || (flags&ALT)) 207 fpprec = prec - MAXFRACT; 208 prec = MAXFRACT; 209 } 210 else if (prec == -1) 211 prec = DEFPREC; 212 /* 213 * softsign avoids negative 0 if _double is < 0 and 214 * no significant digits will be shown 215 */ 216 if (_double < 0) { 217 softsign = '-'; 218 _double = -_double; 219 } 220 else 221 softsign = 0; 222 /* 223 * cvt may have to round up past the "start" of the 224 * buffer, i.e. ``intf("%.2f", (double)9.999);''; 225 * if the first char isn't NULL, it did. 226 */ 227 *buf = NULL; 228 size = cvt(_double, prec, flags, &softsign, *fmt, buf, 229 buf + sizeof(buf)); 230 if (softsign) 231 sign = '-'; 232 t = *buf ? buf : buf + 1; 233 goto pforw; 234 case 'n': 235 if (flags & LONGINT) 236 *va_arg(argp, long *) = cnt; 237 else if (flags & SHORTINT) 238 *va_arg(argp, short *) = cnt; 239 else 240 *va_arg(argp, int *) = cnt; 241 break; 242 case 'O': 243 flags |= LONGINT; 244 /*FALLTHROUGH*/ 245 case 'o': 246 ARG(); 247 base = 8; 248 goto nosign; 249 case 'p': 250 /* 251 * ``The argument shall be a pointer to void. The 252 * value of the pointer is converted to a sequence 253 * of printable characters, in an implementation- 254 * defined manner.'' 255 * -- ANSI X3J11 256 */ 257 /* NOSTRICT */ 258 _ulong = (u_long)va_arg(argp, void *); 259 base = 16; 260 goto nosign; 261 case 's': 262 if (!(t = va_arg(argp, char *))) 263 t = "(null)"; 264 if (prec >= 0) { 265 /* 266 * can't use strlen; can only look for the 267 * NUL in the first `prec' characters, and 268 * strlen() will go further. 269 */ 270 char *p, *memchr(); 271 272 if (p = memchr(t, 0, prec)) { 273 size = p - t; 274 if (size > prec) 275 size = prec; 276 } else 277 size = prec; 278 } else 279 size = strlen(t); 280 sign = '\0'; 281 goto pforw; 282 case 'U': 283 flags |= LONGINT; 284 /*FALLTHROUGH*/ 285 case 'u': 286 ARG(); 287 base = 10; 288 goto nosign; 289 case 'X': 290 digs = "0123456789ABCDEF"; 291 /* FALLTHROUGH */ 292 case 'x': 293 ARG(); 294 base = 16; 295 /* leading 0x/X only if non-zero */ 296 if (flags & ALT && _ulong != 0) 297 flags |= HEXPREFIX; 298 299 /* unsigned conversions */ 300 nosign: sign = '\0'; 301 /* 302 * ``... diouXx conversions ... if a precision is 303 * specified, the 0 flag will be ignored.'' 304 * -- ANSI X3J11 305 */ 306 number: if ((dprec = prec) >= 0) 307 flags &= ~ZEROPAD; 308 309 /* 310 * ``The result of converting a zero value with an 311 * explicit precision of zero is no characters.'' 312 * -- ANSI X3J11 313 */ 314 t = buf + BUF; 315 if (_ulong != 0 || prec != 0) { 316 do { 317 *--t = digs[_ulong % base]; 318 _ulong /= base; 319 } while (_ulong); 320 digs = "0123456789abcdef"; 321 if (flags & ALT && base == 8 && *t != '0') 322 *--t = '0'; /* octal leading 0 */ 323 } 324 size = buf + BUF - t; 325 326 pforw: 327 /* 328 * All reasonable formats wind up here. At this point, 329 * `t' points to a string which (if not flags&LADJUST) 330 * should be padded out to `width' places. If 331 * flags&ZEROPAD, it should first be prefixed by any 332 * sign or other prefix; otherwise, it should be blank 333 * padded before the prefix is emitted. After any 334 * left-hand padding and prefixing, emit zeroes 335 * required by a decimal [diouxX] precision, then print 336 * the string proper, then emit zeroes required by any 337 * leftover floating precision; finally, if LADJUST, 338 * pad with blanks. 339 */ 340 341 /* 342 * compute actual size, so we know how much to pad 343 * fieldsz excludes decimal prec; realsz includes it 344 */ 345 fieldsz = size + fpprec; 346 if (sign) 347 fieldsz++; 348 if (flags & HEXPREFIX) 349 fieldsz += 2; 350 realsz = dprec > fieldsz ? dprec : fieldsz; 351 352 /* right-adjusting blank padding */ 353 if ((flags & (LADJUST|ZEROPAD)) == 0 && width) 354 for (n = realsz; n < width; n++) 355 PUTC(' '); 356 /* prefix */ 357 if (sign) 358 PUTC(sign); 359 if (flags & HEXPREFIX) { 360 PUTC('0'); 361 PUTC((char)*fmt); 362 } 363 /* right-adjusting zero padding */ 364 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 365 for (n = realsz; n < width; n++) 366 PUTC('0'); 367 /* leading zeroes from decimal precision */ 368 for (n = fieldsz; n < dprec; n++) 369 PUTC('0'); 370 371 /* the string or number proper */ 372 if (fp->_cnt - (n = size) >= 0 && 373 (fp->_flag & _IOLBF) == 0) { 374 fp->_cnt -= n; 375 bcopy(t, (char *)fp->_ptr, n); 376 fp->_ptr += n; 377 } else 378 while (--n >= 0) 379 PUTC(*t++); 380 /* trailing f.p. zeroes */ 381 while (--fpprec >= 0) 382 PUTC('0'); 383 /* left-adjusting padding (always blank) */ 384 if (flags & LADJUST) 385 for (n = realsz; n < width; n++) 386 PUTC(' '); 387 /* finally, adjust cnt */ 388 cnt += width > realsz ? width : realsz; 389 break; 390 case '\0': /* "%?" prints ?, unless ? is NULL */ 391 return (cnt); 392 default: 393 PUTC((char)*fmt); 394 cnt++; 395 } 396 } 397 /* NOTREACHED */ 398 } 399 400 static 401 cvt(number, prec, flags, signp, fmtch, startp, endp) 402 double number; 403 register int prec; 404 int flags; 405 u_char fmtch; 406 char *signp, *startp, *endp; 407 { 408 register char *p, *t; 409 double fract, integer, tmp, modf(); 410 int dotrim, expcnt, gformat; 411 char *exponent(), *round(); 412 413 dotrim = expcnt = gformat = 0; 414 fract = modf(number, &integer); 415 416 /* get an extra slot for rounding. */ 417 t = ++startp; 418 419 /* 420 * get integer portion of number; put into the end of the buffer; the 421 * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 422 */ 423 for (p = endp - 1; integer; ++expcnt) { 424 tmp = modf(integer / 10, &integer); 425 *p-- = tochar((int)((tmp + .01) * 10)); 426 } 427 switch(fmtch) { 428 case 'f': 429 /* reverse integer into beginning of buffer */ 430 if (expcnt) 431 for (; ++p < endp; *t++ = *p); 432 else 433 *t++ = '0'; 434 /* 435 * if precision required or alternate flag set, add in a 436 * decimal point. 437 */ 438 if (prec || flags&ALT) 439 *t++ = '.'; 440 /* if requires more precision and some fraction left */ 441 if (fract) { 442 if (prec) 443 do { 444 fract = modf(fract * 10, &tmp); 445 *t++ = tochar((int)tmp); 446 } while (--prec && fract); 447 if (fract) 448 startp = round(fract, (int *)NULL, startp, 449 t - 1, (char)0, signp); 450 } 451 for (; prec--; *t++ = '0'); 452 break; 453 case 'e': 454 case 'E': 455 eformat: if (expcnt) { 456 *t++ = *++p; 457 if (prec || flags&ALT) 458 *t++ = '.'; 459 /* if requires more precision and some integer left */ 460 for (; prec && ++p < endp; --prec) 461 *t++ = *p; 462 /* 463 * if done precision and more of the integer component, 464 * round using it; adjust fract so we don't re-round 465 * later. 466 */ 467 if (!prec && ++p < endp) { 468 fract = 0; 469 startp = round((double)0, &expcnt, startp, 470 t - 1, *p, signp); 471 } 472 /* adjust expcnt for digit in front of decimal */ 473 --expcnt; 474 } 475 /* until first fractional digit, decrement exponent */ 476 else if (fract) { 477 /* adjust expcnt for digit in front of decimal */ 478 for (expcnt = -1;; --expcnt) { 479 fract = modf(fract * 10, &tmp); 480 if (tmp) 481 break; 482 } 483 *t++ = tochar((int)tmp); 484 if (prec || flags&ALT) 485 *t++ = '.'; 486 } 487 else { 488 *t++ = '0'; 489 if (prec || flags&ALT) 490 *t++ = '.'; 491 } 492 /* if requires more precision and some fraction left */ 493 if (fract) { 494 if (prec) 495 do { 496 fract = modf(fract * 10, &tmp); 497 *t++ = tochar((int)tmp); 498 } while (--prec && fract); 499 if (fract) 500 startp = round(fract, &expcnt, startp, 501 t - 1, (char)0, signp); 502 } 503 /* if requires more precision */ 504 for (; prec--; *t++ = '0'); 505 506 /* unless alternate flag, trim any g/G format trailing 0's */ 507 if (gformat && !(flags&ALT)) { 508 while (t > startp && *--t == '0'); 509 if (*t == '.') 510 --t; 511 ++t; 512 } 513 t = exponent(t, expcnt, fmtch); 514 break; 515 case 'g': 516 case 'G': 517 /* a precision of 0 is treated as a precision of 1. */ 518 if (!prec) 519 ++prec; 520 /* 521 * ``The style used depends on the value converted; style e 522 * will be used only if the exponent resulting from the 523 * conversion is less than -4 or greater than the precision.'' 524 * -- ANSI X3J11 525 */ 526 if (expcnt > prec || !expcnt && fract && fract < .0001) { 527 /* 528 * g/G format counts "significant digits, not digits of 529 * precision; for the e/E format, this just causes an 530 * off-by-one problem, i.e. g/G considers the digit 531 * before the decimal point significant and e/E doesn't 532 * count it as precision. 533 */ 534 --prec; 535 fmtch -= 2; /* G->E, g->e */ 536 gformat = 1; 537 goto eformat; 538 } 539 /* 540 * reverse integer into beginning of buffer, 541 * note, decrement precision 542 */ 543 if (expcnt) 544 for (; ++p < endp; *t++ = *p, --prec); 545 else 546 *t++ = '0'; 547 /* 548 * if precision required or alternate flag set, add in a 549 * decimal point. If no digits yet, add in leading 0. 550 */ 551 if (prec || flags&ALT) { 552 dotrim = 1; 553 *t++ = '.'; 554 } 555 else 556 dotrim = 0; 557 /* if requires more precision and some fraction left */ 558 if (fract) { 559 if (prec) { 560 do { 561 fract = modf(fract * 10, &tmp); 562 *t++ = tochar((int)tmp); 563 } while(!tmp); 564 while (--prec && fract) { 565 fract = modf(fract * 10, &tmp); 566 *t++ = tochar((int)tmp); 567 } 568 } 569 if (fract) 570 startp = round(fract, (int *)NULL, startp, 571 t - 1, (char)0, signp); 572 } 573 /* alternate format, adds 0's for precision, else trim 0's */ 574 if (flags&ALT) 575 for (; prec--; *t++ = '0'); 576 else if (dotrim) { 577 while (t > startp && *--t == '0'); 578 if (*t != '.') 579 ++t; 580 } 581 } 582 return(t - startp); 583 } 584 585 static char * 586 round(fract, exp, start, end, ch, signp) 587 double fract; 588 int *exp; 589 register char *start, *end; 590 char ch, *signp; 591 { 592 double tmp; 593 594 if (fract) 595 (void)modf(fract * 10, &tmp); 596 else 597 tmp = todigit(ch); 598 if (tmp > 4) 599 for (;; --end) { 600 if (*end == '.') 601 --end; 602 if (++*end <= '9') 603 break; 604 *end = '0'; 605 if (end == start) { 606 if (exp) { /* e/E; increment exponent */ 607 *end = '1'; 608 ++*exp; 609 } 610 else { /* f; add extra digit */ 611 *--end = '1'; 612 --start; 613 } 614 break; 615 } 616 } 617 /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 618 else if (*signp == '-') 619 for (;; --end) { 620 if (*end == '.') 621 --end; 622 if (*end != '0') 623 break; 624 if (end == start) 625 *signp = 0; 626 } 627 return(start); 628 } 629 630 static char * 631 exponent(p, exp, fmtch) 632 register char *p; 633 register int exp; 634 u_char fmtch; 635 { 636 register char *t; 637 char expbuf[MAXEXP]; 638 639 *p++ = fmtch; 640 if (exp < 0) { 641 exp = -exp; 642 *p++ = '-'; 643 } 644 else 645 *p++ = '+'; 646 t = expbuf + MAXEXP; 647 if (exp > 9) { 648 do { 649 *--t = tochar(exp % 10); 650 } while ((exp /= 10) > 9); 651 *--t = tochar(exp); 652 for (; t < expbuf + MAXEXP; *p++ = *t++); 653 } 654 else { 655 *p++ = '0'; 656 *p++ = tochar(exp); 657 } 658 return(p); 659 } 660