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