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