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