1 /* $NetBSD: vfscanf.c,v 1.34 2003/05/27 14:32:29 thorpej 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[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93"; 43 #else 44 __RCSID("$NetBSD: vfscanf.c,v 1.34 2003/05/27 14:32:29 thorpej Exp $"); 45 #endif 46 #endif /* LIBC_SCCS and not lint */ 47 48 #include "namespace.h" 49 50 #include <assert.h> 51 #include <errno.h> 52 #include <inttypes.h> 53 #include <stdarg.h> 54 #include <stddef.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <ctype.h> 58 59 #include "reentrant.h" 60 #include "local.h" 61 62 #ifdef FLOATING_POINT 63 #include "floatio.h" 64 #endif 65 66 /* 67 * Provide an external name for vfscanf. Note, we don't use the normal 68 * namespace.h method; stdio routines explicitly use the internal name 69 * __svfscanf. 70 */ 71 #ifdef __weak_alias 72 __weak_alias(vfscanf,__svfscanf) 73 #endif 74 75 #define BUF 513 /* Maximum length of numeric string. */ 76 77 /* 78 * Flags used during conversion. 79 */ 80 #define LONG 0x0001 /* l: long or double */ 81 #define LONGDBL 0x0002 /* L: long double; unimplemented */ 82 #define SHORT 0x0004 /* h: short */ 83 #define QUAD 0x0008 /* q: quad */ 84 #define LONGLONG 0x0010 /* ll: long long */ 85 #define MAXINT 0x0020 /* j: intmax_t */ 86 #define PTRINT 0x0040 /* t: ptrdiff_t */ 87 #define SIZEINT 0x0080 /* z: size_t */ 88 #define SUPPRESS 0x0100 /* suppress assignment */ 89 #define POINTER 0x0200 /* weird %p pointer (`fake hex') */ 90 #define NOSKIP 0x0400 /* do not skip blanks */ 91 92 /* 93 * The following are used in numeric conversions only: 94 * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; 95 * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. 96 */ 97 #define SIGNOK 0x0800 /* +/- is (still) legal */ 98 #define HAVESIGN 0x1000 /* sign detected */ 99 #define NDIGITS 0x2000 /* no digits detected */ 100 101 #define DPTOK 0x4000 /* (float) decimal point is still legal */ 102 #define EXPOK 0x8000 /* (float) exponent (e+3, etc) still legal */ 103 104 #define PFXOK 0x4000 /* 0x prefix is (still) legal */ 105 #define NZDIGITS 0x8000 /* no zero digits 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 /* integer, i.e., strtoimax or strtoumax */ 114 #define CT_FLOAT 4 /* floating, i.e., strtod */ 115 116 #define u_char unsigned char 117 #define u_long unsigned long 118 119 static const u_char *__sccl __P((char *, const u_char *)); 120 121 int 122 __svfscanf(fp, fmt0, ap) 123 FILE *fp; 124 const char *fmt0; 125 _BSD_VA_LIST_ ap; 126 { 127 int ret; 128 129 FLOCKFILE(fp); 130 ret = __svfscanf_unlocked(fp, fmt0, ap); 131 FUNLOCKFILE(fp); 132 133 return ret; 134 } 135 136 /* 137 * vfscanf 138 */ 139 int 140 __svfscanf_unlocked(fp, fmt0, ap) 141 FILE *fp; 142 const char *fmt0; 143 _BSD_VA_LIST_ ap; 144 { 145 const u_char *fmt = (const u_char *)fmt0; 146 int c; /* character from format, or conversion */ 147 size_t width; /* field width, or 0 */ 148 char *p; /* points into all kinds of strings */ 149 int n; /* handy integer */ 150 int flags; /* flags as defined above */ 151 char *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 strtoimax/strtoumax */ 155 uintmax_t (*ccfn) __P((const char *, char **, int)); 156 /* conversion function (strtoimax/strtoumax) */ 157 char ccltab[256]; /* character class table for %[...] */ 158 char buf[BUF]; /* buffer for numeric conversions */ 159 160 /* `basefix' is used to avoid `if' tests in the integer scanner */ 161 static const short basefix[17] = 162 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 163 164 _DIAGASSERT(fp != NULL); 165 _DIAGASSERT(fmt0 != NULL); 166 167 _SET_ORIENTATION(fp, -1); 168 169 nassigned = 0; 170 nread = 0; 171 base = 0; /* XXX just to keep gcc happy */ 172 ccfn = NULL; /* XXX just to keep gcc happy */ 173 for (;;) { 174 c = *fmt++; 175 if (c == 0) { 176 return (nassigned); 177 } 178 if (isspace(c)) { 179 while ((fp->_r > 0 || __srefill(fp) == 0) && 180 isspace(*fp->_p)) 181 nread++, fp->_r--, fp->_p++; 182 continue; 183 } 184 if (c != '%') 185 goto literal; 186 width = 0; 187 flags = 0; 188 /* 189 * switch on the format. continue if done; 190 * break once format type is derived. 191 */ 192 again: c = *fmt++; 193 switch (c) { 194 case '%': 195 literal: 196 if (fp->_r <= 0 && __srefill(fp)) 197 goto input_failure; 198 if (*fp->_p != c) 199 goto match_failure; 200 fp->_r--, fp->_p++; 201 nread++; 202 continue; 203 204 case '*': 205 flags |= SUPPRESS; 206 goto again; 207 case 'L': 208 flags |= LONGDBL; 209 goto again; 210 case 'h': 211 flags |= SHORT; 212 goto again; 213 case 'j': 214 flags |= MAXINT; 215 goto again; 216 case 'l': 217 if (*fmt == 'l') { 218 fmt++; 219 flags |= LONGLONG; 220 } else { 221 flags |= LONG; 222 } 223 goto again; 224 case 'q': 225 flags |= QUAD; 226 goto again; 227 case 't': 228 flags |= PTRINT; 229 goto again; 230 case 'z': 231 flags |= SIZEINT; 232 goto again; 233 234 case '0': case '1': case '2': case '3': case '4': 235 case '5': case '6': case '7': case '8': case '9': 236 width = width * 10 + c - '0'; 237 goto again; 238 239 /* 240 * Conversions. 241 * Those marked `compat' are for 4.[123]BSD compatibility. 242 * 243 * (According to ANSI, E and X formats are supposed 244 * to the same as e and x. Sorry about that.) 245 */ 246 case 'D': /* compat */ 247 flags |= LONG; 248 /* FALLTHROUGH */ 249 case 'd': 250 c = CT_INT; 251 ccfn = (uintmax_t (*) __P((const char *, char **, int)))strtoimax; 252 base = 10; 253 break; 254 255 case 'i': 256 c = CT_INT; 257 ccfn = (uintmax_t (*) __P((const char *, char **, int)))strtoimax; 258 base = 0; 259 break; 260 261 case 'O': /* compat */ 262 flags |= LONG; 263 /* FALLTHROUGH */ 264 case 'o': 265 c = CT_INT; 266 ccfn = strtoumax; 267 base = 8; 268 break; 269 270 case 'u': 271 c = CT_INT; 272 ccfn = strtoumax; 273 base = 10; 274 break; 275 276 case 'X': 277 case 'x': 278 flags |= PFXOK; /* enable 0x prefixing */ 279 c = CT_INT; 280 ccfn = strtoumax; 281 base = 16; 282 break; 283 284 #ifdef FLOATING_POINT 285 case 'E': 286 case 'F': 287 case 'G': 288 case 'e': 289 case 'f': 290 case 'g': 291 c = CT_FLOAT; 292 break; 293 #endif 294 295 case 's': 296 c = CT_STRING; 297 break; 298 299 case '[': 300 fmt = __sccl(ccltab, fmt); 301 flags |= NOSKIP; 302 c = CT_CCL; 303 break; 304 305 case 'c': 306 flags |= NOSKIP; 307 c = CT_CHAR; 308 break; 309 310 case 'p': /* pointer format is like hex */ 311 flags |= POINTER | PFXOK; 312 c = CT_INT; 313 ccfn = strtoumax; 314 base = 16; 315 break; 316 317 case 'n': 318 if (flags & SUPPRESS) /* ??? */ 319 continue; 320 if (flags & SHORT) 321 *va_arg(ap, short *) = nread; 322 else if (flags & LONG) 323 *va_arg(ap, long *) = nread; 324 else if (flags & QUAD) 325 *va_arg(ap, quad_t *) = nread; 326 else if (flags & LONGLONG) 327 /* LONGLONG */ 328 *va_arg(ap, long long int *) = nread; 329 else if (flags & SIZEINT) 330 *va_arg(ap, ssize_t *) = nread; 331 else if (flags & PTRINT) 332 *va_arg(ap, ptrdiff_t *) = nread; 333 else if (flags & MAXINT) 334 *va_arg(ap, intmax_t *) = nread; 335 else 336 *va_arg(ap, int *) = nread; 337 continue; 338 339 /* 340 * Disgusting backwards compatibility hacks. XXX 341 */ 342 case '\0': /* compat */ 343 return (EOF); 344 345 default: /* compat */ 346 if (isupper(c)) 347 flags |= LONG; 348 c = CT_INT; 349 ccfn = (uintmax_t (*) __P((const char *, char **, int)))strtoimax; 350 base = 10; 351 break; 352 } 353 354 /* 355 * We have a conversion that requires input. 356 */ 357 if (fp->_r <= 0 && __srefill(fp)) 358 goto input_failure; 359 360 /* 361 * Consume leading white space, except for formats 362 * that suppress this. 363 */ 364 if ((flags & NOSKIP) == 0) { 365 while (isspace(*fp->_p)) { 366 nread++; 367 if (--fp->_r > 0) 368 fp->_p++; 369 else if (__srefill(fp)) 370 goto input_failure; 371 } 372 /* 373 * Note that there is at least one character in 374 * the buffer, so conversions that do not set NOSKIP 375 * ca no longer result in an input failure. 376 */ 377 } 378 379 /* 380 * Do the conversion. 381 */ 382 switch (c) { 383 384 case CT_CHAR: 385 /* scan arbitrary characters (sets NOSKIP) */ 386 if (width == 0) 387 width = 1; 388 if (flags & SUPPRESS) { 389 size_t sum = 0; 390 for (;;) { 391 if ((n = fp->_r) < width) { 392 sum += n; 393 width -= n; 394 fp->_p += n; 395 if (__srefill(fp)) { 396 if (sum == 0) 397 goto input_failure; 398 break; 399 } 400 } else { 401 sum += width; 402 fp->_r -= width; 403 fp->_p += width; 404 break; 405 } 406 } 407 nread += sum; 408 } else { 409 size_t r = fread((void *)va_arg(ap, char *), 1, 410 width, fp); 411 412 if (r == 0) 413 goto input_failure; 414 nread += r; 415 nassigned++; 416 } 417 break; 418 419 case CT_CCL: 420 /* scan a (nonempty) character class (sets NOSKIP) */ 421 if (width == 0) 422 width = ~0U; /* `infinity' */ 423 /* take only those things in the class */ 424 if (flags & SUPPRESS) { 425 n = 0; 426 while (ccltab[*fp->_p]) { 427 n++, fp->_r--, fp->_p++; 428 if (--width == 0) 429 break; 430 if (fp->_r <= 0 && __srefill(fp)) { 431 if (n == 0) 432 goto input_failure; 433 break; 434 } 435 } 436 if (n == 0) 437 goto match_failure; 438 } else { 439 p0 = p = va_arg(ap, char *); 440 while (ccltab[*fp->_p]) { 441 fp->_r--; 442 *p++ = *fp->_p++; 443 if (--width == 0) 444 break; 445 if (fp->_r <= 0 && __srefill(fp)) { 446 if (p == p0) 447 goto input_failure; 448 break; 449 } 450 } 451 n = p - p0; 452 if (n == 0) 453 goto match_failure; 454 *p = 0; 455 nassigned++; 456 } 457 nread += n; 458 break; 459 460 case CT_STRING: 461 /* like CCL, but zero-length string OK, & no NOSKIP */ 462 if (width == 0) 463 width = ~0U; 464 if (flags & SUPPRESS) { 465 n = 0; 466 while (!isspace(*fp->_p)) { 467 n++, fp->_r--, fp->_p++; 468 if (--width == 0) 469 break; 470 if (fp->_r <= 0 && __srefill(fp)) 471 break; 472 } 473 nread += n; 474 } else { 475 p0 = p = va_arg(ap, char *); 476 while (!isspace(*fp->_p)) { 477 fp->_r--; 478 *p++ = *fp->_p++; 479 if (--width == 0) 480 break; 481 if (fp->_r <= 0 && __srefill(fp)) 482 break; 483 } 484 *p = 0; 485 nread += p - p0; 486 nassigned++; 487 } 488 continue; 489 490 case CT_INT: 491 /* scan an integer as if by strtoimax/strtoumax */ 492 #ifdef hardway 493 if (width == 0 || width > sizeof(buf) - 1) 494 width = sizeof(buf) - 1; 495 #else 496 /* size_t is unsigned, hence this optimisation */ 497 if (--width > sizeof(buf) - 2) 498 width = sizeof(buf) - 2; 499 width++; 500 #endif 501 flags |= SIGNOK | NDIGITS | NZDIGITS; 502 for (p = buf; width; width--) { 503 c = *fp->_p; 504 /* 505 * Switch on the character; `goto ok' 506 * if we accept it as a part of number. 507 */ 508 switch (c) { 509 510 /* 511 * The digit 0 is always legal, but is 512 * special. For %i conversions, if no 513 * digits (zero or nonzero) have been 514 * scanned (only signs), we will have 515 * base==0. In that case, we should set 516 * it to 8 and enable 0x prefixing. 517 * Also, if we have not scanned zero digits 518 * before this, do not turn off prefixing 519 * (someone else will turn it off if we 520 * have scanned any nonzero digits). 521 */ 522 case '0': 523 if (base == 0) { 524 base = 8; 525 flags |= PFXOK; 526 } 527 if (flags & NZDIGITS) 528 flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 529 else 530 flags &= ~(SIGNOK|PFXOK|NDIGITS); 531 goto ok; 532 533 /* 1 through 7 always legal */ 534 case '1': case '2': case '3': 535 case '4': case '5': case '6': case '7': 536 base = basefix[base]; 537 flags &= ~(SIGNOK | PFXOK | NDIGITS); 538 goto ok; 539 540 /* digits 8 and 9 ok iff decimal or hex */ 541 case '8': case '9': 542 base = basefix[base]; 543 if (base <= 8) 544 break; /* not legal here */ 545 flags &= ~(SIGNOK | PFXOK | NDIGITS); 546 goto ok; 547 548 /* letters ok iff hex */ 549 case 'A': case 'B': case 'C': 550 case 'D': case 'E': case 'F': 551 case 'a': case 'b': case 'c': 552 case 'd': case 'e': case 'f': 553 /* no need to fix base here */ 554 if (base <= 10) 555 break; /* not legal here */ 556 flags &= ~(SIGNOK | PFXOK | NDIGITS); 557 goto ok; 558 559 /* sign ok only as first character */ 560 case '+': case '-': 561 if (flags & SIGNOK) { 562 flags &= ~SIGNOK; 563 flags |= HAVESIGN; 564 goto ok; 565 } 566 break; 567 568 /* 569 * x ok iff flag still set and 2nd char (or 570 * 3rd char if we have a sign). 571 */ 572 case 'x': case 'X': 573 if (flags & PFXOK && p == 574 buf + 1 + !!(flags & HAVESIGN)) { 575 base = 16; /* if %i */ 576 flags &= ~PFXOK; 577 goto ok; 578 } 579 break; 580 } 581 582 /* 583 * If we got here, c is not a legal character 584 * for a number. Stop accumulating digits. 585 */ 586 break; 587 ok: 588 /* 589 * c is legal: store it and look at the next. 590 */ 591 *p++ = c; 592 if (--fp->_r > 0) 593 fp->_p++; 594 else if (__srefill(fp)) 595 break; /* EOF */ 596 } 597 /* 598 * If we had only a sign, it is no good; push 599 * back the sign. If the number ends in `x', 600 * it was [sign] '0' 'x', so push back the x 601 * and treat it as [sign] '0'. 602 */ 603 if (flags & NDIGITS) { 604 if (p > buf) 605 (void) ungetc(*(u_char *)--p, fp); 606 goto match_failure; 607 } 608 c = ((u_char *)p)[-1]; 609 if (c == 'x' || c == 'X') { 610 --p; 611 (void) ungetc(c, fp); 612 } 613 if ((flags & SUPPRESS) == 0) { 614 uintmax_t res; 615 616 *p = 0; 617 res = (*ccfn)(buf, (char **)NULL, base); 618 if (flags & POINTER) 619 *va_arg(ap, void **) = 620 (void *)(long)res; 621 else if (flags & MAXINT) 622 *va_arg(ap, intmax_t *) = res; 623 else if (flags & PTRINT) 624 *va_arg(ap, ptrdiff_t *) = 625 (ptrdiff_t)res; 626 else if (flags & SIZEINT) 627 *va_arg(ap, ssize_t *) = (ssize_t)res; 628 else if (flags & LONGLONG) 629 /* LONGLONG */ 630 *va_arg(ap, long long int *) = res; 631 else if (flags & QUAD) 632 *va_arg(ap, quad_t *) = (quad_t)res; 633 else if (flags & LONG) 634 *va_arg(ap, long *) = (long)res; 635 else if (flags & SHORT) 636 *va_arg(ap, short *) = (short)res; 637 else 638 *va_arg(ap, int *) = (int)res; 639 nassigned++; 640 } 641 nread += p - buf; 642 break; 643 644 #ifdef FLOATING_POINT 645 case CT_FLOAT: 646 /* scan a floating point number as if by strtod */ 647 #ifdef hardway 648 if (width == 0 || width > sizeof(buf) - 1) 649 width = sizeof(buf) - 1; 650 #else 651 /* size_t is unsigned, hence this optimisation */ 652 if (--width > sizeof(buf) - 2) 653 width = sizeof(buf) - 2; 654 width++; 655 #endif 656 flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; 657 for (p = buf; width; width--) { 658 c = *fp->_p; 659 /* 660 * This code mimicks the integer conversion 661 * code, but is much simpler. 662 */ 663 switch (c) { 664 665 case '0': case '1': case '2': case '3': 666 case '4': case '5': case '6': case '7': 667 case '8': case '9': 668 flags &= ~(SIGNOK | NDIGITS); 669 goto fok; 670 671 case '+': case '-': 672 if (flags & SIGNOK) { 673 flags &= ~SIGNOK; 674 goto fok; 675 } 676 break; 677 case '.': 678 if (flags & DPTOK) { 679 flags &= ~(SIGNOK | DPTOK); 680 goto fok; 681 } 682 break; 683 case 'e': case 'E': 684 /* no exponent without some digits */ 685 if ((flags&(NDIGITS|EXPOK)) == EXPOK) { 686 flags = 687 (flags & ~(EXPOK|DPTOK)) | 688 SIGNOK | NDIGITS; 689 goto fok; 690 } 691 break; 692 } 693 break; 694 fok: 695 *p++ = c; 696 if (--fp->_r > 0) 697 fp->_p++; 698 else if (__srefill(fp)) 699 break; /* EOF */ 700 } 701 /* 702 * If no digits, might be missing exponent digits 703 * (just give back the exponent) or might be missing 704 * regular digits, but had sign and/or decimal point. 705 */ 706 if (flags & NDIGITS) { 707 if (flags & EXPOK) { 708 /* no digits at all */ 709 while (p > buf) 710 ungetc(*(u_char *)--p, fp); 711 goto match_failure; 712 } 713 /* just a bad exponent (e and maybe sign) */ 714 c = *(u_char *)--p; 715 if (c != 'e' && c != 'E') { 716 (void) ungetc(c, fp);/* sign */ 717 c = *(u_char *)--p; 718 } 719 (void) ungetc(c, fp); 720 } 721 if ((flags & SUPPRESS) == 0) { 722 double res; 723 724 *p = 0; 725 res = strtod(buf, (char **) NULL); 726 if (flags & LONGDBL) 727 *va_arg(ap, long double *) = res; 728 else if (flags & LONG) 729 *va_arg(ap, double *) = res; 730 else 731 *va_arg(ap, float *) = res; 732 nassigned++; 733 } 734 nread += p - buf; 735 break; 736 #endif /* FLOATING_POINT */ 737 } 738 } 739 input_failure: 740 return (nassigned ? nassigned : EOF); 741 match_failure: 742 return (nassigned); 743 } 744 745 /* 746 * Fill in the given table from the scanset at the given format 747 * (just after `['). Return a pointer to the character past the 748 * closing `]'. The table has a 1 wherever characters should be 749 * considered part of the scanset. 750 */ 751 static const u_char * 752 __sccl(tab, fmt) 753 char *tab; 754 const u_char *fmt; 755 { 756 int c, n, v; 757 758 _DIAGASSERT(tab != NULL); 759 _DIAGASSERT(fmt != NULL); 760 761 /* first `clear' the whole table */ 762 c = *fmt++; /* first char hat => negated scanset */ 763 if (c == '^') { 764 v = 1; /* default => accept */ 765 c = *fmt++; /* get new first char */ 766 } else 767 v = 0; /* default => reject */ 768 /* should probably use memset here */ 769 for (n = 0; n < 256; n++) 770 tab[n] = v; 771 if (c == 0) 772 return (fmt - 1);/* format ended before closing ] */ 773 774 /* 775 * Now set the entries corresponding to the actual scanset 776 * to the opposite of the above. 777 * 778 * The first character may be ']' (or '-') without being special; 779 * the last character may be '-'. 780 */ 781 v = 1 - v; 782 for (;;) { 783 tab[c] = v; /* take character c */ 784 doswitch: 785 n = *fmt++; /* and examine the next */ 786 switch (n) { 787 788 case 0: /* format ended too soon */ 789 return (fmt - 1); 790 791 case '-': 792 /* 793 * A scanset of the form 794 * [01+-] 795 * is defined as `the digit 0, the digit 1, 796 * the character +, the character -', but 797 * the effect of a scanset such as 798 * [a-zA-Z0-9] 799 * is implementation defined. The V7 Unix 800 * scanf treats `a-z' as `the letters a through 801 * z', but treats `a-a' as `the letter a, the 802 * character -, and the letter a'. 803 * 804 * For compatibility, the `-' is not considerd 805 * to define a range if the character following 806 * it is either a close bracket (required by ANSI) 807 * or is not numerically greater than the character 808 * we just stored in the table (c). 809 */ 810 n = *fmt; 811 if (n == ']' || n < c) { 812 c = '-'; 813 break; /* resume the for(;;) */ 814 } 815 fmt++; 816 do { /* fill in the range */ 817 tab[++c] = v; 818 } while (c < n); 819 #if 1 /* XXX another disgusting compatibility hack */ 820 /* 821 * Alas, the V7 Unix scanf also treats formats 822 * such as [a-c-e] as `the letters a through e'. 823 * This too is permitted by the standard.... 824 */ 825 goto doswitch; 826 #else 827 c = *fmt++; 828 if (c == 0) 829 return (fmt - 1); 830 if (c == ']') 831 return (fmt); 832 break; 833 #endif 834 835 case ']': /* end of scanset */ 836 return (fmt); 837 838 default: /* just another character */ 839 c = n; 840 break; 841 } 842 } 843 /* NOTREACHED */ 844 } 845