Fix sscanf/wcstod parsing of NaNs.

The parsefloat routines -- which let us pass NaNs and infinities on to
strto(f|d|ld) -- come from NetBSD.

Also fix LP64's strtold to return a NaN, and fix all the architectures
to return quiet NaNs.

Also fix wcstof/wcstod/wcstold to use parsefloat so they support hex
floats.

Lots of new tests.

Bug: http://b/31101647
Change-Id: Id7d46ac2d8acb8770b5e8c445e87cfabfde6f111
diff --git a/libc/Android.bp b/libc/Android.bp
index 4fcd83e..ad31192 100644
--- a/libc/Android.bp
+++ b/libc/Android.bp
@@ -15,9 +15,12 @@
     "bionic/sigsetmask.c",
     "bionic/system_properties_compat.c",
     "stdio/fread.c",
+    "stdio/parsefloat.c",
     "stdio/refill.c",
     "stdio/stdio.cpp",
     "stdio/stdio_ext.cpp",
+    "stdio/vfscanf.c",
+    "stdio/vfwscanf.c",
     "stdlib/atexit.c",
     "stdlib/exit.c",
 ]
@@ -397,11 +400,8 @@
         "upstream-openbsd/lib/libc/locale/mbstowcs.c",
         "upstream-openbsd/lib/libc/locale/mbtowc.c",
         "upstream-openbsd/lib/libc/locale/wcscoll.c",
-        "upstream-openbsd/lib/libc/locale/wcstod.c",
-        "upstream-openbsd/lib/libc/locale/wcstof.c",
         "upstream-openbsd/lib/libc/locale/wcstoimax.c",
         "upstream-openbsd/lib/libc/locale/wcstol.c",
-        "upstream-openbsd/lib/libc/locale/wcstold.c",
         "upstream-openbsd/lib/libc/locale/wcstoll.c",
         "upstream-openbsd/lib/libc/locale/wcstombs.c",
         "upstream-openbsd/lib/libc/locale/wcstoul.c",
@@ -453,7 +453,6 @@
         "upstream-openbsd/lib/libc/stdio/ungetwc.c",
         "upstream-openbsd/lib/libc/stdio/vasprintf.c",
         "upstream-openbsd/lib/libc/stdio/vdprintf.c",
-        "upstream-openbsd/lib/libc/stdio/vfscanf.c",
         "upstream-openbsd/lib/libc/stdio/vsscanf.c",
         "upstream-openbsd/lib/libc/stdio/vswprintf.c",
         "upstream-openbsd/lib/libc/stdio/vswscanf.c",
@@ -520,7 +519,6 @@
     srcs: [
         "upstream-openbsd/lib/libc/stdio/vfprintf.c",
         "upstream-openbsd/lib/libc/stdio/vfwprintf.c",
-        "upstream-openbsd/lib/libc/stdio/vfwscanf.c",
     ],
     cflags: [
         "-include openbsd-compat.h",
@@ -1365,6 +1363,7 @@
         "bionic/unlink.cpp",
         "bionic/wait.cpp",
         "bionic/wchar.cpp",
+        "bionic/wcstod.cpp",
         "bionic/wctype.cpp",
         "bionic/wmempcpy.cpp",
     ],
