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