1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1990, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Chris Torek. 9 * 10 * Copyright (c) 2011 The FreeBSD Foundation 11 * 12 * Copyright (c) 2023 Dag-Erling Smørgrav 13 * 14 * Portions of this software were developed by David Chisnall 15 * under sponsorship from the FreeBSD Foundation. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 */ 41 42 #if 0 43 #if defined(LIBC_SCCS) && !defined(lint) 44 static char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93"; 45 #endif /* LIBC_SCCS and not lint */ 46 #endif 47 #include <sys/cdefs.h> 48 #include "namespace.h" 49 #include <ctype.h> 50 #include <inttypes.h> 51 #include <limits.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <stddef.h> 55 #include <stdarg.h> 56 #include <string.h> 57 #include <wchar.h> 58 #include <wctype.h> 59 #include "un-namespace.h" 60 61 #include "libc_private.h" 62 #include "local.h" 63 #include "xlocale_private.h" 64 65 #define BUF 513 /* Maximum length of numeric string. */ 66 67 /* 68 * Flags used during conversion. 69 */ 70 #define LONG 0x01 /* l: long or double */ 71 #define LONGDBL 0x02 /* L: long double */ 72 #define SHORT 0x04 /* h: short */ 73 #define SUPPRESS 0x08 /* *: suppress assignment */ 74 #define POINTER 0x10 /* p: void * (as hex) */ 75 #define NOSKIP 0x20 /* [ or c: do not skip blanks */ 76 #define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */ 77 #define INTMAXT 0x800 /* j: intmax_t */ 78 #define PTRDIFFT 0x1000 /* t: ptrdiff_t */ 79 #define SIZET 0x2000 /* z: size_t */ 80 #define SHORTSHORT 0x4000 /* hh: char */ 81 #define UNSIGNED 0x8000 /* %[oupxX] conversions */ 82 83 /* 84 * Conversion types. 85 */ 86 #define CT_CHAR 0 /* %c conversion */ 87 #define CT_CCL 1 /* %[...] conversion */ 88 #define CT_STRING 2 /* %s conversion */ 89 #define CT_INT 3 /* %[dioupxX] conversion */ 90 #define CT_FLOAT 4 /* %[efgEFG] conversion */ 91 92 #ifndef NO_FLOATING_POINT 93 static int parsefloat(FILE *, wchar_t *, wchar_t *, locale_t); 94 #endif 95 96 struct ccl { 97 const wchar_t *start; /* character class start */ 98 const wchar_t *end; /* character class end */ 99 int compl; /* ccl is complemented? */ 100 }; 101 102 static __inline int 103 inccl(const struct ccl *ccl, wint_t wi) 104 { 105 106 if (ccl->compl) { 107 return (wmemchr(ccl->start, wi, ccl->end - ccl->start) 108 == NULL); 109 } else { 110 return (wmemchr(ccl->start, wi, ccl->end - ccl->start) != NULL); 111 } 112 } 113 114 /* 115 * Conversion functions are passed a pointer to this object instead of 116 * a real parameter to indicate that the assignment-suppression (*) 117 * flag was specified. We could use a NULL pointer to indicate this, 118 * but that would mask bugs in applications that call scanf() with a 119 * NULL pointer. 120 */ 121 static const int suppress; 122 #define SUPPRESS_PTR ((void *)&suppress) 123 124 static const mbstate_t initial_mbs; 125 126 /* 127 * The following conversion functions return the number of characters consumed, 128 * or -1 on input failure. Character class conversion returns 0 on match 129 * failure. 130 */ 131 132 static __inline int 133 convert_char(FILE *fp, char * mbp, int width, locale_t locale) 134 { 135 mbstate_t mbs; 136 size_t nconv; 137 wint_t wi; 138 int n; 139 140 n = 0; 141 mbs = initial_mbs; 142 while (width-- != 0 && (wi = __fgetwc(fp, locale)) != WEOF) { 143 if (mbp != SUPPRESS_PTR) { 144 nconv = wcrtomb(mbp, wi, &mbs); 145 if (nconv == (size_t)-1) 146 return (-1); 147 mbp += nconv; 148 } 149 n++; 150 } 151 if (n == 0) 152 return (-1); 153 return (n); 154 } 155 156 static __inline int 157 convert_wchar(FILE *fp, wchar_t *wcp, int width, locale_t locale) 158 { 159 wint_t wi; 160 int n; 161 162 n = 0; 163 while (width-- != 0 && (wi = __fgetwc(fp, locale)) != WEOF) { 164 if (wcp != SUPPRESS_PTR) 165 *wcp++ = (wchar_t)wi; 166 n++; 167 } 168 if (n == 0) 169 return (-1); 170 return (n); 171 } 172 173 static __inline int 174 convert_ccl(FILE *fp, char * mbp, int width, const struct ccl *ccl, 175 locale_t locale) 176 { 177 mbstate_t mbs; 178 size_t nconv; 179 wint_t wi; 180 int n; 181 182 n = 0; 183 mbs = initial_mbs; 184 while ((wi = __fgetwc(fp, locale)) != WEOF && 185 width-- != 0 && inccl(ccl, wi)) { 186 if (mbp != SUPPRESS_PTR) { 187 nconv = wcrtomb(mbp, wi, &mbs); 188 if (nconv == (size_t)-1) 189 return (-1); 190 mbp += nconv; 191 } 192 n++; 193 } 194 if (wi != WEOF) 195 __ungetwc(wi, fp, locale); 196 if (mbp != SUPPRESS_PTR) 197 *mbp = 0; 198 return (n); 199 } 200 201 static __inline int 202 convert_wccl(FILE *fp, wchar_t *wcp, int width, const struct ccl *ccl, 203 locale_t locale) 204 { 205 wchar_t *wcp0; 206 wint_t wi; 207 int n; 208 209 if (wcp == SUPPRESS_PTR) { 210 n = 0; 211 while ((wi = __fgetwc(fp, locale)) != WEOF && 212 width-- != 0 && inccl(ccl, wi)) 213 n++; 214 if (wi != WEOF) 215 __ungetwc(wi, fp, locale); 216 } else { 217 wcp0 = wcp; 218 while ((wi = __fgetwc(fp, locale)) != WEOF && 219 width-- != 0 && inccl(ccl, wi)) 220 *wcp++ = (wchar_t)wi; 221 if (wi != WEOF) 222 __ungetwc(wi, fp, locale); 223 n = wcp - wcp0; 224 if (n == 0) 225 return (0); 226 *wcp = 0; 227 } 228 return (n); 229 } 230 231 static __inline int 232 convert_string(FILE *fp, char * mbp, int width, locale_t locale) 233 { 234 mbstate_t mbs; 235 size_t nconv; 236 wint_t wi; 237 int nread; 238 239 mbs = initial_mbs; 240 nread = 0; 241 while ((wi = __fgetwc(fp, locale)) != WEOF && width-- != 0 && 242 !iswspace(wi)) { 243 if (mbp != SUPPRESS_PTR) { 244 nconv = wcrtomb(mbp, wi, &mbs); 245 if (nconv == (size_t)-1) 246 return (-1); 247 mbp += nconv; 248 } 249 nread++; 250 } 251 if (wi != WEOF) 252 __ungetwc(wi, fp, locale); 253 if (mbp != SUPPRESS_PTR) 254 *mbp = 0; 255 return (nread); 256 } 257 258 static __inline int 259 convert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale) 260 { 261 wint_t wi; 262 int nread; 263 264 nread = 0; 265 if (wcp == SUPPRESS_PTR) { 266 while ((wi = __fgetwc(fp, locale)) != WEOF && 267 width-- != 0 && !iswspace(wi)) 268 nread++; 269 if (wi != WEOF) 270 __ungetwc(wi, fp, locale); 271 } else { 272 while ((wi = __fgetwc(fp, locale)) != WEOF && 273 width-- != 0 && !iswspace(wi)) { 274 *wcp++ = (wchar_t)wi; 275 nread++; 276 } 277 if (wi != WEOF) 278 __ungetwc(wi, fp, locale); 279 *wcp = '\0'; 280 } 281 return (nread); 282 } 283 284 enum parseint_state { 285 begin, 286 havesign, 287 havezero, 288 haveprefix, 289 any, 290 }; 291 292 static __inline int 293 parseint_fsm(wchar_t c, enum parseint_state *state, int *base) 294 { 295 switch (c) { 296 case '+': 297 case '-': 298 if (*state == begin) { 299 *state = havesign; 300 return 1; 301 } 302 break; 303 case '0': 304 if (*state == begin || *state == havesign) { 305 *state = havezero; 306 } else { 307 *state = any; 308 } 309 return 1; 310 case '1': 311 case '2': 312 case '3': 313 case '4': 314 case '5': 315 case '6': 316 case '7': 317 if (*state == havezero && *base == 0) { 318 *base = 8; 319 } 320 /* FALL THROUGH */ 321 case '8': 322 case '9': 323 if (*state == begin || 324 *state == havesign) { 325 if (*base == 0) { 326 *base = 10; 327 } 328 } 329 if (*state == begin || 330 *state == havesign || 331 *state == havezero || 332 *state == haveprefix || 333 *state == any) { 334 if (*base > c - '0') { 335 *state = any; 336 return 1; 337 } 338 } 339 break; 340 case 'b': 341 if (*state == havezero) { 342 if (*base == 0 || *base == 2) { 343 *state = haveprefix; 344 *base = 2; 345 return 1; 346 } 347 } 348 /* FALL THROUGH */ 349 case 'a': 350 case 'c': 351 case 'd': 352 case 'e': 353 case 'f': 354 if (*state == begin || 355 *state == havesign || 356 *state == havezero || 357 *state == haveprefix || 358 *state == any) { 359 if (*base > c - 'a' + 10) { 360 *state = any; 361 return 1; 362 } 363 } 364 break; 365 case 'B': 366 if (*state == havezero) { 367 if (*base == 0 || *base == 2) { 368 *state = haveprefix; 369 *base = 2; 370 return 1; 371 } 372 } 373 /* FALL THROUGH */ 374 case 'A': 375 case 'C': 376 case 'D': 377 case 'E': 378 case 'F': 379 if (*state == begin || 380 *state == havesign || 381 *state == havezero || 382 *state == haveprefix || 383 *state == any) { 384 if (*base > c - 'A' + 10) { 385 *state = any; 386 return 1; 387 } 388 } 389 break; 390 case 'x': 391 case 'X': 392 if (*state == havezero) { 393 if (*base == 0 || *base == 16) { 394 *state = haveprefix; 395 *base = 16; 396 return 1; 397 } 398 } 399 break; 400 } 401 return 0; 402 } 403 404 /* 405 * Read an integer, storing it in buf. 406 * 407 * Return 0 on a match failure, and the number of characters read 408 * otherwise. 409 */ 410 static __inline int 411 parseint(FILE *fp, wchar_t * __restrict buf, int width, int base, 412 locale_t locale) 413 { 414 enum parseint_state state = begin; 415 wchar_t *wcp; 416 int c; 417 418 for (wcp = buf; width; width--) { 419 c = __fgetwc(fp, locale); 420 if (c == WEOF) 421 break; 422 if (!parseint_fsm(c, &state, &base)) 423 break; 424 *wcp++ = (wchar_t)c; 425 } 426 /* 427 * If we only had a sign, push it back. If we only had a 0b or 0x 428 * prefix (possibly preceded by a sign), we view it as "0" and 429 * push back the letter. In all other cases, if we stopped 430 * because we read a non-number character, push it back. 431 */ 432 if (state == havesign) { 433 wcp--; 434 __ungetwc(*wcp, fp, locale); 435 } else if (state == haveprefix) { 436 wcp--; 437 __ungetwc(c, fp, locale); 438 } else if (width && c != WEOF) { 439 __ungetwc(c, fp, locale); 440 } 441 return (wcp - buf); 442 } 443 444 /* 445 * MT-safe version. 446 */ 447 int 448 vfwscanf_l(FILE * __restrict fp, locale_t locale, 449 const wchar_t * __restrict fmt, va_list ap) 450 { 451 int ret; 452 FIX_LOCALE(locale); 453 454 FLOCKFILE_CANCELSAFE(fp); 455 ORIENT(fp, 1); 456 ret = __vfwscanf(fp, locale, fmt, ap); 457 FUNLOCKFILE_CANCELSAFE(); 458 return (ret); 459 } 460 int 461 vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap) 462 { 463 return vfwscanf_l(fp, __get_locale(), fmt, ap); 464 } 465 466 /* 467 * Non-MT-safe version. 468 */ 469 int 470 __vfwscanf(FILE * __restrict fp, locale_t locale, 471 const wchar_t * __restrict fmt, va_list ap) 472 { 473 #define GETARG(type) ((flags & SUPPRESS) ? SUPPRESS_PTR : va_arg(ap, type)) 474 wint_t c; /* character from format, or conversion */ 475 size_t width; /* field width, or 0 */ 476 int flags; /* flags as defined above */ 477 int nassigned; /* number of fields assigned */ 478 int nconversions; /* number of conversions */ 479 int nr; /* characters read by the current conversion */ 480 int nread; /* number of characters consumed from fp */ 481 int base; /* base argument to conversion function */ 482 struct ccl ccl; /* character class info */ 483 wchar_t buf[BUF]; /* buffer for numeric conversions */ 484 wint_t wi; /* handy wint_t */ 485 486 nassigned = 0; 487 nconversions = 0; 488 nread = 0; 489 ccl.start = ccl.end = NULL; 490 for (;;) { 491 c = *fmt++; 492 if (c == 0) 493 return (nassigned); 494 if (iswspace(c)) { 495 while ((c = __fgetwc(fp, locale)) != WEOF && 496 iswspace_l(c, locale)) 497 nread++; 498 if (c != WEOF) 499 __ungetwc(c, fp, locale); 500 continue; 501 } 502 if (c != '%') 503 goto literal; 504 width = 0; 505 flags = 0; 506 /* 507 * switch on the format. continue if done; 508 * break once format type is derived. 509 */ 510 again: c = *fmt++; 511 switch (c) { 512 case '%': 513 literal: 514 if ((wi = __fgetwc(fp, locale)) == WEOF) 515 goto input_failure; 516 if (wi != c) { 517 __ungetwc(wi, fp, locale); 518 goto match_failure; 519 } 520 nread++; 521 continue; 522 523 case '*': 524 flags |= SUPPRESS; 525 goto again; 526 case 'j': 527 flags |= INTMAXT; 528 goto again; 529 case 'l': 530 if (flags & LONG) { 531 flags &= ~LONG; 532 flags |= LONGLONG; 533 } else 534 flags |= LONG; 535 goto again; 536 case 'q': 537 flags |= LONGLONG; /* not quite */ 538 goto again; 539 case 't': 540 flags |= PTRDIFFT; 541 goto again; 542 case 'z': 543 flags |= SIZET; 544 goto again; 545 case 'L': 546 flags |= LONGDBL; 547 goto again; 548 case 'h': 549 if (flags & SHORT) { 550 flags &= ~SHORT; 551 flags |= SHORTSHORT; 552 } else 553 flags |= SHORT; 554 goto again; 555 556 case '0': case '1': case '2': case '3': case '4': 557 case '5': case '6': case '7': case '8': case '9': 558 width = width * 10 + c - '0'; 559 goto again; 560 561 /* 562 * Conversions. 563 */ 564 case 'B': 565 case 'b': 566 c = CT_INT; 567 flags |= UNSIGNED; 568 base = 2; 569 break; 570 571 case 'd': 572 c = CT_INT; 573 base = 10; 574 break; 575 576 case 'i': 577 c = CT_INT; 578 base = 0; 579 break; 580 581 case 'o': 582 c = CT_INT; 583 flags |= UNSIGNED; 584 base = 8; 585 break; 586 587 case 'u': 588 c = CT_INT; 589 flags |= UNSIGNED; 590 base = 10; 591 break; 592 593 case 'X': 594 case 'x': 595 c = CT_INT; 596 flags |= UNSIGNED; 597 base = 16; 598 break; 599 600 #ifndef NO_FLOATING_POINT 601 case 'A': case 'E': case 'F': case 'G': 602 case 'a': case 'e': case 'f': case 'g': 603 c = CT_FLOAT; 604 break; 605 #endif 606 607 case 'S': 608 flags |= LONG; 609 /* FALLTHROUGH */ 610 case 's': 611 c = CT_STRING; 612 break; 613 614 case '[': 615 ccl.start = fmt; 616 if (*fmt == '^') { 617 ccl.compl = 1; 618 fmt++; 619 } else 620 ccl.compl = 0; 621 if (*fmt == ']') 622 fmt++; 623 while (*fmt != '\0' && *fmt != ']') 624 fmt++; 625 ccl.end = fmt; 626 fmt++; 627 flags |= NOSKIP; 628 c = CT_CCL; 629 break; 630 631 case 'C': 632 flags |= LONG; 633 /* FALLTHROUGH */ 634 case 'c': 635 flags |= NOSKIP; 636 c = CT_CHAR; 637 break; 638 639 case 'p': /* pointer format is like hex */ 640 flags |= POINTER; 641 c = CT_INT; /* assumes sizeof(uintmax_t) */ 642 flags |= UNSIGNED; /* >= sizeof(uintptr_t) */ 643 base = 16; 644 break; 645 646 case 'n': 647 if (flags & SUPPRESS) /* ??? */ 648 continue; 649 if (flags & SHORTSHORT) 650 *va_arg(ap, char *) = nread; 651 else if (flags & SHORT) 652 *va_arg(ap, short *) = nread; 653 else if (flags & LONG) 654 *va_arg(ap, long *) = nread; 655 else if (flags & LONGLONG) 656 *va_arg(ap, long long *) = nread; 657 else if (flags & INTMAXT) 658 *va_arg(ap, intmax_t *) = nread; 659 else if (flags & SIZET) 660 *va_arg(ap, size_t *) = nread; 661 else if (flags & PTRDIFFT) 662 *va_arg(ap, ptrdiff_t *) = nread; 663 else 664 *va_arg(ap, int *) = nread; 665 continue; 666 667 default: 668 goto match_failure; 669 670 /* 671 * Disgusting backwards compatibility hack. XXX 672 */ 673 case '\0': /* compat */ 674 return (EOF); 675 } 676 677 /* 678 * Consume leading white space, except for formats 679 * that suppress this. 680 */ 681 if ((flags & NOSKIP) == 0) { 682 while ((wi = __fgetwc(fp, locale)) != WEOF && iswspace(wi)) 683 nread++; 684 if (wi == WEOF) 685 goto input_failure; 686 __ungetwc(wi, fp, locale); 687 } 688 689 /* 690 * Do the conversion. 691 */ 692 switch (c) { 693 694 case CT_CHAR: 695 /* scan arbitrary characters (sets NOSKIP) */ 696 if (width == 0) 697 width = 1; 698 if (flags & LONG) { 699 nr = convert_wchar(fp, GETARG(wchar_t *), width, 700 locale); 701 } else { 702 nr = convert_char(fp, GETARG(char *), width, 703 locale); 704 } 705 if (nr < 0) 706 goto input_failure; 707 break; 708 709 case CT_CCL: 710 /* scan a (nonempty) character class (sets NOSKIP) */ 711 if (width == 0) 712 width = (size_t)~0; /* `infinity' */ 713 /* take only those things in the class */ 714 if (flags & LONG) { 715 nr = convert_wccl(fp, GETARG(wchar_t *), width, 716 &ccl, locale); 717 } else { 718 nr = convert_ccl(fp, GETARG(char *), width, 719 &ccl, locale); 720 } 721 if (nr <= 0) { 722 if (nr < 0) 723 goto input_failure; 724 else /* nr == 0 */ 725 goto match_failure; 726 } 727 break; 728 729 case CT_STRING: 730 /* like CCL, but zero-length string OK, & no NOSKIP */ 731 if (width == 0) 732 width = (size_t)~0; 733 if (flags & LONG) { 734 nr = convert_wstring(fp, GETARG(wchar_t *), 735 width, locale); 736 } else { 737 nr = convert_string(fp, GETARG(char *), width, 738 locale); 739 } 740 if (nr < 0) 741 goto input_failure; 742 break; 743 744 case CT_INT: 745 /* scan an integer as if by the conversion function */ 746 if (width == 0 || width > sizeof(buf) / 747 sizeof(*buf) - 1) 748 width = sizeof(buf) / sizeof(*buf) - 1; 749 750 nr = parseint(fp, buf, width, base, locale); 751 if (nr == 0) 752 goto match_failure; 753 if ((flags & SUPPRESS) == 0) { 754 uintmax_t res; 755 756 buf[nr] = L'\0'; 757 if ((flags & UNSIGNED) == 0) 758 res = wcstoimax(buf, NULL, base); 759 else 760 res = wcstoumax(buf, NULL, base); 761 if (flags & POINTER) 762 *va_arg(ap, void **) = 763 (void *)(uintptr_t)res; 764 else if (flags & SHORTSHORT) 765 *va_arg(ap, char *) = res; 766 else if (flags & SHORT) 767 *va_arg(ap, short *) = res; 768 else if (flags & LONG) 769 *va_arg(ap, long *) = res; 770 else if (flags & LONGLONG) 771 *va_arg(ap, long long *) = res; 772 else if (flags & INTMAXT) 773 *va_arg(ap, intmax_t *) = res; 774 else if (flags & PTRDIFFT) 775 *va_arg(ap, ptrdiff_t *) = res; 776 else if (flags & SIZET) 777 *va_arg(ap, size_t *) = res; 778 else 779 *va_arg(ap, int *) = res; 780 } 781 break; 782 783 #ifndef NO_FLOATING_POINT 784 case CT_FLOAT: 785 /* scan a floating point number as if by strtod */ 786 if (width == 0 || width > sizeof(buf) / 787 sizeof(*buf) - 1) 788 width = sizeof(buf) / sizeof(*buf) - 1; 789 nr = parsefloat(fp, buf, buf + width, locale); 790 if (nr == 0) 791 goto match_failure; 792 if ((flags & SUPPRESS) == 0) { 793 if (flags & LONGDBL) { 794 long double res = wcstold(buf, NULL); 795 *va_arg(ap, long double *) = res; 796 } else if (flags & LONG) { 797 double res = wcstod(buf, NULL); 798 *va_arg(ap, double *) = res; 799 } else { 800 float res = wcstof(buf, NULL); 801 *va_arg(ap, float *) = res; 802 } 803 } 804 break; 805 #endif /* !NO_FLOATING_POINT */ 806 } 807 if (!(flags & SUPPRESS)) 808 nassigned++; 809 nread += nr; 810 nconversions++; 811 } 812 input_failure: 813 return (nconversions != 0 ? nassigned : EOF); 814 match_failure: 815 return (nassigned); 816 } 817 818 #ifndef NO_FLOATING_POINT 819 static int 820 parsefloat(FILE *fp, wchar_t *buf, wchar_t *end, locale_t locale) 821 { 822 mbstate_t mbs; 823 size_t nconv; 824 wchar_t *commit, *p; 825 int infnanpos = 0; 826 enum { 827 S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX, 828 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS 829 } state = S_START; 830 wchar_t c; 831 wchar_t decpt; 832 _Bool gotmantdig = 0, ishex = 0; 833 834 mbs = initial_mbs; 835 nconv = mbrtowc(&decpt, localeconv()->decimal_point, MB_CUR_MAX, &mbs); 836 if (nconv == (size_t)-1 || nconv == (size_t)-2) 837 decpt = '.'; /* failsafe */ 838 839 /* 840 * We set commit = p whenever the string we have read so far 841 * constitutes a valid representation of a floating point 842 * number by itself. At some point, the parse will complete 843 * or fail, and we will ungetc() back to the last commit point. 844 * To ensure that the file offset gets updated properly, it is 845 * always necessary to read at least one character that doesn't 846 * match; thus, we can't short-circuit "infinity" or "nan(...)". 847 */ 848 commit = buf - 1; 849 c = WEOF; 850 for (p = buf; p < end; ) { 851 if ((c = __fgetwc(fp, locale)) == WEOF) 852 break; 853 reswitch: 854 switch (state) { 855 case S_START: 856 state = S_GOTSIGN; 857 if (c == '-' || c == '+') 858 break; 859 else 860 goto reswitch; 861 case S_GOTSIGN: 862 switch (c) { 863 case '0': 864 state = S_MAYBEHEX; 865 commit = p; 866 break; 867 case 'I': 868 case 'i': 869 state = S_INF; 870 break; 871 case 'N': 872 case 'n': 873 state = S_NAN; 874 break; 875 default: 876 state = S_DIGITS; 877 goto reswitch; 878 } 879 break; 880 case S_INF: 881 if (infnanpos > 6 || 882 (c != "nfinity"[infnanpos] && 883 c != "NFINITY"[infnanpos])) 884 goto parsedone; 885 if (infnanpos == 1 || infnanpos == 6) 886 commit = p; /* inf or infinity */ 887 infnanpos++; 888 break; 889 case S_NAN: 890 switch (infnanpos) { 891 case 0: 892 if (c != 'A' && c != 'a') 893 goto parsedone; 894 break; 895 case 1: 896 if (c != 'N' && c != 'n') 897 goto parsedone; 898 else 899 commit = p; 900 break; 901 case 2: 902 if (c != '(') 903 goto parsedone; 904 break; 905 default: 906 if (c == ')') { 907 commit = p; 908 state = S_DONE; 909 } else if (!iswalnum(c) && c != '_') 910 goto parsedone; 911 break; 912 } 913 infnanpos++; 914 break; 915 case S_DONE: 916 goto parsedone; 917 case S_MAYBEHEX: 918 state = S_DIGITS; 919 if (c == 'X' || c == 'x') { 920 ishex = 1; 921 break; 922 } else { /* we saw a '0', but no 'x' */ 923 gotmantdig = 1; 924 goto reswitch; 925 } 926 case S_DIGITS: 927 if ((ishex && iswxdigit(c)) || iswdigit(c)) 928 gotmantdig = 1; 929 else { 930 state = S_FRAC; 931 if (c != decpt) 932 goto reswitch; 933 } 934 if (gotmantdig) 935 commit = p; 936 break; 937 case S_FRAC: 938 if (((c == 'E' || c == 'e') && !ishex) || 939 ((c == 'P' || c == 'p') && ishex)) { 940 if (!gotmantdig) 941 goto parsedone; 942 else 943 state = S_EXP; 944 } else if ((ishex && iswxdigit(c)) || iswdigit(c)) { 945 commit = p; 946 gotmantdig = 1; 947 } else 948 goto parsedone; 949 break; 950 case S_EXP: 951 state = S_EXPDIGITS; 952 if (c == '-' || c == '+') 953 break; 954 else 955 goto reswitch; 956 case S_EXPDIGITS: 957 if (iswdigit(c)) 958 commit = p; 959 else 960 goto parsedone; 961 break; 962 default: 963 abort(); 964 } 965 *p++ = c; 966 c = WEOF; 967 } 968 969 parsedone: 970 if (c != WEOF) 971 __ungetwc(c, fp, locale); 972 while (commit < --p) 973 __ungetwc(*p, fp, locale); 974 *++commit = '\0'; 975 return (commit - buf); 976 } 977 #endif 978