1 /* $OpenBSD: vfwscanf.c,v 1.4 2014/03/19 05:17:01 guenther Exp $ */ 2 /*- 3 * Copyright (c) 1990, 1993 4 * The Regents of the University of California. 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 #include <inttypes.h> 35 #include <limits.h> 36 #include <locale.h> 37 #include <stdarg.h> 38 #include <stddef.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <wctype.h> 43 #include "local.h" 44 45 #ifdef FLOATING_POINT 46 #include "floatio.h" 47 #endif 48 49 #define BUF 513 /* Maximum length of numeric string. */ 50 51 /* 52 * Flags used during conversion. 53 */ 54 #define LONG 0x00001 /* l: long or double */ 55 #define LONGDBL 0x00002 /* L: long double */ 56 #define SHORT 0x00004 /* h: short */ 57 #define SHORTSHORT 0x00008 /* hh: 8 bit integer */ 58 #define LLONG 0x00010 /* ll: long long (+ deprecated q: quad) */ 59 #define POINTER 0x00020 /* p: void * (as hex) */ 60 #define SIZEINT 0x00040 /* z: (signed) size_t */ 61 #define MAXINT 0x00080 /* j: intmax_t */ 62 #define PTRINT 0x00100 /* t: ptrdiff_t */ 63 #define NOSKIP 0x00200 /* [ or c: do not skip blanks */ 64 #define SUPPRESS 0x00400 /* *: suppress assignment */ 65 #define UNSIGNED 0x00800 /* %[oupxX] conversions */ 66 67 /* 68 * The following are used in numeric conversions only: 69 * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point; 70 * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral. 71 */ 72 #define SIGNOK 0x01000 /* +/- is (still) legal */ 73 #define HAVESIGN 0x02000 /* sign detected */ 74 #define NDIGITS 0x04000 /* no digits detected */ 75 76 #define DPTOK 0x08000 /* (float) decimal point is still legal */ 77 #define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */ 78 79 #define PFXOK 0x08000 /* 0x prefix is (still) legal */ 80 #define NZDIGITS 0x10000 /* no zero digits detected */ 81 82 /* 83 * Conversion types. 84 */ 85 #define CT_CHAR 0 /* %c conversion */ 86 #define CT_CCL 1 /* %[...] conversion */ 87 #define CT_STRING 2 /* %s conversion */ 88 #define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */ 89 #define CT_FLOAT 4 /* floating, i.e., strtod */ 90 91 #define u_char unsigned char 92 #define u_long unsigned long 93 94 #define INCCL(_c) \ 95 (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \ 96 (wmemchr(ccls, (_c), ccle - ccls) != NULL)) 97 98 #pragma GCC diagnostic push 99 #pragma GCC diagnostic ignored "-Wframe-larger-than=" 100 101 /* 102 * vfwscanf 103 */ 104 int 105 __vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, __va_list ap) 106 { 107 wint_t c; /* character from format, or conversion */ 108 size_t width; /* field width, or 0 */ 109 wchar_t *p; /* points into all kinds of strings */ 110 int n; /* handy integer */ 111 int flags; /* flags as defined above */ 112 wchar_t *p0; /* saves original value of p when necessary */ 113 int nassigned; /* number of fields assigned */ 114 int nconversions; /* number of conversions */ 115 int nread; /* number of characters consumed from fp */ 116 int base; /* base argument to strtoimax/strtouimax */ 117 wchar_t buf[BUF]; /* buffer for numeric conversions */ 118 const wchar_t *ccls; /* character class start */ 119 const wchar_t *ccle; /* character class end */ 120 int cclcompl; /* ccl is complemented? */ 121 wint_t wi; /* handy wint_t */ 122 char *mbp; /* multibyte string pointer for %c %s %[ */ 123 size_t nconv; /* number of bytes in mb. conversion */ 124 char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */ 125 mbstate_t mbs; 126 127 /* `basefix' is used to avoid `if' tests in the integer scanner */ 128 static short basefix[17] = 129 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 130 131 _SET_ORIENTATION(fp, 1); 132 133 nassigned = 0; 134 nconversions = 0; 135 nread = 0; 136 base = 0; /* XXX just to keep gcc happy */ 137 ccls = ccle = NULL; 138 for (;;) { 139 c = *fmt++; 140 if (c == 0) { 141 return (nassigned); 142 } 143 if (iswspace(c)) { 144 while ((c = __fgetwc_unlock(fp)) != WEOF && 145 iswspace(c)) 146 ; 147 if (c != WEOF) 148 __ungetwc(c, fp); 149 continue; 150 } 151 if (c != '%') 152 goto literal; 153 width = 0; 154 flags = 0; 155 /* 156 * switch on the format. continue if done; 157 * break once format type is derived. 158 */ 159 again: c = *fmt++; 160 switch (c) { 161 case '%': 162 literal: 163 if ((wi = __fgetwc_unlock(fp)) == WEOF) 164 goto input_failure; 165 if (wi != c) { 166 __ungetwc(wi, fp); 167 goto input_failure; 168 } 169 nread++; 170 continue; 171 172 case '*': 173 flags |= SUPPRESS; 174 goto again; 175 case 'j': 176 flags |= MAXINT; 177 goto again; 178 case 'L': 179 flags |= LONGDBL; 180 goto again; 181 case 'h': 182 if (*fmt == 'h') { 183 fmt++; 184 flags |= SHORTSHORT; 185 } else { 186 flags |= SHORT; 187 } 188 goto again; 189 case 'l': 190 if (*fmt == 'l') { 191 fmt++; 192 flags |= LLONG; 193 } else { 194 flags |= LONG; 195 } 196 goto again; 197 case 'q': 198 flags |= LLONG; /* deprecated */ 199 goto again; 200 case 't': 201 flags |= PTRINT; 202 goto again; 203 case 'z': 204 flags |= SIZEINT; 205 goto again; 206 207 case '0': case '1': case '2': case '3': case '4': 208 case '5': case '6': case '7': case '8': case '9': 209 width = width * 10 + c - '0'; 210 goto again; 211 212 /* 213 * Conversions. 214 * Those marked `compat' are for 4.[123]BSD compatibility. 215 * 216 * (According to ANSI, E and X formats are supposed 217 * to the same as e and x. Sorry about that.) 218 */ 219 case 'D': /* compat */ 220 flags |= LONG; 221 /* FALLTHROUGH */ 222 case 'd': 223 c = CT_INT; 224 base = 10; 225 break; 226 227 case 'i': 228 c = CT_INT; 229 base = 0; 230 break; 231 232 case 'O': /* compat */ 233 flags |= LONG; 234 /* FALLTHROUGH */ 235 case 'o': 236 c = CT_INT; 237 flags |= UNSIGNED; 238 base = 8; 239 break; 240 241 case 'u': 242 c = CT_INT; 243 flags |= UNSIGNED; 244 base = 10; 245 break; 246 247 case 'X': 248 case 'x': 249 flags |= PFXOK; /* enable 0x prefixing */ 250 c = CT_INT; 251 flags |= UNSIGNED; 252 base = 16; 253 break; 254 255 #ifdef FLOATING_POINT 256 case 'e': case 'E': 257 case 'f': case 'F': 258 case 'g': case 'G': 259 case 'a': case 'A': 260 c = CT_FLOAT; 261 break; 262 #endif 263 264 case 's': 265 c = CT_STRING; 266 break; 267 268 case '[': 269 ccls = fmt; 270 if (*fmt == '^') { 271 cclcompl = 1; 272 fmt++; 273 } else 274 cclcompl = 0; 275 if (*fmt == ']') 276 fmt++; 277 while (*fmt != '\0' && *fmt != ']') 278 fmt++; 279 ccle = fmt; 280 fmt++; 281 flags |= NOSKIP; 282 c = CT_CCL; 283 break; 284 285 case 'c': 286 flags |= NOSKIP; 287 c = CT_CHAR; 288 break; 289 290 case 'p': /* pointer format is like hex */ 291 flags |= POINTER | PFXOK; 292 c = CT_INT; 293 flags |= UNSIGNED; 294 base = 16; 295 break; 296 297 case 'n': 298 nconversions++; 299 if (flags & SUPPRESS) 300 continue; 301 if (flags & SHORTSHORT) 302 *va_arg(ap, signed char *) = nread; 303 else if (flags & SHORT) 304 *va_arg(ap, short *) = nread; 305 else if (flags & LONG) 306 *va_arg(ap, long *) = nread; 307 else if (flags & SIZEINT) 308 *va_arg(ap, ssize_t *) = nread; 309 else if (flags & PTRINT) 310 *va_arg(ap, ptrdiff_t *) = nread; 311 else if (flags & LLONG) 312 *va_arg(ap, long long *) = nread; 313 else if (flags & MAXINT) 314 *va_arg(ap, intmax_t *) = nread; 315 else 316 *va_arg(ap, int *) = nread; 317 continue; 318 319 /* 320 * Disgusting backwards compatibility hacks. XXX 321 */ 322 case '\0': /* compat */ 323 return (EOF); 324 325 default: /* compat */ 326 if (iswupper(c)) 327 flags |= LONG; 328 c = CT_INT; 329 base = 10; 330 break; 331 } 332 333 /* 334 * Consume leading white space, except for formats 335 * that suppress this. 336 */ 337 if ((flags & NOSKIP) == 0) { 338 while ((wi = __fgetwc_unlock(fp)) != WEOF && 339 iswspace(wi)) 340 nread++; 341 if (wi == WEOF) 342 goto input_failure; 343 __ungetwc(wi, fp); 344 } 345 346 /* 347 * Do the conversion. 348 */ 349 switch (c) { 350 351 case CT_CHAR: 352 /* scan arbitrary characters (sets NOSKIP) */ 353 if (width == 0) 354 width = 1; 355 if (flags & LONG) { 356 if (!(flags & SUPPRESS)) 357 p = va_arg(ap, wchar_t *); 358 n = 0; 359 while (width-- != 0 && 360 (wi = __fgetwc_unlock(fp)) != WEOF) { 361 if (!(flags & SUPPRESS)) 362 *p++ = (wchar_t)wi; 363 n++; 364 } 365 if (n == 0) 366 goto input_failure; 367 nread += n; 368 if (!(flags & SUPPRESS)) 369 nassigned++; 370 } else { 371 if (!(flags & SUPPRESS)) 372 mbp = va_arg(ap, char *); 373 n = 0; 374 memset(&mbs, 0, sizeof(mbs)); 375 while (width != 0 && 376 (wi = __fgetwc_unlock(fp)) != WEOF) { 377 if (width >= MB_CUR_MAX && 378 !(flags & SUPPRESS)) { 379 nconv = wcrtomb(mbp, wi, &mbs); 380 if (nconv == (size_t)-1) 381 goto input_failure; 382 } else { 383 nconv = wcrtomb(mbbuf, wi, 384 &mbs); 385 if (nconv == (size_t)-1) 386 goto input_failure; 387 if (nconv > width) { 388 __ungetwc(wi, fp); 389 break; 390 } 391 if (!(flags & SUPPRESS)) 392 memcpy(mbp, mbbuf, 393 nconv); 394 } 395 if (!(flags & SUPPRESS)) 396 mbp += nconv; 397 width -= nconv; 398 n++; 399 } 400 if (n == 0) 401 goto input_failure; 402 nread += n; 403 if (!(flags & SUPPRESS)) 404 nassigned++; 405 } 406 nconversions++; 407 break; 408 409 case CT_CCL: 410 /* scan a (nonempty) character class (sets NOSKIP) */ 411 if (width == 0) 412 width = (size_t)~0; /* `infinity' */ 413 /* take only those things in the class */ 414 if ((flags & SUPPRESS) && (flags & LONG)) { 415 n = 0; 416 while ((wi = __fgetwc_unlock(fp)) != WEOF && 417 width-- != 0 && INCCL(wi)) 418 n++; 419 if (wi != WEOF) 420 __ungetwc(wi, fp); 421 if (n == 0) 422 goto match_failure; 423 } else if (flags & LONG) { 424 p0 = p = va_arg(ap, wchar_t *); 425 while ((wi = __fgetwc_unlock(fp)) != WEOF && 426 width-- != 0 && INCCL(wi)) 427 *p++ = (wchar_t)wi; 428 if (wi != WEOF) 429 __ungetwc(wi, fp); 430 n = p - p0; 431 if (n == 0) 432 goto match_failure; 433 *p = 0; 434 nassigned++; 435 } else { 436 if (!(flags & SUPPRESS)) 437 mbp = va_arg(ap, char *); 438 n = 0; 439 memset(&mbs, 0, sizeof(mbs)); 440 while ((wi = __fgetwc_unlock(fp)) != WEOF && 441 width != 0 && INCCL(wi)) { 442 if (width >= MB_CUR_MAX && 443 !(flags & SUPPRESS)) { 444 nconv = wcrtomb(mbp, wi, &mbs); 445 if (nconv == (size_t)-1) 446 goto input_failure; 447 } else { 448 nconv = wcrtomb(mbbuf, wi, 449 &mbs); 450 if (nconv == (size_t)-1) 451 goto input_failure; 452 if (nconv > width) 453 break; 454 if (!(flags & SUPPRESS)) 455 memcpy(mbp, mbbuf, 456 nconv); 457 } 458 if (!(flags & SUPPRESS)) 459 mbp += nconv; 460 width -= nconv; 461 n++; 462 } 463 if (wi != WEOF) 464 __ungetwc(wi, fp); 465 if (!(flags & SUPPRESS)) { 466 *mbp = 0; 467 nassigned++; 468 } 469 } 470 nread += n; 471 nconversions++; 472 break; 473 474 case CT_STRING: 475 /* like CCL, but zero-length string OK, & no NOSKIP */ 476 if (width == 0) 477 width = (size_t)~0; 478 if ((flags & SUPPRESS) && (flags & LONG)) { 479 while ((wi = __fgetwc_unlock(fp)) != WEOF && 480 width-- != 0 && 481 !iswspace(wi)) 482 nread++; 483 if (wi != WEOF) 484 __ungetwc(wi, fp); 485 } else if (flags & LONG) { 486 p0 = p = va_arg(ap, wchar_t *); 487 while ((wi = __fgetwc_unlock(fp)) != WEOF && 488 width-- != 0 && 489 !iswspace(wi)) { 490 *p++ = (wchar_t)wi; 491 nread++; 492 } 493 if (wi != WEOF) 494 __ungetwc(wi, fp); 495 *p = 0; 496 nassigned++; 497 } else { 498 if (!(flags & SUPPRESS)) 499 mbp = va_arg(ap, char *); 500 memset(&mbs, 0, sizeof(mbs)); 501 while ((wi = __fgetwc_unlock(fp)) != WEOF && 502 width != 0 && 503 !iswspace(wi)) { 504 if (width >= MB_CUR_MAX && 505 !(flags & SUPPRESS)) { 506 nconv = wcrtomb(mbp, wi, &mbs); 507 if (nconv == (size_t)-1) 508 goto input_failure; 509 } else { 510 nconv = wcrtomb(mbbuf, wi, 511 &mbs); 512 if (nconv == (size_t)-1) 513 goto input_failure; 514 if (nconv > width) 515 break; 516 if (!(flags & SUPPRESS)) 517 memcpy(mbp, mbbuf, 518 nconv); 519 } 520 if (!(flags & SUPPRESS)) 521 mbp += nconv; 522 width -= nconv; 523 nread++; 524 } 525 if (wi != WEOF) 526 __ungetwc(wi, fp); 527 if (!(flags & SUPPRESS)) { 528 *mbp = 0; 529 nassigned++; 530 } 531 } 532 nconversions++; 533 continue; 534 535 case CT_INT: 536 /* scan an integer as if by strtoimax/strtoumax */ 537 if (width == 0 || width > sizeof(buf) / 538 sizeof(*buf) - 1) 539 width = sizeof(buf) / sizeof(*buf) - 1; 540 flags |= SIGNOK | NDIGITS | NZDIGITS; 541 for (p = buf; width; width--) { 542 c = __fgetwc_unlock(fp); 543 /* 544 * Switch on the character; `goto ok' 545 * if we accept it as a part of number. 546 */ 547 switch (c) { 548 549 /* 550 * The digit 0 is always legal, but is 551 * special. For %i conversions, if no 552 * digits (zero or nonzero) have been 553 * scanned (only signs), we will have 554 * base==0. In that case, we should set 555 * it to 8 and enable 0x prefixing. 556 * Also, if we have not scanned zero digits 557 * before this, do not turn off prefixing 558 * (someone else will turn it off if we 559 * have scanned any nonzero digits). 560 */ 561 case '0': 562 if (base == 0) { 563 base = 8; 564 flags |= PFXOK; 565 } 566 if (flags & NZDIGITS) 567 flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 568 else 569 flags &= ~(SIGNOK|PFXOK|NDIGITS); 570 goto ok; 571 572 /* 1 through 7 always legal */ 573 case '1': case '2': case '3': 574 case '4': case '5': case '6': case '7': 575 base = basefix[base]; 576 flags &= ~(SIGNOK | PFXOK | NDIGITS); 577 goto ok; 578 579 /* digits 8 and 9 ok iff decimal or hex */ 580 case '8': case '9': 581 base = basefix[base]; 582 if (base <= 8) 583 break; /* not legal here */ 584 flags &= ~(SIGNOK | PFXOK | NDIGITS); 585 goto ok; 586 587 /* letters ok iff hex */ 588 case 'A': case 'B': case 'C': 589 case 'D': case 'E': case 'F': 590 case 'a': case 'b': case 'c': 591 case 'd': case 'e': case 'f': 592 /* no need to fix base here */ 593 if (base <= 10) 594 break; /* not legal here */ 595 flags &= ~(SIGNOK | PFXOK | NDIGITS); 596 goto ok; 597 598 /* sign ok only as first character */ 599 case '+': case '-': 600 if (flags & SIGNOK) { 601 flags &= ~SIGNOK; 602 flags |= HAVESIGN; 603 goto ok; 604 } 605 break; 606 607 /* 608 * x ok iff flag still set and 2nd char (or 609 * 3rd char if we have a sign). 610 */ 611 case 'x': case 'X': 612 if ((flags & PFXOK) && p == 613 buf + 1 + !!(flags & HAVESIGN)) { 614 base = 16; /* if %i */ 615 flags &= ~PFXOK; 616 goto ok; 617 } 618 break; 619 } 620 621 /* 622 * If we got here, c is not a legal character 623 * for a number. Stop accumulating digits. 624 */ 625 if (c != WEOF) 626 __ungetwc(c, fp); 627 break; 628 ok: 629 /* 630 * c is legal: store it and look at the next. 631 */ 632 *p++ = (wchar_t)c; 633 } 634 /* 635 * If we had only a sign, it is no good; push 636 * back the sign. If the number ends in `x', 637 * it was [sign] '0' 'x', so push back the x 638 * and treat it as [sign] '0'. 639 */ 640 if (flags & NDIGITS) { 641 if (p > buf) 642 __ungetwc(*--p, fp); 643 goto match_failure; 644 } 645 c = p[-1]; 646 if (c == 'x' || c == 'X') { 647 --p; 648 __ungetwc(c, fp); 649 } 650 if ((flags & SUPPRESS) == 0) { 651 uintmax_t res; 652 653 *p = '\0'; 654 if (flags & UNSIGNED) 655 res = wcstoimax(buf, NULL, base); 656 else 657 res = wcstoumax(buf, NULL, base); 658 if (flags & POINTER) 659 *va_arg(ap, void **) = 660 (void *)(uintptr_t)res; 661 else if (flags & MAXINT) 662 *va_arg(ap, intmax_t *) = res; 663 else if (flags & LLONG) 664 *va_arg(ap, long long *) = res; 665 else if (flags & SIZEINT) 666 *va_arg(ap, ssize_t *) = res; 667 else if (flags & PTRINT) 668 *va_arg(ap, ptrdiff_t *) = res; 669 else if (flags & LONG) 670 *va_arg(ap, long *) = res; 671 else if (flags & SHORT) 672 *va_arg(ap, short *) = res; 673 else if (flags & SHORTSHORT) 674 *va_arg(ap, signed char *) = res; 675 else 676 *va_arg(ap, int *) = res; 677 nassigned++; 678 } 679 nread += p - buf; 680 nconversions++; 681 break; 682 683 #ifdef FLOATING_POINT 684 case CT_FLOAT: 685 /* scan a floating point number as if by strtod */ 686 if (width == 0 || width > sizeof(buf) / 687 sizeof(*buf) - 1) 688 width = sizeof(buf) / sizeof(*buf) - 1; 689 if ((width = wparsefloat(fp, buf, buf + width)) == 0) 690 goto match_failure; 691 if ((flags & SUPPRESS) == 0) { 692 if (flags & LONGDBL) { 693 long double res = wcstold(buf, &p); 694 *va_arg(ap, long double *) = res; 695 } else if (flags & LONG) { 696 double res = wcstod(buf, &p); 697 *va_arg(ap, double *) = res; 698 } else { 699 float res = wcstof(buf, &p); 700 *va_arg(ap, float *) = res; 701 } 702 if (p - buf != (ptrdiff_t)width) abort(); 703 nassigned++; 704 } 705 nread += width; 706 nconversions++; 707 break; 708 #endif /* FLOATING_POINT */ 709 } 710 } 711 input_failure: 712 return (nconversions != 0 ? nassigned : EOF); 713 match_failure: 714 return (nassigned); 715 } 716 #pragma GCC diagnostic pop 717 718 int 719 vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, __va_list ap) 720 { 721 int r; 722 723 FLOCKFILE(fp); 724 r = __vfwscanf(fp, fmt, ap); 725 FUNLOCKFILE(fp); 726 return (r); 727 } 728