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