]> git.saurik.com Git - apple/libc.git/blobdiff - stdio/FreeBSD/vfwscanf.c
Libc-825.24.tar.gz
[apple/libc.git] / stdio / FreeBSD / vfwscanf.c
index f452c8ec596fcd6125b9d37fd0da0fc3e55c4dbb..bba07f2735cc72ceb1752514da8513e7a48e8ca4 100644 (file)
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
  * 4. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
 #if 0
 #if defined(LIBC_SCCS) && !defined(lint)
 static char sccsid[] = "@(#)vfscanf.c  8.1 (Berkeley) 6/4/93";
 #endif /* LIBC_SCCS and not lint */
-__FBSDID("FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.32 2003/06/28 09:03:05 das Exp ");
 #endif
-__FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.6 2003/07/05 03:39:23 tjr Exp $");
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.17 2009/01/19 06:19:51 das Exp $");
+
+#include "xlocale_private.h"
 
 #include "namespace.h"
 #include <ctype.h>
 #include <inttypes.h>
+#include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stddef.h>
@@ -58,9 +56,7 @@ __FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.6 2003/07/05 03:39:23 tjr
 #include "libc_private.h"
 #include "local.h"
 
-#define FLOATING_POINT
-
-#ifdef FLOATING_POINT
+#ifndef NO_FLOATING_POINT
 #include <locale.h>
 #endif
 
@@ -90,6 +86,7 @@ __FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.6 2003/07/05 03:39:23 tjr
 #define        NDIGITS         0x80    /* no digits detected */
 #define        PFXOK           0x100   /* 0x prefix is (still) legal */
 #define        NZDIGITS        0x200   /* no zero digits detected */
+#define        HAVESIGN        0x10000 /* sign detected */
 
 /*
  * Conversion types.
@@ -100,14 +97,16 @@ __FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.6 2003/07/05 03:39:23 tjr
 #define        CT_INT          3       /* %[dioupxX] conversion */
 #define        CT_FLOAT        4       /* %[efgEFG] conversion */
 
-static int parsefloat(FILE *, wchar_t *, wchar_t *);
-
-extern int __scanfdebug;
+#ifndef NO_FLOATING_POINT
+static int parsefloat(FILE *, wchar_t **, size_t, locale_t loc);
+#endif
 
 #define        INCCL(_c)       \
        (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
        (wmemchr(ccls, (_c), ccle - ccls) != NULL))
 
+static const mbstate_t initial_mbs;
+
 /*
  * MT-safe version.
  */
@@ -118,7 +117,21 @@ vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
 
        FLOCKFILE(fp);
        ORIENT(fp, 1);
-       ret = __vfwscanf(fp, fmt, ap);
+       ret = __vfwscanf(fp, __current_locale(), fmt, ap);
+       FUNLOCKFILE(fp);
+       return (ret);
+}
+
+int
+vfwscanf_l(FILE * __restrict fp, locale_t loc, const wchar_t * __restrict fmt,
+    va_list ap)
+{
+       int ret;
+
+       NORMALIZE_LOCALE(loc);
+       FLOCKFILE(fp);
+       ORIENT(fp, 1);
+       ret = __vfwscanf(fp, loc, fmt, ap);
        FUNLOCKFILE(fp);
        return (ret);
 }
@@ -126,8 +139,9 @@ vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
 /*
  * Non-MT-safe version.
  */
