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