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.34 (Berkeley) 06/08/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 = 0; dprec = 0; fpprec = 0; 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 register double fract; 410 int dotrim, expcnt, gformat; 411 double integer, tmp, modf(); 412 char *exponent(), *round(); 413 414 dotrim = expcnt = gformat = 0; 415 fract = modf(number, &integer); 416 417 /* get an extra slot for rounding. */ 418 t = ++startp; 419 420 /* 421 * get integer portion of number; put into the end of the buffer; the 422 * .01 is added for modf(356.0 / 10, &integer) returning .59999999... 423 */ 424 for (p = endp - 1; integer; ++expcnt) { 425 tmp = modf(integer / 10, &integer); 426 *p-- = tochar((int)((tmp + .01) * 10)); 427 } 428 switch(fmtch) { 429 case 'f': 430 /* reverse integer into beginning of buffer */ 431 if (expcnt) 432 for (; ++p < endp; *t++ = *p); 433 else 434 *t++ = '0'; 435 /* 436 * if precision required or alternate flag set, add in a 437 * decimal point. 438 */ 439 if (prec || flags&ALT) 440 *t++ = '.'; 441 /* if requires more precision and some fraction left */ 442 if (fract) { 443 if (prec) 444 do { 445 fract = modf(fract * 10, &tmp); 446 *t++ = tochar((int)tmp); 447 } while (--prec && fract); 448 if (fract) 449 startp = round(fract, (int *)NULL, startp, 450 t - 1, (char)0, signp); 451 } 452 for (; prec--; *t++ = '0'); 453 break; 454 case 'e': 455 case 'E': 456 eformat: if (expcnt) { 457 *t++ = *++p; 458 if (prec || flags&ALT) 459 *t++ = '.'; 460 /* if requires more precision and some integer left */ 461 for (; prec && ++p < endp; --prec) 462 *t++ = *p; 463 /* 464 * if done precision and more of the integer component, 465 * round using it; adjust fract so we don't re-round 466 * later. 467 */ 468 if (!prec && ++p < endp) { 469 fract = 0; 470 startp = round((double)0, &expcnt, startp, 471 t - 1, *p, signp); 472 } 473 /* adjust expcnt for digit in front of decimal */ 474 --expcnt; 475 } 476 /* until first fractional digit, decrement exponent */ 477 else if (fract) { 478 /* adjust expcnt for digit in front of decimal */ 479 for (expcnt = -1;; --expcnt) { 480 fract = modf(fract * 10, &tmp); 481 if (tmp) 482 break; 483 } 484 *t++ = tochar((int)tmp); 485 if (prec || flags&ALT) 486 *t++ = '.'; 487 } 488 else { 489 *t++ = '0'; 490 if (prec || flags&ALT) 491 *t++ = '.'; 492 } 493 /* if requires more precision and some fraction left */ 494 if (fract) { 495 if (prec) 496 do { 497 fract = modf(fract * 10, &tmp); 498 *t++ = tochar((int)tmp); 499 } while (--prec && fract); 500 if (fract) 501 startp = round(fract, &expcnt, startp, 502 t - 1, (char)0, signp); 503 } 504 /* if requires more precision */ 505 for (; prec--; *t++ = '0'); 506 507 /* unless alternate flag, trim any g/G format trailing 0's */ 508 if (gformat && !(flags&ALT)) { 509 while (t > startp && *--t == '0'); 510 if (*t == '.') 511 --t; 512 ++t; 513 } 514 t = exponent(t, expcnt, fmtch); 515 break; 516 case 'g': 517 case 'G': 518 /* a precision of 0 is treated as a precision of 1. */ 519 if (!prec) 520 ++prec; 521 /* 522 * ``The style used depends on the value converted; style e 523 * will be used only if the exponent resulting from the 524 * conversion is less than -4 or greater than the precision.'' 525 * -- ANSI X3J11 526 */ 527 if (expcnt > prec || !expcnt && fract && fract < .0001) { 528 /* 529 * g/G format counts "significant digits, not digits of 530 * precision; for the e/E format, this just causes an 531 * off-by-one problem, i.e. g/G considers the digit 532 * before the decimal point significant and e/E doesn't 533 * count it as precision. 534 */ 535 --prec; 536 fmtch -= 2; /* G->E, g->e */ 537 gformat = 1; 538 goto eformat; 539 } 540 /* 541 * reverse integer into beginning of buffer, 542 * note, decrement precision 543 */ 544 if (expcnt) 545 for (; ++p < endp; *t++ = *p, --prec); 546 else 547 *t++ = '0'; 548 /* 549 * if precision required or alternate flag set, add in a 550 * decimal point. If no digits yet, add in leading 0. 551 */ 552 if (prec || flags&ALT) { 553 dotrim = 1; 554 *t++ = '.'; 555 } 556 else 557 dotrim = 0; 558 /* if requires more precision and some fraction left */ 559 if (fract) { 560 if (prec) { 561 do { 562 fract = modf(fract * 10, &tmp); 563 *t++ = tochar((int)tmp); 564 } while(!tmp); 565 while (--prec && fract) { 566 fract = modf(fract * 10, &tmp); 567 *t++ = tochar((int)tmp); 568 } 569 } 570 if (fract) 571 startp = round(fract, (int *)NULL, startp, 572 t - 1, (char)0, signp); 573 } 574 /* alternate format, adds 0's for precision, else trim 0's */ 575 if (flags&ALT) 576 for (; prec--; *t++ = '0'); 577 else if (dotrim) { 578 while (t > startp && *--t == '0'); 579 if (*t != '.') 580 ++t; 581 } 582 } 583 return(t - startp); 584 } 585 586 static char * 587 round(fract, exp, start, end, ch, signp) 588 double fract; 589 int *exp; 590 register char *start, *end; 591 char ch, *signp; 592 { 593 double tmp; 594 595 if (fract) 596 (void)modf(fract * 10, &tmp); 597 else 598 tmp = todigit(ch); 599 if (tmp > 4) 600 for (;; --end) { 601 if (*end == '.') 602 --end; 603 if (++*end <= '9') 604 break; 605 *end = '0'; 606 if (end == start) { 607 if (exp) { /* e/E; increment exponent */ 608 *end = '1'; 609 ++*exp; 610 } 611 else { /* f; add extra digit */ 612 *--end = '1'; 613 --start; 614 } 615 break; 616 } 617 } 618 /* ``"%.3f", (double)-0.0004'' gives you a negative 0. */ 619 else if (*signp == '-') 620 for (;; --end) { 621 if (*end == '.') 622 --end; 623 if (*end != '0') 624 break; 625 if (end == start) 626 *signp = 0; 627 } 628 return(start); 629 } 630 631 static char * 632 exponent(p, exp, fmtch) 633 register char *p; 634 register int exp; 635 u_char fmtch; 636 { 637 register char *t; 638 char expbuf[MAXEXP]; 639 640 *p++ = fmtch; 641 if (exp < 0) { 642 exp = -exp; 643 *p++ = '-'; 644 } 645 else 646 *p++ = '+'; 647 t = expbuf + MAXEXP; 648 if (exp > 9) { 649 do { 650 *--t = tochar(exp % 10); 651 } while ((exp /= 10) > 9); 652 *--t = tochar(exp); 653 for (; t < expbuf + MAXEXP; *p++ = *t++); 654 } 655 else { 656 *p++ = '0'; 657 *p++ = tochar(exp); 658 } 659 return(p); 660 } 661