]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - lib/libc/stdio/vfscanf.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.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  * 4. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32
33 #if defined(LIBC_SCCS) && !defined(lint)
34 static char sccsid[] = "@(#)vfscanf.c   8.1 (Berkeley) 6/4/93";
35 #endif /* LIBC_SCCS and not lint */
36 #include <sys/cdefs.h>
37 __FBSDID("$FreeBSD$");
38
39 #include "namespace.h"
40 #include <ctype.h>
41 #include <inttypes.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <stddef.h>
45 #include <stdarg.h>
46 #include <string.h>
47 #include <wchar.h>
48 #include <wctype.h>
49 #include "un-namespace.h"
50
51 #include "collate.h"
52 #include "libc_private.h"
53 #include "local.h"
54
55 #ifndef NO_FLOATING_POINT
56 #include <locale.h>
57 #endif
58
59 #define BUF             513     /* Maximum length of numeric string. */
60
61 /*
62  * Flags used during conversion.
63  */
64 #define LONG            0x01    /* l: long or double */
65 #define LONGDBL         0x02    /* L: long double */
66 #define SHORT           0x04    /* h: short */
67 #define SUPPRESS        0x08    /* *: suppress assignment */
68 #define POINTER         0x10    /* p: void * (as hex) */
69 #define NOSKIP          0x20    /* [ or c: do not skip blanks */
70 #define LONGLONG        0x400   /* ll: long long (+ deprecated q: quad) */
71 #define INTMAXT         0x800   /* j: intmax_t */
72 #define PTRDIFFT        0x1000  /* t: ptrdiff_t */
73 #define SIZET           0x2000  /* z: size_t */
74 #define SHORTSHORT      0x4000  /* hh: char */
75 #define UNSIGNED        0x8000  /* %[oupxX] conversions */
76
77 /*
78  * The following are used in integral conversions only:
79  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
80  */
81 #define SIGNOK          0x40    /* +/- is (still) legal */
82 #define NDIGITS         0x80    /* no digits detected */
83 #define PFXOK           0x100   /* 0x prefix is (still) legal */
84 #define NZDIGITS        0x200   /* no zero digits detected */
85 #define HAVESIGN        0x10000 /* sign detected */
86
87 /*
88  * Conversion types.
89  */
90 #define CT_CHAR         0       /* %c conversion */
91 #define CT_CCL          1       /* %[...] conversion */
92 #define CT_STRING       2       /* %s conversion */
93 #define CT_INT          3       /* %[dioupxX] conversion */
94 #define CT_FLOAT        4       /* %[efgEFG] conversion */
95
96 static const u_char *__sccl(char *, const u_char *);
97 #ifndef NO_FLOATING_POINT
98 static int parsefloat(FILE *, char *, char *);
99 #endif
100
101 int __scanfdebug = 0;
102
103 __weak_reference(__vfscanf, vfscanf);
104
105 /*
106  * __vfscanf - MT-safe version
107  */
108 int
109 __vfscanf(FILE *fp, char const *fmt0, va_list ap)
110 {
111         int ret;
112
113         FLOCKFILE(fp);
114         ret = __svfscanf(fp, fmt0, ap);
115         FUNLOCKFILE(fp);
116         return (ret);
117 }
118
119 /*
120  * __svfscanf - non-MT-safe version of __vfscanf
121  */
122 int
123 __svfscanf(FILE *fp, const char *fmt0, va_list ap)
124 {
125         const u_char *fmt = (const u_char *)fmt0;
126         int c;                  /* character from format, or conversion */
127         size_t width;           /* field width, or 0 */
128         char *p;                /* points into all kinds of strings */
129         int n;                  /* handy integer */
130         int flags;              /* flags as defined above */
131         char *p0;               /* saves original value of p when necessary */
132         int nassigned;          /* number of fields assigned */
133         int nconversions;       /* number of conversions */
134         int nread;              /* number of characters consumed from fp */
135         int base;               /* base argument to conversion function */
136         char ccltab[256];       /* character class table for %[...] */
137         char buf[BUF];          /* buffer for numeric and mb conversions */
138         wchar_t *wcp;           /* handy wide character pointer */
139         size_t nconv;           /* length of multibyte sequence converted */
140         static const mbstate_t initial;
141         mbstate_t mbs;
142
143         /* `basefix' is used to avoid `if' tests in the integer scanner */
144         static short basefix[17] =
145                 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
146
147         ORIENT(fp, -1);
148
149         nassigned = 0;
150         nconversions = 0;
151         nread = 0;
152         for (;;) {
153                 c = *fmt++;
154                 if (c == 0)
155                         return (nassigned);
156                 if (isspace(c)) {
157                         while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p))
158                                 nread++, fp->_r--, fp->_p++;
159                         continue;
160                 }
161                 if (c != '%')
162                         goto literal;
163                 width = 0;
164                 flags = 0;
165                 /*
166                  * switch on the format.  continue if done;
167                  * break once format type is derived.
168                  */
169 again:          c = *fmt++;
170                 switch (c) {
171                 case '%':
172 literal:
173                         if (fp->_r <= 0 && __srefill(fp))
174                                 goto input_failure;
175                         if (*fp->_p != c)
176                                 goto match_failure;
177                         fp->_r--, fp->_p++;
178                         nread++;
179                         continue;
180
181                 case '*':
182                         flags |= SUPPRESS;
183                         goto again;
184                 case 'j':
185                         flags |= INTMAXT;
186                         goto again;
187                 case 'l':
188                         if (flags & LONG) {
189                                 flags &= ~LONG;
190                                 flags |= LONGLONG;
191                         } else
192                                 flags |= LONG;
193                         goto again;
194                 case 'q':
195                         flags |= LONGLONG;      /* not quite */
196                         goto again;
197                 case 't':
198                         flags |= PTRDIFFT;
199                         goto again;
200                 case 'z':
201                         flags |= SIZET;
202                         goto again;
203                 case 'L':
204                         flags |= LONGDBL;
205                         goto again;
206                 case 'h':
207                         if (flags & SHORT) {
208                                 flags &= ~SHORT;
209                                 flags |= SHORTSHORT;
210                         } else
211                                 flags |= SHORT;
212                         goto again;
213
214                 case '0': case '1': case '2': case '3': case '4':
215                 case '5': case '6': case '7': case '8': case '9':
216                         width = width * 10 + c - '0';
217                         goto again;
218
219                 /*
220                  * Conversions.
221                  */
222                 case 'd':
223                         c = CT_INT;
224                         base = 10;
225                         break;
226
227                 case 'i':
228                         c = CT_INT;
229                         base = 0;
230                         break;
231
232                 case 'o':
233                         c = CT_INT;
234                         flags |= UNSIGNED;
235                         base = 8;
236                         break;
237
238                 case 'u':
239                         c = CT_INT;
240                         flags |= UNSIGNED;
241                         base = 10;
242                         break;
243
244                 case 'X':
245                 case 'x':
246                         flags |= PFXOK; /* enable 0x prefixing */
247                         c = CT_INT;
248                         flags |= UNSIGNED;
249                         base = 16;
250                         break;
251
252 #ifndef NO_FLOATING_POINT
253                 case 'A': case 'E': case 'F': case 'G':
254                 case 'a': case 'e': case 'f': case 'g':
255                         c = CT_FLOAT;
256                         break;
257 #endif
258
259                 case 'S':
260                         flags |= LONG;
261                         /* FALLTHROUGH */
262                 case 's':
263                         c = CT_STRING;
264                         break;
265
266                 case '[':
267                         fmt = __sccl(ccltab, fmt);
268                         flags |= NOSKIP;
269                         c = CT_CCL;
270                         break;
271
272                 case 'C':
273                         flags |= LONG;
274                         /* FALLTHROUGH */
275                 case 'c':
276                         flags |= NOSKIP;
277                         c = CT_CHAR;
278                         break;
279
280                 case 'p':       /* pointer format is like hex */
281                         flags |= POINTER | PFXOK;
282                         c = CT_INT;             /* assumes sizeof(uintmax_t) */
283                         flags |= UNSIGNED;      /*      >= sizeof(uintptr_t) */
284                         base = 16;
285                         break;
286
287                 case 'n':
288                         nconversions++;
289                         if (flags & SUPPRESS)   /* ??? */
290                                 continue;
291                         if (flags & SHORTSHORT)
292                                 *va_arg(ap, char *) = nread;
293                         else if (flags & SHORT)
294                                 *va_arg(ap, short *) = nread;
295                         else if (flags & LONG)
296                                 *va_arg(ap, long *) = nread;
297                         else if (flags & LONGLONG)
298                                 *va_arg(ap, long long *) = nread;
299                         else if (flags & INTMAXT)
300                                 *va_arg(ap, intmax_t *) = nread;
301                         else if (flags & SIZET)
302                                 *va_arg(ap, size_t *) = nread;
303                         else if (flags & PTRDIFFT)
304                                 *va_arg(ap, ptrdiff_t *) = nread;
305                         else
306                                 *va_arg(ap, int *) = nread;
307                         continue;
308
309                 default:
310                         goto match_failure;
311
312                 /*
313                  * Disgusting backwards compatibility hack.     XXX
314                  */
315                 case '\0':      /* compat */
316                         return (EOF);
317                 }
318
319                 /*
320                  * We have a conversion that requires input.
321                  */
322                 if (fp->_r <= 0 && __srefill(fp))
323                         goto input_failure;
324
325                 /*
326                  * Consume leading white space, except for formats
327                  * that suppress this.
328                  */
329                 if ((flags & NOSKIP) == 0) {
330                         while (isspace(*fp->_p)) {
331                                 nread++;
332                                 if (--fp->_r > 0)
333                                         fp->_p++;
334                                 else if (__srefill(fp))
335                                         goto input_failure;
336                         }
337                         /*
338                          * Note that there is at least one character in
339                          * the buffer, so conversions that do not set NOSKIP
340                          * ca no longer result in an input failure.
341                          */
342                 }
343
344                 /*
345                  * Do the conversion.
346                  */
347                 switch (c) {
348
349                 case CT_CHAR:
350                         /* scan arbitrary characters (sets NOSKIP) */
351                         if (width == 0)
352                                 width = 1;
353                         if (flags & LONG) {
354                                 if ((flags & SUPPRESS) == 0)
355                                         wcp = va_arg(ap, wchar_t *);
356                                 else
357                                         wcp = NULL;
358                                 n = 0;
359                                 while (width != 0) {
360                                         if (n == MB_CUR_MAX) {
361                                                 fp->_flags |= __SERR;
362                                                 goto input_failure;
363                                         }
364                                         buf[n++] = *fp->_p;
365                                         fp->_p++;
366                                         fp->_r--;
367                                         mbs = initial;
368                                         nconv = mbrtowc(wcp, buf, n, &mbs);
369                                         if (nconv == (size_t)-1) {
370                                                 fp->_flags |= __SERR;
371                                                 goto input_failure;
372                                         }
373                                         if (nconv == 0 && !(flags & SUPPRESS))
374                                                 *wcp = L'\0';
375                                         if (nconv != (size_t)-2) {
376                                                 nread += n;
377                                                 width--;
378                                                 if (!(flags & SUPPRESS))
379                                                         wcp++;
380                                                 n = 0;
381                                         }
382                                         if (fp->_r <= 0 && __srefill(fp)) {
383                                                 if (n != 0) {
384                                                         fp->_flags |= __SERR;
385                                                         goto input_failure;
386                                                 }
387                                                 break;
388                                         }
389                                 }
390                                 if (!(flags & SUPPRESS))
391                                         nassigned++;
392                         } else if (flags & SUPPRESS) {
393                                 size_t sum = 0;
394                                 for (;;) {
395                                         if ((n = fp->_r) < width) {
396                                                 sum += n;
397                                                 width -= n;
398                                                 fp->_p += n;
399                                                 if (__srefill(fp)) {
400                                                         if (sum == 0)
401                                                             goto input_failure;
402                                                         break;
403                                                 }
404                                         } else {
405                                                 sum += width;
406                                                 fp->_r -= width;
407                                                 fp->_p += width;
408                                                 break;
409                                         }
410                                 }
411                                 nread += sum;
412                         } else {
413                                 size_t r = __fread((void *)va_arg(ap, char *), 1,
414                                     width, fp);
415
416                                 if (r == 0)
417                                         goto input_failure;
418                                 nread += r;
419                                 nassigned++;
420                         }
421                         nconversions++;
422                         break;
423
424                 case CT_CCL:
425                         /* scan a (nonempty) character class (sets NOSKIP) */
426                         if (width == 0)
427                                 width = (size_t)~0;     /* `infinity' */
428                         /* take only those things in the class */
429                         if (flags & LONG) {
430                                 wchar_t twc;
431                                 int nchars;
432
433                                 if ((flags & SUPPRESS) == 0)
434                                         wcp = va_arg(ap, wchar_t *);
435                                 else
436                                         wcp = &twc;
437                                 n = 0;
438                                 nchars = 0;
439                                 while (width != 0) {
440                                         if (n == MB_CUR_MAX) {
441                                                 fp->_flags |= __SERR;
442                                                 goto input_failure;
443                                         }
444                                         buf[n++] = *fp->_p;
445                                         fp->_p++;
446                                         fp->_r--;
447                                         mbs = initial;
448                                         nconv = mbrtowc(wcp, buf, n, &mbs);
449                                         if (nconv == (size_t)-1) {
450                                                 fp->_flags |= __SERR;
451                                                 goto input_failure;
452                                         }
453                                         if (nconv == 0)
454                                                 *wcp = L'\0';
455                                         if (nconv != (size_t)-2) {
456                                                 if (wctob(*wcp) != EOF &&
457                                                     !ccltab[wctob(*wcp)]) {
458                                                         while (n != 0) {
459                                                                 n--;
460                                                                 __ungetc(buf[n],
461                                                                     fp);
462                                                         }
463                                                         break;
464                                                 }
465                                                 nread += n;
466                                                 width--;
467                                                 if (!(flags & SUPPRESS))
468                                                         wcp++;
469                                                 nchars++;
470                                                 n = 0;
471                                         }
472                                         if (fp->_r <= 0 && __srefill(fp)) {
473                                                 if (n != 0) {
474                                                         fp->_flags |= __SERR;
475                                                         goto input_failure;
476                                                 }
477                                                 break;
478                                         }
479                                 }
480                                 if (n != 0) {
481                                         fp->_flags |= __SERR;
482                                         goto input_failure;
483                                 }
484                                 n = nchars;
485                                 if (n == 0)
486                                         goto match_failure;
487                                 if (!(flags & SUPPRESS)) {
488                                         *wcp = L'\0';
489                                         nassigned++;
490                                 }
491                         } else if (flags & SUPPRESS) {
492                                 n = 0;
493                                 while (ccltab[*fp->_p]) {
494                                         n++, fp->_r--, fp->_p++;
495                                         if (--width == 0)
496                                                 break;
497                                         if (fp->_r <= 0 && __srefill(fp)) {
498                                                 if (n == 0)
499                                                         goto input_failure;
500                                                 break;
501                                         }
502                                 }
503                                 if (n == 0)
504                                         goto match_failure;
505                         } else {
506                                 p0 = p = va_arg(ap, char *);
507                                 while (ccltab[*fp->_p]) {
508                                         fp->_r--;
509                                         *p++ = *fp->_p++;
510                                         if (--width == 0)
511                                                 break;
512                                         if (fp->_r <= 0 && __srefill(fp)) {
513                                                 if (p == p0)
514                                                         goto input_failure;
515                                                 break;
516                                         }
517                                 }
518                                 n = p - p0;
519                                 if (n == 0)
520                                         goto match_failure;
521                                 *p = 0;
522                                 nassigned++;
523                         }
524                         nread += n;
525                         nconversions++;
526                         break;
527
528                 case CT_STRING:
529                         /* like CCL, but zero-length string OK, & no NOSKIP */
530                         if (width == 0)
531                                 width = (size_t)~0;
532                         if (flags & LONG) {
533                                 wchar_t twc;
534
535                                 if ((flags & SUPPRESS) == 0)
536                                         wcp = va_arg(ap, wchar_t *);
537                                 else
538                                         wcp = &twc;
539                                 n = 0;
540                                 while (!isspace(*fp->_p) && width != 0) {
541                                         if (n == MB_CUR_MAX) {
542                                                 fp->_flags |= __SERR;
543                                                 goto input_failure;
544                                         }
545                                         buf[n++] = *fp->_p;
546                                         fp->_p++;
547                                         fp->_r--;
548                                         mbs = initial;
549                                         nconv = mbrtowc(wcp, buf, n, &mbs);
550                                         if (nconv == (size_t)-1) {
551                                                 fp->_flags |= __SERR;
552                                                 goto input_failure;
553                                         }
554                                         if (nconv == 0)
555                                                 *wcp = L'\0';
556                                         if (nconv != (size_t)-2) {
557                                                 if (iswspace(*wcp)) {
558                                                         while (n != 0) {
559                                                                 n--;
560                                                                 __ungetc(buf[n],
561                                                                     fp);
562                                                         }
563                                                         break;
564                                                 }
565                                                 nread += n;
566                                                 width--;
567                                                 if (!(flags & SUPPRESS))
568                                                         wcp++;
569                                                 n = 0;
570                                         }
571                                         if (fp->_r <= 0 && __srefill(fp)) {
572                                                 if (n != 0) {
573                                                         fp->_flags |= __SERR;
574                                                         goto input_failure;
575                                                 }
576                                                 break;
577                                         }
578                                 }
579                                 if (!(flags & SUPPRESS)) {
580                                         *wcp = L'\0';
581                                         nassigned++;
582                                 }
583                         } else if (flags & SUPPRESS) {
584                                 n = 0;
585                                 while (!isspace(*fp->_p)) {
586                                         n++, fp->_r--, fp->_p++;
587                                         if (--width == 0)
588                                                 break;
589                                         if (fp->_r <= 0 && __srefill(fp))
590                                                 break;
591                                 }
592                                 nread += n;
593                         } else {
594                                 p0 = p = va_arg(ap, char *);
595                                 while (!isspace(*fp->_p)) {
596                                         fp->_r--;
597                                         *p++ = *fp->_p++;
598                                         if (--width == 0)
599                                                 break;
600                                         if (fp->_r <= 0 && __srefill(fp))
601                                                 break;
602                                 }
603                                 *p = 0;
604                                 nread += p - p0;
605                                 nassigned++;
606                         }
607                         nconversions++;
608                         continue;
609
610                 case CT_INT:
611                         /* scan an integer as if by the conversion function */
612 #ifdef hardway
613                         if (width == 0 || width > sizeof(buf) - 1)
614                                 width = sizeof(buf) - 1;
615 #else
616                         /* size_t is unsigned, hence this optimisation */
617                         if (--width > sizeof(buf) - 2)
618                                 width = sizeof(buf) - 2;
619                         width++;
620 #endif
621                         flags |= SIGNOK | NDIGITS | NZDIGITS;
622                         for (p = buf; width; width--) {
623                                 c = *fp->_p;
624                                 /*
625                                  * Switch on the character; `goto ok'
626                                  * if we accept it as a part of number.
627                                  */
628                                 switch (c) {
629
630                                 /*
631                                  * The digit 0 is always legal, but is
632                                  * special.  For %i conversions, if no
633                                  * digits (zero or nonzero) have been
634                                  * scanned (only signs), we will have
635                                  * base==0.  In that case, we should set
636                                  * it to 8 and enable 0x prefixing.
637                                  * Also, if we have not scanned zero digits
638                                  * before this, do not turn off prefixing
639                                  * (someone else will turn it off if we
640                                  * have scanned any nonzero digits).
641                                  */
642                                 case '0':
643                                         if (base == 0) {
644                                                 base = 8;
645                                                 flags |= PFXOK;
646                                         }
647                                         if (flags & NZDIGITS)
648                                             flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
649                                         else
650                                             flags &= ~(SIGNOK|PFXOK|NDIGITS);
651                                         goto ok;
652
653                                 /* 1 through 7 always legal */
654                                 case '1': case '2': case '3':
655                                 case '4': case '5': case '6': case '7':
656                                         base = basefix[base];
657                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
658                                         goto ok;
659
660                                 /* digits 8 and 9 ok iff decimal or hex */
661                                 case '8': case '9':
662                                         base = basefix[base];
663                                         if (base <= 8)
664                                                 break;  /* not legal here */
665                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
666                                         goto ok;
667
668                                 /* letters ok iff hex */
669                                 case 'A': case 'B': case 'C':
670                                 case 'D': case 'E': case 'F':
671                                 case 'a': case 'b': case 'c':
672                                 case 'd': case 'e': case 'f':
673                                         /* no need to fix base here */
674                                         if (base <= 10)
675                                                 break;  /* not legal here */
676                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
677                                         goto ok;
678
679                                 /* sign ok only as first character */
680                                 case '+': case '-':
681                                         if (flags & SIGNOK) {
682                                                 flags &= ~SIGNOK;
683                                                 flags |= HAVESIGN;
684                                                 goto ok;
685                                         }
686                                         break;
687                                         
688                                 /*
689                                  * x ok iff flag still set & 2nd char (or
690                                  * 3rd char if we have a sign).
691                                  */
692                                 case 'x': case 'X':
693                                         if (flags & PFXOK && p ==
694                                             buf + 1 + !!(flags & HAVESIGN)) {
695                                                 base = 16;      /* if %i */
696                                                 flags &= ~PFXOK;
697                                                 goto ok;
698                                         }
699                                         break;
700                                 }
701
702                                 /*
703                                  * If we got here, c is not a legal character
704                                  * for a number.  Stop accumulating digits.
705                                  */
706                                 break;
707                 ok:
708                                 /*
709                                  * c is legal: store it and look at the next.
710                                  */
711                                 *p++ = c;
712                                 if (--fp->_r > 0)
713                                         fp->_p++;
714                                 else if (__srefill(fp))
715                                         break;          /* EOF */
716                         }
717                         /*
718                          * If we had only a sign, it is no good; push
719                          * back the sign.  If the number ends in `x',
720                          * it was [sign] '0' 'x', so push back the x
721                          * and treat it as [sign] '0'.
722                          */
723                         if (flags & NDIGITS) {
724                                 if (p > buf)
725                                         (void) __ungetc(*(u_char *)--p, fp);
726                                 goto match_failure;
727                         }
728                         c = ((u_char *)p)[-1];
729                         if (c == 'x' || c == 'X') {
730                                 --p;
731                                 (void) __ungetc(c, fp);
732                         }
733                         if ((flags & SUPPRESS) == 0) {
734                                 uintmax_t res;
735
736                                 *p = 0;
737                                 if ((flags & UNSIGNED) == 0)
738                                     res = strtoimax(buf, (char **)NULL, base);
739                                 else
740                                     res = strtoumax(buf, (char **)NULL, base);
741                                 if (flags & POINTER)
742                                         *va_arg(ap, void **) =
743                                                         (void *)(uintptr_t)res;
744                                 else if (flags & SHORTSHORT)
745                                         *va_arg(ap, char *) = res;
746                                 else if (flags & SHORT)
747                                         *va_arg(ap, short *) = res;
748                                 else if (flags & LONG)
749                                         *va_arg(ap, long *) = res;
750                                 else if (flags & LONGLONG)
751                                         *va_arg(ap, long long *) = res;
752                                 else if (flags & INTMAXT)
753                                         *va_arg(ap, intmax_t *) = res;
754                                 else if (flags & PTRDIFFT)
755                                         *va_arg(ap, ptrdiff_t *) = res;
756                                 else if (flags & SIZET)
757                                         *va_arg(ap, size_t *) = res;
758                                 else
759                                         *va_arg(ap, int *) = res;
760                                 nassigned++;
761                         }
762                         nread += p - buf;
763                         nconversions++;
764                         break;
765
766 #ifndef NO_FLOATING_POINT
767                 case CT_FLOAT:
768                         /* scan a floating point number as if by strtod */
769                         if (width == 0 || width > sizeof(buf) - 1)
770                                 width = sizeof(buf) - 1;
771                         if ((width = parsefloat(fp, buf, buf + width)) == 0)
772                                 goto match_failure;
773                         if ((flags & SUPPRESS) == 0) {
774                                 if (flags & LONGDBL) {
775                                         long double res = strtold(buf, &p);
776                                         *va_arg(ap, long double *) = res;
777                                 } else if (flags & LONG) {
778                                         double res = strtod(buf, &p);
779                                         *va_arg(ap, double *) = res;
780                                 } else {
781                                         float res = strtof(buf, &p);
782                                         *va_arg(ap, float *) = res;
783                                 }
784                                 if (__scanfdebug && p - buf != width)
785                                         abort();
786                                 nassigned++;
787                         }
788                         nread += width;
789                         nconversions++;
790                         break;
791 #endif /* !NO_FLOATING_POINT */
792                 }
793         }
794 input_failure:
795         return (nconversions != 0 ? nassigned : EOF);
796 match_failure:
797         return (nassigned);
798 }
799
800 /*
801  * Fill in the given table from the scanset at the given format
802  * (just after `[').  Return a pointer to the character past the
803  * closing `]'.  The table has a 1 wherever characters should be
804  * considered part of the scanset.
805  */
806 static const u_char *
807 __sccl(tab, fmt)
808         char *tab;
809         const u_char *fmt;
810 {
811         int c, n, v, i;
812
813         /* first `clear' the whole table */
814         c = *fmt++;             /* first char hat => negated scanset */
815         if (c == '^') {
816                 v = 1;          /* default => accept */
817                 c = *fmt++;     /* get new first char */
818         } else
819                 v = 0;          /* default => reject */
820
821         /* XXX: Will not work if sizeof(tab*) > sizeof(char) */
822         (void) memset(tab, v, 256);
823
824         if (c == 0)
825                 return (fmt - 1);/* format ended before closing ] */
826
827         /*
828          * Now set the entries corresponding to the actual scanset
829          * to the opposite of the above.
830          *
831          * The first character may be ']' (or '-') without being special;
832          * the last character may be '-'.
833          */
834         v = 1 - v;
835         for (;;) {
836                 tab[c] = v;             /* take character c */
837 doswitch:
838                 n = *fmt++;             /* and examine the next */
839                 switch (n) {
840
841                 case 0:                 /* format ended too soon */
842                         return (fmt - 1);
843
844                 case '-':
845                         /*
846                          * A scanset of the form
847                          *      [01+-]
848                          * is defined as `the digit 0, the digit 1,
849                          * the character +, the character -', but
850                          * the effect of a scanset such as
851                          *      [a-zA-Z0-9]
852                          * is implementation defined.  The V7 Unix
853                          * scanf treats `a-z' as `the letters a through
854                          * z', but treats `a-a' as `the letter a, the
855                          * character -, and the letter a'.
856                          *
857                          * For compatibility, the `-' is not considerd
858                          * to define a range if the character following
859                          * it is either a close bracket (required by ANSI)
860                          * or is not numerically greater than the character
861                          * we just stored in the table (c).
862                          */
863                         n = *fmt;
864                         if (n == ']'
865                             || (__collate_load_error ? n < c :
866                                 __collate_range_cmp (n, c) < 0
867                                )
868                            ) {
869                                 c = '-';
870                                 break;  /* resume the for(;;) */
871                         }
872                         fmt++;
873                         /* fill in the range */
874                         if (__collate_load_error) {
875                                 do {
876                                         tab[++c] = v;
877                                 } while (c < n);
878                         } else {
879                                 for (i = 0; i < 256; i ++)
880                                         if (   __collate_range_cmp (c, i) < 0
881                                             && __collate_range_cmp (i, n) <= 0
882                                            )
883                                                 tab[i] = v;
884                         }
885 #if 1   /* XXX another disgusting compatibility hack */
886                         c = n;
887                         /*
888                          * Alas, the V7 Unix scanf also treats formats
889                          * such as [a-c-e] as `the letters a through e'.
890                          * This too is permitted by the standard....
891                          */
892                         goto doswitch;
893 #else
894                         c = *fmt++;
895                         if (c == 0)
896                                 return (fmt - 1);
897                         if (c == ']')
898                                 return (fmt);
899 #endif
900                         break;
901
902                 case ']':               /* end of scanset */
903                         return (fmt);
904
905                 default:                /* just another character */
906                         c = n;
907                         break;
908                 }
909         }
910         /* NOTREACHED */
911 }
912
913 #ifndef NO_FLOATING_POINT
914 static int
915 parsefloat(FILE *fp, char *buf, char *end)
916 {
917         char *commit, *p;
918         int infnanpos = 0;
919         enum {
920                 S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
921                 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
922         } state = S_START;
923         unsigned char c;
924         char decpt = *localeconv()->decimal_point;
925         _Bool gotmantdig = 0, ishex = 0;
926
927         /*
928          * We set commit = p whenever the string we have read so far
929          * constitutes a valid representation of a floating point
930          * number by itself.  At some point, the parse will complete
931          * or fail, and we will ungetc() back to the last commit point.
932          * To ensure that the file offset gets updated properly, it is
933          * always necessary to read at least one character that doesn't
934          * match; thus, we can't short-circuit "infinity" or "nan(...)".
935          */
936         commit = buf - 1;
937         for (p = buf; p < end; ) {
938                 c = *fp->_p;
939 reswitch:
940                 switch (state) {
941                 case S_START:
942                         state = S_GOTSIGN;
943                         if (c == '-' || c == '+')
944                                 break;
945                         else
946                                 goto reswitch;
947                 case S_GOTSIGN:
948                         switch (c) {
949                         case '0':
950                                 state = S_MAYBEHEX;
951                                 commit = p;
952                                 break;
953                         case 'I':
954                         case 'i':
955                                 state = S_INF;
956                                 break;
957                         case 'N':
958                         case 'n':
959                                 state = S_NAN;
960                                 break;
961                         default:
962                                 state = S_DIGITS;
963                                 goto reswitch;
964                         }
965                         break;
966                 case S_INF:
967                         if (infnanpos > 6 ||
968                             (c != "nfinity"[infnanpos] &&
969                              c != "NFINITY"[infnanpos]))
970                                 goto parsedone;
971                         if (infnanpos == 1 || infnanpos == 6)
972                                 commit = p;     /* inf or infinity */
973                         infnanpos++;
974                         break;
975                 case S_NAN:
976                         switch (infnanpos) {
977                         case -1:        /* XXX kludge to deal with nan(...) */
978                                 goto parsedone;
979                         case 0:
980                                 if (c != 'A' && c != 'a')
981                                         goto parsedone;
982                                 break;
983                         case 1:
984                                 if (c != 'N' && c != 'n')
985                                         goto parsedone;
986                                 else
987                                         commit = p;
988                                 break;
989                         case 2:
990                                 if (c != '(')
991                                         goto parsedone;
992                                 break;
993                         default:
994                                 if (c == ')') {
995                                         commit = p;
996                                         infnanpos = -2;
997                                 } else if (!isalnum(c) && c != '_')
998                                         goto parsedone;
999                                 break;
1000                         }
1001                         infnanpos++;
1002                         break;
1003                 case S_MAYBEHEX:
1004                         state = S_DIGITS;
1005                         if (c == 'X' || c == 'x') {
1006                                 ishex = 1;
1007                                 break;
1008                         } else {        /* we saw a '0', but no 'x' */
1009                                 gotmantdig = 1;
1010                                 goto reswitch;
1011                         }
1012                 case S_DIGITS:
1013                         if ((ishex && isxdigit(c)) || isdigit(c))
1014                                 gotmantdig = 1;
1015                         else {
1016                                 state = S_FRAC;
1017                                 if (c != decpt)
1018                                         goto reswitch;
1019                         }
1020                         if (gotmantdig)
1021                                 commit = p;
1022                         break;
1023                 case S_FRAC:
1024                         if (((c == 'E' || c == 'e') && !ishex) ||
1025                             ((c == 'P' || c == 'p') && ishex)) {
1026                                 if (!gotmantdig)
1027                                         goto parsedone;
1028                                 else
1029                                         state = S_EXP;
1030                         } else if ((ishex && isxdigit(c)) || isdigit(c)) {
1031                                 commit = p;
1032                                 gotmantdig = 1;
1033                         } else
1034                                 goto parsedone;
1035                         break;
1036                 case S_EXP:
1037                         state = S_EXPDIGITS;
1038                         if (c == '-' || c == '+')
1039                                 break;
1040                         else
1041                                 goto reswitch;
1042                 case S_EXPDIGITS:
1043                         if (isdigit(c))
1044                                 commit = p;
1045                         else
1046                                 goto parsedone;
1047                         break;
1048                 default:
1049                         abort();
1050                 }
1051                 *p++ = c;
1052                 if (--fp->_r > 0)
1053                         fp->_p++;
1054                 else if (__srefill(fp))
1055                         break;  /* EOF */
1056         }
1057
1058 parsedone:
1059         while (commit < --p)
1060                 __ungetc(*(u_char *)p, fp);
1061         *++commit = '\0';
1062         return (commit - buf);
1063 }
1064 #endif