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