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