]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libc/stdio/vfwscanf.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / lib / libc / stdio / vfwscanf.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  * Copyright (c) 2011 The FreeBSD Foundation
9  * All rights reserved.
10  * Portions of this software were developed by David Chisnall
11  * under sponsorship from the FreeBSD Foundation.
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  * 4. 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 0
39 #if defined(LIBC_SCCS) && !defined(lint)
40 static char sccsid[] = "@(#)vfscanf.c   8.1 (Berkeley) 6/4/93";
41 #endif /* LIBC_SCCS and not lint */
42 #endif
43 #include <sys/cdefs.h>
44 __FBSDID("$FreeBSD$");
45
46 #include "namespace.h"
47 #include <ctype.h>
48 #include <inttypes.h>
49 #include <limits.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <stddef.h>
53 #include <stdarg.h>
54 #include <string.h>
55 #include <wchar.h>
56 #include <wctype.h>
57 #include "un-namespace.h"
58
59 #include "libc_private.h"
60 #include "local.h"
61 #include "xlocale_private.h"
62
63 #define BUF             513     /* Maximum length of numeric string. */
64
65 /*
66  * Flags used during conversion.
67  */
68 #define LONG            0x01    /* l: long or double */
69 #define LONGDBL         0x02    /* L: long double */
70 #define SHORT           0x04    /* h: short */
71 #define SUPPRESS        0x08    /* *: suppress assignment */
72 #define POINTER         0x10    /* p: void * (as hex) */
73 #define NOSKIP          0x20    /* [ or c: do not skip blanks */
74 #define LONGLONG        0x400   /* ll: long long (+ deprecated q: quad) */
75 #define INTMAXT         0x800   /* j: intmax_t */
76 #define PTRDIFFT        0x1000  /* t: ptrdiff_t */
77 #define SIZET           0x2000  /* z: size_t */
78 #define SHORTSHORT      0x4000  /* hh: char */
79 #define UNSIGNED        0x8000  /* %[oupxX] conversions */
80
81 /*
82  * The following are used in integral conversions only:
83  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
84  */
85 #define SIGNOK          0x40    /* +/- is (still) legal */
86 #define NDIGITS         0x80    /* no digits detected */
87 #define PFXOK           0x100   /* 0x prefix is (still) legal */
88 #define NZDIGITS        0x200   /* no zero digits detected */
89 #define HAVESIGN        0x10000 /* sign detected */
90
91 /*
92  * Conversion types.
93  */
94 #define CT_CHAR         0       /* %c conversion */
95 #define CT_CCL          1       /* %[...] conversion */
96 #define CT_STRING       2       /* %s conversion */
97 #define CT_INT          3       /* %[dioupxX] conversion */
98 #define CT_FLOAT        4       /* %[efgEFG] conversion */
99
100 #ifndef NO_FLOATING_POINT
101 static int parsefloat(FILE *, wchar_t *, wchar_t *, locale_t);
102 #endif
103
104 #define INCCL(_c)       \
105         (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
106         (wmemchr(ccls, (_c), ccle - ccls) != NULL))
107
108 static const mbstate_t initial_mbs;
109
110 /*
111  * MT-safe version.
112  */
113 int
114 vfwscanf_l(FILE * __restrict fp, locale_t locale,
115                 const wchar_t * __restrict fmt, va_list ap)
116 {
117         int ret;
118         FIX_LOCALE(locale);
119
120         FLOCKFILE(fp);
121         ORIENT(fp, 1);
122         ret = __vfwscanf(fp, locale, fmt, ap);
123         FUNLOCKFILE(fp);
124         return (ret);
125 }
126 int
127 vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
128 {
129         return vfwscanf_l(fp, __get_locale(), fmt, ap);
130 }
131
132 /*
133  * Non-MT-safe version.
134  */
135 int
136 __vfwscanf(FILE * __restrict fp, locale_t locale,
137                 const wchar_t * __restrict fmt, va_list ap)
138 {
139         wint_t c;               /* character from format, or conversion */
140         size_t width;           /* field width, or 0 */
141         wchar_t *p;             /* points into all kinds of strings */
142         int n;                  /* handy integer */
143         int flags;              /* flags as defined above */
144         wchar_t *p0;            /* saves original value of p when necessary */
145         int nassigned;          /* number of fields assigned */
146         int nconversions;       /* number of conversions */
147         int nread;              /* number of characters consumed from fp */
148         int base;               /* base argument to conversion function */
149         wchar_t buf[BUF];       /* buffer for numeric conversions */
150         const wchar_t *ccls;    /* character class start */
151         const wchar_t *ccle;    /* character class end */
152         int cclcompl;           /* ccl is complemented? */
153         wint_t wi;              /* handy wint_t */
154         char *mbp;              /* multibyte string pointer for %c %s %[ */
155         size_t nconv;           /* number of bytes in mb. conversion */
156         char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
157         mbstate_t mbs;
158
159         /* `basefix' is used to avoid `if' tests in the integer scanner */
160         static short basefix[17] =
161                 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
162
163         nassigned = 0;
164         nconversions = 0;
165         nread = 0;
166         ccls = ccle = NULL;
167         for (;;) {
168                 c = *fmt++;
169                 if (c == 0)
170                         return (nassigned);
171                 if (iswspace(c)) {
172                         while ((c = __fgetwc(fp, locale)) != WEOF &&
173                             iswspace_l(c, locale))
174                                 ;
175                         if (c != WEOF)
176                                 __ungetwc(c, fp, locale);
177                         continue;
178                 }
179                 if (c != '%')
180                         goto literal;
181                 width = 0;
182                 flags = 0;
183                 /*
184                  * switch on the format.  continue if done;
185                  * break once format type is derived.
186                  */
187 again:          c = *fmt++;
188                 switch (c) {
189                 case '%':
190 literal:
191                         if ((wi = __fgetwc(fp, locale)) == WEOF)
192                                 goto input_failure;
193                         if (wi != c) {
194                                 __ungetwc(wi, fp, locale);
195                                 goto input_failure;
196                         }
197                         nread++;
198                         continue;
199
200                 case '*':
201                         flags |= SUPPRESS;
202                         goto again;
203                 case 'j':
204                         flags |= INTMAXT;
205                         goto again;
206                 case 'l':
207                         if (flags & LONG) {
208                                 flags &= ~LONG;
209                                 flags |= LONGLONG;
210                         } else
211                                 flags |= LONG;
212                         goto again;
213                 case 'q':
214                         flags |= LONGLONG;      /* not quite */
215                         goto again;
216                 case 't':
217                         flags |= PTRDIFFT;
218                         goto again;
219                 case 'z':
220                         flags |= SIZET;
221                         goto again;
222                 case 'L':
223                         flags |= LONGDBL;
224                         goto again;
225                 case 'h':
226                         if (flags & SHORT) {
227                                 flags &= ~SHORT;
228                                 flags |= SHORTSHORT;
229                         } else
230                                 flags |= SHORT;
231                         goto again;
232
233                 case '0': case '1': case '2': case '3': case '4':
234                 case '5': case '6': case '7': case '8': case '9':
235                         width = width * 10 + c - '0';
236                         goto again;
237
238                 /*
239                  * Conversions.
240                  */
241                 case 'd':
242                         c = CT_INT;
243                         base = 10;
244                         break;
245
246                 case 'i':
247                         c = CT_INT;
248                         base = 0;
249                         break;
250
251                 case 'o':
252                         c = CT_INT;
253                         flags |= UNSIGNED;
254                         base = 8;
255                         break;
256
257                 case 'u':
258                         c = CT_INT;
259                         flags |= UNSIGNED;
260                         base = 10;
261                         break;
262
263                 case 'X':
264                 case 'x':
265                         flags |= PFXOK; /* enable 0x prefixing */
266                         c = CT_INT;
267                         flags |= UNSIGNED;
268                         base = 16;
269                         break;
270
271 #ifndef NO_FLOATING_POINT
272                 case 'A': case 'E': case 'F': case 'G':
273                 case 'a': case 'e': case 'f': case 'g':
274                         c = CT_FLOAT;
275                         break;
276 #endif
277
278                 case 'S':
279                         flags |= LONG;
280                         /* FALLTHROUGH */
281                 case 's':
282                         c = CT_STRING;
283                         break;
284
285                 case '[':
286                         ccls = fmt;
287                         if (*fmt == '^') {
288                                 cclcompl = 1;
289                                 fmt++;
290                         } else
291                                 cclcompl = 0;
292                         if (*fmt == ']')
293                                 fmt++;
294                         while (*fmt != '\0' && *fmt != ']')
295                                 fmt++;
296                         ccle = fmt;
297                         fmt++;
298                         flags |= NOSKIP;
299                         c = CT_CCL;
300                         break;
301
302                 case 'C':
303                         flags |= LONG;
304                         /* FALLTHROUGH */
305                 case 'c':
306                         flags |= NOSKIP;
307                         c = CT_CHAR;
308                         break;
309
310                 case 'p':       /* pointer format is like hex */
311                         flags |= POINTER | PFXOK;
312                         c = CT_INT;             /* assumes sizeof(uintmax_t) */
313                         flags |= UNSIGNED;      /*      >= sizeof(uintptr_t) */
314                         base = 16;
315                         break;
316
317                 case 'n':
318                         nconversions++;
319                         if (flags & SUPPRESS)   /* ??? */
320                                 continue;
321                         if (flags & SHORTSHORT)
322                                 *va_arg(ap, char *) = nread;
323                         else if (flags & SHORT)
324                                 *va_arg(ap, short *) = nread;
325                         else if (flags & LONG)
326                                 *va_arg(ap, long *) = nread;
327                         else if (flags & LONGLONG)
328                                 *va_arg(ap, long long *) = nread;
329                         else if (flags & INTMAXT)
330                                 *va_arg(ap, intmax_t *) = nread;
331                         else if (flags & SIZET)
332                                 *va_arg(ap, size_t *) = nread;
333                         else if (flags & PTRDIFFT)
334                                 *va_arg(ap, ptrdiff_t *) = nread;
335                         else
336                                 *va_arg(ap, int *) = nread;
337                         continue;
338
339                 default:
340                         goto match_failure;
341
342                 /*
343                  * Disgusting backwards compatibility hack.     XXX
344                  */
345                 case '\0':      /* compat */
346                         return (EOF);
347                 }
348
349                 /*
350                  * Consume leading white space, except for formats
351                  * that suppress this.
352                  */
353                 if ((flags & NOSKIP) == 0) {
354                         while ((wi = __fgetwc(fp, locale)) != WEOF && iswspace(wi))
355                                 nread++;
356                         if (wi == WEOF)
357                                 goto input_failure;
358                         __ungetwc(wi, fp, locale);
359                 }
360
361                 /*
362                  * Do the conversion.
363                  */
364                 switch (c) {
365
366                 case CT_CHAR:
367                         /* scan arbitrary characters (sets NOSKIP) */
368                         if (width == 0)
369                                 width = 1;
370                         if (flags & LONG) {
371                                 if (!(flags & SUPPRESS))
372                                         p = va_arg(ap, wchar_t *);
373                                 n = 0;
374                                 while (width-- != 0 &&
375                                     (wi = __fgetwc(fp, locale)) != WEOF) {
376                                         if (!(flags & SUPPRESS))
377                                                 *p++ = (wchar_t)wi;
378                                         n++;
379                                 }
380                                 if (n == 0)
381                                         goto input_failure;
382                                 nread += n;
383                                 if (!(flags & SUPPRESS))
384                                         nassigned++;
385                         } else {
386                                 if (!(flags & SUPPRESS))
387                                         mbp = va_arg(ap, char *);
388                                 n = 0;
389                                 mbs = initial_mbs;
390                                 while (width != 0 &&
391                                     (wi = __fgetwc(fp, locale)) != WEOF) {
392                                         if (width >= MB_CUR_MAX &&
393                                             !(flags & SUPPRESS)) {
394                                                 nconv = wcrtomb(mbp, wi, &mbs);
395                                                 if (nconv == (size_t)-1)
396                                                         goto input_failure;
397                                         } else {
398                                                 nconv = wcrtomb(mbbuf, wi,
399                                                     &mbs);
400                                                 if (nconv == (size_t)-1)
401                                                         goto input_failure;
402                                                 if (nconv > width) {
403                                                         __ungetwc(wi, fp, locale);
404                                                         break;
405                                                 }
406                                                 if (!(flags & SUPPRESS))
407                                                         memcpy(mbp, mbbuf,
408                                                             nconv);
409                                         }
410                                         if (!(flags & SUPPRESS))
411                                                 mbp += nconv;
412                                         width -= nconv;
413                                         n++;
414                                 }
415                                 if (n == 0)
416                                         goto input_failure;
417                                 nread += n;
418                                 if (!(flags & SUPPRESS))
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 & SUPPRESS) && (flags & LONG)) {
430                                 n = 0;
431                                 while ((wi = __fgetwc(fp, locale)) != WEOF &&
432                                     width-- != 0 && INCCL(wi))
433                                         n++;
434                                 if (wi != WEOF)
435                                         __ungetwc(wi, fp, locale);
436                                 if (n == 0)
437                                         goto match_failure;
438                         } else if (flags & LONG) {
439                                 p0 = p = va_arg(ap, wchar_t *);
440                                 while ((wi = __fgetwc(fp, locale)) != WEOF &&
441                                     width-- != 0 && INCCL(wi))
442                                         *p++ = (wchar_t)wi;
443                                 if (wi != WEOF)
444                                         __ungetwc(wi, fp, locale);
445                                 n = p - p0;
446                                 if (n == 0)
447                                         goto match_failure;
448                                 *p = 0;
449                                 nassigned++;
450                         } else {
451                                 if (!(flags & SUPPRESS))
452                                         mbp = va_arg(ap, char *);
453                                 n = 0;
454                                 mbs = initial_mbs;
455                                 while ((wi = __fgetwc(fp, locale)) != WEOF &&
456                                     width != 0 && INCCL(wi)) {
457                                         if (width >= MB_CUR_MAX &&
458                                            !(flags & SUPPRESS)) {
459                                                 nconv = wcrtomb(mbp, wi, &mbs);
460                                                 if (nconv == (size_t)-1)
461                                                         goto input_failure;
462                                         } else {
463                                                 nconv = wcrtomb(mbbuf, wi,
464                                                     &mbs);
465                                                 if (nconv == (size_t)-1)
466                                                         goto input_failure;
467                                                 if (nconv > width)
468                                                         break;
469                                                 if (!(flags & SUPPRESS))
470                                                         memcpy(mbp, mbbuf,
471                                                             nconv);
472                                         }
473                                         if (!(flags & SUPPRESS))
474                                                 mbp += nconv;
475                                         width -= nconv;
476                                         n++;
477                                 }
478                                 if (wi != WEOF)
479                                         __ungetwc(wi, fp, locale);
480                                 if (!(flags & SUPPRESS)) {
481                                         *mbp = 0;
482                                         nassigned++;
483                                 }
484                         }
485                         nread += n;
486                         nconversions++;
487                         break;
488
489                 case CT_STRING:
490                         /* like CCL, but zero-length string OK, & no NOSKIP */
491                         if (width == 0)
492                                 width = (size_t)~0;
493                         if ((flags & SUPPRESS) && (flags & LONG)) {
494                                 while ((wi = __fgetwc(fp, locale)) != WEOF &&
495                                     width-- != 0 &&
496                                     !iswspace(wi))
497                                         nread++;
498                                 if (wi != WEOF)
499                                         __ungetwc(wi, fp, locale);
500                         } else if (flags & LONG) {
501                                 p0 = p = va_arg(ap, wchar_t *);
502                                 while ((wi = __fgetwc(fp, locale)) != WEOF &&
503                                     width-- != 0 &&
504                                     !iswspace(wi)) {
505                                         *p++ = (wchar_t)wi;
506                                         nread++;
507                                 }
508                                 if (wi != WEOF)
509                                         __ungetwc(wi, fp, locale);
510                                 *p = '\0';
511                                 nassigned++;
512                         } else {
513                                 if (!(flags & SUPPRESS))
514                                         mbp = va_arg(ap, char *);
515                                 mbs = initial_mbs;
516                                 while ((wi = __fgetwc(fp, locale)) != WEOF &&
517                                     width != 0 &&
518                                     !iswspace(wi)) {
519                                         if (width >= MB_CUR_MAX &&
520                                             !(flags & SUPPRESS)) {
521                                                 nconv = wcrtomb(mbp, wi, &mbs);
522                                                 if (nconv == (size_t)-1)
523                                                         goto input_failure;
524                                         } else {
525                                                 nconv = wcrtomb(mbbuf, wi,
526                                                     &mbs);
527                                                 if (nconv == (size_t)-1)
528                                                         goto input_failure;
529                                                 if (nconv > width)
530                                                         break;
531                                                 if (!(flags & SUPPRESS))
532                                                         memcpy(mbp, mbbuf,
533                                                             nconv);
534                                         }
535                                         if (!(flags & SUPPRESS))
536                                                 mbp += nconv;
537                                         width -= nconv;
538                                         nread++;
539                                 }
540                                 if (wi != WEOF)
541                                         __ungetwc(wi, fp, locale);
542                                 if (!(flags & SUPPRESS)) {
543                                         *mbp = 0;
544                                         nassigned++;
545                                 }
546                         }
547                         nconversions++;
548                         continue;
549
550                 case CT_INT:
551                         /* scan an integer as if by the conversion function */
552                         if (width == 0 || width > sizeof(buf) /
553                             sizeof(*buf) - 1)
554                                 width = sizeof(buf) / sizeof(*buf) - 1;
555                         flags |= SIGNOK | NDIGITS | NZDIGITS;
556                         for (p = buf; width; width--) {
557                                 c = __fgetwc(fp, locale);
558                                 /*
559                                  * Switch on the character; `goto ok'
560                                  * if we accept it as a part of number.
561                                  */
562                                 switch (c) {
563
564                                 /*
565                                  * The digit 0 is always legal, but is
566                                  * special.  For %i conversions, if no
567                                  * digits (zero or nonzero) have been
568                                  * scanned (only signs), we will have
569                                  * base==0.  In that case, we should set
570                                  * it to 8 and enable 0x prefixing.
571                                  * Also, if we have not scanned zero digits
572                                  * before this, do not turn off prefixing
573                                  * (someone else will turn it off if we
574                                  * have scanned any nonzero digits).
575                                  */
576                                 case '0':
577                                         if (base == 0) {
578                                                 base = 8;
579                                                 flags |= PFXOK;
580                                         }
581                                         if (flags & NZDIGITS)
582                                             flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
583                                         else
584                                             flags &= ~(SIGNOK|PFXOK|NDIGITS);
585                                         goto ok;
586
587                                 /* 1 through 7 always legal */
588                                 case '1': case '2': case '3':
589                                 case '4': case '5': case '6': case '7':
590                                         base = basefix[base];
591                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
592                                         goto ok;
593
594                                 /* digits 8 and 9 ok iff decimal or hex */
595                                 case '8': case '9':
596                                         base = basefix[base];
597                                         if (base <= 8)
598                                                 break;  /* not legal here */
599                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
600                                         goto ok;
601
602                                 /* letters ok iff hex */
603                                 case 'A': case 'B': case 'C':
604                                 case 'D': case 'E': case 'F':
605                                 case 'a': case 'b': case 'c':
606                                 case 'd': case 'e': case 'f':
607                                         /* no need to fix base here */
608                                         if (base <= 10)
609                                                 break;  /* not legal here */
610                                         flags &= ~(SIGNOK | PFXOK | NDIGITS);
611                                         goto ok;
612
613                                 /* sign ok only as first character */
614                                 case '+': case '-':
615                                         if (flags & SIGNOK) {
616                                                 flags &= ~SIGNOK;
617                                                 flags |= HAVESIGN;
618                                                 goto ok;
619                                         }
620                                         break;
621                                         
622                                 /*
623                                  * x ok iff flag still set & 2nd char (or
624                                  * 3rd char if we have a sign).
625                                  */
626                                 case 'x': case 'X':
627                                         if (flags & PFXOK && p ==
628                                             buf + 1 + !!(flags & HAVESIGN)) {
629                                                 base = 16;      /* if %i */
630                                                 flags &= ~PFXOK;
631                                                 goto ok;
632                                         }
633                                         break;
634                                 }
635
636                                 /*
637                                  * If we got here, c is not a legal character
638                                  * for a number.  Stop accumulating digits.
639                                  */
640                                 if (c != WEOF)
641                                         __ungetwc(c, fp, locale);
642                                 break;
643                 ok:
644                                 /*
645                                  * c is legal: store it and look at the next.
646                                  */
647                                 *p++ = (wchar_t)c;
648                         }
649                         /*
650                          * If we had only a sign, it is no good; push
651                          * back the sign.  If the number ends in `x',
652                          * it was [sign] '0' 'x', so push back the x
653                          * and treat it as [sign] '0'.
654                          */
655                         if (flags & NDIGITS) {
656                                 if (p > buf)
657                                         __ungetwc(*--p, fp, locale);
658                                 goto match_failure;
659                         }
660                         c = p[-1];
661                         if (c == 'x' || c == 'X') {
662                                 --p;
663                                 __ungetwc(c, fp, locale);
664                         }
665                         if ((flags & SUPPRESS) == 0) {
666                                 uintmax_t res;
667
668                                 *p = 0;
669                                 if ((flags & UNSIGNED) == 0)
670                                     res = wcstoimax(buf, NULL, base);
671                                 else
672                                     res = wcstoumax(buf, NULL, base);
673                                 if (flags & POINTER)
674                                         *va_arg(ap, void **) =
675                                                         (void *)(uintptr_t)res;
676                                 else if (flags & SHORTSHORT)
677                                         *va_arg(ap, char *) = res;
678                                 else if (flags & SHORT)
679                                         *va_arg(ap, short *) = res;
680                                 else if (flags & LONG)
681                                         *va_arg(ap, long *) = res;
682                                 else if (flags & LONGLONG)
683                                         *va_arg(ap, long long *) = res;
684                                 else if (flags & INTMAXT)
685                                         *va_arg(ap, intmax_t *) = res;
686                                 else if (flags & PTRDIFFT)
687                                         *va_arg(ap, ptrdiff_t *) = res;
688                                 else if (flags & SIZET)
689                                         *va_arg(ap, size_t *) = res;
690                                 else
691                                         *va_arg(ap, int *) = res;
692                                 nassigned++;
693                         }
694                         nread += p - buf;
695                         nconversions++;
696                         break;
697
698 #ifndef NO_FLOATING_POINT
699                 case CT_FLOAT:
700                         /* scan a floating point number as if by strtod */
701                         if (width == 0 || width > sizeof(buf) /
702                             sizeof(*buf) - 1)
703                                 width = sizeof(buf) / sizeof(*buf) - 1;
704                         if ((width = parsefloat(fp, buf, buf + width, locale)) == 0)
705                                 goto match_failure;
706                         if ((flags & SUPPRESS) == 0) {
707                                 if (flags & LONGDBL) {
708                                         long double res = wcstold(buf, &p);
709                                         *va_arg(ap, long double *) = res;
710                                 } else if (flags & LONG) {
711                                         double res = wcstod(buf, &p);
712                                         *va_arg(ap, double *) = res;
713                                 } else {
714                                         float res = wcstof(buf, &p);
715                                         *va_arg(ap, float *) = res;
716                                 }
717                                 nassigned++;
718                         }
719                         nread += width;
720                         nconversions++;
721                         break;
722 #endif /* !NO_FLOATING_POINT */
723                 }
724         }
725 input_failure:
726         return (nconversions != 0 ? nassigned : EOF);
727 match_failure:
728         return (nassigned);
729 }
730
731 #ifndef NO_FLOATING_POINT
732 static int
733 parsefloat(FILE *fp, wchar_t *buf, wchar_t *end, locale_t locale)
734 {
735         mbstate_t mbs;
736         size_t nconv;
737         wchar_t *commit, *p;
738         int infnanpos = 0;
739         enum {
740                 S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
741                 S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
742         } state = S_START;
743         wchar_t c;
744         wchar_t decpt;
745         _Bool gotmantdig = 0, ishex = 0;
746
747         mbs = initial_mbs;
748         nconv = mbrtowc(&decpt, localeconv()->decimal_point, MB_CUR_MAX, &mbs);
749         if (nconv == (size_t)-1 || nconv == (size_t)-2)
750                 decpt = '.';    /* failsafe */
751
752         /*
753          * We set commit = p whenever the string we have read so far
754          * constitutes a valid representation of a floating point
755          * number by itself.  At some point, the parse will complete
756          * or fail, and we will ungetc() back to the last commit point.
757          * To ensure that the file offset gets updated properly, it is
758          * always necessary to read at least one character that doesn't
759          * match; thus, we can't short-circuit "infinity" or "nan(...)".
760          */
761         commit = buf - 1;
762         c = WEOF;
763         for (p = buf; p < end; ) {
764                 if ((c = __fgetwc(fp, locale)) == WEOF)
765                         break;
766 reswitch:
767                 switch (state) {
768                 case S_START:
769                         state = S_GOTSIGN;
770                         if (c == '-' || c == '+')
771                                 break;
772                         else
773                                 goto reswitch;
774                 case S_GOTSIGN:
775                         switch (c) {
776                         case '0':
777                                 state = S_MAYBEHEX;
778                                 commit = p;
779                                 break;
780                         case 'I':
781                         case 'i':
782                                 state = S_INF;
783                                 break;
784                         case 'N':
785                         case 'n':
786                                 state = S_NAN;
787                                 break;
788                         default:
789                                 state = S_DIGITS;
790                                 goto reswitch;
791                         }
792                         break;
793                 case S_INF:
794                         if (infnanpos > 6 ||
795                             (c != "nfinity"[infnanpos] &&
796                              c != "NFINITY"[infnanpos]))
797                                 goto parsedone;
798                         if (infnanpos == 1 || infnanpos == 6)
799                                 commit = p;     /* inf or infinity */
800                         infnanpos++;
801                         break;
802                 case S_NAN:
803                         switch (infnanpos) {
804                         case 0:
805                                 if (c != 'A' && c != 'a')
806                                         goto parsedone;
807                                 break;
808                         case 1:
809                                 if (c != 'N' && c != 'n')
810                                         goto parsedone;
811                                 else
812                                         commit = p;
813                                 break;
814                         case 2:
815                                 if (c != '(')
816                                         goto parsedone;
817                                 break;
818                         default:
819                                 if (c == ')') {
820                                         commit = p;
821                                         state = S_DONE;
822                                 } else if (!iswalnum(c) && c != '_')
823                                         goto parsedone;
824                                 break;
825                         }
826                         infnanpos++;
827                         break;
828                 case S_DONE:
829                         goto parsedone;
830                 case S_MAYBEHEX:
831                         state = S_DIGITS;
832                         if (c == 'X' || c == 'x') {
833                                 ishex = 1;
834                                 break;
835                         } else {        /* we saw a '0', but no 'x' */
836                                 gotmantdig = 1;
837                                 goto reswitch;
838                         }
839                 case S_DIGITS:
840                         if ((ishex && iswxdigit(c)) || iswdigit(c))
841                                 gotmantdig = 1;
842                         else {
843                                 state = S_FRAC;
844                                 if (c != decpt)
845                                         goto reswitch;
846                         }
847                         if (gotmantdig)
848                                 commit = p;
849                         break;
850                 case S_FRAC:
851                         if (((c == 'E' || c == 'e') && !ishex) ||
852                             ((c == 'P' || c == 'p') && ishex)) {
853                                 if (!gotmantdig)
854                                         goto parsedone;
855                                 else
856                                         state = S_EXP;
857                         } else if ((ishex && iswxdigit(c)) || iswdigit(c)) {
858                                 commit = p;
859                                 gotmantdig = 1;
860                         } else
861                                 goto parsedone;
862                         break;
863                 case S_EXP:
864                         state = S_EXPDIGITS;
865                         if (c == '-' || c == '+')
866                                 break;
867                         else
868                                 goto reswitch;
869                 case S_EXPDIGITS:
870                         if (iswdigit(c))
871                                 commit = p;
872                         else
873                                 goto parsedone;
874                         break;
875                 default:
876                         abort();
877                 }
878                 *p++ = c;
879                 c = WEOF;
880         }
881
882 parsedone:
883         if (c != WEOF)
884                 __ungetwc(c, fp, locale);
885         while (commit < --p)
886                 __ungetwc(*p, fp, locale);
887         *++commit = '\0';
888         return (commit - buf);
889 }
890 #endif