diff --git a/libc/bionic/wcstod.cpp b/libc/bionic/wcstod.cpp
new file mode 100644
index 0000000..eb66ba0
--- /dev/null
+++ b/libc/bionic/wcstod.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <wchar.h>
+
+#include <stdlib.h>
+
+#include "local.h"
+
+template <typename float_type> float_type wcstod(const wchar_t* str, wchar_t** end,
+                                                 float_type strtod_fn(const char*, char**)) {
+  // What's the longest span of the input that might be part of the float?
+  size_t max_len = wcsspn(str, L"-+0123456789.xXeEpP()nNaAiIfFtTyY");
+
+  // We know the only valid characters are ASCII, so convert them by brute force.
+  char* ascii_str = new char[max_len + 1];
+  if (!ascii_str) return float_type();
+  for (size_t i = 0; i < max_len; ++i) {
+    ascii_str[i] = str[i] & 0xff;
+  }
+  ascii_str[max_len] = 0;
+
+  // Set up a fake FILE that points to those ASCII characters, for `parsefloat`.
+  FILE f;
+  __sfileext fext;
+  _FILEEXT_SETUP(&f, &fext);
+  f._flags = __SRD;
+  f._bf._base = f._p = reinterpret_cast<unsigned char*>(ascii_str);
+  f._bf._size = f._r = max_len;
+  f._read = [](void*, char*, int) { return 0; }; // aka `eofread`, aka "no more data".
+  f._lb._base = NULL;
+
+  // Ask `parsefloat` to look at the same data more carefully.
+
+  // We can't just do this straight away because we can't construct a suitable FILE*
+  // in the absence of any `fwmemopen` analogous to `fmemopen`. And we don't want to
+  // duplicate the `parsefloat` logic. We also don't want to actually have to have wchar_t
+  // implementations of the ASCII `strtod` logic (though if you were designing a libc
+  // from scratch, you'd probably want to just make that more generic and lose all the
+  // cruft on top).
+  size_t actual_len = parsefloat(&f, ascii_str, ascii_str + max_len);
+
+  // Finally let the ASCII conversion function do the work.
+  char* ascii_end;
+  float_type result = strtod_fn(ascii_str, &ascii_end);
+  if (ascii_end != ascii_str + actual_len) abort();
+
+  if (end) *end = const_cast<wchar_t*>(str) + actual_len;
+
+  delete[] ascii_str;
+  return result;
+}
+
+float wcstof(const wchar_t* s, wchar_t** end) {
+  return wcstod<float>(s, end, strtof);
+}
+
+double wcstod(const wchar_t* s, wchar_t** end) {
+  return wcstod<double>(s, end, strtod);
+}
+
+long double wcstold(const wchar_t* s, wchar_t** end) {
+  return wcstod<long double>(s, end, strtold);
+}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/floatio.h b/libc/stdio/floatio.h
similarity index 100%
rename from libc/upstream-openbsd/lib/libc/stdio/floatio.h
rename to libc/stdio/floatio.h
diff --git a/libc/stdio/local.h b/libc/stdio/local.h
index 7be5a7c..575a428 100644
--- a/libc/stdio/local.h
+++ b/libc/stdio/local.h
@@ -147,11 +147,7 @@
 #define __SNPT 0
 #define __SOPT 0
 
-#if defined(__cplusplus)
-#define _EXT(fp) reinterpret_cast<__sfileext*>((fp)->_ext._base)
-#else
-#define _EXT(fp) ((struct __sfileext *)((fp)->_ext._base))
-#endif
+#define _EXT(fp) __BIONIC_CAST(reinterpret_cast, struct __sfileext*, (fp)->_ext._base)
 
 #define _UB(fp) _EXT(fp)->_ub
 #define _FLOCK(fp)  _EXT(fp)->_lock
@@ -171,7 +167,7 @@
 
 #define _FILEEXT_SETUP(f, fext) \
 do { \
-	(f)->_ext._base = (unsigned char *)(fext); \
+	(f)->_ext._base = __BIONIC_CAST(reinterpret_cast, unsigned char*, fext); \
 	_FILEEXT_INIT(f); \
 } while (0)
 
@@ -259,6 +255,9 @@
 extern void __sinit(void); // Not actually implemented.
 #define __sdidinit 1
 
+size_t parsefloat(FILE*, char*, char*);
+size_t wparsefloat(FILE*, wchar_t*, wchar_t*);
+
 __END_DECLS
 
 #endif