-int
-__vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
+__private_extern__ int
+__vfwscanf(FILE * __restrict fp, locale_t loc, const wchar_t * __restrict fmt,
+    va_list ap)
 {
        wint_t c;               /* character from format, or conversion */
        size_t width;           /* field width, or 0 */
@@ -136,7 +150,6 @@ __vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
        int flags;              /* flags as defined above */
        wchar_t *p0;            /* saves original value of p when necessary */
        int nassigned;          /* number of fields assigned */
-       int nconversions;       /* number of conversions */
        int nread;              /* number of characters consumed from fp */
        int base;               /* base argument to conversion function */
        wchar_t buf[BUF];       /* buffer for numeric conversions */
@@ -146,31 +159,37 @@ __vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
        wint_t wi;              /* handy wint_t */
        char *mbp;              /* multibyte string pointer for %c %s %[ */
        size_t nconv;           /* number of bytes in mb. conversion */
-       mbstate_t mbs;          /* multibyte state */
        char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
+       int index;              /* for %index$ */
+       va_list ap_orig;        /* to reset ap to first argument */
+       mbstate_t mbs;
+       int mb_cur_max = MB_CUR_MAX_L(loc);
 
        /* `basefix' is used to avoid `if' tests in the integer scanner */
        static short basefix[17] =
                { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
 
        nassigned = 0;
-       nconversions = 0;
        nread = 0;
        ccls = ccle = NULL;
+       va_copy(ap_orig, ap);
        for (;;) {
                c = *fmt++;
                if (c == 0)
                        return (nassigned);
-               if (iswspace(c)) {
-                       while ((c = __fgetwc(fp)) != WEOF &&
-                           iswspace(c))
+               if (iswspace_l(c, loc)) {
+                       while ((c = __fgetwc(fp, loc)) != WEOF &&
+                           iswspace_l(c, loc))
                                ;
                        if (c != WEOF)
-                               __ungetwc(c, fp);
+                               __ungetwc(c, fp, loc);
                        continue;
                }
-               if (c != '%')
+               if (c != '%') {
+                       if ((wi = __fgetwc(fp, loc)) == WEOF)
+                               goto input_failure;
                        goto literal;
+               }
                width = 0;
                flags = 0;
                /*
@@ -180,16 +199,34 @@ __vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
 again:         c = *fmt++;
                switch (c) {
                case '%':
+               /* Consume leading white space */
+                       for(;;) {
+                               if ((wi = __fgetwc(fp, loc)) == WEOF)
+                                       goto input_failure;
+                               if (!iswspace_l(wi, loc))
+                                       break;
+                               nread++;
+                       }
 literal:
-                       if ((wi = __fgetwc(fp)) == WEOF)
-                               goto input_failure;
                        if (wi != c) {
-                               __ungetwc(wi, fp);
-                               goto input_failure;
+                               __ungetwc(wi, fp, loc);
+                               goto match_failure;
                        }
                        nread++;
                        continue;
 
+               case '$':
+                       index = width;
+                       if (index < 1 || index > NL_ARGMAX || fmt[-3] != '%') {
+                               goto input_failure;
+                       }
+                       width = 0;
+                       va_end(ap);
+                       va_copy(ap, ap_orig); /* reset to %1$ */
+                       for (; index > 1; index--) {
+                               va_arg(ap, void*);
+                       }
+                       goto again;
                case '*':
                        flags |= SUPPRESS;
                        goto again;
@@ -261,7 +298,7 @@ literal:
                        base = 16;
                        break;
 
-#ifdef FLOATING_POINT
+#ifndef NO_FLOATING_POINT
                case 'A': case 'E': case 'F': case 'G':
                case 'a': case 'e': case 'f': case 'g':
                        c = CT_FLOAT;
@@ -308,27 +345,28 @@ literal:
                        break;
 
                case 'n':
-                       nconversions++;
-                       if (flags & SUPPRESS)   /* ??? */
+               {
+                       void *ptr = va_arg(ap, void *);
+                       if ((ptr == NULL) || (flags & SUPPRESS))        /* ??? */
                                continue;
-                       if (flags & SHORTSHORT)
-                               *va_arg(ap, char *) = nread;
+                       else if (flags & SHORTSHORT)
+                               *(char *)ptr = nread;
                        else if (flags & SHORT)
-                               *va_arg(ap, short *) = nread;
+                               *(short *)ptr = nread;
                        else if (flags & LONG)
-                               *va_arg(ap, long *) = nread;
+                               *(long *)ptr = nread;
                        else if (flags & LONGLONG)
-                               *va_arg(ap, long long *) = nread;
+                               *(long long *)ptr = nread;
                        else if (flags & INTMAXT)
-                               *va_arg(ap, intmax_t *) = nread;
+                               *(intmax_t *)ptr = nread;
                        else if (flags & SIZET)
-                               *va_arg(ap, size_t *) = nread;
+                               *(size_t *)ptr = nread;
                        else if (flags & PTRDIFFT)
-                               *va_arg(ap, ptrdiff_t *) = nread;
+                               *(ptrdiff_t *)ptr = nread;
                        else
-                               *va_arg(ap, int *) = nread;
+                               *(int *)ptr = nread;
                        continue;
-
+               }
                default:
                        goto match_failure;
 
@@ -344,11 +382,11 @@ literal:
                 * that suppress this.
                 */
                if ((flags & NOSKIP) == 0) {
-                       while ((wi = __fgetwc(fp)) != WEOF && iswspace(wi))
+                       while ((wi = __fgetwc(fp, loc)) != WEOF && iswspace_l(wi, loc))
                                nread++;
                        if (wi == WEOF)
                                goto input_failure;
-                       __ungetwc(wi, fp);
+                       __ungetwc(wi, fp, loc);
                }
 
                /*
@@ -365,7 +403,7 @@ literal:
                                        p = va_arg(ap, wchar_t *);
                                n = 0;
                                while (width-- != 0 &&
-                                   (wi = __fgetwc(fp)) != WEOF) {
+                                   (wi = __fgetwc(fp, loc)) != WEOF) {
                                        if (!(flags & SUPPRESS))
                                                *p++ = (wchar_t)wi;
                                        n++;
@@ -379,21 +417,21 @@ literal:
                                if (!(flags & SUPPRESS))
                                        mbp = va_arg(ap, char *);
                                n = 0;
-                               memset(&mbs, 0, sizeof(mbs));
+                               mbs = initial_mbs;
                                while (width != 0 &&
-                                   (wi = __fgetwc(fp)) != WEOF) {
-                                       if (width >= MB_CUR_MAX &&
+                                   (wi = __fgetwc(fp, loc)) != WEOF) {
+                                       if (width >= mb_cur_max &&
                                            !(flags & SUPPRESS)) {
-                                               nconv = wcrtomb(mbp, wi, &mbs);
+                                               nconv = wcrtomb_l(mbp, wi, &mbs, loc);
                                                if (nconv == (size_t)-1)
                                                        goto input_failure;
                                        } else {
-                                               nconv = wcrtomb(mbbuf, wi,
-                                                   &mbs);
+                                               nconv = wcrtomb_l(mbbuf, wi,
+                                                   &mbs, loc);
                                                if (nconv == (size_t)-1)
                                                        goto input_failure;
                                                if (nconv > width) {
-                                                       __ungetwc(wi, fp);
+                                                       __ungetwc(wi, fp, loc);
                                                        break;
                                                }
                                                if (!(flags & SUPPRESS))
@@ -411,7 +449,6 @@ literal:
                                if (!(flags & SUPPRESS))
                                        nassigned++;
                        }
-                       nconversions++;
                        break;
 
                case CT_CCL:
@@ -421,20 +458,20 @@ literal:
                        /* take only those things in the class */
                        if ((flags & SUPPRESS) && (flags & LONG)) {
                                n = 0;
-                               while ((wi = __fgetwc(fp)) != WEOF &&
+                               while ((wi = __fgetwc(fp, loc)) != WEOF &&
                                    width-- != 0 && INCCL(wi))
                                        n++;
                                if (wi != WEOF)
-                                       __ungetwc(wi, fp);
+                                       __ungetwc(wi, fp, loc);
                                if (n == 0)
                                        goto match_failure;
                        } else if (flags & LONG) {
                                p0 = p = va_arg(ap, wchar_t *);
-                               while ((wi = __fgetwc(fp)) != WEOF &&
+                               while ((wi = __fgetwc(fp, loc)) != WEOF &&
                                    width-- != 0 && INCCL(wi))
                                        *p++ = (wchar_t)wi;
                                if (wi != WEOF)
-                                       __ungetwc(wi, fp);
+                                       __ungetwc(wi, fp, loc);
                                n = p - p0;
                                if (n == 0)
                                        goto match_failure;
@@ -444,17 +481,17 @@ literal:
                                if (!(flags & SUPPRESS))
                                        mbp = va_arg(ap, char *);
                                n = 0;
-                               memset(&mbs, 0, sizeof(mbs));
-                               while ((wi = __fgetwc(fp)) != WEOF &&
+                               mbs = initial_mbs;
+                               while ((wi = __fgetwc(fp, loc)) != WEOF &&
                                    width != 0 && INCCL(wi)) {
-                                       if (width >= MB_CUR_MAX &&
+                                       if (width >= mb_cur_max &&
                                           !(flags & SUPPRESS)) {
-                                               nconv = wcrtomb(mbp, wi, &mbs);
+                                               nconv = wcrtomb_l(mbp, wi, &mbs, loc);
                                                if (nconv == (size_t)-1)
                                                        goto input_failure;
                                        } else {
-                                               nconv = wcrtomb(mbbuf, wi,
-                                                   &mbs);
+                                               nconv = wcrtomb_l(mbbuf, wi,
+                                                   &mbs, loc);
                                                if (nconv == (size_t)-1)
                                                        goto input_failure;
                                                if (nconv > width)
@@ -469,14 +506,15 @@ literal:
                                        n++;
                                }
                                if (wi != WEOF)
-                                       __ungetwc(wi, fp);
+                                       __ungetwc(wi, fp, loc);
+                               if (n == 0)
+                                       goto match_failure;
                                if (!(flags & SUPPRESS)) {
                                        *mbp = 0;
                                        nassigned++;
                                }
                        }
                        nread += n;
-                       nconversions++;
                        break;
 
                case CT_STRING:
@@ -484,39 +522,39 @@ literal:
                        if (width == 0)
                                width = (size_t)~0;
                        if ((flags & SUPPRESS) && (flags & LONG)) {
-                               while ((wi = __fgetwc(fp)) != WEOF &&
+                               while ((wi = __fgetwc(fp, loc)) != WEOF &&
                                    width-- != 0 &&
-                                   !iswspace(wi))
+                                   !iswspace_l(wi, loc))
                                        nread++;
                                if (wi != WEOF)
-                                       __ungetwc(wi, fp);
+                                       __ungetwc(wi, fp, loc);
                        } else if (flags & LONG) {
                                p0 = p = va_arg(ap, wchar_t *);
-                               while ((wi = __fgetwc(fp)) != WEOF &&
+                               while ((wi = __fgetwc(fp, loc)) != WEOF &&
                                    width-- != 0 &&
-                                   !iswspace(wi)) {
+                                   !iswspace_l(wi, loc)) {
                                        *p++ = (wchar_t)wi;
                                        nread++;
                                }
                                if (wi != WEOF)
-                                       __ungetwc(wi, fp);
+                                       __ungetwc(wi, fp, loc);
                                *p = '\0';
                                nassigned++;
                        } else {
                                if (!(flags & SUPPRESS))
                                        mbp = va_arg(ap, char *);
-                               memset(&mbs, 0, sizeof(mbs));
-                               while ((wi = __fgetwc(fp)) != WEOF &&
+                               mbs = initial_mbs;
+                               while ((wi = __fgetwc(fp, loc)) != WEOF &&
                                    width != 0 &&
-                                   !iswspace(wi)) {
-                                       if (width >= MB_CUR_MAX &&
+                                   !iswspace_l(wi, loc)) {
+                                       if (width >= mb_cur_max &&
                                            !(flags & SUPPRESS)) {
-                                               nconv = wcrtomb(mbp, wi, &mbs);
+                                               nconv = wcrtomb_l(mbp, wi, &mbs, loc);
                                                if (nconv == (size_t)-1)
                                                        goto input_failure;
                                        } else {
-                                               nconv = wcrtomb(mbbuf, wi,
-                                                   &mbs);
+                                               nconv = wcrtomb_l(mbbuf, wi,
+                                                   &mbs, loc);
                                                if (nconv == (size_t)-1)
                                                        goto input_failure;
                                                if (nconv > width)
@@ -531,13 +569,12 @@ literal:
                                        nread++;
                                }
                                if (wi != WEOF)
-                                       __ungetwc(wi, fp);
+                                       __ungetwc(wi, fp, loc);
                                if (!(flags & SUPPRESS)) {
                                        *mbp = 0;
                                        nassigned++;
                                }
                        }
-                       nconversions++;
                        continue;
 
                case CT_INT:
@@ -547,7 +584,7 @@ literal:
                                width = sizeof(buf) / sizeof(*buf) - 1;
                        flags |= SIGNOK | NDIGITS | NZDIGITS;
                        for (p = buf; width; width--) {
-                               c = __fgetwc(fp);
+                               c = __fgetwc(fp, loc);
                                /*
                                 * Switch on the character; `goto ok'
                                 * if we accept it as a part of number.
@@ -607,13 +644,18 @@ literal:
                                case '+': case '-':
                                        if (flags & SIGNOK) {
                                                flags &= ~SIGNOK;
+                                               flags |= HAVESIGN;
                                                goto ok;
                                        }
                                        break;
-
-                               /* x ok iff flag still set & 2nd char */
+                                       
+                               /*
+                                * x ok iff flag still set & 2nd char (or
+                                * 3rd char if we have a sign).
+                                */
                                case 'x': case 'X':
-                                       if (flags & PFXOK && p == buf + 1) {
+                                       if (flags & PFXOK && p ==
+                                           buf + 1 + !!(flags & HAVESIGN)) {
                                                base = 16;      /* if %i */
                                                flags &= ~PFXOK;
                                                goto ok;
@@ -626,7 +668,7 @@ literal:
                                 * for a number.  Stop accumulating digits.
                                 */
                                if (c != WEOF)
-                                       __ungetwc(c, fp);
+                                       __ungetwc(c, fp, loc);
                                break;
                ok:
                                /*
@@ -642,22 +684,22 @@ literal:
                         */
                        if (flags & NDIGITS) {
                                if (p > buf)
-                                       __ungetwc(*--p, fp);
+                                       __ungetwc(*--p, fp, loc);
                                goto match_failure;
                        }
                        c = p[-1];
                        if (c == 'x' || c == 'X') {
                                --p;
-                               __ungetwc(c, fp);
+                               __ungetwc(c, fp, loc);
                        }
                        if ((flags & SUPPRESS) == 0) {
                                uintmax_t res;
 
                                *p = 0;
                                if ((flags & UNSIGNED) == 0)
-                                   res = wcstoimax(buf, NULL, base);
+                                   res = wcstoimax_l(buf, NULL, base, loc);
                                else
-                                   res = wcstoumax(buf, NULL, base);
+                                   res = wcstoumax_l(buf, NULL, base, loc);
                                if (flags & POINTER)
                                        *va_arg(ap, void **) =
                                                        (void *)(uintptr_t)res;
@@ -680,58 +722,73 @@ literal:
                                nassigned++;
                        }
                        nread += p - buf;
-                       nconversions++;
                        break;
 
-#ifdef FLOATING_POINT
+#ifndef NO_FLOATING_POINT
                case CT_FLOAT:
+               {
+                       wchar_t *pbuf;
                        /* scan a floating point number as if by strtod */
-                       if (width == 0 || width > sizeof(buf) /
-                           sizeof(*buf) - 1)
-                               width = sizeof(buf) / sizeof(*buf) - 1;
-                       if ((width = parsefloat(fp, buf, buf + width)) == 0)
+                       if ((width = parsefloat(fp, &pbuf, width, loc)) == 0)
                                goto match_failure;
                        if ((flags & SUPPRESS) == 0) {
                                if (flags & LONGDBL) {
-                                       long double res = wcstold(buf, &p);
+                                       long double res = wcstold_l(pbuf, &p, loc);
                                        *va_arg(ap, long double *) = res;
                                } else if (flags & LONG) {
-                                       double res = wcstod(buf, &p);
+                                       double res = wcstod_l(pbuf, &p, loc);
                                        *va_arg(ap, double *) = res;
                                } else {
-                                       float res = wcstof(buf, &p);
+                                       float res = wcstof_l(pbuf, &p, loc);
                                        *va_arg(ap, float *) = res;
                                }
-                               if (__scanfdebug && p - buf != width)
-                                       abort();
                                nassigned++;
                        }
                        nread += width;
-                       nconversions++;
                        break;
-#endif /* FLOATING_POINT */
+               }
+#endif /* !NO_FLOATING_POINT */
                }
        }
 input_failure:
-       return (nconversions != 0 ? nassigned : EOF);
+       return (nassigned ? nassigned : EOF);
 match_failure:
        return (nassigned);
 }
 
-#ifdef FLOATING_POINT
+#ifndef NO_FLOATING_POINT
+extern char *__parsefloat_buf(size_t s);  /* see vfscanf-fbsd.c */
+
 static int
-parsefloat(FILE *fp, wchar_t *buf, wchar_t *end)
+parsefloat(FILE *fp, wchar_t **buf, size_t width, locale_t loc)
 {
+       mbstate_t mbs;
+       size_t nconv;
        wchar_t *commit, *p;
        int infnanpos = 0;
        enum {
-               S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
+               S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
                S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
        } state = S_START;
        wchar_t c;
-       wchar_t decpt = (wchar_t)(unsigned char)*localeconv()->decimal_point;
+       wchar_t decpt;
        _Bool gotmantdig = 0, ishex = 0;
+       wchar_t *b;
+       wchar_t *e;
+       size_t s;
 
+       mbs = initial_mbs;
+
+       nconv = mbrtowc_l(&decpt, localeconv()->decimal_point, MB_CUR_MAX_L(loc), &mbs, loc);
+       if (nconv == (size_t)-1 || nconv == (size_t)-2)
+               decpt = '.';    /* failsafe */
+
+       s = (width == 0 ? BUF : (width + 1));
+       if ((b = (wchar_t *)__parsefloat_buf(s * sizeof(wchar_t))) == NULL) {
+               *buf = NULL;
+               return 0;
+       }
+       e = b + (s - 1);
        /*
         * We set commit = p whenever the string we have read so far
         * constitutes a valid representation of a floating point
@@ -741,10 +798,10 @@ parsefloat(FILE *fp, wchar_t *buf, wchar_t *end)
         * always necessary to read at least one character that doesn't
         * match; thus, we can't short-circuit "infinity" or "nan(...)".
         */
-       commit = buf - 1;
+       commit = b - 1;
        c = WEOF;
-       for (p = buf; p < end; ) {
-               if ((c = __fgetwc(fp)) == WEOF)
+       for (p = b; width == 0 || p < e; ) {
+               if ((c = __fgetwc(fp, loc)) == WEOF)
                        break;
 reswitch:
                switch (state) {
@@ -784,8 +841,6 @@ reswitch:
                        break;
                case S_NAN:
                        switch (infnanpos) {
-                       case -1:        /* XXX kludge to deal with nan(...) */
-                               goto parsedone;
                        case 0:
                                if (c != 'A' && c != 'a')
                                        goto parsedone;
@@ -803,13 +858,15 @@ reswitch:
                        default:
                                if (c == ')') {
                                        commit = p;
-                                       infnanpos = -2;
-                               } else if (!iswalnum(c) && c != '_')
+                                       state = S_DONE;
+                               } else if (!iswalnum_l(c, loc) && c != '_')
                                        goto parsedone;
                                break;
                        }
                        infnanpos++;
                        break;
+               case S_DONE:
+                       goto parsedone;
                case S_MAYBEHEX:
                        state = S_DIGITS;
                        if (c == 'X' || c == 'x') {
@@ -820,7 +877,7 @@ reswitch:
                                goto reswitch;
                        }
                case S_DIGITS:
-                       if (ishex && iswxdigit(c) || iswdigit(c))
+                       if ((ishex && iswxdigit_l(c, loc)) || iswdigit_l(c, loc))
                                gotmantdig = 1;
                        else {
                                state = S_FRAC;
@@ -831,13 +888,13 @@ reswitch:
                                commit = p;
                        break;
                case S_FRAC:
-                       if ((c == 'E' || c == 'e') && !ishex ||
-                           (c == 'P' || c == 'p') && ishex) {
+                       if (((c == 'E' || c == 'e') && !ishex) ||
+                           ((c == 'P' || c == 'p') && ishex)) {
                                if (!gotmantdig)
                                        goto parsedone;
                                else
                                        state = S_EXP;
-                       } else if (ishex && iswxdigit(c) || iswdigit(c)) {
+                       } else if ((ishex && iswxdigit_l(c, loc)) || iswdigit_l(c, loc)) {
                                commit = p;
                                gotmantdig = 1;
                        } else
@@ -850,13 +907,26 @@ reswitch:
                        else
                                goto reswitch;
                case S_EXPDIGITS:
-                       if (iswdigit(c))
+                       if (iswdigit_l(c, loc))
                                commit = p;
                        else
                                goto parsedone;
                        break;
                default:
-                       abort();
+                       LIBC_ABORT("unknown state %d", state);
+               }
+               if (p >= e) {
+                       ssize_t diff = (p - b);
+                       ssize_t com = (commit - b);
+                       s += BUF;
+                       b = (wchar_t *)__parsefloat_buf(s * sizeof(wchar_t));
+                       if (b == NULL) {
+                               *buf = NULL;
+                               return 0;
+                       }
+                       e = b + (s - 1);
+                       p = b + diff;
+                       commit = b + com;
                }
                *p++ = c;
                c = WEOF;
@@ -864,10 +934,11 @@ reswitch:
 
 parsedone:
        if (c != WEOF)
-               __ungetwc(c, fp);
+               __ungetwc(c, fp, loc);
        while (commit < --p)
-               __ungetwc(*p, fp);
+               __ungetwc(*p, fp, loc);
        *++commit = '\0';
-       return (commit - buf);
+       *buf = b;
+       return (commit - b);
 }
 #endif