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