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