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