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