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"