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