diff --git a/libc/stdio/parsefloat.c b/libc/stdio/parsefloat.c
new file mode 100644
index 0000000..e911da4
--- /dev/null
+++ b/libc/stdio/parsefloat.c
@@ -0,0 +1,336 @@
+/*-
+ * Copyright (c) 1990, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 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. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+
+#include "local.h"
+#include "floatio.h"
+
+#define	BUF		513	/* Maximum length of numeric string. */
+
+size_t parsefloat(FILE *fp, char *buf, char *end) {
+	char *commit, *p;
+	int infnanpos = 0;
+	enum {
+		S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
+		S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
+	} state = S_START;
+	unsigned char c;
+	int gotmantdig = 0, ishex = 0;
+
+	/*
+	 * We set commit = p whenever the string we have read so far
+	 * constitutes a valid representation of a floating point
+	 * number by itself.  At some point, the parse will complete
+	 * or fail, and we will ungetc() back to the last commit point.
+	 * To ensure that the file offset gets updated properly, it is
+	 * 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; ) {
+		c = *fp->_p;
+reswitch:
+		switch (state) {
+		case S_START:
+			state = S_GOTSIGN;
+			if (c == '-' || c == '+')
+				break;
+			else
+				goto reswitch;
+		case S_GOTSIGN:
+			switch (c) {
+			case '0':
+				state = S_MAYBEHEX;
+				commit = p;
+				break;
+			case 'I':
+			case 'i':
+				state = S_INF;
+				break;
+			case 'N':
+			case 'n':
+				state = S_NAN;
+				break;
+			default:
+				state = S_DIGITS;
+				goto reswitch;
+			}
+			break;
+		case S_INF:
+			if (infnanpos > 6 ||
+			    (c != "nfinity"[infnanpos] &&
+			     c != "NFINITY"[infnanpos]))
+				goto parsedone;
+			if (infnanpos == 1 || infnanpos == 6)
+				commit = p;	/* inf or infinity */
+			infnanpos++;
+			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;
+				break;
+			case 1:
+				if (c != 'N' && c != 'n')
+					goto parsedone;
+				else
+					commit = p;
+				break;
+			case 2:
+				if (c != '(')
+					goto parsedone;
+				break;
+			default:
+				if (c == ')') {
+					commit = p;
+					infnanpos = -2;
+				} else if (!isalnum(c) && c != '_')
+					goto parsedone;
+				break;
+			}
+			infnanpos++;
+			break;
+		case S_MAYBEHEX:
+			state = S_DIGITS;
+			if (c == 'X' || c == 'x') {
+				ishex = 1;
+				break;
+			} else {	/* we saw a '0', but no 'x' */
+				gotmantdig = 1;
+				goto reswitch;
+			}
+		case S_DIGITS:
+			if ((ishex && isxdigit(c)) || isdigit(c))
+				gotmantdig = 1;
+			else {
+				state = S_FRAC;
+				if (c != '.')
+					goto reswitch;
+			}
+			if (gotmantdig)
+				commit = p;
+			break;
+		case S_FRAC:
+			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)) {
+				commit = p;
+				gotmantdig = 1;
+			} else
+				goto parsedone;
+			break;
+		case S_EXP:
+			state = S_EXPDIGITS;
+			if (c == '-' || c == '+')
+				break;
+			else
+				goto reswitch;
+		case S_EXPDIGITS:
+			if (isdigit(c))
+				commit = p;
+			else
+				goto parsedone;
+			break;
+		default:
+			abort();
+		}
+		*p++ = c;
+		if (--fp->_r > 0)
+			fp->_p++;
+		else if (__srefill(fp))
+			break;	/* EOF */
+	}
+
+parsedone:
+	while (commit < --p)
+		(void)ungetc(*(unsigned char *)p, fp);
+	*++commit = '\0';
+	return commit - buf;
+}
+
+size_t wparsefloat(FILE *fp, wchar_t *buf, wchar_t *end) {
+	wchar_t *commit, *p;
+	int infnanpos = 0;
+	enum {
+		S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
+		S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
+	} state = S_START;
+	wint_t c;
+	int gotmantdig = 0, ishex = 0;
+
+	/*
+	 * We set commit = p whenever the string we have read so far
+	 * constitutes a valid representation of a floating point
+	 * number by itself.  At some point, the parse will complete
+	 * or fail, and we will ungetc() back to the last commit point.
+	 * To ensure that the file offset gets updated properly, it is
+	 * always necessary to read at least one character that doesn't
+	 * match; thus, we can't short-circuit "infinity" or "nan(...)".
+	 */
+	commit = buf - 1;
+	c = WEOF;
+	for (p = buf; p < end; ) {
+		if ((c = __fgetwc_unlock(fp)) == WEOF)
+			break;
+reswitch:
+		switch (state) {
+		case S_START:
+			state = S_GOTSIGN;
+			if (c == '-' || c == '+')
+				break;
+			else
+				goto reswitch;
+		case S_GOTSIGN:
+			switch (c) {
+			case '0':
+				state = S_MAYBEHEX;
+				commit = p;
+				break;
+			case 'I':
+			case 'i':
+				state = S_INF;
+				break;
+			case 'N':
+			case 'n':
+				state = S_NAN;
+				break;
+			default:
+				state = S_DIGITS;
+				goto reswitch;
+			}
+			break;
+		case S_INF:
+			if (infnanpos > 6 ||
+			    (c != (wint_t)"nfinity"[infnanpos] &&
+			     c != (wint_t)"NFINITY"[infnanpos]))
+				goto parsedone;
+			if (infnanpos == 1 || infnanpos == 6)
+				commit = p;	/* inf or infinity */
+			infnanpos++;
+			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;
+				break;
+			case 1:
+				if (c != 'N' && c != 'n')
+					goto parsedone;
+				else
+					commit = p;
+				break;
+			case 2:
+				if (c != '(')
+					goto parsedone;
+				break;
+			default:
+				if (c == ')') {
+					commit = p;
+					infnanpos = -2;
+				} else if (!iswalnum(c) && c != '_')
+					goto parsedone;
+				break;
+			}
+			infnanpos++;
+			break;
+		case S_MAYBEHEX:
+			state = S_DIGITS;
+			if (c == 'X' || c == 'x') {
+				ishex = 1;
+				break;
+			} else {	/* we saw a '0', but no 'x' */
+				gotmantdig = 1;
+				goto reswitch;
+			}
+		case S_DIGITS:
+			if ((ishex && iswxdigit(c)) || iswdigit(c))
+				gotmantdig = 1;
+			else {
+				state = S_FRAC;
+				if (c != L'.')
+					goto reswitch;
+			}
+			if (gotmantdig)
+				commit = p;
+			break;
+		case S_FRAC:
+			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)) {
+				commit = p;
+				gotmantdig = 1;
+			} else
+				goto parsedone;
+			break;
+		case S_EXP:
+			state = S_EXPDIGITS;
+			if (c == '-' || c == '+')
+				break;
+			else
+				goto reswitch;
+		case S_EXPDIGITS:
+			if (iswdigit(c))
+				commit = p;
+			else
+				goto parsedone;
+			break;
+		default:
+			abort();
+		}
+		*p++ = c;
+		c = WEOF;
+	}
+
+parsedone:
+	if (c != WEOF)
+		ungetwc(c, fp);
+	while (commit < --p)
+		ungetwc(*p, fp);
+	*++commit = '\0';
+	return (int)(commit - buf);
+}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/vfscanf.c b/libc/stdio/vfscanf.c
similarity index 90%
rename from libc/upstream-openbsd/lib/libc/stdio/vfscanf.c
rename to libc/stdio/vfscanf.c
index abefe32..e47d0df 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/vfscanf.c
+++ b/libc/stdio/vfscanf.c
@@ -346,14 +346,14 @@
 					wcp = NULL;
 				n = 0;
 				while (width != 0) {
-					if (n == MB_CUR_MAX) {
+					if (n == (int)MB_CUR_MAX) {
 						fp->_flags |= __SERR;
 						goto input_failure;
 					}
 					buf[n++] = *fp->_p;
 					fp->_p++;
 					fp->_r--;
-					bzero(&mbs, sizeof(mbs));
+					memset(&mbs, 0, sizeof(mbs));
 					nconv = mbrtowc(wcp, buf, n, &mbs);
 					if (nconv == (size_t)-1) {
 						fp->_flags |= __SERR;
@@ -383,7 +383,7 @@
 			if (flags & SUPPRESS) {
 				size_t sum = 0;
 				for (;;) {
-					if ((n = fp->_r) < width) {
+					if ((n = fp->_r) < (int)width) {
 						sum += n;
 						width -= n;
 						fp->_p += n;
@@ -428,14 +428,14 @@
 				n = 0;
 				nchars = 0;
 				while (width != 0) {
-					if (n == MB_CUR_MAX) {
+					if (n == (int)MB_CUR_MAX) {
 						fp->_flags |= __SERR;
 						goto input_failure;
 					}
 					buf[n++] = *fp->_p;
 					fp->_p++;
 					fp->_r--;
-					bzero(&mbs, sizeof(mbs));
+					memset(&mbs, 0, sizeof(mbs));
 					nconv = mbrtowc(wcp, buf, n, &mbs);
 					if (nconv == (size_t)-1) {
 						fp->_flags |= __SERR;
@@ -532,14 +532,14 @@
 					wcp = &twc;
 				n = 0;
 				while (!isspace(*fp->_p) && width != 0) {
-					if (n == MB_CUR_MAX) {
+					if (n == (int)MB_CUR_MAX) {
 						fp->_flags |= __SERR;
 						goto input_failure;
 					}
 					buf[n++] = *fp->_p;
 					fp->_p++;
 					fp->_r--;
-					bzero(&mbs, sizeof(mbs));
+					memset(&mbs, 0, sizeof(mbs));
 					nconv = mbrtowc(wcp, buf, n, &mbs);
 					if (nconv == (size_t)-1) {
 						fp->_flags |= __SERR;
@@ -760,96 +760,25 @@
 #ifdef FLOATING_POINT
 		case CT_FLOAT:
 			/* scan a floating point number as if by strtod */
-#ifdef hardway
 			if (width == 0 || width > sizeof(buf) - 1)
 				width = sizeof(buf) - 1;
-#else
-			/* size_t is unsigned, hence this optimisation */
-			if (--width > sizeof(buf) - 2)
-				width = sizeof(buf) - 2;
-			width++;
-#endif
-			flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
-			for (p = buf; width; width--) {
-				c = *fp->_p;
-				/*
-				 * This code mimicks the integer conversion
-				 * code, but is much simpler.
-				 */
-				switch (c) {
-
-				case '0': case '1': case '2': case '3':
-				case '4': case '5': case '6': case '7':
-				case '8': case '9':
-					flags &= ~(SIGNOK | NDIGITS);
-					goto fok;
-
-				case '+': case '-':
-					if (flags & SIGNOK) {
-						flags &= ~SIGNOK;
-						goto fok;
-					}
-					break;
-				case '.':
-					if (flags & DPTOK) {
-						flags &= ~(SIGNOK | DPTOK);
-						goto fok;
-					}
-					break;
-				case 'e': case 'E':
-					/* no exponent without some digits */
-					if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
-						flags =
-						    (flags & ~(EXPOK|DPTOK)) |
-						    SIGNOK | NDIGITS;
-						goto fok;
-					}
-					break;
-				}
-				break;
-		fok:
-				*p++ = c;
-				if (--fp->_r > 0)
-					fp->_p++;
-				else if (__srefill(fp))
-					break;	/* EOF */
-			}
-			/*
-			 * If no digits, might be missing exponent digits
-			 * (just give back the exponent) or might be missing
-			 * regular digits, but had sign and/or decimal point.
-			 */
-			if (flags & NDIGITS) {
-				if (flags & EXPOK) {
-					/* no digits at all */
-					while (p > buf)
-						ungetc(*(u_char *)--p, fp);
-					goto match_failure;
-				}
-				/* just a bad exponent (e and maybe sign) */
-				c = *(u_char *)--p;
-				if (c != 'e' && c != 'E') {
-					(void) ungetc(c, fp);/* sign */
-					c = *(u_char *)--p;
-				}
-				(void) ungetc(c, fp);
-			}
+			if ((width = parsefloat(fp, buf, buf + width)) == 0)
+				goto match_failure;
 			if ((flags & SUPPRESS) == 0) {
-				*p = '\0';
 				if (flags & LONGDBL) {
-					long double res = strtold(buf,
-					    (char **)NULL);
+					long double res = strtold(buf, &p);
 					*va_arg(ap, long double *) = res;
 				} else if (flags & LONG) {
-					double res = strtod(buf, (char **)NULL);
+					double res = strtod(buf, &p);
 					*va_arg(ap, double *) = res;
 				} else {
-					float res = strtof(buf, (char **)NULL);
+					float res = strtof(buf, &p);
 					*va_arg(ap, float *) = res;
 				}
+				if ((size_t)(p - buf) != width) abort();
 				nassigned++;
 			}
-			nread += p - buf;
+			nread += width;
 			break;
 #endif /* FLOATING_POINT */
 		}
diff --git a/libc/upstream-openbsd/lib/libc/stdio/vfwscanf.c b/libc/stdio/vfwscanf.c
similarity index 89%
rename from libc/upstream-openbsd/lib/libc/stdio/vfwscanf.c
rename to libc/stdio/vfwscanf.c
index cbb36be..0a7bfa9 100644
--- a/libc/upstream-openbsd/lib/libc/stdio/vfwscanf.c
+++ b/libc/stdio/vfwscanf.c
@@ -95,6 +95,9 @@
 	(cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \
 	(wmemchr(ccls, (_c), ccle - ccls) != NULL))
 
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wframe-larger-than="
+
 /*
  * vfwscanf
  */
@@ -120,9 +123,6 @@
 	size_t nconv;		/* number of bytes in mb. conversion */
 	char mbbuf[MB_LEN_MAX];	/* temporary mb. character buffer */
  	mbstate_t mbs;
-#ifdef FLOATING_POINT
-	wchar_t decimal_point = 0;
-#endif
 
 	/* `basefix' is used to avoid `if' tests in the integer scanner */
 	static short basefix[17] =
@@ -371,7 +371,7 @@
 				if (!(flags & SUPPRESS))
 					mbp = va_arg(ap, char *);
 				n = 0;
-				bzero(&mbs, sizeof(mbs));
+				memset(&mbs, 0, sizeof(mbs));
 				while (width != 0 &&
 				    (wi = __fgetwc_unlock(fp)) != WEOF) {
 					if (width >= MB_CUR_MAX &&
@@ -436,7 +436,7 @@
 				if (!(flags & SUPPRESS))
 					mbp = va_arg(ap, char *);
 				n = 0;
-				bzero(&mbs, sizeof(mbs));
+				memset(&mbs, 0, sizeof(mbs));
 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
 				    width != 0 && INCCL(wi)) {
 					if (width >= MB_CUR_MAX &&
@@ -497,7 +497,7 @@
 			} else {
 				if (!(flags & SUPPRESS))
 					mbp = va_arg(ap, char *);
-				bzero(&mbs, sizeof(mbs));
+				memset(&mbs, 0, sizeof(mbs));
 				while ((wi = __fgetwc_unlock(fp)) != WEOF &&
 				    width != 0 &&
 				    !iswspace(wi)) {
@@ -686,95 +686,23 @@
 			if (width == 0 || width > sizeof(buf) /
 			    sizeof(*buf) - 1)
 				width = sizeof(buf) / sizeof(*buf) - 1;
-			flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
-			for (p = buf; width; width--) {
-				c = __fgetwc_unlock(fp);
-				/*
-				 * This code mimicks the integer conversion
-				 * code, but is much simpler.
-				 */
-				switch (c) {
-
-				case '0': case '1': case '2': case '3':
-				case '4': case '5': case '6': case '7':
-				case '8': case '9':
-					flags &= ~(SIGNOK | NDIGITS);
-					goto fok;
-
-				case '+': case '-':
-					if (flags & SIGNOK) {
-						flags &= ~SIGNOK;
-						goto fok;
-					}
-					break;
-				case 'e': case 'E':
-					/* no exponent without some digits */
-					if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
-						flags =
-						    (flags & ~(EXPOK|DPTOK)) |
-						    SIGNOK | NDIGITS;
-						goto fok;
-					}
-					break;
-				default:
-					if (decimal_point == 0) {
-						bzero(&mbs, sizeof(mbs));
-						nconv = mbrtowc(&decimal_point,
-						    localeconv()->decimal_point,
-					    	    MB_CUR_MAX, &mbs);
-						if (nconv == 0 ||
-						    nconv == (size_t)-1 ||
-						    nconv == (size_t)-2)
-							decimal_point = '.';
-					}
-					if (c == decimal_point &&
-					    (flags & DPTOK)) {
-						flags &= ~(SIGNOK | DPTOK);
-						goto fok;
-					}
-					break;
-				}
-				if (c != WEOF)
-					__ungetwc(c, fp);
-				break;
-		fok:
-				*p++ = c;
-			}
-			/*
-			 * If no digits, might be missing exponent digits
-			 * (just give back the exponent) or might be missing
-			 * regular digits, but had sign and/or decimal point.
-			 */
-			if (flags & NDIGITS) {
-				if (flags & EXPOK) {
-					/* no digits at all */
-					while (p > buf)
-						__ungetwc(*--p, fp);
-					goto match_failure;
-				}
-				/* just a bad exponent (e and maybe sign) */
-				c = *--p;
-				if (c != 'e' && c != 'E') {
-					__ungetwc(c, fp);/* sign */
-					c = *--p;
-				}
-				__ungetwc(c, fp);
-			}
+			if ((width = wparsefloat(fp, buf, buf + width)) == 0)
+				goto match_failure;
 			if ((flags & SUPPRESS) == 0) {
-				*p = 0;
 				if (flags & LONGDBL) {
-					long double res = wcstold(buf, NULL);
+					long double res = wcstold(buf, &p);
 					*va_arg(ap, long double *) = res;
 				} else if (flags & LONG) {
-					double res = wcstod(buf, NULL);
+					double res = wcstod(buf, &p);
 					*va_arg(ap, double *) = res;
 				} else {
-					float res = wcstof(buf, NULL);
+					float res = wcstof(buf, &p);
 					*va_arg(ap, float *) = res;
 				}
+				if (p - buf != (ptrdiff_t)width) abort();
 				nassigned++;
 			}
-			nread += p - buf;
+			nread += width;
 			nconversions++;
 			break;
 #endif /* FLOATING_POINT */
@@ -785,6 +713,7 @@
 match_failure:
 	return (nassigned);
 }
+#pragma GCC diagnostic pop
 
 int
 vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, __va_list ap)
diff --git a/libc/upstream-openbsd/android/include/gd_qnan.h b/libc/upstream-openbsd/android/include/gd_qnan.h
index e8e907b..e5bf973 100644
--- a/libc/upstream-openbsd/android/include/gd_qnan.h
+++ b/libc/upstream-openbsd/android/include/gd_qnan.h
@@ -14,35 +14,20 @@
  * limitations under the License.
  */
 
-#if __arm__
+//
+// The values in this file came from reading the bits of <math.h>'s NAN back from a union.
+//
 
-#define f_QNAN  0xffffffff
-
-#define d_QNAN0 0xffffffff
-#define d_QNAN1 0xffffffff
-
-#elif __mips__
-
-#define f_QNAN  0x7fbfffff
-
-#define d_QNAN0 0x7ff7ffff
-#define d_QNAN1 0xffffffff
-
-#else
-
-#define f_QNAN  0xffc00000
+#define f_QNAN 0x7fc00000
 
 #define d_QNAN0 0x00000000
-#define d_QNAN1 0xfff80000
+#define d_QNAN1 0x7ff80000
 
-#endif
-
-/* long double. */
 #if __LP64__
-#define ld_QNAN0 0x7fff8000
+#define ld_QNAN0 0x00000000
 #define ld_QNAN1 0x00000000
 #define ld_QNAN2 0x00000000
-#define ld_QNAN3 0x00000000
+#define ld_QNAN3 0x7fff8000
 #else
-/* sizeof(long double) == sizeof(double), so we shouldn't be trying to use these constants. */
+// LP32 sizeof(long double) == sizeof(double), so LP32 shouldn't try to use these constants.
 #endif
diff --git a/libc/upstream-openbsd/lib/libc/locale/_wcstod.h b/libc/upstream-openbsd/lib/libc/locale/_wcstod.h
deleted file mode 100644
index ae993ad..0000000
--- a/libc/upstream-openbsd/lib/libc/locale/_wcstod.h
+++ /dev/null
@@ -1,153 +0,0 @@
-/*	$OpenBSD: _wcstod.h,v 1.2 2013/06/02 15:22:20 matthew Exp $	*/
-/* $NetBSD: wcstod.c,v 1.4 2001/10/28 12:08:43 yamt Exp $ */
-
-/*-
- * Copyright (c)1999, 2000, 2001 Citrus Project,
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- *	$Citrus: xpg4dl/FreeBSD/lib/libc/locale/wcstod.c,v 1.2 2001/09/27 16:23:57 yamt Exp $
- */
-
-/*
- * function template for wcstof, wcstod and wcstold.
- *
- * parameters:
- *	FUNCNAME : function name
- *      float_type : return type
- *      STRTOD_FUNC : conversion function
- */
-
-float_type
-FUNCNAME(const wchar_t *nptr, wchar_t **endptr)
-{
-	const wchar_t *src;
-	size_t size;
-	const wchar_t *start;
-	const wchar_t *aftersign;
-
-	/*
-	 * check length of string and call strtod
-	 */
-	src = nptr;
-
-	/* skip space first */
-	while (iswspace(*src)) {
-		src++;
-	}
-
-	/* get length of string */
-	start = src;
-	if (*src && wcschr(L"+-", *src))
-		src++;
-	aftersign = src;
-	if (wcsncasecmp(src, L"inf", 3) == 0) {
-		src += 3;
-		if (wcsncasecmp(src, L"inity", 5) == 0)
-			src += 5;
-		goto match;
-	}
-	if (wcsncasecmp(src, L"nan", 3) == 0) {
-		src += 3;
-		if (*src == L'(') {
-			size = 1;
-			while (src[size] != L'\0' && src[size] != L')')
-				size++;
-			if (src[size] == L')')
-				src += size + 1;
-		}
-		goto match;
-	}
-	size = wcsspn(src, L"0123456789");
-	src += size;
-	if (*src == L'.') {/* XXX use localeconv */
-		src++;
-		size = wcsspn(src, L"0123456789");
-		src += size;
-	}
-	if (*src && wcschr(L"Ee", *src)) {
-		src++;
-		if (*src && wcschr(L"+-", *src))
-			src++;
-		size = wcsspn(src, L"0123456789");
-		src += size;
-	}
-match:
-	size = src - start;
-
-	/*
-	 * convert to a char-string and pass it to strtod.
-	 */
-	if (src > aftersign) {
-		mbstate_t st;
-		char *buf;
-		char *end;
-		const wchar_t *s;
-		size_t size_converted;
-		float_type result;
-		size_t bufsize;
-
-		s = start;
-		memset(&st, 0, sizeof(st));
-		bufsize = wcsnrtombs(NULL, &s, size, 0, &st);
-
-		buf = malloc(bufsize + 1);
-		if (!buf) {
-			errno = ENOMEM; /* XXX */
-			goto fail;
-		}
-
-		s = start;
-		memset(&st, 0, sizeof(st));
-		size_converted = wcsnrtombs(buf, &s, size, bufsize, &st);
-		if (size_converted != bufsize) {
-			/* XXX should not happen */
-			free(buf);
-			errno = EILSEQ;
-			goto fail;
-		}
-
-		buf[bufsize] = 0;
-		result = STRTOD_FUNC(buf, &end);
-
-		if (endptr) {
-			const char *s = buf;
-			memset(&st, 0, sizeof(st));
-			size = mbsnrtowcs(NULL, &s, end - buf, 0, &st);
-
-			/* LINTED bad interface */
-			*endptr = (wchar_t*)start + size;
-		}
-
-		free(buf);
-
-		return result;
-	}
-
-fail:
-	if (endptr)
-		/* LINTED bad interface */
-		*endptr = (wchar_t*)nptr;
-
-	return 0;
-}
diff --git a/libc/upstream-openbsd/lib/libc/locale/wcstod.c b/libc/upstream-openbsd/lib/libc/locale/wcstod.c
deleted file mode 100644
index 957d0a1..0000000
--- a/libc/upstream-openbsd/lib/libc/locale/wcstod.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/*	$OpenBSD: wcstod.c,v 1.3 2009/01/13 18:18:31 kettenis Exp $	*/
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#define FUNCNAME	wcstod
-typedef double		float_type;
-#define STRTOD_FUNC	strtod
-
-#include "_wcstod.h"
diff --git a/libc/upstream-openbsd/lib/libc/locale/wcstof.c b/libc/upstream-openbsd/lib/libc/locale/wcstof.c
deleted file mode 100644
index 40d76c7..0000000
--- a/libc/upstream-openbsd/lib/libc/locale/wcstof.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/*	$OpenBSD: wcstof.c,v 1.1 2009/01/13 18:18:31 kettenis Exp $	*/
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#define FUNCNAME	wcstof
-typedef float		float_type;
-#define STRTOD_FUNC	strtof
-
-#include "_wcstod.h"
diff --git a/libc/upstream-openbsd/lib/libc/locale/wcstold.c b/libc/upstream-openbsd/lib/libc/locale/wcstold.c
deleted file mode 100644
index a642542..0000000
--- a/libc/upstream-openbsd/lib/libc/locale/wcstold.c
+++ /dev/null
@@ -1,13 +0,0 @@
-/*	$OpenBSD: wcstold.c,v 1.1 2009/01/13 18:18:31 kettenis Exp $	*/
-
-#include <errno.h>
-#include <stdlib.h>
-#include <string.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#define FUNCNAME	wcstold
-typedef long double	float_type;
-#define STRTOD_FUNC	strtold
-
-#include "_wcstod.h"