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. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #if defined(LIBC_SCCS) && !defined(lint) 34 static char *rcsid = "$OpenBSD: vfprintf.c,v 1.23 2004/09/18 19:28:06 otto Exp $"; 35 #endif /* LIBC_SCCS and not lint */ 36 37 /* 38 * Actual printf innards. 39 * 40 * This code is large and complicated... 41 */ 42 43 #include <sys/types.h> 44 #include <sys/mman.h> 45 46 #include <stddef.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <errno.h> 51 #include <stdarg.h> 52 53 #include "local.h" 54 #include "fvwrite.h" 55 56 static void __find_arguments(const char *fmt0, va_list ap, va_list **argtable, 57 size_t *argtablesiz); 58 static int __grow_type_table(unsigned char **typetable, int *tablesize); 59 60 /* 61 * Flush out all the vectors defined by the given uio, 62 * then reset it so that it can be reused. 63 */ 64 static int 65 __sprint(fp, uio) 66 FILE *fp; 67 register struct __suio *uio; 68 { 69 register int err; 70 71 if (uio->uio_resid == 0) { 72 uio->uio_iovcnt = 0; 73 return (0); 74 } 75 err = __sfvwrite(fp, uio); 76 uio->uio_resid = 0; 77 uio->uio_iovcnt = 0; 78 return (err); 79 } 80 81 /* 82 * Helper function for `fprintf to unbuffered unix file': creates a 83 * temporary buffer. We only work on write-only files; this avoids 84 * worries about ungetc buffers and so forth. 85 */ 86 static int 87 __sbprintf(fp, fmt, ap) 88 register FILE *fp; 89 const char *fmt; 90 va_list ap; 91 { 92 int ret; 93 FILE fake; 94 unsigned char buf[BUFSIZ]; 95 96 /* copy the important variables */ 97 fake._flags = fp->_flags & ~__SNBF; 98 fake._file = fp->_file; 99 fake._cookie = fp->_cookie; 100 fake._write = fp->_write; 101 102 /* set up the buffer */ 103 fake._bf._base = fake._p = buf; 104 fake._bf._size = fake._w = sizeof(buf); 105 fake._lbfsize = 0; /* not actually used, but Just In Case */ 106 107 /* do the work, then copy any error status */ 108 ret = vfprintf(&fake, fmt, ap); 109 if (ret >= 0 && fflush(&fake)) 110 ret = EOF; 111 if (fake._flags & __SERR) 112 fp->_flags |= __SERR; 113 return (ret); 114 } 115 116 117 #ifdef FLOATING_POINT 118 #include <locale.h> 119 #include <math.h> 120 #include "floatio.h" 121 122 #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ 123 #define DEFPREC 6 124 125 static char *cvt(double, int, int, char *, int *, int, int *); 126 static int exponent(char *, int, int); 127 128 #else /* no FLOATING_POINT */ 129 #define BUF 40 130 #endif /* FLOATING_POINT */ 131 132 #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ 133 134 135 /* 136 * Macros for converting digits to letters and vice versa 137 */ 138 #define to_digit(c) ((c) - '0') 139 #define is_digit(c) ((unsigned)to_digit(c) <= 9) 140 #define to_char(n) ((n) + '0') 141 142 /* 143 * Flags used during conversion. 144 */ 145 #define ALT 0x001 /* alternate form */ 146 #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ 147 #define LADJUST 0x004 /* left adjustment */ 148 #define LONGDBL 0x008 /* long double; unimplemented */ 149 #define LONGINT 0x010 /* long integer */ 150 #define QUADINT 0x020 /* quad integer */ 151 #define SHORTINT 0x040 /* short integer */ 152 #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ 153 #define FPT 0x100 /* Floating point number */ 154 #define PTRINT 0x200 /* (unsigned) ptrdiff_t */ 155 #define SIZEINT 0x400 /* (signed) size_t */ 156 157 int 158 vfprintf(fp, fmt0, ap) 159 FILE *fp; 160 const char *fmt0; 161 _BSD_VA_LIST_ ap; 162 { 163 register char *fmt; /* format string */ 164 register int ch; /* character from fmt */ 165 register int n, m, n2; /* handy integers (short term usage) */ 166 register char *cp; /* handy char pointer (short term usage) */ 167 register struct __siov *iovp;/* for PRINT macro */ 168 register int flags; /* flags as above */ 169 int ret; /* return value accumulator */ 170 int width; /* width from format (%8d), or 0 */ 171 int prec; /* precision from format (%.3d), or -1 */ 172 char sign; /* sign prefix (' ', '+', '-', or \0) */ 173 wchar_t wc; 174 #ifdef FLOATING_POINT 175 char *decimal_point = localeconv()->decimal_point; 176 char softsign; /* temporary negative sign for floats */ 177 double _double; /* double precision arguments %[eEfgG] */ 178 int expt; /* integer value of exponent */ 179 int expsize; /* character count for expstr */ 180 int ndig; /* actual number of digits returned by cvt */ 181 char expstr[7]; /* buffer for exponent string */ 182 #endif 183 184 #ifdef __GNUC__ /* gcc has builtin quad type (long long) SOS */ 185 #define quad_t long long 186 #define u_quad_t unsigned long long 187 #endif 188 189 u_quad_t _uquad; /* integer arguments %[diouxX] */ 190 enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */ 191 int dprec; /* a copy of prec if [diouxX], 0 otherwise */ 192 int realsz; /* field size expanded by dprec */ 193 int size; /* size of converted field or string */ 194 char *xdigs; /* digits for [xX] conversion */ 195 #define NIOV 8 196 struct __suio uio; /* output information: summary */ 197 struct __siov iov[NIOV];/* ... and individual io vectors */ 198 char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ 199 char ox[2]; /* space for 0x hex-prefix */ 200 va_list *argtable; /* args, built due to positional arg */ 201 va_list statargtable[STATIC_ARG_TBL_SIZE]; 202 size_t argtablesiz; 203 int nextarg; /* 1-based argument index */ 204 va_list orgap; /* original argument pointer */ 205 206 /* 207 * Choose PADSIZE to trade efficiency vs. size. If larger printf 208 * fields occur frequently, increase PADSIZE and make the initialisers 209 * below longer. 210 */ 211 #define PADSIZE 16 /* pad chunk size */ 212 static char blanks[PADSIZE] = 213 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; 214 static char zeroes[PADSIZE] = 215 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; 216 217 /* 218 * BEWARE, these `goto error' on error, and PAD uses `n'. 219 */ 220 #define PRINT(ptr, len) do { \ 221 iovp->iov_base = (ptr); \ 222 iovp->iov_len = (len); \ 223 uio.uio_resid += (len); \ 224 iovp++; \ 225 if (++uio.uio_iovcnt >= NIOV) { \ 226 if (__sprint(fp, &uio)) \ 227 goto error; \ 228 iovp = iov; \ 229 } \ 230 } while (0) 231 #define PAD(howmany, with) do { \ 232 if ((n = (howmany)) > 0) { \ 233 while (n > PADSIZE) { \ 234 PRINT(with, PADSIZE); \ 235 n -= PADSIZE; \ 236 } \ 237 PRINT(with, n); \ 238 } \ 239 } while (0) 240 #define FLUSH() do { \ 241 if (uio.uio_resid && __sprint(fp, &uio)) \ 242 goto error; \ 243 uio.uio_iovcnt = 0; \ 244 iovp = iov; \ 245 } while (0) 246 247 /* 248 * To extend shorts properly, we need both signed and unsigned 249 * argument extraction methods. 250 */ 251 #define SARG() \ 252 (flags&QUADINT ? va_arg(ap, quad_t) : \ 253 flags&LONGINT ? GETARG(long) : \ 254 flags&PTRINT ? GETARG(ptrdiff_t) : \ 255 flags&SIZEINT ? GETARG(ssize_t) : \ 256 flags&SHORTINT ? (long)(short)GETARG(int) : \ 257 (long)GETARG(int)) 258 #define UARG() \ 259 (flags&QUADINT ? va_arg(ap, u_quad_t) : \ 260 flags&LONGINT ? GETARG(u_long) : \ 261 flags&PTRINT ? GETARG(ptrdiff_t) : /* XXX */ \ 262 flags&SIZEINT ? GETARG(size_t) : \ 263 flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ 264 (u_long)GETARG(u_int)) 265 266 /* 267 * Get * arguments, including the form *nn$. Preserve the nextarg 268 * that the argument can be gotten once the type is determined. 269 */ 270 #define GETASTER(val) \ 271 n2 = 0; \ 272 cp = fmt; \ 273 while (is_digit(*cp)) { \ 274 n2 = 10 * n2 + to_digit(*cp); \ 275 cp++; \ 276 } \ 277 if (*cp == '$') { \ 278 int hold = nextarg; \ 279 if (argtable == NULL) { \ 280 argtable = statargtable; \ 281 __find_arguments(fmt0, orgap, &argtable, &argtablesiz); \ 282 } \ 283 nextarg = n2; \ 284 val = GETARG(int); \ 285 nextarg = hold; \ 286 fmt = ++cp; \ 287 } else { \ 288 val = GETARG(int); \ 289 } 290 291 /* 292 * Get the argument indexed by nextarg. If the argument table is 293 * built, use it to get the argument. If its not, get the next 294 * argument (and arguments must be gotten sequentially). 295 */ 296 #define GETARG(type) \ 297 (((argtable != NULL) ? (void)(ap = argtable[nextarg]) : (void)0), \ 298 nextarg++, va_arg(ap, type)) 299 300 /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ 301 if (cantwrite(fp)) { 302 errno = EBADF; 303 return (EOF); 304 } 305 306 /* optimise fprintf(stderr) (and other unbuffered Unix files) */ 307 if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && 308 fp->_file >= 0) 309 return (__sbprintf(fp, fmt0, ap)); 310 311 fmt = (char *)fmt0; 312 argtable = NULL; 313 nextarg = 1; 314 va_copy(orgap, ap); 315 uio.uio_iov = iovp = iov; 316 uio.uio_resid = 0; 317 uio.uio_iovcnt = 0; 318 ret = 0; 319 320 /* 321 * Scan the format for conversions (`%' character). 322 */ 323 for (;;) { 324 cp = fmt; 325 while ((n = mbtowc(&wc, fmt, MB_CUR_MAX)) > 0) { 326 fmt += n; 327 if (wc == '%') { 328 fmt--; 329 break; 330 } 331 } 332 if ((m = fmt - cp) != 0) { 333 PRINT(cp, m); 334 ret += m; 335 } 336 if (n <= 0) 337 goto done; 338 fmt++; /* skip over '%' */ 339 340 flags = 0; 341 dprec = 0; 342 width = 0; 343 prec = -1; 344 sign = '\0'; 345 346 rflag: ch = *fmt++; 347 reswitch: switch (ch) { 348 case ' ': 349 /* 350 * ``If the space and + flags both appear, the space 351 * flag will be ignored.'' 352 * -- ANSI X3J11 353 */ 354 if (!sign) 355 sign = ' '; 356 goto rflag; 357 case '#': 358 flags |= ALT; 359 goto rflag; 360 case '*': 361 /* 362 * ``A negative field width argument is taken as a 363 * - flag followed by a positive field width.'' 364 * -- ANSI X3J11 365 * They don't exclude field widths read from args. 366 */ 367 GETASTER(width); 368 if (width >= 0) 369 goto rflag; 370 width = -width; 371 /* FALLTHROUGH */ 372 case '-': 373 flags |= LADJUST; 374 goto rflag; 375 case '+': 376 sign = '+'; 377 goto rflag; 378 case '.': 379 if ((ch = *fmt++) == '*') { 380 GETASTER(n); 381 prec = n < 0 ? -1 : n; 382 goto rflag; 383 } 384 n = 0; 385 while (is_digit(ch)) { 386 n = 10 * n + to_digit(ch); 387 ch = *fmt++; 388 } 389 if (ch == '$') { 390 nextarg = n; 391 if (argtable == NULL) { 392 argtable = statargtable; 393 __find_arguments(fmt0, orgap, 394 &argtable, &argtablesiz); 395 } 396 goto rflag; 397 } 398 prec = n < 0 ? -1 : n; 399 goto reswitch; 400 case '0': 401 /* 402 * ``Note that 0 is taken as a flag, not as the 403 * beginning of a field width.'' 404 * -- ANSI X3J11 405 */ 406 flags |= ZEROPAD; 407 goto rflag; 408 case '1': case '2': case '3': case '4': 409 case '5': case '6': case '7': case '8': case '9': 410 n = 0; 411 do { 412 n = 10 * n + to_digit(ch); 413 ch = *fmt++; 414 } while (is_digit(ch)); 415 if (ch == '$') { 416 nextarg = n; 417 if (argtable == NULL) { 418 argtable = statargtable; 419 __find_arguments(fmt0, orgap, 420 &argtable, &argtablesiz); 421 } 422 goto rflag; 423 } 424 width = n; 425 goto reswitch; 426 #ifdef FLOATING_POINT 427 case 'L': 428 flags |= LONGDBL; 429 goto rflag; 430 #endif 431 case 'h': 432 flags |= SHORTINT; 433 goto rflag; 434 case 'l': 435 if (*fmt == 'l') { 436 fmt++; 437 flags |= QUADINT; 438 } else { 439 flags |= LONGINT; 440 } 441 goto rflag; 442 case 'q': 443 flags |= QUADINT; 444 goto rflag; 445 case 't': 446 flags |= PTRINT; 447 goto rflag; 448 case 'z': 449 flags |= SIZEINT; 450 goto rflag; 451 case 'c': 452 *(cp = buf) = GETARG(int); 453 size = 1; 454 sign = '\0'; 455 break; 456 case 'D': 457 flags |= LONGINT; 458 /*FALLTHROUGH*/ 459 case 'd': 460 case 'i': 461 _uquad = SARG(); 462 if ((quad_t)_uquad < 0) { 463 _uquad = -_uquad; 464 sign = '-'; 465 } 466 base = DEC; 467 goto number; 468 #ifdef FLOATING_POINT 469 case 'e': 470 case 'E': 471 case 'f': 472 case 'g': 473 case 'G': 474 if (prec == -1) { 475 prec = DEFPREC; 476 } else if ((ch == 'g' || ch == 'G') && prec == 0) { 477 prec = 1; 478 } 479 480 if (flags & LONGDBL) { 481 _double = (double) GETARG(long double); 482 } else { 483 _double = GETARG(double); 484 } 485 486 /* do this before tricky precision changes */ 487 if (isinf(_double)) { 488 if (_double < 0) 489 sign = '-'; 490 cp = "Inf"; 491 size = 3; 492 break; 493 } 494 if (isnan(_double)) { 495 cp = "NaN"; 496 size = 3; 497 break; 498 } 499 500 flags |= FPT; 501 cp = cvt(_double, prec, flags, &softsign, 502 &expt, ch, &ndig); 503 if (ch == 'g' || ch == 'G') { 504 if (expt <= -4 || expt > prec) 505 ch = (ch == 'g') ? 'e' : 'E'; 506 else 507 ch = 'g'; 508 } 509 if (ch <= 'e') { /* 'e' or 'E' fmt */ 510 --expt; 511 expsize = exponent(expstr, expt, ch); 512 size = expsize + ndig; 513 if (ndig > 1 || flags & ALT) 514 ++size; 515 } else if (ch == 'f') { /* f fmt */ 516 if (expt > 0) { 517 size = expt; 518 if (prec || flags & ALT) 519 size += prec + 1; 520 } else /* "0.X" */ 521 size = prec + 2; 522 } else if (expt >= ndig) { /* fixed g fmt */ 523 size = expt; 524 if (flags & ALT) 525 ++size; 526 } else 527 size = ndig + (expt > 0 ? 528 1 : 2 - expt); 529 530 if (softsign) 531 sign = '-'; 532 break; 533 #endif /* FLOATING_POINT */ 534 case 'n': 535 if (flags & QUADINT) 536 *GETARG(quad_t *) = ret; 537 else if (flags & LONGINT) 538 *GETARG(long *) = ret; 539 else if (flags & SHORTINT) 540 *GETARG(short *) = ret; 541 else if (flags & PTRINT) 542 *GETARG(ptrdiff_t *) = ret; 543 else if (flags & SIZEINT) 544 *GETARG(ssize_t *) = ret; 545 else 546 *GETARG(int *) = ret; 547 continue; /* no output */ 548 case 'O': 549 flags |= LONGINT; 550 /*FALLTHROUGH*/ 551 case 'o': 552 _uquad = UARG(); 553 base = OCT; 554 goto nosign; 555 case 'p': 556 /* 557 * ``The argument shall be a pointer to void. The 558 * value of the pointer is converted to a sequence 559 * of printable characters, in an implementation- 560 * defined manner.'' 561 * -- ANSI X3J11 562 */ 563 /* NOSTRICT */ 564 _uquad = (u_long)GETARG(void *); 565 base = HEX; 566 xdigs = "0123456789abcdef"; 567 flags |= HEXPREFIX; 568 ch = 'x'; 569 goto nosign; 570 case 's': 571 if ((cp = GETARG(char *)) == NULL) 572 cp = "(null)"; 573 if (prec >= 0) { 574 /* 575 * can't use strlen; can only look for the 576 * NUL in the first `prec' characters, and 577 * strlen() will go further. 578 */ 579 char *p = memchr(cp, 0, prec); 580 581 if (p != NULL) { 582 size = p - cp; 583 if (size > prec) 584 size = prec; 585 } else 586 size = prec; 587 } else 588 size = strlen(cp); 589 sign = '\0'; 590 break; 591 case 'U': 592 flags |= LONGINT; 593 /*FALLTHROUGH*/ 594 case 'u': 595 _uquad = UARG(); 596 base = DEC; 597 goto nosign; 598 case 'X': 599 xdigs = "0123456789ABCDEF"; 600 goto hex; 601 case 'x': 602 xdigs = "0123456789abcdef"; 603 hex: _uquad = UARG(); 604 base = HEX; 605 /* leading 0x/X only if non-zero */ 606 if (flags & ALT && _uquad != 0) 607 flags |= HEXPREFIX; 608 609 /* unsigned conversions */ 610 nosign: sign = '\0'; 611 /* 612 * ``... diouXx conversions ... if a precision is 613 * specified, the 0 flag will be ignored.'' 614 * -- ANSI X3J11 615 */ 616 number: if ((dprec = prec) >= 0) 617 flags &= ~ZEROPAD; 618 619 /* 620 * ``The result of converting a zero value with an 621 * explicit precision of zero is no characters.'' 622 * -- ANSI X3J11 623 */ 624 cp = buf + BUF; 625 if (_uquad != 0 || prec != 0) { 626 /* 627 * Unsigned mod is hard, and unsigned mod 628 * by a constant is easier than that by 629 * a variable; hence this switch. 630 */ 631 switch (base) { 632 case OCT: 633 do { 634 *--cp = to_char(_uquad & 7); 635 _uquad >>= 3; 636 } while (_uquad); 637 /* handle octal leading 0 */ 638 if (flags & ALT && *cp != '0') 639 *--cp = '0'; 640 break; 641 642 case DEC: 643 /* many numbers are 1 digit */ 644 while (_uquad >= 10) { 645 *--cp = to_char(_uquad % 10); 646 _uquad /= 10; 647 } 648 *--cp = to_char(_uquad); 649 break; 650 651 case HEX: 652 do { 653 *--cp = xdigs[_uquad & 15]; 654 _uquad >>= 4; 655 } while (_uquad); 656 break; 657 658 default: 659 cp = "bug in vfprintf: bad base"; 660 size = strlen(cp); 661 goto skipsize; 662 } 663 } 664 size = buf + BUF - cp; 665 skipsize: 666 break; 667 default: /* "%?" prints ?, unless ? is NUL */ 668 if (ch == '\0') 669 goto done; 670 /* pretend it was %c with argument ch */ 671 cp = buf; 672 *cp = ch; 673 size = 1; 674 sign = '\0'; 675 break; 676 } 677 678 /* 679 * All reasonable formats wind up here. At this point, `cp' 680 * points to a string which (if not flags&LADJUST) should be 681 * padded out to `width' places. If flags&ZEROPAD, it should 682 * first be prefixed by any sign or other prefix; otherwise, 683 * it should be blank padded before the prefix is emitted. 684 * After any left-hand padding and prefixing, emit zeroes 685 * required by a decimal [diouxX] precision, then print the 686 * string proper, then emit zeroes required by any leftover 687 * floating precision; finally, if LADJUST, pad with blanks. 688 * 689 * Compute actual size, so we know how much to pad. 690 * size excludes decimal prec; realsz includes it. 691 */ 692 realsz = dprec > size ? dprec : size; 693 if (sign) 694 realsz++; 695 else if (flags & HEXPREFIX) 696 realsz+= 2; 697 698 /* right-adjusting blank padding */ 699 if ((flags & (LADJUST|ZEROPAD)) == 0) 700 PAD(width - realsz, blanks); 701 702 /* prefix */ 703 if (sign) { 704 PRINT(&sign, 1); 705 } else if (flags & HEXPREFIX) { 706 ox[0] = '0'; 707 ox[1] = ch; 708 PRINT(ox, 2); 709 } 710 711 /* right-adjusting zero padding */ 712 if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) 713 PAD(width - realsz, zeroes); 714 715 /* leading zeroes from decimal precision */ 716 PAD(dprec - size, zeroes); 717 718 /* the string or number proper */ 719 #ifdef FLOATING_POINT 720 if ((flags & FPT) == 0) { 721 PRINT(cp, size); 722 } else { /* glue together f_p fragments */ 723 if (ch >= 'f') { /* 'f' or 'g' */ 724 if (_double == 0) { 725 /* kludge for __dtoa irregularity */ 726 PRINT("0", 1); 727 if (expt < ndig || (flags & ALT) != 0) { 728 PRINT(decimal_point, 1); 729 PAD(ndig - 1, zeroes); 730 } 731 } else if (expt <= 0) { 732 PRINT("0", 1); 733 PRINT(decimal_point, 1); 734 PAD(-expt, zeroes); 735 PRINT(cp, ndig); 736 } else if (expt >= ndig) { 737 PRINT(cp, ndig); 738 PAD(expt - ndig, zeroes); 739 if (flags & ALT) 740 PRINT(".", 1); 741 } else { 742 PRINT(cp, expt); 743 cp += expt; 744 PRINT(".", 1); 745 PRINT(cp, ndig-expt); 746 } 747 } else { /* 'e' or 'E' */ 748 if (ndig > 1 || flags & ALT) { 749 ox[0] = *cp++; 750 ox[1] = '.'; 751 PRINT(ox, 2); 752 if (_double) { 753 PRINT(cp, ndig-1); 754 } else /* 0.[0..] */ 755 /* __dtoa irregularity */ 756 PAD(ndig - 1, zeroes); 757 } else /* XeYYY */ 758 PRINT(cp, 1); 759 PRINT(expstr, expsize); 760 } 761 } 762 #else 763 PRINT(cp, size); 764 #endif 765 /* left-adjusting padding (always blank) */ 766 if (flags & LADJUST) 767 PAD(width - realsz, blanks); 768 769 /* finally, adjust ret */ 770 ret += width > realsz ? width : realsz; 771 772 FLUSH(); /* copy out the I/O vectors */ 773 } 774 done: 775 FLUSH(); 776 error: 777 if (argtable != NULL && argtable != statargtable) { 778 munmap(argtable, argtablesiz); 779 argtable = NULL; 780 } 781 return (__sferror(fp) ? EOF : ret); 782 /* NOTREACHED */ 783 } 784 785 /* 786 * Type ids for argument type table. 787 */ 788 #define T_UNUSED 0 789 #define T_SHORT 1 790 #define T_U_SHORT 2 791 #define TP_SHORT 3 792 #define T_INT 4 793 #define T_U_INT 5 794 #define TP_INT 6 795 #define T_LONG 7 796 #define T_U_LONG 8 797 #define TP_LONG 9 798 #define T_QUAD 10 799 #define T_U_QUAD 11 800 #define TP_QUAD 12 801 #define T_DOUBLE 13 802 #define T_LONG_DOUBLE 14 803 #define TP_CHAR 15 804 #define TP_VOID 16 805 #define T_PTRINT 17 806 #define TP_PTRINT 18 807 #define T_SIZEINT 19 808 #define T_SSIZEINT 20 809 #define TP_SSIZEINT 21 810 811 /* 812 * Find all arguments when a positional parameter is encountered. Returns a 813 * table, indexed by argument number, of pointers to each arguments. The 814 * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. 815 * It will be replaces with a malloc-ed on if it overflows. 816 */ 817 static void 818 __find_arguments(fmt0, ap, argtable, argtablesiz) 819 const char *fmt0; 820 va_list ap; 821 va_list **argtable; 822 size_t *argtablesiz; 823 { 824 register char *fmt; /* format string */ 825 register int ch; /* character from fmt */ 826 register int n, n2; /* handy integer (short term usage) */ 827 register char *cp; /* handy char pointer (short term usage) */ 828 register int flags; /* flags as above */ 829 unsigned char *typetable; /* table of types */ 830 unsigned char stattypetable[STATIC_ARG_TBL_SIZE]; 831 int tablesize; /* current size of type table */ 832 int tablemax; /* largest used index in table */ 833 int nextarg; /* 1-based argument index */ 834 835 /* 836 * Add an argument type to the table, expanding if necessary. 837 */ 838 #define ADDTYPE(type) \ 839 ((nextarg >= tablesize) ? \ 840 __grow_type_table(&typetable, &tablesize) : 0, \ 841 typetable[nextarg++] = type, \ 842 (nextarg > tablemax) ? tablemax = nextarg : 0) 843 844 #define ADDSARG() \ 845 ((flags&LONGINT) ? ADDTYPE(T_LONG) : \ 846 ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT))) 847 848 #define ADDUARG() \ 849 ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \ 850 ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT))) 851 852 /* 853 * Add * arguments to the type array. 854 */ 855 #define ADDASTER() \ 856 n2 = 0; \ 857 cp = fmt; \ 858 while (is_digit(*cp)) { \ 859 n2 = 10 * n2 + to_digit(*cp); \ 860 cp++; \ 861 } \ 862 if (*cp == '$') { \ 863 int hold = nextarg; \ 864 nextarg = n2; \ 865 ADDTYPE(T_INT); \ 866 nextarg = hold; \ 867 fmt = ++cp; \ 868 } else { \ 869 ADDTYPE(T_INT); \ 870 } 871 fmt = (char *)fmt0; 872 typetable = stattypetable; 873 tablesize = STATIC_ARG_TBL_SIZE; 874 tablemax = 0; 875 nextarg = 1; 876 memset(typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); 877 878 /* 879 * Scan the format for conversions (`%' character). 880 */ 881 for (;;) { 882 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) 883 /* void */; 884 if (ch == '\0') 885 goto done; 886 fmt++; /* skip over '%' */ 887 888 flags = 0; 889 890 rflag: ch = *fmt++; 891 reswitch: switch (ch) { 892 case ' ': 893 case '#': 894 goto rflag; 895 case '*': 896 ADDASTER(); 897 goto rflag; 898 case '-': 899 case '+': 900 goto rflag; 901 case '.': 902 if ((ch = *fmt++) == '*') { 903 ADDASTER(); 904 goto rflag; 905 } 906 while (is_digit(ch)) { 907 ch = *fmt++; 908 } 909 goto reswitch; 910 case '0': 911 goto rflag; 912 case '1': case '2': case '3': case '4': 913 case '5': case '6': case '7': case '8': case '9': 914 n = 0; 915 do { 916 n = 10 * n + to_digit(ch); 917 ch = *fmt++; 918 } while (is_digit(ch)); 919 if (ch == '$') { 920 nextarg = n; 921 goto rflag; 922 } 923 goto reswitch; 924 #ifdef FLOATING_POINT 925 case 'L': 926 flags |= LONGDBL; 927 goto rflag; 928 #endif 929 case 'h': 930 flags |= SHORTINT; 931 goto rflag; 932 case 'l': 933 if (*fmt == 'l') { 934 fmt++; 935 flags |= QUADINT; 936 } else { 937 flags |= LONGINT; 938 } 939 goto rflag; 940 case 'q': 941 flags |= QUADINT; 942 goto rflag; 943 case 't': 944 flags |= PTRINT; 945 goto rflag; 946 case 'z': 947 flags |= SIZEINT; 948 goto rflag; 949 case 'c': 950 ADDTYPE(T_INT); 951 break; 952 case 'D': 953 flags |= LONGINT; 954 /*FALLTHROUGH*/ 955 case 'd': 956 case 'i': 957 if (flags & QUADINT) 958 ADDTYPE(T_QUAD); 959 else if (flags & PTRINT) 960 ADDTYPE(T_PTRINT); 961 else if (flags & SIZEINT) 962 ADDTYPE(T_SSIZEINT); 963 else 964 ADDSARG(); 965 break; 966 #ifdef FLOATING_POINT 967 case 'e': 968 case 'E': 969 case 'f': 970 case 'g': 971 case 'G': 972 if (flags & LONGDBL) 973 ADDTYPE(T_LONG_DOUBLE); 974 else 975 ADDTYPE(T_DOUBLE); 976 break; 977 #endif /* FLOATING_POINT */ 978 case 'n': 979 if (flags & QUADINT) 980 ADDTYPE(TP_QUAD); 981 else if (flags & LONGINT) 982 ADDTYPE(TP_LONG); 983 else if (flags & SHORTINT) 984 ADDTYPE(TP_SHORT); 985 else if (flags & PTRINT) 986 ADDTYPE(TP_PTRINT); 987 else if (flags & SIZEINT) 988 ADDTYPE(TP_SSIZEINT); 989 else 990 ADDTYPE(TP_INT); 991 continue; /* no output */ 992 case 'O': 993 flags |= LONGINT; 994 /*FALLTHROUGH*/ 995 case 'o': 996 if (flags & QUADINT) 997 ADDTYPE(T_U_QUAD); 998 else 999 ADDUARG(); 1000 break; 1001 case 'p': 1002 ADDTYPE(TP_VOID); 1003 break; 1004 case 's': 1005 ADDTYPE(TP_CHAR); 1006 break; 1007 case 'U': 1008 flags |= LONGINT; 1009 /*FALLTHROUGH*/ 1010 case 'u': 1011 if (flags & QUADINT) 1012 ADDTYPE(T_U_QUAD); 1013 else 1014 ADDUARG(); 1015 break; 1016 case 'X': 1017 case 'x': 1018 if (flags & QUADINT) 1019 ADDTYPE(T_U_QUAD); 1020 else if (flags & PTRINT) 1021 ADDTYPE(T_PTRINT); 1022 else if (flags & SIZEINT) 1023 ADDTYPE(T_SIZEINT); 1024 else 1025 ADDUARG(); 1026 break; 1027 default: /* "%?" prints ?, unless ? is NUL */ 1028 if (ch == '\0') 1029 goto done; 1030 break; 1031 } 1032 } 1033 done: 1034 /* 1035 * Build the argument table. 1036 */ 1037 if (tablemax >= STATIC_ARG_TBL_SIZE) { 1038 *argtablesiz = sizeof (va_list) * (tablemax + 1); 1039 *argtable = (va_list *)mmap(NULL, *argtablesiz, 1040 PROT_WRITE|PROT_READ, MAP_ANON|MAP_PRIVATE, -1, 0); 1041 } 1042 1043 #if 0 1044 /* XXX is this required? */ 1045 (*argtable) [0] = NULL; 1046 #endif 1047 for (n = 1; n <= tablemax; n++) { 1048 va_copy((*argtable)[n], ap); 1049 switch (typetable[n]) { 1050 case T_UNUSED: 1051 (void) va_arg(ap, int); 1052 break; 1053 case T_SHORT: 1054 (void) va_arg(ap, int); 1055 break; 1056 case T_U_SHORT: 1057 (void) va_arg(ap, int); 1058 break; 1059 case TP_SHORT: 1060 (void) va_arg(ap, short *); 1061 break; 1062 case T_INT: 1063 (void) va_arg(ap, int); 1064 break; 1065 case T_U_INT: 1066 (void) va_arg(ap, unsigned int); 1067 break; 1068 case TP_INT: 1069 (void) va_arg(ap, int *); 1070 break; 1071 case T_LONG: 1072 (void) va_arg(ap, long); 1073 break; 1074 case T_U_LONG: 1075 (void) va_arg(ap, unsigned long); 1076 break; 1077 case TP_LONG: 1078 (void) va_arg(ap, long *); 1079 break; 1080 case T_QUAD: 1081 (void) va_arg(ap, quad_t); 1082 break; 1083 case T_U_QUAD: 1084 (void) va_arg(ap, u_quad_t); 1085 break; 1086 case TP_QUAD: 1087 (void) va_arg(ap, quad_t *); 1088 break; 1089 case T_DOUBLE: 1090 (void) va_arg(ap, double); 1091 break; 1092 case T_LONG_DOUBLE: 1093 (void) va_arg(ap, long double); 1094 break; 1095 case TP_CHAR: 1096 (void) va_arg(ap, char *); 1097 break; 1098 case TP_VOID: 1099 (void) va_arg(ap, void *); 1100 break; 1101 case T_PTRINT: 1102 (void) va_arg(ap, ptrdiff_t); 1103 break; 1104 case TP_PTRINT: 1105 (void) va_arg(ap, ptrdiff_t *); 1106 break; 1107 case T_SIZEINT: 1108 (void) va_arg(ap, size_t); 1109 break; 1110 case T_SSIZEINT: 1111 (void) va_arg(ap, ssize_t); 1112 break; 1113 case TP_SSIZEINT: 1114 (void) va_arg(ap, ssize_t *); 1115 break; 1116 } 1117 } 1118 1119 if (typetable != NULL && typetable != stattypetable) { 1120 munmap(typetable, *argtablesiz); 1121 typetable = NULL; 1122 } 1123 } 1124 1125 /* 1126 * Increase the size of the type table. 1127 */ 1128 static int 1129 __grow_type_table(typetable, tablesize) 1130 unsigned char **typetable; 1131 int *tablesize; 1132 { 1133 unsigned char *oldtable = *typetable; 1134 int newsize = *tablesize * 2; 1135 1136 if (*tablesize == STATIC_ARG_TBL_SIZE) { 1137 *typetable = (unsigned char *)mmap(NULL, 1138 sizeof (unsigned char) * newsize, PROT_WRITE|PROT_READ, 1139 MAP_ANON|MAP_PRIVATE, -1, 0); 1140 /* XXX unchecked */ 1141 bcopy(oldtable, *typetable, *tablesize); 1142 } else { 1143 unsigned char *new = (unsigned char *)mmap(NULL, 1144 sizeof (unsigned char) * newsize, PROT_WRITE|PROT_READ, 1145 MAP_ANON|MAP_PRIVATE, -1, 0); 1146 memmove(new, *typetable, *tablesize); 1147 munmap(*typetable, *tablesize); 1148 *typetable = new; 1149 /* XXX unchecked */ 1150 } 1151 memset(*typetable + *tablesize, T_UNUSED, (newsize - *tablesize)); 1152 1153 *tablesize = newsize; 1154 return(0); 1155 } 1156 1157 1158 #ifdef FLOATING_POINT 1159 1160 extern char *__dtoa(double, int, int, int *, int *, char **); 1161 1162 static char * 1163 cvt(value, ndigits, flags, sign, decpt, ch, length) 1164 double value; 1165 int ndigits, flags, *decpt, ch, *length; 1166 char *sign; 1167 { 1168 int mode, dsgn; 1169 char *digits, *bp, *rve; 1170 1171 if (ch == 'f') { 1172 mode = 3; /* ndigits after the decimal point */ 1173 } else { 1174 /* To obtain ndigits after the decimal point for the 'e' 1175 * and 'E' formats, round to ndigits + 1 significant 1176 * figures. 1177 */ 1178 if (ch == 'e' || ch == 'E') { 1179 ndigits++; 1180 } 1181 mode = 2; /* ndigits significant digits */ 1182 } 1183 1184 if (value < 0) { 1185 value = -value; 1186 *sign = '-'; 1187 } else 1188 *sign = '\000'; 1189 digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve); 1190 if ((ch != 'g' && ch != 'G') || flags & ALT) { /* Print trailing zeros */ 1191 bp = digits + ndigits; 1192 if (ch == 'f') { 1193 if (*digits == '0' && value) 1194 *decpt = -ndigits + 1; 1195 bp += *decpt; 1196 } 1197 if (value == 0) /* kludge for __dtoa irregularity */ 1198 rve = bp; 1199 while (rve < bp) 1200 *rve++ = '0'; 1201 } 1202 *length = rve - digits; 1203 return (digits); 1204 } 1205 1206 static int 1207 exponent(p0, exp, fmtch) 1208 char *p0; 1209 int exp, fmtch; 1210 { 1211 register char *p, *t; 1212 char expbuf[MAXEXP]; 1213 1214 p = p0; 1215 *p++ = fmtch; 1216 if (exp < 0) { 1217 exp = -exp; 1218 *p++ = '-'; 1219 } 1220 else 1221 *p++ = '+'; 1222 t = expbuf + MAXEXP; 1223 if (exp > 9) { 1224 do { 1225 *--t = to_char(exp % 10); 1226 } while ((exp /= 10) > 9); 1227 *--t = to_char(exp); 1228 for (; t < expbuf + MAXEXP; *p++ = *t++); 1229 } 1230 else { 1231 *p++ = '0'; 1232 *p++ = to_char(exp); 1233 } 1234 return (p - p0); 1235 } 1236 #endif /* FLOATING_POINT */ 1237