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