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