1 /* $OpenBSD: vfwprintf.c,v 1.15 2015/12/28 22:08:18 mmcc Exp $ */ 2 /*- 3 * Copyright (c) 1990 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * Chris Torek. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34 #define CHAR_TYPE wchar_t 35 #define FUNCTION_NAME __vfwprintf 36 #define CHAR_TYPE_STRLEN wcslen 37 #define CHAR_TYPE_STRNLEN wcsnlen 38 #define CHAR_TYPE_INF L"INF" 39 #define CHAR_TYPE_inf L"inf" 40 #define CHAR_TYPE_NAN L"NAN" 41 #define CHAR_TYPE_nan L"nan" 42 #define CHAR_TYPE_ORIENTATION 1 43 #include "printf_common.h" 44 45 int FUNCTION_NAME(FILE* fp, const CHAR_TYPE* fmt0, va_list ap) { 46 int n, n2; 47 CHAR_TYPE* cp; /* handy char pointer (short term usage) */ 48 CHAR_TYPE sign; /* sign prefix (' ', '+', '-', or \0) */ 49 int flags; /* flags as above */ 50 int ret; /* return value accumulator */ 51 int width; /* width from format (%8d), or 0 */ 52 int prec; /* precision from format; <0 for N/A */ 53 /* 54 * We can decompose the printed representation of floating 55 * point numbers into several parts, some of which may be empty: 56 * 57 * [+|-| ] [0x|0X] MMM . NNN [e|E|p|P] [+|-] ZZ 58 * A B ---C--- D E F 59 * 60 * A: 'sign' holds this value if present; '\0' otherwise 61 * B: ox[1] holds the 'x' or 'X'; '\0' if not hexadecimal 62 * C: cp points to the string MMMNNN. Leading and trailing 63 * zeros are not in the string and must be added. 64 * D: expchar holds this character; '\0' if no exponent, e.g. %f 65 * F: at least two digits for decimal, at least one digit for hex 66 */ 67 char* decimal_point = NULL; 68 int signflag; /* true if float is negative */ 69 union { /* floating point arguments %[aAeEfFgG] */ 70 double dbl; 71 long double ldbl; 72 } fparg; 73 int expt; /* integer value of exponent */ 74 char expchar; /* exponent character: [eEpP\0] */ 75 char* dtoaend; /* pointer to end of converted digits */ 76 int expsize; /* character count for expstr */ 77 int lead; /* sig figs before decimal or group sep */ 78 int ndig; /* actual number of digits returned by dtoa */ 79 CHAR_TYPE expstr[MAXEXPDIG + 2]; /* buffer for exponent string: e+ZZZ */ 80 char* dtoaresult = NULL; 81 82 uintmax_t _umax; /* integer arguments %[diouxX] */ 83 enum { OCT, DEC, HEX } base; /* base for %[diouxX] conversion */ 84 int dprec; /* a copy of prec if %[diouxX], 0 otherwise */ 85 int realsz; /* field size expanded by dprec */ 86 int size; /* size of converted field or string */ 87 const char* xdigs; /* digits for %[xX] conversion */ 88 #define NIOV 8 89 struct __suio uio; /* output information: summary */ 90 struct __siov iov[NIOV]; /* ... and individual io vectors */ 91 struct __siov* iovp; /* for PRINT macro */ 92 CHAR_TYPE buf[BUF]; /* buffer with space for digits of uintmax_t */ 93 CHAR_TYPE ox[2]; /* space for 0x; ox[1] is either x, X, or \0 */ 94 union arg* argtable; /* args, built due to positional arg */ 95 union arg statargtable[STATIC_ARG_TBL_SIZE]; 96 size_t argtablesiz; 97 int nextarg; /* 1-based argument index */ 98 va_list orgap; /* original argument pointer */ 99 CHAR_TYPE* convbuf; /* buffer for wide/multibyte conversion */ 100 101 /* 102 * Choose PADSIZE to trade efficiency vs. size. If larger printf 103 * fields occur frequently, increase PADSIZE and make the initialisers 104 * below longer. 105 */ 106 #define PADSIZE 16 /* pad chunk size */ 107 static CHAR_TYPE blanks[PADSIZE] = { 108 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' 109 }; 110 static CHAR_TYPE zeroes[PADSIZE] = { 111 '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0' 112 }; 113 114 static const char xdigs_lower[] = "0123456789abcdef"; 115 static const char xdigs_upper[] = "0123456789ABCDEF"; 116 117 #define PRINT(ptr, len) \ 118 do { \ 119 for (int n3 = 0; n3 < (len); n3++) { \ 120 if ((helpers::xfputwc((ptr)[n3], fp)) == WEOF) goto error; \ 121 } \ 122 } while (0) 123 124 _SET_ORIENTATION(fp, CHAR_TYPE_ORIENTATION); 125 126 // Writing "" to a read only file returns EOF, not 0. 127 if (cantwrite(fp)) { 128 errno = EBADF; 129 return EOF; 130 } 131 132 // Optimize writes to stderr and other unbuffered files). 133 if ((fp->_flags & (__SNBF | __SWR | __SRW)) == (__SNBF | __SWR) && fp->_file >= 0) { 134 return (__sbprintf(fp, fmt0, ap)); 135 } 136 137 CHAR_TYPE* fmt = const_cast<CHAR_TYPE*>(fmt0); 138 argtable = NULL; 139 nextarg = 1; 140 va_copy(orgap, ap); 141 uio.uio_iov = iovp = iov; 142 uio.uio_resid = 0; 143 uio.uio_iovcnt = 0; 144 ret = 0; 145 convbuf = NULL; 146 147 /* 148 * Scan the format for conversions (`%' character). 149 */ 150 for (;;) { 151 int ch; 152 for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) continue; 153 if (fmt != cp) { 154 ptrdiff_t m = fmt - cp; 155 if (m < 0 || m > INT_MAX - ret) goto overflow; 156 PRINT(cp, m); 157 ret += m; 158 } 159 if (ch == '\0') goto done; 160 fmt++; /* skip over '%' */ 161 162 flags = 0; 163 dprec = 0; 164 width = 0; 165 prec = -1; 166 sign = '\0'; 167 ox[1] = '\0'; 168 169 rflag: 170 ch = *fmt++; 171 reswitch: 172 switch (ch) { 173 case ' ': 174 /* 175 * ``If the space and + flags both appear, the space 176 * flag will be ignored.'' 177 * -- ANSI X3J11 178 */ 179 if (!sign) sign = ' '; 180 goto rflag; 181 case '#': 182 flags |= ALT; 183 goto rflag; 184 case '\'': 185 /* grouping not implemented */ 186 goto rflag; 187 case '*': 188 /* 189 * ``A negative field width argument is taken as a 190 * - flag followed by a positive field width.'' 191 * -- ANSI X3J11 192 * They don't exclude field widths read from args. 193 */ 194 GETASTER(width); 195 if (width >= 0) goto rflag; 196 if (width == INT_MIN) goto overflow; 197 width = -width; 198 /* FALLTHROUGH */ 199 case '-': 200 flags |= LADJUST; 201 goto rflag; 202 case '+': 203 sign = '+'; 204 goto rflag; 205 case '.': 206 if ((ch = *fmt++) == '*') { 207 GETASTER(n); 208 prec = n < 0 ? -1 : n; 209 goto rflag; 210 } 211 n = 0; 212 while (is_digit(ch)) { 213 APPEND_DIGIT(n, ch); 214 ch = *fmt++; 215 } 216 if (ch == '$') { 217 nextarg = n; 218 if (argtable == NULL) { 219 argtable = statargtable; 220 if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { 221 ret = -1; 222 goto error; 223 } 224 } 225 goto rflag; 226 } 227 prec = n; 228 goto reswitch; 229 case '0': 230 /* 231 * ``Note that 0 is taken as a flag, not as the 232 * beginning of a field width.'' 233 * -- ANSI X3J11 234 */ 235 flags |= ZEROPAD; 236 goto rflag; 237 case '1': 238 case '2': 239 case '3': 240 case '4': 241 case '5': 242 case '6': 243 case '7': 244 case '8': 245 case '9': 246 n = 0; 247 do { 248 APPEND_DIGIT(n, ch); 249 ch = *fmt++; 250 } while (is_digit(ch)); 251 if (ch == '$') { 252 nextarg = n; 253 if (argtable == NULL) { 254 argtable = statargtable; 255 if (__find_arguments(fmt0, orgap, &argtable, &argtablesiz) == -1) { 256 ret = -1; 257 goto error; 258 } 259 } 260 goto rflag; 261 } 262 width = n; 263 goto reswitch; 264 case 'L': 265 flags |= LONGDBL; 266 goto rflag; 267 case 'h': 268 if (*fmt == 'h') { 269 fmt++; 270 flags |= CHARINT; 271 } else { 272 flags |= SHORTINT; 273 } 274 goto rflag; 275 case 'j': 276 flags |= MAXINT; 277 goto rflag; 278 case 'l': 279 if (*fmt == 'l') { 280 fmt++; 281 flags |= LLONGINT; 282 } else { 283 flags |= LONGINT; 284 } 285 goto rflag; 286 case 'q': 287 flags |= LLONGINT; 288 goto rflag; 289 case 't': 290 flags |= PTRINT; 291 goto rflag; 292 case 'z': 293 flags |= SIZEINT; 294 goto rflag; 295 case 'C': 296 flags |= LONGINT; 297 /*FALLTHROUGH*/ 298 case 'c': 299 if (flags & LONGINT) 300 *(cp = buf) = (wchar_t)GETARG(wint_t); 301 else 302 *(cp = buf) = (wchar_t)btowc(GETARG(int)); 303 size = 1; 304 sign = '\0'; 305 break; 306 case 'D': 307 flags |= LONGINT; 308 /*FALLTHROUGH*/ 309 case 'd': 310 case 'i': 311 _umax = SARG(); 312 if ((intmax_t)_umax < 0) { 313 _umax = -_umax; 314 sign = '-'; 315 } 316 base = DEC; 317 goto number; 318 case 'a': 319 case 'A': 320 if (ch == 'a') { 321 ox[1] = 'x'; 322 xdigs = xdigs_lower; 323 expchar = 'p'; 324 } else { 325 ox[1] = 'X'; 326 xdigs = xdigs_upper; 327 expchar = 'P'; 328 } 329 if (prec >= 0) prec++; 330 if (dtoaresult) __freedtoa(dtoaresult); 331 if (flags & LONGDBL) { 332 fparg.ldbl = GETARG(long double); 333 dtoaresult = __hldtoa(fparg.ldbl, xdigs, prec, &expt, &signflag, &dtoaend); 334 if (dtoaresult == NULL) { 335 errno = ENOMEM; 336 goto error; 337 } 338 } else { 339 fparg.dbl = GETARG(double); 340 dtoaresult = __hdtoa(fparg.dbl, xdigs, prec, &expt, &signflag, &dtoaend); 341 if (dtoaresult == NULL) { 342 errno = ENOMEM; 343 goto error; 344 } 345 } 346 if (prec < 0) prec = dtoaend - dtoaresult; 347 if (expt == INT_MAX) ox[1] = '\0'; 348 free(convbuf); 349 cp = convbuf = helpers::mbsconv(dtoaresult, -1); 350 if (cp == NULL) goto error; 351 ndig = dtoaend - dtoaresult; 352 goto fp_common; 353 case 'e': 354 case 'E': 355 expchar = ch; 356 if (prec < 0) /* account for digit before decpt */ 357 prec = DEFPREC + 1; 358 else 359 prec++; 360 goto fp_begin; 361 case 'f': 362 case 'F': 363 expchar = '\0'; 364 goto fp_begin; 365 case 'g': 366 case 'G': 367 expchar = ch - ('g' - 'e'); 368 if (prec == 0) prec = 1; 369 fp_begin: 370 if (prec < 0) prec = DEFPREC; 371 if (dtoaresult) __freedtoa(dtoaresult); 372 if (flags & LONGDBL) { 373 fparg.ldbl = GETARG(long double); 374 dtoaresult = __ldtoa(&fparg.ldbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); 375 if (dtoaresult == NULL) { 376 errno = ENOMEM; 377 goto error; 378 } 379 } else { 380 fparg.dbl = GETARG(double); 381 dtoaresult = __dtoa(fparg.dbl, expchar ? 2 : 3, prec, &expt, &signflag, &dtoaend); 382 if (dtoaresult == NULL) { 383 errno = ENOMEM; 384 goto error; 385 } 386 if (expt == 9999) expt = INT_MAX; 387 } 388 free(convbuf); 389 cp = convbuf = helpers::mbsconv(dtoaresult, -1); 390 if (cp == NULL) goto error; 391 ndig = dtoaend - dtoaresult; 392 fp_common: 393 if (signflag) sign = '-'; 394 if (expt == INT_MAX) { /* inf or nan */ 395 if (*cp == 'N') { 396 cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_nan : CHAR_TYPE_NAN); 397 } else { 398 cp = const_cast<CHAR_TYPE*>((ch >= 'a') ? CHAR_TYPE_inf : CHAR_TYPE_INF); 399 } 400 size = 3; 401 flags &= ~ZEROPAD; 402 break; 403 } 404 flags |= FPT; 405 if (ch == 'g' || ch == 'G') { 406 if (expt > -4 && expt <= prec) { 407 /* Make %[gG] smell like %[fF] */ 408 expchar = '\0'; 409 if (flags & ALT) 410 prec -= expt; 411 else 412 prec = ndig - expt; 413 if (prec < 0) prec = 0; 414 } else { 415 /* 416 * Make %[gG] smell like %[eE], but 417 * trim trailing zeroes if no # flag. 418 */ 419 if (!(flags & ALT)) prec = ndig; 420 } 421 } 422 if (expchar) { 423 expsize = exponent(expstr, expt - 1, expchar); 424 size = expsize + prec; 425 if (prec > 1 || flags & ALT) ++size; 426 } else { 427 /* space for digits before decimal point */ 428 if (expt > 0) 429 size = expt; 430 else /* "0" */ 431 size = 1; 432 /* space for decimal pt and following digits */ 433 if (prec || flags & ALT) size += prec + 1; 434 lead = expt; 435 } 436 break; 437 #ifndef NO_PRINTF_PERCENT_N 438 case 'n': 439 if (flags & LLONGINT) 440 *GETARG(long long*) = ret; 441 else if (flags & LONGINT) 442 *GETARG(long*) = ret; 443 else if (flags & SHORTINT) 444 *GETARG(short*) = ret; 445 else if (flags & CHARINT) 446 *GETARG(signed char*) = ret; 447 else if (flags & PTRINT) 448 *GETARG(ptrdiff_t*) = ret; 449 else if (flags & SIZEINT) 450 *GETARG(ssize_t*) = ret; 451 else if (flags & MAXINT) 452 *GETARG(intmax_t*) = ret; 453 else 454 *GETARG(int*) = ret; 455 continue; /* no output */ 456 #endif /* NO_PRINTF_PERCENT_N */ 457 case 'O': 458 flags |= LONGINT; 459 /*FALLTHROUGH*/ 460 case 'o': 461 _umax = UARG(); 462 base = OCT; 463 goto nosign; 464 case 'p': 465 /* 466 * ``The argument shall be a pointer to void. The 467 * value of the pointer is converted to a sequence 468 * of printable characters, in an implementation- 469 * defined manner.'' 470 * -- ANSI X3J11 471 */ 472 _umax = (u_long)GETARG(void*); 473 base = HEX; 474 xdigs = xdigs_lower; 475 ox[1] = 'x'; 476 goto nosign; 477 case 'S': 478 flags |= LONGINT; 479 /*FALLTHROUGH*/ 480 case 's': 481 if (flags & LONGINT) { 482 if ((cp = GETARG(wchar_t*)) == NULL) cp = const_cast<wchar_t*>(L"(null)"); 483 } else { 484 char* mbsarg; 485 if ((mbsarg = GETARG(char*)) == NULL) mbsarg = const_cast<char*>("(null)"); 486 free(convbuf); 487 convbuf = helpers::mbsconv(mbsarg, prec); 488 if (convbuf == NULL) { 489 fp->_flags |= __SERR; 490 goto error; 491 } else { 492 cp = convbuf; 493 } 494 } 495 if (prec >= 0) { 496 size = CHAR_TYPE_STRNLEN(cp, prec); 497 } else { 498 size_t len; 499 500 if ((len = CHAR_TYPE_STRLEN(cp)) > INT_MAX) goto overflow; 501 size = (int)len; 502 } 503 sign = '\0'; 504 break; 505 case 'U': 506 flags |= LONGINT; 507 /*FALLTHROUGH*/ 508 case 'u': 509 _umax = UARG(); 510 base = DEC; 511 goto nosign; 512 case 'X': 513 xdigs = xdigs_upper; 514 goto hex; 515 case 'x': 516 xdigs = xdigs_lower; 517 hex: 518 _umax = UARG(); 519 base = HEX; 520 /* leading 0x/X only if non-zero */ 521 if (flags & ALT && _umax != 0) ox[1] = ch; 522 523 /* unsigned conversions */ 524 nosign: 525 sign = '\0'; 526 /* 527 * ``... diouXx conversions ... if a precision is 528 * specified, the 0 flag will be ignored.'' 529 * -- ANSI X3J11 530 */ 531 number: 532 if ((dprec = prec) >= 0) flags &= ~ZEROPAD; 533 534 /* 535 * ``The result of converting a zero value with an 536 * explicit precision of zero is no characters.'' 537 * -- ANSI X3J11 538 */ 539 cp = buf + BUF; 540 if (_umax != 0 || prec != 0) { 541 /* 542 * Unsigned mod is hard, and unsigned mod 543 * by a constant is easier than that by 544 * a variable; hence this switch. 545 */ 546 switch (base) { 547 case OCT: 548 do { 549 *--cp = to_char(_umax & 7); 550 _umax >>= 3; 551 } while (_umax); 552 /* handle octal leading 0 */ 553 if (flags & ALT && *cp != '0') *--cp = '0'; 554 break; 555 556 case DEC: 557 /* many numbers are 1 digit */ 558 while (_umax >= 10) { 559 *--cp = to_char(_umax % 10); 560 _umax /= 10; 561 } 562 *--cp = to_char(_umax); 563 break; 564 565 case HEX: 566 do { 567 *--cp = xdigs[_umax & 15]; 568 _umax >>= 4; 569 } while (_umax); 570 break; 571 572 default: 573 abort(); 574 } 575 } 576 size = buf + BUF - cp; 577 if (size > BUF) abort(); /* should never happen */ 578 break; 579 default: /* "%?" prints ?, unless ? is NUL */ 580 if (ch == '\0') goto done; 581 /* pretend it was %c with argument ch */ 582 cp = buf; 583 *cp = ch; 584 size = 1; 585 sign = '\0'; 586 break; 587 } 588 589 /* 590 * All reasonable formats wind up here. At this point, `cp' 591 * points to a string which (if not flags&LADJUST) should be 592 * padded out to `width' places. If flags&ZEROPAD, it should 593 * first be prefixed by any sign or other prefix; otherwise, 594 * it should be blank padded before the prefix is emitted. 595 * After any left-hand padding and prefixing, emit zeroes 596 * required by a decimal %[diouxX] precision, then print the 597 * string proper, then emit zeroes required by any leftover 598 * floating precision; finally, if LADJUST, pad with blanks. 599 * 600 * Compute actual size, so we know how much to pad. 601 * size excludes decimal prec; realsz includes it. 602 */ 603 realsz = dprec > size ? dprec : size; 604 if (sign) realsz++; 605 if (ox[1]) realsz += 2; 606 607 /* right-adjusting blank padding */ 608 if ((flags & (LADJUST | ZEROPAD)) == 0) PAD(width - realsz, blanks); 609 610 /* prefix */ 611 if (sign) PRINT(&sign, 1); 612 if (ox[1]) { /* ox[1] is either x, X, or \0 */ 613 ox[0] = '0'; 614 PRINT(ox, 2); 615 } 616 617 /* right-adjusting zero padding */ 618 if ((flags & (LADJUST | ZEROPAD)) == ZEROPAD) PAD(width - realsz, zeroes); 619 620 /* leading zeroes from decimal precision */ 621 PAD(dprec - size, zeroes); 622 623 /* the string or number proper */ 624 if ((flags & FPT) == 0) { 625 PRINT(cp, size); 626 } else { /* glue together f_p fragments */ 627 if (decimal_point == NULL) decimal_point = nl_langinfo(RADIXCHAR); 628 if (!expchar) { /* %[fF] or sufficiently short %[gG] */ 629 if (expt <= 0) { 630 PRINT(zeroes, 1); 631 if (prec || flags & ALT) PRINT(decimal_point, 1); 632 PAD(-expt, zeroes); 633 /* already handled initial 0's */ 634 prec += expt; 635 } else { 636 PRINTANDPAD(cp, convbuf + ndig, lead, zeroes); 637 cp += lead; 638 if (prec || flags & ALT) PRINT(decimal_point, 1); 639 } 640 PRINTANDPAD(cp, convbuf + ndig, prec, zeroes); 641 } else { /* %[eE] or sufficiently long %[gG] */ 642 if (prec > 1 || flags & ALT) { 643 buf[0] = *cp++; 644 buf[1] = *decimal_point; 645 PRINT(buf, 2); 646 PRINT(cp, ndig - 1); 647 PAD(prec - ndig, zeroes); 648 } else { /* XeYYY */ 649 PRINT(cp, 1); 650 } 651 PRINT(expstr, expsize); 652 } 653 } 654 /* left-adjusting padding (always blank) */ 655 if (flags & LADJUST) PAD(width - realsz, blanks); 656 657 /* finally, adjust ret */ 658 if (width < realsz) width = realsz; 659 if (width > INT_MAX - ret) goto overflow; 660 ret += width; 661 } 662 done: 663 error: 664 va_end(orgap); 665 if (__sferror(fp)) ret = -1; 666 goto finish; 667 668 overflow: 669 errno = ENOMEM; 670 ret = -1; 671 672 finish: 673 free(convbuf); 674 if (dtoaresult) __freedtoa(dtoaresult); 675 if (argtable != NULL && argtable != statargtable) { 676 munmap(argtable, argtablesiz); 677 argtable = NULL; 678 } 679 return (ret); 680 } 681