OSDN Git Service

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