More spring-cleaning.
[dragonfly.git] / contrib / libio / iovfscanf.c
1 /* 
2 Copyright (C) 1993 Free Software Foundation
3
4 This file is part of the GNU IO Library.  This library is free
5 software; you can redistribute it and/or modify it under the
6 terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this library; see the file COPYING.  If not, write to the Free
17 Software Foundation, 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 As a special exception, if you link this library with files
20 compiled with a GNU compiler to produce an executable, this does not cause
21 the resulting executable to be covered by the GNU General Public License.
22 This exception does not however invalidate any other reasons why
23 the executable file might be covered by the GNU General Public License. */
24
25 /*
26  * Copyright (c) 1990 The Regents of the University of California.
27  * All rights reserved.
28  *
29  * Redistribution and use in source and binary forms are permitted
30  * provided that the above copyright notice and this paragraph are
31  * duplicated in all such forms and that any documentation,
32  * advertising materials, and other materials related to such
33  * distribution and use acknowledge that the software was developed
34  * by the University of California, Berkeley.  The name of the
35  * University may not be used to endorse or promote products derived
36  * from this software without specific prior written permission.
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
38  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
39  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
40  */
41
42 /* Extensively hacked for GNU iostream by Per Bothner 1991, 1992, 1993.
43    Changes copyright Free Software Foundation 1992, 1993. */
44
45 #if defined(LIBC_SCCS) && !defined(lint)
46 static char sccsid[] = "%W% (Berkeley) %G%";
47 #endif /* LIBC_SCCS and not lint */
48
49 #include <libioP.h>
50 #include <ctype.h>
51 #ifdef __STDC__
52 #include <stdarg.h>
53 #else
54 #include <varargs.h>
55 #endif
56
57 #ifndef NO_FLOATING_POINT
58 #define FLOATING_POINT
59 #endif
60
61 #ifdef FLOATING_POINT
62 #include "floatio.h"
63 #define BUF     (MAXEXP+MAXFRACT+3)     /* 3 = sign + decimal point + NUL */
64 #else
65 #define BUF     40
66 #endif
67
68 /*
69  * Flags used during conversion.
70  */
71 #define LONG            0x01    /* l: long or double */
72 #define LONGDBL         0x02    /* L: long double; unimplemented */
73 #define SHORT           0x04    /* h: short */
74 #define SUPPRESS        0x08    /* suppress assignment */
75 #define POINTER         0x10    /* weird %p pointer (`fake hex') */
76 #define NOSKIP          0x20    /* do not skip blanks */
77 #define WIDTH           0x40    /* width */
78
79 /*
80  * The following are used in numeric conversions only:
81  * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point;
82  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral.
83  */
84 #define SIGNOK          0x40    /* +/- is (still) legal */
85 #define NDIGITS         0x80    /* no digits detected */
86
87 #define DPTOK           0x100   /* (float) decimal point is still legal */
88 #define EXPOK           0x200   /* (float) exponent (e+3, etc) still legal */
89
90 #define PFXOK           0x100   /* 0x prefix is (still) legal */
91 #define NZDIGITS        0x200   /* no zero digits detected */
92
93 /*
94  * Conversion types.
95  */
96 #define CT_CHAR         0       /* %c conversion */
97 #define CT_CCL          1       /* %[...] conversion */
98 #define CT_STRING       2       /* %s conversion */
99 #define CT_INT          3       /* integer, i.e., strtol or strtoul */
100 #define CT_FLOAT        4       /* floating, i.e., strtod */
101
102 #define u_char unsigned char
103 #define u_long unsigned long
104
105 #ifdef __cplusplus
106 extern "C" {
107 #endif
108 extern u_long strtoul __P((const char*, char**, int));
109 extern long strtol __P((const char*, char**, int));
110 static const u_char *__sccl __P((char *tab, const u_char *fmt));
111 #ifndef _IO_USE_DTOA
112 extern double atof();
113 #endif
114 #ifdef __cplusplus
115 }
116 #endif
117
118 /* If errp != NULL, *errp|=1 if we see a premature EOF;
119    *errp|=2 if we an invalid character. */
120
121 int
122 _IO_vfscanf (fp, fmt0, ap, errp)
123      _IO_FILE *fp;
124      char const *fmt0;
125      _IO_va_list ap;
126      int *errp;
127 {
128         register const u_char *fmt = (const u_char *)fmt0;
129         register int c;         /* character from format, or conversion */
130         register _IO_ssize_t width;     /* field width, or 0 */
131         register char *p;       /* points into all kinds of strings */
132         register int n;         /* handy integer */
133         register int flags = 0; /* flags as defined above */
134         register char *p0;      /* saves original value of p when necessary */
135         int nassigned;          /* number of fields assigned */
136         int nread;              /* number of characters consumed from fp */
137         /* Assignments to base and ccfn are just to suppress warnings from gcc.*/
138         int base = 0;           /* base argument to strtol/strtoul */
139         typedef u_long (*strtoulfn) __P((const char*, char**, int));
140         strtoulfn ccfn = 0;
141         /* conversion function (strtol/strtoul) */
142         char ccltab[256];       /* character class table for %[...] */
143         char buf[BUF];          /* buffer for numeric conversions */
144         int seen_eof = 0;
145
146         /* `basefix' is used to avoid `if' tests in the integer scanner */
147         static short basefix[17] =
148                 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
149
150         nassigned = 0;
151         nread = 0;
152         for (;;) {
153                 c = *fmt++;
154                 if (c == 0)
155                         goto done;
156                 if (isspace(c)) {
157                         for (;;) {
158                                 c = _IO_getc(fp);
159                                 if (c == EOF) {
160                                   seen_eof++;
161                                   break;
162                                 }
163                                 if (!isspace(c)) {
164                                     _IO_ungetc (c, fp);
165                                     break;
166                                 }
167                                 nread++;
168                         }
169                         continue;
170                 }
171                 if (c != '%')
172                         goto literal;
173                 width = 0;
174                 flags = 0;
175                 /*
176                  * switch on the format.  continue if done;
177                  * break once format type is derived.
178                  */
179 again:          c = *fmt++;
180                 switch (c) {
181                 case '%':
182 literal:
183                         n = _IO_getc(fp);
184                         if (n == EOF)
185                             goto eof_failure;
186                         if (n != c) {
187                             _IO_ungetc (n, fp);
188                             goto match_failure;
189                         }
190                         nread++;
191                         continue;
192
193                 case '*':
194                         if (flags) goto control_failure;
195                         flags = SUPPRESS;
196                         goto again;
197                 case 'l':
198                         if (flags & ~(SUPPRESS | WIDTH)) goto control_failure;
199                         flags |= LONG;
200                         goto again;
201                 case 'L':
202                         if (flags & ~(SUPPRESS | WIDTH)) goto control_failure;
203                         flags |= LONGDBL;
204                         goto again;
205                 case 'h':
206                         if (flags & ~(SUPPRESS | WIDTH)) goto control_failure;
207                         flags |= SHORT;
208                         goto again;
209
210                 case '0': case '1': case '2': case '3': case '4':
211                 case '5': case '6': case '7': case '8': case '9':
212                         if (flags & ~(SUPPRESS | WIDTH)) goto control_failure;
213                         flags |= WIDTH;
214                         width = width * 10 + c - '0';
215                         goto again;
216
217                 /*
218                  * Conversions.
219                  * Those marked `compat' are for 4.[123]BSD compatibility.
220                  *
221                  * (According to ANSI, E and X formats are supposed
222                  * to the same as e and x.  Sorry about that.)
223                  */
224                 case 'D':       /* compat */
225                         flags |= LONG;
226                         /* FALLTHROUGH */
227                 case 'd':
228                         c = CT_INT;
229                         ccfn = (strtoulfn)strtol;
230                         base = 10;
231                         break;
232
233                 case 'i':
234                         c = CT_INT;
235                         ccfn = (strtoulfn)strtol;
236                         base = 0;
237                         break;
238
239                 case 'O':       /* compat */
240                         flags |= LONG;
241                         /* FALLTHROUGH */
242                 case 'o':
243                         c = CT_INT;
244                         ccfn = strtoul;
245                         base = 8;
246                         break;
247
248                 case 'u':
249                         c = CT_INT;
250                         ccfn = strtoul;
251                         base = 10;
252                         break;
253
254                 case 'X':
255                 case 'x':
256                         flags |= PFXOK; /* enable 0x prefixing */
257                         c = CT_INT;
258                         ccfn = strtoul;
259                         base = 16;
260                         break;
261
262 #ifdef FLOATING_POINT
263                 case 'E': case 'F':
264                 case 'e': case 'f': case 'g':
265                         c = CT_FLOAT;
266                         break;
267 #endif
268
269                 case 's':
270                         c = CT_STRING;
271                         break;
272
273                 case '[':
274                         fmt = __sccl(ccltab, fmt);
275                         flags |= NOSKIP;
276                         c = CT_CCL;
277                         break;
278
279                 case 'c':
280                         flags |= NOSKIP;
281                         c = CT_CHAR;
282                         break;
283
284                 case 'p':       /* pointer format is like hex */
285                         flags |= POINTER | PFXOK;
286                         c = CT_INT;
287                         ccfn = strtoul;
288                         base = 16;
289                         break;
290
291                 case 'n':
292                         if (flags & SUPPRESS)   /* ??? */
293                                 continue;
294                         if (flags & SHORT)
295                                 *va_arg(ap, short *) = nread;
296                         else if (flags & LONG)
297                                 *va_arg(ap, long *) = nread;
298                         else
299                                 *va_arg(ap, int *) = nread;
300                         continue;
301
302                 /*
303                  * Disgusting backwards compatibility hacks.    XXX
304                  */
305                 case '\0':      /* compat */
306                         nassigned = EOF;
307                         goto done;
308
309                 default:        /* compat */
310                         if (isupper(c))
311                                 flags |= LONG;
312                         c = CT_INT;
313                         ccfn = (strtoulfn)strtol;
314                         base = 10;
315                         break;
316                 }
317
318                 /*
319                  * We have a conversion that requires input.
320                  */
321                 if (_IO_peekc(fp) == EOF)
322                         goto eof_failure;
323
324                 /*
325                  * Consume leading white space, except for formats
326                  * that suppress this.
327                  */
328                 if ((flags & NOSKIP) == 0) {
329                     n = (unsigned char)*fp->_IO_read_ptr;
330                     while (isspace(n)) {
331                         fp->_IO_read_ptr++;
332                         nread++;
333                         n = _IO_peekc(fp);
334                         if (n == EOF)
335                             goto eof_failure;
336                     }
337                     /* Note that there is at least one character in
338                        the buffer, so conversions that do not set NOSKIP
339                        can no longer result in an input failure. */
340                 }
341
342                 /*
343                  * Do the conversion.
344                  */
345                 switch (c) {
346
347                 case CT_CHAR:
348                         /* scan arbitrary characters (sets NOSKIP) */
349                         if (width == 0) /* FIXME! */
350                                 width = 1;
351                         if (flags & SUPPRESS) {
352                             _IO_size_t sum = 0;
353                             for (;;) {
354                                 n = fp->_IO_read_end - fp->_IO_read_ptr;
355                                 if (n < (int)width) {
356                                     sum += n;
357                                     width -= n;
358                                     fp->_IO_read_ptr += n;
359                                     if (__underflow(fp) == EOF)
360                                         if (sum == 0)
361                                             goto eof_failure;
362                                         else {
363                                             seen_eof++;
364                                             break;
365                                         }
366                                 } else {
367                                     sum += width;
368                                     fp->_IO_read_ptr += width;
369                                     break;
370                                 }
371                             }
372                             nread += sum;
373                         } else {
374                             _IO_size_t r =
375
376                               _IO_XSGETN (fp, (char*)va_arg(ap, char*), width);
377                             if (r != width)
378                                 goto eof_failure;
379                             nread += r;
380                             nassigned++;
381                         }
382                         break;
383
384                 case CT_CCL:
385                         /* scan a (nonempty) character class (sets NOSKIP) */
386                         if (width == 0)
387                                 width = ~0;     /* `infinity' */
388                         /* take only those things in the class */
389                         if (flags & SUPPRESS) {
390                                 n = 0;
391                                 while (ccltab[(unsigned char)*fp->_IO_read_ptr]) {
392                                     n++, fp->_IO_read_ptr++;
393                                     if (--width == 0)
394                                         break;
395                                     if (_IO_peekc(fp) == EOF) {
396                                         if (n == 0)
397                                             goto eof_failure;
398                                         seen_eof++;
399                                         break;
400                                     }
401                                 }
402                                 if (n == 0)
403                                         goto match_failure;
404                         } else {
405                             p0 = p = va_arg(ap, char *);
406                             while (ccltab[(unsigned char)*fp->_IO_read_ptr]) {
407                                 *p++ = *fp->_IO_read_ptr++;
408                                 if (--width == 0)
409                                     break;
410                                 if (_IO_peekc(fp) == EOF) {
411                                     if (p == p0)
412                                         goto eof_failure;
413                                     seen_eof++;
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 = ~0;
430                         if (flags & SUPPRESS) {
431                                 n = 0;
432                                 while (!isspace((unsigned char)*fp->_IO_read_ptr)) {
433                                         n++, fp->_IO_read_ptr++;
434                                         if (--width == 0)
435                                                 break;
436                                         if (_IO_peekc(fp) == EOF) {
437                                             seen_eof++;
438                                             break;
439                                         }
440                                 }
441                                 nread += n;
442                         } else {
443                                 p0 = p = va_arg(ap, char *);
444                                 while (!isspace((unsigned char)*fp->_IO_read_ptr)) {
445                                         *p++ = *fp->_IO_read_ptr++;
446                                         if (--width == 0)
447                                                 break;
448                                         if (_IO_peekc(fp) == EOF) {
449                                             seen_eof++;
450                                             break;
451                                         }
452                                 }
453                                 *p = 0;
454                                 nread += p - p0;
455                                 nassigned++;
456                         }
457                         continue;
458
459                 case CT_INT:
460                         /* scan an integer as if by strtol/strtoul */
461                         if (width == 0 || width > sizeof(buf) - 1)
462                                 width = sizeof(buf) - 1;
463                         flags |= SIGNOK | NDIGITS | NZDIGITS;
464                         for (p = buf; width; width--) {
465                                 c = (unsigned char)*fp->_IO_read_ptr;
466                                 /*
467                                  * Switch on the character; `goto ok'
468                                  * if we accept it as a part of number.
469                                  */
470                                 switch (c) {
471
472                                 /*
473                                  * The digit 0 is always legal, but is
474                                  * special.  For %i conversions, if no
475                                  * digits (zero or nonzero) have been
476                                  * scanned (only signs), we will have
477                                  * base==0.  In that case, we should set
478                                  * it to 8 and enable 0x prefixing.
479                                  * Also, if we have not scanned zero digits
480                                  * before this, do not turn off prefixing
481                                  * (someone else will turn it off if we
482                                  * have scanned any nonzero digits).
483                                  */
484                                 case '0':
485                                         if (base == 0) {
486                                                 base = 8;
487                                                 flags |= PFXOK;
488                                         }
489                                         if (flags & NZDIGITS)
490                                             flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
491                                         else
492                                             flags &= ~(SIGNOK|PFXOK|NDIGITS);
493                                         goto ok;
494
495                                 /* 1 through 7 always legal */
496                                 case '1': case '2': case '3':
497                                 case '4': case '5': case '6': case '7':
498                                         base = basefix[base];
499                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
500                                         goto ok;
501
502                                 /* digits 8 and 9 ok iff decimal or hex */
503                                 case '8': case '9':
504                                         base = basefix[base];
505                                         if (base <= 8)
506                                                 break;  /* not legal here */
507                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
508                                         goto ok;
509
510                                 /* letters ok iff hex */
511                                 case 'A': case 'B': case 'C':
512                                 case 'D': case 'E': case 'F':
513                                 case 'a': case 'b': case 'c':
514                                 case 'd': case 'e': case 'f':
515                                         /* no need to fix base here */
516                                         if (base <= 10)
517                                                 break;  /* not legal here */
518                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
519                                         goto ok;
520
521                                 /* sign ok only as first character */
522                                 case '+': case '-':
523                                         if (flags & SIGNOK) {
524                                                 flags &= ~SIGNOK;
525                                                 goto ok;
526                                         }
527                                         break;
528
529                                 /* x ok iff flag still set & 2nd char */
530                                 case 'x': case 'X':
531                                         if (flags & PFXOK && p == buf + 1) {
532                                                 base = 16;      /* if %i */
533                                                 flags &= ~PFXOK;
534                                                 goto ok;
535                                         }
536                                         break;
537                                 }
538
539                                 /*
540                                  * If we got here, c is not a legal character
541                                  * for a number.  Stop accumulating digits.
542                                  */
543                                 break;
544                 ok:
545                                 /*
546                                  * c is legal: store it and look at the next.
547                                  */
548                                 *p++ = c;
549                                 fp->_IO_read_ptr++;
550                                 if (_IO_peekc(fp) == EOF) {
551                                     seen_eof++;
552                                     break;              /* EOF */
553                                 }
554                         }
555                         /*
556                          * If we had only a sign, it is no good; push
557                          * back the sign.  If the number ends in `x',
558                          * it was [sign] '0' 'x', so push back the x
559                          * and treat it as [sign] '0'.
560                          */
561                         if (flags & NDIGITS) {
562                                 if (p > buf)
563                                         (void) _IO_ungetc(*(u_char *)--p, fp);
564                                 goto match_failure;
565                         }
566                         c = ((u_char *)p)[-1];
567                         if (c == 'x' || c == 'X') {
568                                 --p;
569                                 (void) _IO_ungetc (c, fp);
570                         }
571                         if ((flags & SUPPRESS) == 0) {
572                                 u_long res;
573
574                                 *p = 0;
575                                 res = (*ccfn)(buf, (char **)NULL, base);
576                                 if (flags & POINTER)
577                                         *va_arg(ap, void **) = (void *)res;
578                                 else if (flags & SHORT)
579                                         *va_arg(ap, short *) = res;
580                                 else if (flags & LONG)
581                                         *va_arg(ap, long *) = res;
582                                 else
583                                         *va_arg(ap, int *) = res;
584                                 nassigned++;
585                         }
586                         nread += p - buf;
587                         break;
588
589 #ifdef FLOATING_POINT
590                 case CT_FLOAT:
591                         /* scan a floating point number as if by strtod */
592                         if (width == 0 || width > sizeof(buf) - 1)
593                                 width = sizeof(buf) - 1;
594                         flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
595                         for (p = buf; width; width--) {
596                                 c = (unsigned char)*fp->_IO_read_ptr;
597                                 /*
598                                  * This code mimicks the integer conversion
599                                  * code, but is much simpler.
600                                  */
601                                 switch (c) {
602
603                                 case '0': case '1': case '2': case '3':
604                                 case '4': case '5': case '6': case '7':
605                                 case '8': case '9':
606                                         flags &= ~(SIGNOK | NDIGITS);
607                                         goto fok;
608
609                                 case '+': case '-':
610                                         if (flags & SIGNOK) {
611                                                 flags &= ~SIGNOK;
612                                                 goto fok;
613                                         }
614                                         break;
615                                 case '.':
616                                         if (flags & DPTOK) {
617                                                 flags &= ~(SIGNOK | DPTOK);
618                                                 goto fok;
619                                         }
620                                         break;
621                                 case 'e': case 'E':
622                                         /* no exponent without some digits */
623                                         if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
624                                                 flags =
625                                                     (flags & ~(EXPOK|DPTOK)) |
626                                                     SIGNOK | NDIGITS;
627                                                 goto fok;
628                                         }
629                                         break;
630                                 }
631                                 break;
632                 fok:
633                                 *p++ = c;
634                                 fp->_IO_read_ptr++;
635                                 if (_IO_peekc(fp) == EOF) {
636                                     seen_eof++;
637                                     break;      /* EOF */
638                                 }
639                         }
640                         /*
641                          * If no digits, might be missing exponent digits
642                          * (just give back the exponent) or might be missing
643                          * regular digits, but had sign and/or decimal point.
644                          */
645                         if (flags & NDIGITS) {
646                                 if (flags & EXPOK) {
647                                         /* no digits at all */
648                                         while (p > buf)
649                                             _IO_ungetc (*(u_char *)--p, fp);
650                                         goto match_failure;
651                                 }
652                                 /* just a bad exponent (e and maybe sign) */
653                                 c = *(u_char *)--p;
654                                 if (c != 'e' && c != 'E') {
655                                         (void) _IO_ungetc (c, fp);/* sign */
656                                         c = *(u_char *)--p;
657                                 }
658                                 (void) _IO_ungetc (c, fp);
659                         }
660                         if ((flags & SUPPRESS) == 0) {
661                                 double res;
662                                 *p = 0;
663 #ifdef _IO_USE_DTOA
664                                 res = _IO_strtod(buf, NULL);
665 #else
666                                 res = atof(buf);
667 #endif
668                                 if (flags & LONG)
669                                         *va_arg(ap, double *) = res;
670                                 else
671                                         *va_arg(ap, float *) = res;
672                                 nassigned++;
673                         }
674                         nread += p - buf;
675                         break;
676 #endif /* FLOATING_POINT */
677                 }
678         }
679 eof_failure:
680         seen_eof++;
681 input_failure:
682         if (nassigned == 0)
683             nassigned = -1;
684 control_failure:
685 match_failure:
686         if (errp)
687             *errp |= 2;
688 done:
689         if (errp && seen_eof)
690                 *errp |= 1;
691         return (nassigned);
692 }
693
694 /*
695  * Fill in the given table from the scanset at the given format
696  * (just after `[').  Return a pointer to the character past the
697  * closing `]'.  The table has a 1 wherever characters should be
698  * considered part of the scanset.
699  */
700 static const u_char *
701 __sccl (tab, fmt)
702      char *tab;
703      const u_char *fmt;
704 {
705         register int c, n, v;
706
707         /* first `clear' the whole table */
708         c = *fmt++;             /* first char hat => negated scanset */
709         if (c == '^') {
710                 v = 1;          /* default => accept */
711                 c = *fmt++;     /* get new first char */
712         } else
713                 v = 0;          /* default => reject */
714         /* should probably use memset here */
715         for (n = 0; n < 256; n++)
716                 tab[n] = v;
717         if (c == 0)
718                 return (fmt - 1);/* format ended before closing ] */
719
720         /*
721          * Now set the entries corresponding to the actual scanset
722          * to the opposite of the above.
723          *
724          * The first character may be ']' (or '-') without being special;
725          * the last character may be '-'.
726          */
727         v = 1 - v;
728         for (;;) {
729                 tab[c] = v;             /* take character c */
730 doswitch:
731                 n = *fmt++;             /* and examine the next */
732                 switch (n) {
733
734                 case 0:                 /* format ended too soon */
735                         return (fmt - 1);
736
737                 case '-':
738                         /*
739                          * A scanset of the form
740                          *      [01+-]
741                          * is defined as `the digit 0, the digit 1,
742                          * the character +, the character -', but
743                          * the effect of a scanset such as
744                          *      [a-zA-Z0-9]
745                          * is implementation defined.  The V7 Unix
746                          * scanf treats `a-z' as `the letters a through
747                          * z', but treats `a-a' as `the letter a, the
748                          * character -, and the letter a'.
749                          *
750                          * For compatibility, the `-' is not considerd
751                          * to define a range if the character following
752                          * it is either a close bracket (required by ANSI)
753                          * or is not numerically greater than the character
754                          * we just stored in the table (c).
755                          */
756                         n = *fmt;
757                         if (n == ']' || n < c) {
758                                 c = '-';
759                                 break;  /* resume the for(;;) */
760                         }
761                         fmt++;
762                         do {            /* fill in the range */
763                                 tab[++c] = v;
764                         } while (c < n);
765 #if 1   /* XXX another disgusting compatibility hack */
766                         /*
767                          * Alas, the V7 Unix scanf also treats formats
768                          * such as [a-c-e] as `the letters a through e'.
769                          * This too is permitted by the standard....
770                          */
771                         goto doswitch;
772 #else
773                         c = *fmt++;
774                         if (c == 0)
775                                 return (fmt - 1);
776                         if (c == ']')
777                                 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 }