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