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