]> git.saurik.com Git - apple/libc.git/blobdiff - stdio/FreeBSD/vfscanf.c
Libc-1439.100.3.tar.gz
[apple/libc.git] / stdio / FreeBSD / vfscanf.c
index 5a9321165f89a68db01a611e98121eed346d4a53..eca276601f75f301aed8ca83ba2107095c6ea27b 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.
  */
 
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wcomma"
+
 #if defined(LIBC_SCCS) && !defined(lint)
 static char sccsid[] = "@(#)vfscanf.c  8.1 (Berkeley) 6/4/93";
 #endif /* LIBC_SCCS and not lint */
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.32 2003/06/28 09:03:05 das Exp $");
+__FBSDID("$FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.43 2009/01/19 06:19:51 das Exp $");
+
+#include "xlocale_private.h"
 
 #include "namespace.h"
 #include <ctype.h>
@@ -50,15 +51,14 @@ __FBSDID("$FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.32 2003/06/28 09:03:05 das
 #include <string.h>
 #include <wchar.h>
 #include <wctype.h>
+#include <pthread.h>
 #include "un-namespace.h"
 
 #include "collate.h"
 #include "libc_private.h"
 #include "local.h"
 
-#define FLOATING_POINT
-
-#ifdef FLOATING_POINT
+#ifndef NO_FLOATING_POINT
 #include <locale.h>
 #endif
 
@@ -88,6 +88,7 @@ __FBSDID("$FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.32 2003/06/28 09:03:05 das
 #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.
@@ -98,10 +99,10 @@ __FBSDID("$FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.32 2003/06/28 09:03:05 das
 #define        CT_INT          3       /* %[dioupxX] conversion */
 #define        CT_FLOAT        4       /* %[efgEFG] conversion */
 
-static const u_char *__sccl(char *, const u_char *);
-static int parsefloat(FILE *, char *, char *);
-
-int __scanfdebug = 0;
+static const u_char *__sccl(char *, const u_char *, locale_t);
+#ifndef NO_FLOATING_POINT
+static int parsefloat(FILE *, char **, size_t, locale_t);
+#endif
 
 __weak_reference(__vfscanf, vfscanf);
 
@@ -109,12 +110,24 @@ __weak_reference(__vfscanf, vfscanf);
  * __vfscanf - MT-safe version
  */
 int
-__vfscanf(FILE *fp, char const *fmt0, va_list ap)
+__vfscanf(FILE * __restrict fp, char const * __restrict fmt0, va_list ap)
 {
        int ret;
 
        FLOCKFILE(fp);
-       ret = __svfscanf(fp, fmt0, ap);
+       ret = __svfscanf_l(fp, __current_locale(), fmt0, ap);
+       FUNLOCKFILE(fp);
+       return (ret);
+}
+
+int
+vfscanf_l(FILE * __restrict fp, locale_t loc, char const * __restrict fmt0, va_list ap)
+{
+       int ret;
+
+       NORMALIZE_LOCALE(loc);
+       FLOCKFILE(fp);
+       ret = __svfscanf_l(fp, loc, fmt0, ap);
        FUNLOCKFILE(fp);
        return (ret);
 }
@@ -122,8 +135,8 @@ __vfscanf(FILE *fp, char const *fmt0, va_list ap)
 /*
  * __svfscanf - non-MT-safe version of __vfscanf
  */
-int
-__svfscanf(FILE *fp, const char *fmt0, va_list ap)
+__private_extern__ int
+__svfscanf_l(FILE * __restrict fp, locale_t loc, const char * __restrict fmt0, va_list ap)
 {
        const u_char *fmt = (const u_char *)fmt0;
        int c;                  /* character from format, or conversion */
@@ -133,36 +146,43 @@ __svfscanf(FILE *fp, const char *fmt0, va_list ap)
        int flags;              /* flags as defined above */
        char *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 */
        char ccltab[256];       /* character class table for %[...] */
        char buf[BUF];          /* buffer for numeric and mb conversions */
        wchar_t *wcp;           /* handy wide character pointer */
-       wchar_t *wcp0;          /* saves original value of wcp */
-       mbstate_t mbs;          /* multibyte conversion state */
        size_t nconv;           /* length of multibyte sequence converted */
+       int index;              /* %index$, zero if unset */
+       va_list ap_orig;        /* to reset ap to first argument */
+       static const mbstate_t initial;
+       mbstate_t mbs;
+       int mb_cur_max;
 
        /* `basefix' is used to avoid `if' tests in the integer scanner */
-       static short basefix[17] =
+       static const short basefix[17] =
                { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
 
+       NORMALIZE_LOCALE(loc);
+       mb_cur_max = MB_CUR_MAX_L(loc);
        ORIENT(fp, -1);
 
        nassigned = 0;
-       nconversions = 0;
        nread = 0;
+       va_copy(ap_orig, ap);
        for (;;) {
                c = *fmt++;
                if (c == 0)
                        return (nassigned);
-               if (isspace(c)) {
-                       while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p))
+               if (isspace_l(c, loc)) {
+                       while ((fp->_r > 0 || __srefill(fp) == 0) && isspace_l(*fp->_p, loc))
                                nread++, fp->_r--, fp->_p++;
                        continue;
                }
-               if (c != '%')
+               if (c != '%') {
+                       if (fp->_r <= 0 && __srefill(fp))
+                               goto input_failure;
                        goto literal;
+               }
                width = 0;
                flags = 0;
                /*
@@ -172,15 +192,35 @@ __svfscanf(FILE *fp, const char *fmt0, va_list ap)
 again:         c = *fmt++;
                switch (c) {
                case '%':
+                       /* Consume leading white space */
+                       for(;;) {
+                               if (fp->_r <= 0 && __srefill(fp))
+                                       goto input_failure;
+                               if (!isspace_l(*fp->_p, loc))
+                                       break;
+                               nread++;
+                               fp->_r--;
+                               fp->_p++;
+                       }
 literal:
-                       if (fp->_r <= 0 && __srefill(fp))
-                               goto input_failure;
                        if (*fp->_p != c)
                                goto match_failure;
                        fp->_r--, fp->_p++;
                        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;
@@ -252,7 +292,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;
@@ -267,7 +307,7 @@ literal:
                        break;
 
                case '[':
-                       fmt = __sccl(ccltab, fmt);
+                       fmt = __sccl(ccltab, fmt, loc);
                        flags |= NOSKIP;
                        c = CT_CCL;
                        break;
@@ -288,27 +328,30 @@ literal:
                        break;
 
                case 'n':
-                       nconversions++;
+               {
                        if (flags & SUPPRESS)   /* ??? */
                                continue;
-                       if (flags & SHORTSHORT)
-                               *va_arg(ap, char *) = nread;
+                       void *ptr = va_arg(ap, void *);
+                       if (ptr == NULL)
+                               continue;
+                       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;
 
@@ -330,7 +373,7 @@ literal:
                 * that suppress this.
                 */
                if ((flags & NOSKIP) == 0) {
-                       while (isspace(*fp->_p)) {
+                       while (isspace_l(*fp->_p, loc)) {
                                nread++;
                                if (--fp->_r > 0)
                                        fp->_p++;
@@ -360,15 +403,15 @@ literal:
                                        wcp = NULL;
                                n = 0;
                                while (width != 0) {
-                                       if (n == MB_CUR_MAX) {
+                                       if (n == mb_cur_max) {
                                                fp->_flags |= __SERR;
                                                goto input_failure;
                                        }
                                        buf[n++] = *fp->_p;
                                        fp->_p++;
                                        fp->_r--;
-                                       memset(&mbs, 0, sizeof(mbs));
-                                       nconv = mbrtowc(wcp, buf, n, &mbs);
+                                       mbs = initial;
+                                       nconv = mbrtowc_l(wcp, buf, n, &mbs, loc);
                                        if (nconv == (size_t)-1) {
                                                fp->_flags |= __SERR;
                                                goto input_failure;
@@ -413,7 +456,7 @@ literal:
                                }
                                nread += sum;
                        } else {
-                               size_t r = fread((void *)va_arg(ap, char *), 1,
+                               size_t r = __fread((void *)va_arg(ap, char *), 1,
                                    width, fp);
 
                                if (r == 0)
@@ -421,7 +464,6 @@ literal:
                                nread += r;
                                nassigned++;
                        }
-                       nconversions++;
                        break;
 
                case CT_CCL:
@@ -434,21 +476,21 @@ literal:
                                int nchars;
 
                                if ((flags & SUPPRESS) == 0)
-                                       wcp = wcp0 = va_arg(ap, wchar_t *);
+                                       wcp = va_arg(ap, wchar_t *);
                                else
-                                       wcp = wcp0 = &twc;
+                                       wcp = &twc;
                                n = 0;
                                nchars = 0;
                                while (width != 0) {
-                                       if (n == MB_CUR_MAX) {
+                                       if (n == mb_cur_max) {
                                                fp->_flags |= __SERR;
                                                goto input_failure;
                                        }
                                        buf[n++] = *fp->_p;
                                        fp->_p++;
                                        fp->_r--;
-                                       memset(&mbs, 0, sizeof(mbs));
-                                       nconv = mbrtowc(wcp, buf, n, &mbs);
+                                       mbs = initial;
+                                       nconv = mbrtowc_l(wcp, buf, n, &mbs, loc);
                                        if (nconv == (size_t)-1) {
                                                fp->_flags |= __SERR;
                                                goto input_failure;
@@ -456,8 +498,8 @@ literal:
                                        if (nconv == 0)
                                                *wcp = L'\0';
                                        if (nconv != (size_t)-2) {
-                                               if (wctob(*wcp) != EOF &&
-                                                   !ccltab[wctob(*wcp)]) {
+                                               if (wctob_l(*wcp, loc) != EOF &&
+                                                   !ccltab[wctob_l(*wcp, loc)]) {
                                                        while (n != 0) {
                                                                n--;
                                                                __ungetc(buf[n],
@@ -525,7 +567,6 @@ literal:
                                nassigned++;
                        }
                        nread += n;
-                       nconversions++;
                        break;
 
                case CT_STRING:
@@ -540,16 +581,16 @@ literal:
                                else
                                        wcp = &twc;
                                n = 0;
-                               while (!isspace(*fp->_p) && width != 0) {
-                                       if (n == MB_CUR_MAX) {
+                               while (width != 0) {
+                                       if (n == mb_cur_max) {
                                                fp->_flags |= __SERR;
                                                goto input_failure;
                                        }
                                        buf[n++] = *fp->_p;
                                        fp->_p++;
                                        fp->_r--;
-                                       memset(&mbs, 0, sizeof(mbs));
-                                       nconv = mbrtowc(wcp, buf, n, &mbs);
+                                       mbs = initial;
+                                       nconv = mbrtowc_l(wcp, buf, n, &mbs, loc);
                                        if (nconv == (size_t)-1) {
                                                fp->_flags |= __SERR;
                                                goto input_failure;
@@ -557,7 +598,7 @@ literal:
                                        if (nconv == 0)
                                                *wcp = L'\0';
                                        if (nconv != (size_t)-2) {
-                                               if (iswspace(*wcp)) {
+                                               if (iswspace_l(*wcp, loc)) {
                                                        while (n != 0) {
                                                                n--;
                                                                __ungetc(buf[n],
@@ -585,7 +626,7 @@ literal:
                                }
                        } else if (flags & SUPPRESS) {
                                n = 0;
-                               while (!isspace(*fp->_p)) {
+                               while (!isspace_l(*fp->_p, loc)) {
                                        n++, fp->_r--, fp->_p++;
                                        if (--width == 0)
                                                break;
@@ -595,7 +636,7 @@ literal:
                                nread += n;
                        } else {
                                p0 = p = va_arg(ap, char *);
-                               while (!isspace(*fp->_p)) {
+                               while (!isspace_l(*fp->_p, loc)) {
                                        fp->_r--;
                                        *p++ = *fp->_p++;
                                        if (--width == 0)
@@ -607,7 +648,6 @@ literal:
                                nread += p - p0;
                                nassigned++;
                        }
-                       nconversions++;
                        continue;
 
                case CT_INT:
@@ -683,13 +723,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;
@@ -733,9 +778,9 @@ literal:
 
                                *p = 0;
                                if ((flags & UNSIGNED) == 0)
-                                   res = strtoimax(buf, (char **)NULL, base);
+                                   res = strtoimax_l(buf, (char **)NULL, base, loc);
                                else
-                                   res = strtoumax(buf, (char **)NULL, base);
+                                   res = strtoumax_l(buf, (char **)NULL, base, loc);
                                if (flags & POINTER)
                                        *va_arg(ap, void **) =
                                                        (void *)(uintptr_t)res;
@@ -758,43 +803,46 @@ literal:
                                nassigned++;
                        }
                        nread += p - buf;
-                       nconversions++;
                        break;
 
-#ifdef FLOATING_POINT
+#ifndef NO_FLOATING_POINT
                case CT_FLOAT:
+               {
+                       char *pbuf;
                        /* scan a floating point number as if by strtod */
-                       if (width == 0 || width > sizeof(buf) - 1)
-                               width = 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 = strtold(buf, &p);
+                                       long double res = strtold_l(pbuf, &p, loc);
                                        *va_arg(ap, long double *) = res;
                                } else if (flags & LONG) {
-                                       double res = strtod(buf, &p);
+                                       double res = strtod_l(pbuf, &p, loc);
                                        *va_arg(ap, double *) = res;
                                } else {
-                                       float res = strtof(buf, &p);
+                                       float res = strtof_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);
 }
 
+int
+__svfscanf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap)
+{
+       return __svfscanf_l(fp, __current_locale(), fmt0, ap);
+}
+
 /*
  * Fill in the given table from the scanset at the given format
  * (just after `[').  Return a pointer to the character past the
@@ -802,9 +850,10 @@ match_failure:
  * considered part of the scanset.
  */
 static const u_char *
-__sccl(tab, fmt)
+__sccl(tab, fmt, loc)
        char *tab;
        const u_char *fmt;
+       locale_t loc;
 {
        int c, n, v, i;
 
@@ -840,6 +889,7 @@ doswitch:
                        return (fmt - 1);
 
                case '-':
+               {
                        /*
                         * A scanset of the form
                         *      [01+-]
@@ -860,8 +910,8 @@ doswitch:
                         */
                        n = *fmt;
                        if (n == ']'
-                           || (__collate_load_error ? n < c :
-                               __collate_range_cmp (n, c) < 0
+                           || (loc->__collate_load_error ? n < c :
+                               __collate_range_cmp (n, c, loc) < 0
                               )
                           ) {
                                c = '-';
@@ -869,14 +919,14 @@ doswitch:
                        }
                        fmt++;
                        /* fill in the range */
-                       if (__collate_load_error) {
+                       if (loc->__collate_load_error) {
                                do {
                                        tab[++c] = v;
                                } while (c < n);
                        } else {
                                for (i = 0; i < 256; i ++)
-                                       if (   __collate_range_cmp (c, i) < 0
-                                           && __collate_range_cmp (i, n) <= 0
+                                       if (   __collate_range_cmp (c, i, loc) < 0
+                                           && __collate_range_cmp (i, n, loc) <= 0
                                           )
                                                tab[i] = v;
                        }
@@ -896,7 +946,7 @@ doswitch:
                                return (fmt);
 #endif
                        break;
-
+               }
                case ']':               /* end of scanset */
                        return (fmt);
 
@@ -908,20 +958,75 @@ doswitch:
        /* NOTREACHED */
 }
 
-#ifdef FLOATING_POINT
+#ifndef NO_FLOATING_POINT
+/*
+ * Maintain a per-thread parsefloat buffer, shared by __svfscanf_l and
+ * __vfwscanf.
+ */
+#ifdef BUILDING_VARIANT
+extern char *__parsefloat_buf(size_t s);
+#else /* !BUILDING_VARIANT */
+__private_extern__ char *
+__parsefloat_buf(size_t s)
+{
+       char *b;
+       static pthread_key_t    parsefloat_tsd_key = (pthread_key_t)-1;
+       static pthread_mutex_t  parsefloat_tsd_lock = PTHREAD_MUTEX_INITIALIZER;
+       static size_t bsiz = 0;
+
+       if (parsefloat_tsd_key == (pthread_key_t)-1) {
+               pthread_mutex_lock(&parsefloat_tsd_lock);
+               if (parsefloat_tsd_key == (pthread_key_t)-1) {
+                       parsefloat_tsd_key = __LIBC_PTHREAD_KEY_PARSEFLOAT;
+                       pthread_key_init_np(parsefloat_tsd_key, free);
+               }
+               pthread_mutex_unlock(&parsefloat_tsd_lock);
+       }
+       if ((b = (char *)pthread_getspecific(parsefloat_tsd_key)) == NULL) {
+               bsiz = s > BUF ? s : BUF;
+               b = (char *)malloc(bsiz);
+               if (b == NULL) {
+                       bsiz = 0;
+                       return NULL;
+               }
+               pthread_setspecific(parsefloat_tsd_key, b);
+               return b;
+       }
+       if (s > bsiz) {
+               b = (char *)reallocf(b, s);
+               pthread_setspecific(parsefloat_tsd_key, b);
+               if (b == NULL) {
+                       bsiz = 0;
+                       return NULL;
+               }
+               bsiz = s;
+       }
+       return b;
+}
+#endif /* BUILDING_VARIANT */
+
 static int
-parsefloat(FILE *fp, char *buf, char *end)
+parsefloat(FILE *fp, char **buf, size_t width, locale_t loc)
 {
        char *commit, *p;
-       int infnanpos = 0;
+       int infnanpos = 0, decptpos = 0;
        enum {
-               S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
-               S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
+               S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
+               S_DIGITS, S_DECPT, S_FRAC, S_EXP, S_EXPDIGITS
        } state = S_START;
        unsigned char c;
-       char decpt = *localeconv()->decimal_point;
+       const char *decpt = localeconv_l(loc)->decimal_point;
        _Bool gotmantdig = 0, ishex = 0;
-
+       char *b;
+       char *e;
+       size_t s;
+
+       s = (width == 0 ? BUF : (width + 1));
+       if ((b = __parsefloat_buf(s)) == 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
@@ -931,8 +1036,8 @@ parsefloat(FILE *fp, char *buf, char *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;
-       for (p = buf; p < end; ) {
+       commit = b - 1;
+       for (p = b; width == 0 || p < e; ) {
                c = *fp->_p;
 reswitch:
                switch (state) {
@@ -972,8 +1077,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;
@@ -991,13 +1094,15 @@ reswitch:
                        default:
                                if (c == ')') {
                                        commit = p;
-                                       infnanpos = -2;
-                               } else if (!isalnum(c) && c != '_')
+                                       state = S_DONE;
+                               } else if (!isalnum_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') {
@@ -1008,24 +1113,42 @@ reswitch:
                                goto reswitch;
                        }
                case S_DIGITS:
-                       if (ishex && isxdigit(c) || isdigit(c))
+                       if ((ishex && isxdigit_l(c, loc)) || isdigit_l(c, loc)) {
                                gotmantdig = 1;
-                       else {
+                               commit = p;
+                               break;
+                       } else {
+                               state = S_DECPT;
+                               goto reswitch;
+                       }
+               case S_DECPT:
+                       if (c == decpt[decptpos]) {
+                               if (decpt[++decptpos] == '\0') {
+                                       /* We read the complete decpt seq. */
+                                       state = S_FRAC;
+                                       if (gotmantdig)
+                                               commit = p;
+                               }
+                               break;
+                       } else if (!decptpos) {
+                               /* We didn't read any decpt characters. */
                                state = S_FRAC;
-                               if (c != decpt)
-                                       goto reswitch;
+                               goto reswitch;
+                       } else {
+                               /*
+                                * We read part of a multibyte decimal point,
+                                * but the rest is invalid, so bail.
+                                */
+                               goto parsedone;
                        }
-                       if (gotmantdig)
-                               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 && isxdigit(c) || isdigit(c)) {
+                       } else if ((ishex && isxdigit_l(c, loc)) || isdigit_l(c, loc)) {
                                commit = p;
                                gotmantdig = 1;
                        } else
@@ -1038,13 +1161,26 @@ reswitch:
                        else
                                goto reswitch;
                case S_EXPDIGITS:
-                       if (isdigit(c))
+                       if (isdigit_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 = __parsefloat_buf(s);
+                       if (b == NULL) {
+                               *buf = NULL;
+                               return 0;
+                       }
+                       e = b + (s - 1);
+                       p = b + diff;
+                       commit = b + com;
                }
                *p++ = c;
                if (--fp->_r > 0)
@@ -1057,6 +1193,8 @@ parsedone:
        while (commit < --p)
                __ungetc(*(u_char *)p, fp);
        *++commit = '\0';
-       return (commit - buf);
+       *buf = b;
+       return (commit - b);
 }
 #endif
+#pragma clang diagnostic push