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