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