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