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