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