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