1 /* 2 * Copyright (c) 2000-2001, 2004 Proofpoint, Inc. and its suppliers. 3 * All rights reserved. 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 * By using this file, you agree to the terms and conditions set 11 * forth in the LICENSE file which can be found at the top level of 12 * the sendmail distribution. 13 */ 14 15 #include <sm/gen.h> 16 SM_IDSTR(id, "@(#)$Id: vfscanf.c,v 1.55 2013-11-22 20:51:44 ca Exp $") 17 18 #include <ctype.h> 19 #include <stdlib.h> 20 #include <errno.h> 21 #include <setjmp.h> 22 #include <sm/time.h> 23 #include <sm/varargs.h> 24 #include <sm/config.h> 25 #include <sm/io.h> 26 #include <sm/signal.h> 27 #include <sm/clock.h> 28 #include <sm/string.h> 29 #include "local.h" 30 31 #define BUF 513 /* Maximum length of numeric string. */ 32 33 /* Flags used during conversion. */ 34 #define LONG 0x01 /* l: long or double */ 35 #define SHORT 0x04 /* h: short */ 36 #define QUAD 0x08 /* q: quad (same as ll) */ 37 #define SUPPRESS 0x10 /* suppress assignment */ 38 #define POINTER 0x20 /* weird %p pointer (`fake hex') */ 39 #define NOSKIP 0x40 /* do not skip blanks */ 40 41 /* 42 ** The following are used in numeric conversions only: 43 ** SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; 44 ** SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. 45 */ 46 47 #define SIGNOK 0x080 /* +/- is (still) legal */ 48 #define NDIGITS 0x100 /* no digits detected */ 49 50 #define DPTOK 0x200 /* (float) decimal point is still legal */ 51 #define EXPOK 0x400 /* (float) exponent (e+3, etc) still legal */ 52 53 #define PFXOK 0x200 /* 0x prefix is (still) legal */ 54 #define NZDIGITS 0x400 /* no zero digits detected */ 55 56 /* Conversion types. */ 57 #define CT_CHAR 0 /* %c conversion */ 58 #define CT_CCL 1 /* %[...] conversion */ 59 #define CT_STRING 2 /* %s conversion */ 60 #define CT_INT 3 /* integer, i.e., strtoll or strtoull */ 61 #define CT_FLOAT 4 /* floating, i.e., strtod */ 62 63 static void scanalrm __P((int)); 64 static unsigned char *sm_sccl __P((char *, unsigned char *)); 65 static jmp_buf ScanTimeOut; 66 67 /* 68 ** SCANALRM -- handler when timeout activated for sm_io_vfscanf() 69 ** 70 ** Returns flow of control to where setjmp(ScanTimeOut) was set. 71 ** 72 ** Parameters: 73 ** sig -- unused 74 ** 75 ** Returns: 76 ** does not return 77 ** 78 ** Side Effects: 79 ** returns flow of control to setjmp(ScanTimeOut). 80 ** 81 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 82 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 83 ** DOING. 84 */ 85 86 /* ARGSUSED0 */ 87 static void 88 scanalrm(sig) 89 int sig; 90 { 91 longjmp(ScanTimeOut, 1); 92 } 93 94 /* 95 ** SM_VFSCANF -- convert input into data units 96 ** 97 ** Parameters: 98 ** fp -- file pointer for input data 99 ** timeout -- time intvl allowed to complete (milliseconds) 100 ** fmt0 -- format for finding data units 101 ** ap -- vectors for memory location for storing data units 102 ** 103 ** Results: 104 ** Success: number of data units assigned 105 ** Failure: SM_IO_EOF 106 */ 107 108 int 109 sm_vfscanf(fp, timeout, fmt0, ap) 110 register SM_FILE_T *fp; 111 int SM_NONVOLATILE timeout; 112 char const *fmt0; 113 va_list ap; 114 { 115 register unsigned char *SM_NONVOLATILE fmt = (unsigned char *) fmt0; 116 register int c; /* character from format, or conversion */ 117 register size_t width; /* field width, or 0 */ 118 register char *p; /* points into all kinds of strings */ 119 register int n; /* handy integer */ 120 register int flags; /* flags as defined above */ 121 register char *p0; /* saves original value of p when necessary */ 122 int nassigned; /* number of fields assigned */ 123 int nread; /* number of characters consumed from fp */ 124 int base; /* base argument to strtoll/strtoull */ 125 126 /* conversion function (strtoll/strtoull) */ 127 ULONGLONG_T (*ccfn) __P((const char *, char **, int)); 128 char ccltab[256]; /* character class table for %[...] */ 129 char buf[BUF]; /* buffer for numeric conversions */ 130 SM_EVENT *evt = NULL; 131 132 /* `basefix' is used to avoid `if' tests in the integer scanner */ 133 static short basefix[17] = 134 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 135 136 if (timeout == SM_TIME_DEFAULT) 137 timeout = fp->f_timeout; 138 if (timeout == SM_TIME_IMMEDIATE) 139 { 140 /* 141 ** Filling the buffer will take time and we are wanted to 142 ** return immediately. So... 143 */ 144 145 errno = EAGAIN; 146 return SM_IO_EOF; 147 } 148 149 if (timeout != SM_TIME_FOREVER) 150 { 151 if (setjmp(ScanTimeOut) != 0) 152 { 153 errno = EAGAIN; 154 return SM_IO_EOF; 155 } 156 157 evt = sm_seteventm(timeout, scanalrm, 0); 158 } 159 160 nassigned = 0; 161 nread = 0; 162 base = 0; /* XXX just to keep gcc happy */ 163 ccfn = NULL; /* XXX just to keep gcc happy */ 164 for (;;) 165 { 166 c = *fmt++; 167 if (c == 0) 168 { 169 if (evt != NULL) 170 sm_clrevent(evt); /* undo our timeout */ 171 return nassigned; 172 } 173 if (isspace(c)) 174 { 175 while ((fp->f_r > 0 || sm_refill(fp, SM_TIME_FOREVER) 176 == 0) && 177 isspace(*fp->f_p)) 178 nread++, fp->f_r--, fp->f_p++; 179 continue; 180 } 181 if (c != '%') 182 goto literal; 183 width = 0; 184 flags = 0; 185 186 /* 187 ** switch on the format. continue if done; 188 ** break once format type is derived. 189 */ 190 191 again: c = *fmt++; 192 switch (c) 193 { 194 case '%': 195 literal: 196 if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER)) 197 goto input_failure; 198 if (*fp->f_p != c) 199 goto match_failure; 200 fp->f_r--, fp->f_p++; 201 nread++; 202 continue; 203 204 case '*': 205 flags |= SUPPRESS; 206 goto again; 207 case 'h': 208 flags |= SHORT; 209 goto again; 210 case 'l': 211 if (*fmt == 'l') 212 { 213 fmt++; 214 flags |= QUAD; 215 } 216 else 217 { 218 flags |= LONG; 219 } 220 goto again; 221 case 'q': 222 flags |= QUAD; 223 goto again; 224 225 case '0': case '1': case '2': case '3': case '4': 226 case '5': case '6': case '7': case '8': case '9': 227 width = width * 10 + c - '0'; 228 goto again; 229 230 /* 231 ** Conversions. 232 ** Those marked `compat' are for 4.[123]BSD compatibility. 233 ** 234 ** (According to ANSI, E and X formats are supposed 235 ** to the same as e and x. Sorry about that.) 236 */ 237 238 case 'D': /* compat */ 239 flags |= LONG; 240 /* FALLTHROUGH */ 241 case 'd': 242 c = CT_INT; 243 ccfn = (ULONGLONG_T (*)())sm_strtoll; 244 base = 10; 245 break; 246 247 case 'i': 248 c = CT_INT; 249 ccfn = (ULONGLONG_T (*)())sm_strtoll; 250 base = 0; 251 break; 252 253 case 'O': /* compat */ 254 flags |= LONG; 255 /* FALLTHROUGH */ 256 case 'o': 257 c = CT_INT; 258 ccfn = sm_strtoull; 259 base = 8; 260 break; 261 262 case 'u': 263 c = CT_INT; 264 ccfn = sm_strtoull; 265 base = 10; 266 break; 267 268 case 'X': 269 case 'x': 270 flags |= PFXOK; /* enable 0x prefixing */ 271 c = CT_INT; 272 ccfn = sm_strtoull; 273 base = 16; 274 break; 275 276 case 'E': 277 case 'G': 278 case 'e': 279 case 'f': 280 case 'g': 281 c = CT_FLOAT; 282 break; 283 284 case 's': 285 c = CT_STRING; 286 break; 287 288 case '[': 289 fmt = sm_sccl(ccltab, fmt); 290 flags |= NOSKIP; 291 c = CT_CCL; 292 break; 293 294 case 'c': 295 flags |= NOSKIP; 296 c = CT_CHAR; 297 break; 298 299 case 'p': /* pointer format is like hex */ 300 flags |= POINTER | PFXOK; 301 c = CT_INT; 302 ccfn = sm_strtoull; 303 base = 16; 304 break; 305 306 case 'n': 307 if (flags & SUPPRESS) /* ??? */ 308 continue; 309 if (flags & SHORT) 310 *SM_VA_ARG(ap, short *) = nread; 311 else if (flags & LONG) 312 *SM_VA_ARG(ap, long *) = nread; 313 else 314 *SM_VA_ARG(ap, int *) = nread; 315 continue; 316 317 /* Disgusting backwards compatibility hacks. XXX */ 318 case '\0': /* compat */ 319 if (evt != NULL) 320 sm_clrevent(evt); /* undo our timeout */ 321 return SM_IO_EOF; 322 323 default: /* compat */ 324 if (isupper(c)) 325 flags |= LONG; 326 c = CT_INT; 327 ccfn = (ULONGLONG_T (*)()) sm_strtoll; 328 base = 10; 329 break; 330 } 331 332 /* We have a conversion that requires input. */ 333 if (fp->f_r <= 0 && sm_refill(fp, SM_TIME_FOREVER)) 334 goto input_failure; 335 336 /* 337 ** Consume leading white space, except for formats 338 ** that suppress this. 339 */ 340 341 if ((flags & NOSKIP) == 0) 342 { 343 while (isspace(*fp->f_p)) 344 { 345 nread++; 346 if (--fp->f_r > 0) 347 fp->f_p++; 348 else if (sm_refill(fp, SM_TIME_FOREVER)) 349 goto input_failure; 350 } 351 /* 352 ** Note that there is at least one character in 353 ** the buffer, so conversions that do not set NOSKIP 354 ** can no longer result in an input failure. 355 */ 356 } 357 358 /* Do the conversion. */ 359 switch (c) 360 { 361 case CT_CHAR: 362 /* scan arbitrary characters (sets NOSKIP) */ 363 if (width == 0) 364 width = 1; 365 if (flags & SUPPRESS) 366 { 367 size_t sum = 0; 368 for (;;) 369 { 370 if ((size_t) (n = fp->f_r) < width) 371 { 372 sum += n; 373 width -= n; 374 fp->f_p += n; 375 if (sm_refill(fp, 376 SM_TIME_FOREVER)) 377 { 378 if (sum == 0) 379 goto input_failure; 380 break; 381 } 382 } 383 else 384 { 385 sum += width; 386 fp->f_r -= width; 387 fp->f_p += width; 388 break; 389 } 390 } 391 nread += sum; 392 } 393 else 394 { 395 size_t r; 396 397 r = sm_io_read(fp, SM_TIME_FOREVER, 398 (void *) SM_VA_ARG(ap, char *), 399 width); 400 if (r == 0) 401 goto input_failure; 402 nread += r; 403 nassigned++; 404 } 405 break; 406 407 case CT_CCL: 408 /* scan a (nonempty) character class (sets NOSKIP) */ 409 if (width == 0) 410 width = (size_t)~0; /* `infinity' */ 411 412 /* take only those things in the class */ 413 if (flags & SUPPRESS) 414 { 415 n = 0; 416 while (ccltab[*fp->f_p] != '\0') 417 { 418 n++, fp->f_r--, fp->f_p++; 419 if (--width == 0) 420 break; 421 if (fp->f_r <= 0 && 422 sm_refill(fp, SM_TIME_FOREVER)) 423 { 424 if (n == 0) /* XXX how? */ 425 goto input_failure; 426 break; 427 } 428 } 429 if (n == 0) 430 goto match_failure; 431 } 432 else 433 { 434 p0 = p = SM_VA_ARG(ap, char *); 435 while (ccltab[*fp->f_p] != '\0') 436 { 437 fp->f_r--; 438 *p++ = *fp->f_p++; 439 if (--width == 0) 440 break; 441 if (fp->f_r <= 0 && 442 sm_refill(fp, SM_TIME_FOREVER)) 443 { 444 if (p == p0) 445 goto input_failure; 446 break; 447 } 448 } 449 n = p - p0; 450 if (n == 0) 451 goto match_failure; 452 *p = 0; 453 nassigned++; 454 } 455 nread += n; 456 break; 457 458 case CT_STRING: 459 /* like CCL, but zero-length string OK, & no NOSKIP */ 460 if (width == 0) 461 width = (size_t)~0; 462 if (flags & SUPPRESS) 463 { 464 n = 0; 465 while (!isspace(*fp->f_p)) 466 { 467 n++, fp->f_r--, fp->f_p++; 468 if (--width == 0) 469 break; 470 if (fp->f_r <= 0 && 471 sm_refill(fp, SM_TIME_FOREVER)) 472 break; 473 } 474 nread += n; 475 } 476 else 477 { 478 p0 = p = SM_VA_ARG(ap, char *); 479 while (!isspace(*fp->f_p)) 480 { 481 fp->f_r--; 482 *p++ = *fp->f_p++; 483 if (--width == 0) 484 break; 485 if (fp->f_r <= 0 && 486 sm_refill(fp, SM_TIME_FOREVER)) 487 break; 488 } 489 *p = 0; 490 nread += p - p0; 491 nassigned++; 492 } 493 continue; 494 495 case CT_INT: 496 /* scan an integer as if by strtoll/strtoull */ 497 #if SM_CONF_BROKEN_SIZE_T 498 if (width == 0 || width > sizeof(buf) - 1) 499 width = sizeof(buf) - 1; 500 #else /* SM_CONF_BROKEN_SIZE_T */ 501 /* size_t is unsigned, hence this optimisation */ 502 if (--width > sizeof(buf) - 2) 503 width = sizeof(buf) - 2; 504 width++; 505 #endif /* SM_CONF_BROKEN_SIZE_T */ 506 flags |= SIGNOK | NDIGITS | NZDIGITS; 507 for (p = buf; width > 0; width--) 508 { 509 c = *fp->f_p; 510 511 /* 512 ** Switch on the character; `goto ok' 513 ** if we accept it as a part of number. 514 */ 515 516 switch (c) 517 { 518 519 /* 520 ** The digit 0 is always legal, but is 521 ** special. For %i conversions, if no 522 ** digits (zero or nonzero) have been 523 ** scanned (only signs), we will have 524 ** base==0. In that case, we should set 525 ** it to 8 and enable 0x prefixing. 526 ** Also, if we have not scanned zero digits 527 ** before this, do not turn off prefixing 528 ** (someone else will turn it off if we 529 ** have scanned any nonzero digits). 530 */ 531 532 case '0': 533 if (base == 0) 534 { 535 base = 8; 536 flags |= PFXOK; 537 } 538 if (flags & NZDIGITS) 539 flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 540 else 541 flags &= ~(SIGNOK|PFXOK|NDIGITS); 542 goto ok; 543 544 /* 1 through 7 always legal */ 545 case '1': case '2': case '3': 546 case '4': case '5': case '6': case '7': 547 base = basefix[base]; 548 flags &= ~(SIGNOK | PFXOK | NDIGITS); 549 goto ok; 550 551 /* digits 8 and 9 ok iff decimal or hex */ 552 case '8': case '9': 553 base = basefix[base]; 554 if (base <= 8) 555 break; /* not legal here */ 556 flags &= ~(SIGNOK | PFXOK | NDIGITS); 557 goto ok; 558 559 /* letters ok iff hex */ 560 case 'A': case 'B': case 'C': 561 case 'D': case 'E': case 'F': 562 case 'a': case 'b': case 'c': 563 case 'd': case 'e': case 'f': 564 565 /* no need to fix base here */ 566 if (base <= 10) 567 break; /* not legal here */ 568 flags &= ~(SIGNOK | PFXOK | NDIGITS); 569 goto ok; 570 571 /* sign ok only as first character */ 572 case '+': case '-': 573 if (flags & SIGNOK) 574 { 575 flags &= ~SIGNOK; 576 goto ok; 577 } 578 break; 579 580 /* x ok iff flag still set & 2nd char */ 581 case 'x': case 'X': 582 if (flags & PFXOK && p == buf + 1) 583 { 584 base = 16; /* if %i */ 585 flags &= ~PFXOK; 586 goto ok; 587 } 588 break; 589 } 590 591 /* 592 ** If we got here, c is not a legal character 593 ** for a number. Stop accumulating digits. 594 */ 595 596 break; 597 ok: 598 /* c is legal: store it and look at the next. */ 599 *p++ = c; 600 if (--fp->f_r > 0) 601 fp->f_p++; 602 else if (sm_refill(fp, SM_TIME_FOREVER)) 603 break; /* SM_IO_EOF */ 604 } 605 606 /* 607 ** If we had only a sign, it is no good; push 608 ** back the sign. If the number ends in `x', 609 ** it was [sign] '0' 'x', so push back the x 610 ** and treat it as [sign] '0'. 611 */ 612 613 if (flags & NDIGITS) 614 { 615 if (p > buf) 616 (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, 617 *(unsigned char *)--p); 618 goto match_failure; 619 } 620 c = ((unsigned char *)p)[-1]; 621 if (c == 'x' || c == 'X') 622 { 623 --p; 624 (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); 625 } 626 if ((flags & SUPPRESS) == 0) 627 { 628 ULONGLONG_T res; 629 630 *p = 0; 631 res = (*ccfn)(buf, (char **)NULL, base); 632 if (flags & POINTER) 633 *SM_VA_ARG(ap, void **) = 634 (void *)(long) res; 635 else if (flags & QUAD) 636 *SM_VA_ARG(ap, LONGLONG_T *) = res; 637 else if (flags & LONG) 638 *SM_VA_ARG(ap, long *) = res; 639 else if (flags & SHORT) 640 *SM_VA_ARG(ap, short *) = res; 641 else 642 *SM_VA_ARG(ap, int *) = res; 643 nassigned++; 644 } 645 nread += p - buf; 646 break; 647 648 case CT_FLOAT: 649 /* scan a floating point number as if by strtod */ 650 if (width == 0 || width > sizeof(buf) - 1) 651 width = sizeof(buf) - 1; 652 flags |= SIGNOK | NDIGITS | DPTOK | EXPOK; 653 for (p = buf; width; width--) 654 { 655 c = *fp->f_p; 656 657 /* 658 ** This code mimics the integer conversion 659 ** code, but is much simpler. 660 */ 661 662 switch (c) 663 { 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 { 674 flags &= ~SIGNOK; 675 goto fok; 676 } 677 break; 678 case '.': 679 if (flags & DPTOK) 680 { 681 flags &= ~(SIGNOK | DPTOK); 682 goto fok; 683 } 684 break; 685 case 'e': case 'E': 686 687 /* no exponent without some digits */ 688 if ((flags&(NDIGITS|EXPOK)) == EXPOK) 689 { 690 flags = 691 (flags & ~(EXPOK|DPTOK)) | 692 SIGNOK | NDIGITS; 693 goto fok; 694 } 695 break; 696 } 697 break; 698 fok: 699 *p++ = c; 700 if (--fp->f_r > 0) 701 fp->f_p++; 702 else if (sm_refill(fp, SM_TIME_FOREVER)) 703 break; /* SM_IO_EOF */ 704 } 705 706 /* 707 ** If no digits, might be missing exponent digits 708 ** (just give back the exponent) or might be missing 709 ** regular digits, but had sign and/or decimal point. 710 */ 711 712 if (flags & NDIGITS) 713 { 714 if (flags & EXPOK) 715 { 716 /* no digits at all */ 717 while (p > buf) 718 (void) sm_io_ungetc(fp, 719 SM_TIME_DEFAULT, 720 *(unsigned char *)--p); 721 goto match_failure; 722 } 723 724 /* just a bad exponent (e and maybe sign) */ 725 c = *(unsigned char *) --p; 726 if (c != 'e' && c != 'E') 727 { 728 (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, 729 c); /* sign */ 730 c = *(unsigned char *)--p; 731 } 732 (void) sm_io_ungetc(fp, SM_TIME_DEFAULT, c); 733 } 734 if ((flags & SUPPRESS) == 0) 735 { 736 double res; 737 738 *p = 0; 739 res = strtod(buf, (char **) NULL); 740 if (flags & LONG) 741 *SM_VA_ARG(ap, double *) = res; 742 else 743 *SM_VA_ARG(ap, float *) = res; 744 nassigned++; 745 } 746 nread += p - buf; 747 break; 748 } 749 } 750 input_failure: 751 if (evt != NULL) 752 sm_clrevent(evt); /* undo our timeout */ 753 return nassigned ? nassigned : -1; 754 match_failure: 755 if (evt != NULL) 756 sm_clrevent(evt); /* undo our timeout */ 757 return nassigned; 758 } 759 760 /* 761 ** SM_SCCL -- sequenced character comparison list 762 ** 763 ** Fill in the given table from the scanset at the given format 764 ** (just after `['). Return a pointer to the character past the 765 ** closing `]'. The table has a 1 wherever characters should be 766 ** considered part of the scanset. 767 ** 768 ** Parameters: 769 ** tab -- array flagging "active" char's to match (returned) 770 ** fmt -- character list (within "[]") 771 ** 772 ** Results: 773 */ 774 775 static unsigned char * 776 sm_sccl(tab, fmt) 777 register char *tab; 778 register unsigned char *fmt; 779 { 780 register int c, n, v; 781 782 /* first `clear' the whole table */ 783 c = *fmt++; /* first char hat => negated scanset */ 784 if (c == '^') 785 { 786 v = 1; /* default => accept */ 787 c = *fmt++; /* get new first char */ 788 } 789 else 790 v = 0; /* default => reject */ 791 792 /* should probably use memset here */ 793 for (n = 0; n < 256; n++) 794 tab[n] = v; 795 if (c == 0) 796 return fmt - 1; /* format ended before closing ] */ 797 798 /* 799 ** Now set the entries corresponding to the actual scanset 800 ** to the opposite of the above. 801 ** 802 ** The first character may be ']' (or '-') without being special; 803 ** the last character may be '-'. 804 */ 805 806 v = 1 - v; 807 for (;;) 808 { 809 tab[c] = v; /* take character c */ 810 doswitch: 811 n = *fmt++; /* and examine the next */ 812 switch (n) 813 { 814 815 case 0: /* format ended too soon */ 816 return fmt - 1; 817 818 case '-': 819 /* 820 ** A scanset of the form 821 ** [01+-] 822 ** is defined as `the digit 0, the digit 1, 823 ** the character +, the character -', but 824 ** the effect of a scanset such as 825 ** [a-zA-Z0-9] 826 ** is implementation defined. The V7 Unix 827 ** scanf treats `a-z' as `the letters a through 828 ** z', but treats `a-a' as `the letter a, the 829 ** character -, and the letter a'. 830 ** 831 ** For compatibility, the `-' is not considered 832 ** to define a range if the character following 833 ** it is either a close bracket (required by ANSI) 834 ** or is not numerically greater than the character 835 ** we just stored in the table (c). 836 */ 837 838 n = *fmt; 839 if (n == ']' || n < c) 840 { 841 c = '-'; 842 break; /* resume the for(;;) */ 843 } 844 fmt++; 845 do 846 { 847 /* fill in the range */ 848 tab[++c] = v; 849 } while (c < n); 850 #if 1 /* XXX another disgusting compatibility hack */ 851 852 /* 853 ** Alas, the V7 Unix scanf also treats formats 854 ** such as [a-c-e] as `the letters a through e'. 855 ** This too is permitted by the standard.... 856 */ 857 858 goto doswitch; 859 #else 860 c = *fmt++; 861 if (c == 0) 862 return fmt - 1; 863 if (c == ']') 864 return fmt; 865 break; 866 #endif 867 868 case ']': /* end of scanset */ 869 return fmt; 870 871 default: /* just another character */ 872 c = n; 873 break; 874 } 875 } 876 /* NOTREACHED */ 877 } 878