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 defined(LIBC_SCCS) && !defined(lint) 34static char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93"; 35#endif /* LIBC_SCCS and not lint */ 36#include <sys/cdefs.h> 37__FBSDID("$FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.43 2009/01/19 06:19:51 das Exp $"); 38 39#include "xlocale_private.h" 40 41#include "namespace.h" 42#include <ctype.h> 43#include <inttypes.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <stddef.h> 47#include <stdarg.h> 48#include <string.h> 49#include <wchar.h> 50#include <wctype.h> 51#include <pthread.h> 52#include "un-namespace.h" 53 54#include "collate.h" 55#include "libc_private.h" 56#include "local.h" 57 58#ifndef NO_FLOATING_POINT 59#include <locale.h> 60#endif 61 62#define BUF 513 /* Maximum length of numeric string. */ 63 64/* 65 * Flags used during conversion. 66 */ 67#define LONG 0x01 /* l: long or double */ 68#define LONGDBL 0x02 /* L: long double */ 69#define SHORT 0x04 /* h: short */ 70#define SUPPRESS 0x08 /* *: suppress assignment */ 71#define POINTER 0x10 /* p: void * (as hex) */ 72#define NOSKIP 0x20 /* [ or c: do not skip blanks */ 73#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */ 74#define INTMAXT 0x800 /* j: intmax_t */ 75#define PTRDIFFT 0x1000 /* t: ptrdiff_t */ 76#define SIZET 0x2000 /* z: size_t */ 77#define SHORTSHORT 0x4000 /* hh: char */ 78#define UNSIGNED 0x8000 /* %[oupxX] conversions */ 79 80/* 81 * The following are used in integral conversions only: 82 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS 83 */ 84#define SIGNOK 0x40 /* +/- is (still) legal */ 85#define NDIGITS 0x80 /* no digits detected */ 86#define PFXOK 0x100 /* 0x prefix is (still) legal */ 87#define NZDIGITS 0x200 /* no zero digits detected */ 88#define HAVESIGN 0x10000 /* sign detected */ 89 90/* 91 * Conversion types. 92 */ 93#define CT_CHAR 0 /* %c conversion */ 94#define CT_CCL 1 /* %[...] conversion */ 95#define CT_STRING 2 /* %s conversion */ 96#define CT_INT 3 /* %[dioupxX] conversion */ 97#define CT_FLOAT 4 /* %[efgEFG] conversion */ 98 99static const u_char *__sccl(char *, const u_char *, locale_t); 100#ifndef NO_FLOATING_POINT 101static int parsefloat(FILE *, char **, size_t, locale_t); 102#endif 103 104__weak_reference(__vfscanf, vfscanf); 105 106/* 107 * __vfscanf - MT-safe version 108 */ 109int 110__vfscanf(FILE * __restrict fp, char const * __restrict fmt0, va_list ap) 111{ 112 int ret; 113 114 FLOCKFILE(fp); 115 ret = __svfscanf_l(fp, __current_locale(), fmt0, ap); 116 FUNLOCKFILE(fp); 117 return (ret); 118} 119 120int 121vfscanf_l(FILE * __restrict fp, locale_t loc, char const * __restrict fmt0, va_list ap) 122{ 123 int ret; 124 125 NORMALIZE_LOCALE(loc); 126 FLOCKFILE(fp); 127 ret = __svfscanf_l(fp, loc, fmt0, ap); 128 FUNLOCKFILE(fp); 129 return (ret); 130} 131 132/* 133 * __svfscanf - non-MT-safe version of __vfscanf 134 */ 135__private_extern__ int 136__svfscanf_l(FILE * __restrict fp, locale_t loc, const char * __restrict fmt0, va_list ap) 137{ 138 const u_char *fmt = (const u_char *)fmt0; 139 int c; /* character from format, or conversion */ 140 size_t width; /* field width, or 0 */ 141 char *p; /* points into all kinds of strings */ 142 int n; /* handy integer */ 143 int flags; /* flags as defined above */ 144 char *p0; /* saves original value of p when necessary */ 145 int nassigned; /* number of fields assigned */ 146 int nread; /* number of characters consumed from fp */ 147 int base; /* base argument to conversion function */ 148 char ccltab[256]; /* character class table for %[...] */ 149 char buf[BUF]; /* buffer for numeric and mb conversions */ 150 wchar_t *wcp; /* handy wide character pointer */ 151 size_t nconv; /* length of multibyte sequence converted */ 152 int index; /* %index$, zero if unset */ 153 va_list ap_orig; /* to reset ap to first argument */ 154 static const mbstate_t initial; 155 mbstate_t mbs; 156 int mb_cur_max; 157 158 /* `basefix' is used to avoid `if' tests in the integer scanner */ 159 static const short basefix[17] = 160 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 161 162 NORMALIZE_LOCALE(loc); 163 mb_cur_max = MB_CUR_MAX_L(loc); 164 ORIENT(fp, -1); 165 166 nassigned = 0; 167 nread = 0; 168 va_copy(ap_orig, ap); 169 for (;;) { 170 c = *fmt++; 171 if (c == 0) 172 return (nassigned); 173 if (isspace_l(c, loc)) { 174 while ((fp->_r > 0 || __srefill(fp) == 0) && isspace_l(*fp->_p, loc)) 175 nread++, fp->_r--, fp->_p++; 176 continue; 177 } 178 if (c != '%') { 179 if (fp->_r <= 0 && __srefill(fp)) 180 goto input_failure; 181 goto literal; 182 } 183 width = 0; 184 flags = 0; 185 /* 186 * switch on the format. continue if done; 187 * break once format type is derived. 188 */ 189again: c = *fmt++; 190 switch (c) { 191 case '%': 192 /* Consume leading white space */ 193 for(;;) { 194 if (fp->_r <= 0 && __srefill(fp)) 195 goto input_failure; 196 if (!isspace_l(*fp->_p, loc)) 197 break; 198 nread++; 199 fp->_r--; 200 fp->_p++; 201 } 202literal: 203 if (*fp->_p != c) 204 goto match_failure; 205 fp->_r--, fp->_p++; 206 nread++; 207 continue; 208 209 case '$': 210 index = width; 211 if (index < 1 || index > NL_ARGMAX || fmt[-3] != '%') { 212 goto input_failure; 213 } 214 width = 0; 215 va_end(ap); 216 va_copy(ap, ap_orig); /* reset to %1$ */ 217 for (; index > 1; index--) { 218 va_arg(ap, void*); 219 } 220 goto again; 221 case '*': 222 flags |= SUPPRESS; 223 goto again; 224 case 'j': 225 flags |= INTMAXT; 226 goto again; 227 case 'l': 228 if (flags & LONG) { 229 flags &= ~LONG; 230 flags |= LONGLONG; 231 } else 232 flags |= LONG; 233 goto again; 234 case 'q': 235 flags |= LONGLONG; /* not quite */ 236 goto again; 237 case 't': 238 flags |= PTRDIFFT; 239 goto again; 240 case 'z': 241 flags |= SIZET; 242 goto again; 243 case 'L': 244 flags |= LONGDBL; 245 goto again; 246 case 'h': 247 if (flags & SHORT) { 248 flags &= ~SHORT; 249 flags |= SHORTSHORT; 250 } else 251 flags |= SHORT; 252 goto again; 253 254 case '0': case '1': case '2': case '3': case '4': 255 case '5': case '6': case '7': case '8': case '9': 256 width = width * 10 + c - '0'; 257 goto again; 258 259 /* 260 * Conversions. 261 */ 262 case 'd': 263 c = CT_INT; 264 base = 10; 265 break; 266 267 case 'i': 268 c = CT_INT; 269 base = 0; 270 break; 271 272 case 'o': 273 c = CT_INT; 274 flags |= UNSIGNED; 275 base = 8; 276 break; 277 278 case 'u': 279 c = CT_INT; 280 flags |= UNSIGNED; 281 base = 10; 282 break; 283 284 case 'X': 285 case 'x': 286 flags |= PFXOK; /* enable 0x prefixing */ 287 c = CT_INT; 288 flags |= UNSIGNED; 289 base = 16; 290 break; 291 292#ifndef NO_FLOATING_POINT 293 case 'A': case 'E': case 'F': case 'G': 294 case 'a': case 'e': case 'f': case 'g': 295 c = CT_FLOAT; 296 break; 297#endif 298 299 case 'S': 300 flags |= LONG; 301 /* FALLTHROUGH */ 302 case 's': 303 c = CT_STRING; 304 break; 305 306 case '[': 307 fmt = __sccl(ccltab, fmt, loc); 308 flags |= NOSKIP; 309 c = CT_CCL; 310 break; 311 312 case 'C': 313 flags |= LONG; 314 /* FALLTHROUGH */ 315 case 'c': 316 flags |= NOSKIP; 317 c = CT_CHAR; 318 break; 319 320 case 'p': /* pointer format is like hex */ 321 flags |= POINTER | PFXOK; 322 c = CT_INT; /* assumes sizeof(uintmax_t) */ 323 flags |= UNSIGNED; /* >= sizeof(uintptr_t) */ 324 base = 16; 325 break; 326 327 case 'n': 328 { 329 if (flags & SUPPRESS) /* ??? */ 330 continue; 331 void *ptr = va_arg(ap, void *); 332 if (ptr == NULL) 333 continue; 334 else if (flags & SHORTSHORT) 335 *(char *)ptr = nread; 336 else if (flags & SHORT) 337 *(short *)ptr = nread; 338 else if (flags & LONG) 339 *(long *)ptr = nread; 340 else if (flags & LONGLONG) 341 *(long long *)ptr = nread; 342 else if (flags & INTMAXT) 343 *(intmax_t *)ptr = nread; 344 else if (flags & SIZET) 345 *(size_t *)ptr = nread; 346 else if (flags & PTRDIFFT) 347 *(ptrdiff_t *)ptr = nread; 348 else 349 *(int *)ptr = nread; 350 continue; 351 } 352 default: 353 goto match_failure; 354 355 /* 356 * Disgusting backwards compatibility hack. XXX 357 */ 358 case '\0': /* compat */ 359 return (EOF); 360 } 361 362 /* 363 * We have a conversion that requires input. 364 */ 365 if (fp->_r <= 0 && __srefill(fp)) 366 goto input_failure; 367 368 /* 369 * Consume leading white space, except for formats 370 * that suppress this. 371 */ 372 if ((flags & NOSKIP) == 0) { 373 while (isspace_l(*fp->_p, loc)) { 374 nread++; 375 if (--fp->_r > 0) 376 fp->_p++; 377 else if (__srefill(fp)) 378 goto input_failure; 379 } 380 /* 381 * Note that there is at least one character in 382 * the buffer, so conversions that do not set NOSKIP 383 * ca no longer result in an input failure. 384 */ 385 } 386 387 /* 388 * Do the conversion. 389 */ 390 switch (c) { 391 392 case CT_CHAR: 393 /* scan arbitrary characters (sets NOSKIP) */ 394 if (width == 0) 395 width = 1; 396 if (flags & LONG) { 397 if ((flags & SUPPRESS) == 0) 398 wcp = va_arg(ap, wchar_t *); 399 else 400 wcp = NULL; 401 n = 0; 402 while (width != 0) { 403 if (n == mb_cur_max) { 404 fp->_flags |= __SERR; 405 goto input_failure; 406 } 407 buf[n++] = *fp->_p; 408 fp->_p++; 409 fp->_r--; 410 mbs = initial; 411 nconv = mbrtowc_l(wcp, buf, n, &mbs, loc); 412 if (nconv == (size_t)-1) { 413 fp->_flags |= __SERR; 414 goto input_failure; 415 } 416 if (nconv == 0 && !(flags & SUPPRESS)) 417 *wcp = L'\0'; 418 if (nconv != (size_t)-2) { 419 nread += n; 420 width--; 421 if (!(flags & SUPPRESS)) 422 wcp++; 423 n = 0; 424 } 425 if (fp->_r <= 0 && __srefill(fp)) { 426 if (n != 0) { 427 fp->_flags |= __SERR; 428 goto input_failure; 429 } 430 break; 431 } 432 } 433 if (!(flags & SUPPRESS)) 434 nassigned++; 435 } else if (flags & SUPPRESS) { 436 size_t sum = 0; 437 for (;;) { 438 if ((n = fp->_r) < width) { 439 sum += n; 440 width -= n; 441 fp->_p += n; 442 if (__srefill(fp)) { 443 if (sum == 0) 444 goto input_failure; 445 break; 446 } 447 } else { 448 sum += width; 449 fp->_r -= width; 450 fp->_p += width; 451 break; 452 } 453 } 454 nread += sum; 455 } else { 456 size_t r = __fread((void *)va_arg(ap, char *), 1, 457 width, fp); 458 459 if (r == 0) 460 goto input_failure; 461 nread += r; 462 nassigned++; 463 } 464 break; 465 466 case CT_CCL: 467 /* scan a (nonempty) character class (sets NOSKIP) */ 468 if (width == 0) 469 width = (size_t)~0; /* `infinity' */ 470 /* take only those things in the class */ 471 if (flags & LONG) { 472 wchar_t twc; 473 int nchars; 474 475 if ((flags & SUPPRESS) == 0) 476 wcp = va_arg(ap, wchar_t *); 477 else 478 wcp = &twc; 479 n = 0; 480 nchars = 0; 481 while (width != 0) { 482 if (n == mb_cur_max) { 483 fp->_flags |= __SERR; 484 goto input_failure; 485 } 486 buf[n++] = *fp->_p; 487 fp->_p++; 488 fp->_r--; 489 mbs = initial; 490 nconv = mbrtowc_l(wcp, buf, n, &mbs, loc); 491 if (nconv == (size_t)-1) { 492 fp->_flags |= __SERR; 493 goto input_failure; 494 } 495 if (nconv == 0) 496 *wcp = L'\0'; 497 if (nconv != (size_t)-2) { 498 if (wctob_l(*wcp, loc) != EOF && 499 !ccltab[wctob_l(*wcp, loc)]) { 500 while (n != 0) { 501 n--; 502 __ungetc(buf[n], 503 fp); 504 } 505 break; 506 } 507 nread += n; 508 width--; 509 if (!(flags & SUPPRESS)) 510 wcp++; 511 nchars++; 512 n = 0; 513 } 514 if (fp->_r <= 0 && __srefill(fp)) { 515 if (n != 0) { 516 fp->_flags |= __SERR; 517 goto input_failure; 518 } 519 break; 520 } 521 } 522 if (n != 0) { 523 fp->_flags |= __SERR; 524 goto input_failure; 525 } 526 n = nchars; 527 if (n == 0) 528 goto match_failure; 529 if (!(flags & SUPPRESS)) { 530 *wcp = L'\0'; 531 nassigned++; 532 } 533 } else if (flags & SUPPRESS) { 534 n = 0; 535 while (ccltab[*fp->_p]) { 536 n++, fp->_r--, fp->_p++; 537 if (--width == 0) 538 break; 539 if (fp->_r <= 0 && __srefill(fp)) { 540 if (n == 0) 541 goto input_failure; 542 break; 543 } 544 } 545 if (n == 0) 546 goto match_failure; 547 } else { 548 p0 = p = va_arg(ap, char *); 549 while (ccltab[*fp->_p]) { 550 fp->_r--; 551 *p++ = *fp->_p++; 552 if (--width == 0) 553 break; 554 if (fp->_r <= 0 && __srefill(fp)) { 555 if (p == p0) 556 goto input_failure; 557 break; 558 } 559 } 560 n = p - p0; 561 if (n == 0) 562 goto match_failure; 563 *p = 0; 564 nassigned++; 565 } 566 nread += n; 567 break; 568 569 case CT_STRING: 570 /* like CCL, but zero-length string OK, & no NOSKIP */ 571 if (width == 0) 572 width = (size_t)~0; 573 if (flags & LONG) { 574 wchar_t twc; 575 576 if ((flags & SUPPRESS) == 0) 577 wcp = va_arg(ap, wchar_t *); 578 else 579 wcp = &twc; 580 n = 0; 581 while (width != 0) { 582 if (n == mb_cur_max) { 583 fp->_flags |= __SERR; 584 goto input_failure; 585 } 586 buf[n++] = *fp->_p; 587 fp->_p++; 588 fp->_r--; 589 mbs = initial; 590 nconv = mbrtowc_l(wcp, buf, n, &mbs, loc); 591 if (nconv == (size_t)-1) { 592 fp->_flags |= __SERR; 593 goto input_failure; 594 } 595 if (nconv == 0) 596 *wcp = L'\0'; 597 if (nconv != (size_t)-2) { 598 if (iswspace_l(*wcp, loc)) { 599 while (n != 0) { 600 n--; 601 __ungetc(buf[n], 602 fp); 603 } 604 break; 605 } 606 nread += n; 607 width--; 608 if (!(flags & SUPPRESS)) 609 wcp++; 610 n = 0; 611 } 612 if (fp->_r <= 0 && __srefill(fp)) { 613 if (n != 0) { 614 fp->_flags |= __SERR; 615 goto input_failure; 616 } 617 break; 618 } 619 } 620 if (!(flags & SUPPRESS)) { 621 *wcp = L'\0'; 622 nassigned++; 623 } 624 } else if (flags & SUPPRESS) { 625 n = 0; 626 while (!isspace_l(*fp->_p, loc)) { 627 n++, fp->_r--, fp->_p++; 628 if (--width == 0) 629 break; 630 if (fp->_r <= 0 && __srefill(fp)) 631 break; 632 } 633 nread += n; 634 } else { 635 p0 = p = va_arg(ap, char *); 636 while (!isspace_l(*fp->_p, loc)) { 637 fp->_r--; 638 *p++ = *fp->_p++; 639 if (--width == 0) 640 break; 641 if (fp->_r <= 0 && __srefill(fp)) 642 break; 643 } 644 *p = 0; 645 nread += p - p0; 646 nassigned++; 647 } 648 continue; 649 650 case CT_INT: 651 /* scan an integer as if by the conversion function */ 652#ifdef hardway 653 if (width == 0 || width > sizeof(buf) - 1) 654 width = sizeof(buf) - 1; 655#else 656 /* size_t is unsigned, hence this optimisation */ 657 if (--width > sizeof(buf) - 2) 658 width = sizeof(buf) - 2; 659 width++; 660#endif 661 flags |= SIGNOK | NDIGITS | NZDIGITS; 662 for (p = buf; width; width--) { 663 c = *fp->_p; 664 /* 665 * Switch on the character; `goto ok' 666 * if we accept it as a part of number. 667 */ 668 switch (c) { 669 670 /* 671 * The digit 0 is always legal, but is 672 * special. For %i conversions, if no 673 * digits (zero or nonzero) have been 674 * scanned (only signs), we will have 675 * base==0. In that case, we should set 676 * it to 8 and enable 0x prefixing. 677 * Also, if we have not scanned zero digits 678 * before this, do not turn off prefixing 679 * (someone else will turn it off if we 680 * have scanned any nonzero digits). 681 */ 682 case '0': 683 if (base == 0) { 684 base = 8; 685 flags |= PFXOK; 686 } 687 if (flags & NZDIGITS) 688 flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 689 else 690 flags &= ~(SIGNOK|PFXOK|NDIGITS); 691 goto ok; 692 693 /* 1 through 7 always legal */ 694 case '1': case '2': case '3': 695 case '4': case '5': case '6': case '7': 696 base = basefix[base]; 697 flags &= ~(SIGNOK | PFXOK | NDIGITS); 698 goto ok; 699 700 /* digits 8 and 9 ok iff decimal or hex */ 701 case '8': case '9': 702 base = basefix[base]; 703 if (base <= 8) 704 break; /* not legal here */ 705 flags &= ~(SIGNOK | PFXOK | NDIGITS); 706 goto ok; 707 708 /* letters ok iff hex */ 709 case 'A': case 'B': case 'C': 710 case 'D': case 'E': case 'F': 711 case 'a': case 'b': case 'c': 712 case 'd': case 'e': case 'f': 713 /* no need to fix base here */ 714 if (base <= 10) 715 break; /* not legal here */ 716 flags &= ~(SIGNOK | PFXOK | NDIGITS); 717 goto ok; 718 719 /* sign ok only as first character */ 720 case '+': case '-': 721 if (flags & SIGNOK) { 722 flags &= ~SIGNOK; 723 flags |= HAVESIGN; 724 goto ok; 725 } 726 break; 727 728 /* 729 * x ok iff flag still set & 2nd char (or 730 * 3rd char if we have a sign). 731 */ 732 case 'x': case 'X': 733 if (flags & PFXOK && p == 734 buf + 1 + !!(flags & HAVESIGN)) { 735 base = 16; /* if %i */ 736 flags &= ~PFXOK; 737 goto ok; 738 } 739 break; 740 } 741 742 /* 743 * If we got here, c is not a legal character 744 * for a number. Stop accumulating digits. 745 */ 746 break; 747 ok: 748 /* 749 * c is legal: store it and look at the next. 750 */ 751 *p++ = c; 752 if (--fp->_r > 0) 753 fp->_p++; 754 else if (__srefill(fp)) 755 break; /* EOF */ 756 } 757 /* 758 * If we had only a sign, it is no good; push 759 * back the sign. If the number ends in `x', 760 * it was [sign] '0' 'x', so push back the x 761 * and treat it as [sign] '0'. 762 */ 763 if (flags & NDIGITS) { 764 if (p > buf) 765 (void) __ungetc(*(u_char *)--p, fp); 766 goto match_failure; 767 } 768 c = ((u_char *)p)[-1]; 769 if (c == 'x' || c == 'X') { 770 --p; 771 (void) __ungetc(c, fp); 772 } 773 if ((flags & SUPPRESS) == 0) { 774 uintmax_t res; 775 776 *p = 0; 777 if ((flags & UNSIGNED) == 0) 778 res = strtoimax_l(buf, (char **)NULL, base, loc); 779 else 780 res = strtoumax_l(buf, (char **)NULL, base, loc); 781 if (flags & POINTER) 782 *va_arg(ap, void **) = 783 (void *)(uintptr_t)res; 784 else if (flags & SHORTSHORT) 785 *va_arg(ap, char *) = res; 786 else if (flags & SHORT) 787 *va_arg(ap, short *) = res; 788 else if (flags & LONG) 789 *va_arg(ap, long *) = res; 790 else if (flags & LONGLONG) 791 *va_arg(ap, long long *) = res; 792 else if (flags & INTMAXT) 793 *va_arg(ap, intmax_t *) = res; 794 else if (flags & PTRDIFFT) 795 *va_arg(ap, ptrdiff_t *) = res; 796 else if (flags & SIZET) 797 *va_arg(ap, size_t *) = res; 798 else 799 *va_arg(ap, int *) = res; 800 nassigned++; 801 } 802 nread += p - buf; 803 break; 804 805#ifndef NO_FLOATING_POINT 806 case CT_FLOAT: 807 { 808 char *pbuf; 809 /* scan a floating point number as if by strtod */ 810 if ((width = parsefloat(fp, &pbuf, width, loc)) == 0) 811 goto match_failure; 812 if ((flags & SUPPRESS) == 0) { 813 if (flags & LONGDBL) { 814 long double res = strtold_l(pbuf, &p, loc); 815 *va_arg(ap, long double *) = res; 816 } else if (flags & LONG) { 817 double res = strtod_l(pbuf, &p, loc); 818 *va_arg(ap, double *) = res; 819 } else { 820 float res = strtof_l(pbuf, &p, loc); 821 *va_arg(ap, float *) = res; 822 } 823 nassigned++; 824 } 825 nread += width; 826 break; 827 } 828#endif /* !NO_FLOATING_POINT */ 829 } 830 } 831input_failure: 832 return (nassigned ? nassigned : EOF); 833match_failure: 834 return (nassigned); 835} 836 837int 838__svfscanf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap) 839{ 840 return __svfscanf_l(fp, __current_locale(), fmt0, ap); 841} 842 843/* 844 * Fill in the given table from the scanset at the given format 845 * (just after `['). Return a pointer to the character past the 846 * closing `]'. The table has a 1 wherever characters should be 847 * considered part of the scanset. 848 */ 849static const u_char * 850__sccl(tab, fmt, loc) 851 char *tab; 852 const u_char *fmt; 853 locale_t loc; 854{ 855 int c, n, v, i; 856 857 /* first `clear' the whole table */ 858 c = *fmt++; /* first char hat => negated scanset */ 859 if (c == '^') { 860 v = 1; /* default => accept */ 861 c = *fmt++; /* get new first char */ 862 } else 863 v = 0; /* default => reject */ 864 865 /* XXX: Will not work if sizeof(tab*) > sizeof(char) */ 866 (void) memset(tab, v, 256); 867 868 if (c == 0) 869 return (fmt - 1);/* format ended before closing ] */ 870 871 /* 872 * Now set the entries corresponding to the actual scanset 873 * to the opposite of the above. 874 * 875 * The first character may be ']' (or '-') without being special; 876 * the last character may be '-'. 877 */ 878 v = 1 - v; 879 for (;;) { 880 tab[c] = v; /* take character c */ 881doswitch: 882 n = *fmt++; /* and examine the next */ 883 switch (n) { 884 885 case 0: /* format ended too soon */ 886 return (fmt - 1); 887 888 case '-': 889 { 890 /* 891 * A scanset of the form 892 * [01+-] 893 * is defined as `the digit 0, the digit 1, 894 * the character +, the character -', but 895 * the effect of a scanset such as 896 * [a-zA-Z0-9] 897 * is implementation defined. The V7 Unix 898 * scanf treats `a-z' as `the letters a through 899 * z', but treats `a-a' as `the letter a, the 900 * character -, and the letter a'. 901 * 902 * For compatibility, the `-' is not considerd 903 * to define a range if the character following 904 * it is either a close bracket (required by ANSI) 905 * or is not numerically greater than the character 906 * we just stored in the table (c). 907 */ 908 n = *fmt; 909 if (n == ']' 910 || (loc->__collate_load_error ? n < c : 911 __collate_range_cmp (n, c, loc) < 0 912 ) 913 ) { 914 c = '-'; 915 break; /* resume the for(;;) */ 916 } 917 fmt++; 918 /* fill in the range */ 919 if (loc->__collate_load_error) { 920 do { 921 tab[++c] = v; 922 } while (c < n); 923 } else { 924 for (i = 0; i < 256; i ++) 925 if ( __collate_range_cmp (c, i, loc) < 0 926 && __collate_range_cmp (i, n, loc) <= 0 927 ) 928 tab[i] = v; 929 } 930#if 1 /* XXX another disgusting compatibility hack */ 931 c = n; 932 /* 933 * Alas, the V7 Unix scanf also treats formats 934 * such as [a-c-e] as `the letters a through e'. 935 * This too is permitted by the standard.... 936 */ 937 goto doswitch; 938#else 939 c = *fmt++; 940 if (c == 0) 941 return (fmt - 1); 942 if (c == ']') 943 return (fmt); 944#endif 945 break; 946 } 947 case ']': /* end of scanset */ 948 return (fmt); 949 950 default: /* just another character */ 951 c = n; 952 break; 953 } 954 } 955 /* NOTREACHED */ 956} 957 958#ifndef NO_FLOATING_POINT 959/* 960 * Maintain a per-thread parsefloat buffer, shared by __svfscanf_l and 961 * __vfwscanf. 962 */ 963#ifdef BUILDING_VARIANT 964extern char *__parsefloat_buf(size_t s); 965#else /* !BUILDING_VARIANT */ 966__private_extern__ char * 967__parsefloat_buf(size_t s) 968{ 969 char *b; 970 static pthread_key_t parsefloat_tsd_key = (pthread_key_t)-1; 971 static pthread_mutex_t parsefloat_tsd_lock = PTHREAD_MUTEX_INITIALIZER; 972 static size_t bsiz = 0; 973 974 if (parsefloat_tsd_key == (pthread_key_t)-1) { 975 pthread_mutex_lock(&parsefloat_tsd_lock); 976 if (parsefloat_tsd_key == (pthread_key_t)-1) { 977 parsefloat_tsd_key = __LIBC_PTHREAD_KEY_PARSEFLOAT; 978 pthread_key_init_np(parsefloat_tsd_key, free); 979 } 980 pthread_mutex_unlock(&parsefloat_tsd_lock); 981 } 982 if ((b = (char *)pthread_getspecific(parsefloat_tsd_key)) == NULL) { 983 bsiz = s > BUF ? s : BUF; 984 b = (char *)malloc(bsiz); 985 if (b == NULL) { 986 bsiz = 0; 987 return NULL; 988 } 989 pthread_setspecific(parsefloat_tsd_key, b); 990 return b; 991 } 992 if (s > bsiz) { 993 b = (char *)reallocf(b, s); 994 pthread_setspecific(parsefloat_tsd_key, b); 995 if (b == NULL) { 996 bsiz = 0; 997 return NULL; 998 } 999 bsiz = s; 1000 } 1001 return b; 1002} 1003#endif /* BUILDING_VARIANT */ 1004 1005static int 1006parsefloat(FILE *fp, char **buf, size_t width, locale_t loc) 1007{ 1008 char *commit, *p; 1009 int infnanpos = 0, decptpos = 0; 1010 enum { 1011 S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX, 1012 S_DIGITS, S_DECPT, S_FRAC, S_EXP, S_EXPDIGITS 1013 } state = S_START; 1014 unsigned char c; 1015 const char *decpt = localeconv_l(loc)->decimal_point; 1016 _Bool gotmantdig = 0, ishex = 0; 1017 char *b; 1018 char *e; 1019 size_t s; 1020 1021 s = (width == 0 ? BUF : (width + 1)); 1022 if ((b = __parsefloat_buf(s)) == NULL) { 1023 *buf = NULL; 1024 return 0; 1025 } 1026 e = b + (s - 1); 1027 /* 1028 * We set commit = p whenever the string we have read so far 1029 * constitutes a valid representation of a floating point 1030 * number by itself. At some point, the parse will complete 1031 * or fail, and we will ungetc() back to the last commit point. 1032 * To ensure that the file offset gets updated properly, it is 1033 * always necessary to read at least one character that doesn't 1034 * match; thus, we can't short-circuit "infinity" or "nan(...)". 1035 */ 1036 commit = b - 1; 1037 for (p = b; width == 0 || p < e; ) { 1038 c = *fp->_p; 1039reswitch: 1040 switch (state) { 1041 case S_START: 1042 state = S_GOTSIGN; 1043 if (c == '-' || c == '+') 1044 break; 1045 else 1046 goto reswitch; 1047 case S_GOTSIGN: 1048 switch (c) { 1049 case '0': 1050 state = S_MAYBEHEX; 1051 commit = p; 1052 break; 1053 case 'I': 1054 case 'i': 1055 state = S_INF; 1056 break; 1057 case 'N': 1058 case 'n': 1059 state = S_NAN; 1060 break; 1061 default: 1062 state = S_DIGITS; 1063 goto reswitch; 1064 } 1065 break; 1066 case S_INF: 1067 if (infnanpos > 6 || 1068 (c != "nfinity"[infnanpos] && 1069 c != "NFINITY"[infnanpos])) 1070 goto parsedone; 1071 if (infnanpos == 1 || infnanpos == 6) 1072 commit = p; /* inf or infinity */ 1073 infnanpos++; 1074 break; 1075 case S_NAN: 1076 switch (infnanpos) { 1077 case 0: 1078 if (c != 'A' && c != 'a') 1079 goto parsedone; 1080 break; 1081 case 1: 1082 if (c != 'N' && c != 'n') 1083 goto parsedone; 1084 else 1085 commit = p; 1086 break; 1087 case 2: 1088 if (c != '(') 1089 goto parsedone; 1090 break; 1091 default: 1092 if (c == ')') { 1093 commit = p; 1094 state = S_DONE; 1095 } else if (!isalnum_l(c, loc) && c != '_') 1096 goto parsedone; 1097 break; 1098 } 1099 infnanpos++; 1100 break; 1101 case S_DONE: 1102 goto parsedone; 1103 case S_MAYBEHEX: 1104 state = S_DIGITS; 1105 if (c == 'X' || c == 'x') { 1106 ishex = 1; 1107 break; 1108 } else { /* we saw a '0', but no 'x' */ 1109 gotmantdig = 1; 1110 goto reswitch; 1111 } 1112 case S_DIGITS: 1113 if ((ishex && isxdigit_l(c, loc)) || isdigit_l(c, loc)) { 1114 gotmantdig = 1; 1115 commit = p; 1116 break; 1117 } else { 1118 state = S_DECPT; 1119 goto reswitch; 1120 } 1121 case S_DECPT: 1122 if (c == decpt[decptpos]) { 1123 if (decpt[++decptpos] == '\0') { 1124 /* We read the complete decpt seq. */ 1125 state = S_FRAC; 1126 if (gotmantdig) 1127 commit = p; 1128 } 1129 break; 1130 } else if (!decptpos) { 1131 /* We didn't read any decpt characters. */ 1132 state = S_FRAC; 1133 goto reswitch; 1134 } else { 1135 /* 1136 * We read part of a multibyte decimal point, 1137 * but the rest is invalid, so bail. 1138 */ 1139 goto parsedone; 1140 } 1141 case S_FRAC: 1142 if (((c == 'E' || c == 'e') && !ishex) || 1143 ((c == 'P' || c == 'p') && ishex)) { 1144 if (!gotmantdig) 1145 goto parsedone; 1146 else 1147 state = S_EXP; 1148 } else if ((ishex && isxdigit_l(c, loc)) || isdigit_l(c, loc)) { 1149 commit = p; 1150 gotmantdig = 1; 1151 } else 1152 goto parsedone; 1153 break; 1154 case S_EXP: 1155 state = S_EXPDIGITS; 1156 if (c == '-' || c == '+') 1157 break; 1158 else 1159 goto reswitch; 1160 case S_EXPDIGITS: 1161 if (isdigit_l(c, loc)) 1162 commit = p; 1163 else 1164 goto parsedone; 1165 break; 1166 default: 1167 LIBC_ABORT("unknown state %d", state); 1168 } 1169 if (p >= e) { 1170 ssize_t diff = (p - b); 1171 ssize_t com = (commit - b); 1172 s += BUF; 1173 b = __parsefloat_buf(s); 1174 if (b == NULL) { 1175 *buf = NULL; 1176 return 0; 1177 } 1178 e = b + (s - 1); 1179 p = b + diff; 1180 commit = b + com; 1181 } 1182 *p++ = c; 1183 if (--fp->_r > 0) 1184 fp->_p++; 1185 else if (__srefill(fp)) 1186 break; /* EOF */ 1187 } 1188 1189parsedone: 1190 while (commit < --p) 1191 __ungetc(*(u_char *)p, fp); 1192 *++commit = '\0'; 1193 *buf = b; 1194 return (commit - b); 1195} 1196#endif 1197