about summary refs log tree commit diff
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2011-04-03 18:44:37 -0400
committerRich Felker <dalias@aerifal.cx>2011-04-03 18:44:37 -0400
commite898a790538ba4c1b7fdb0d16e1cc4c98dd84185 (patch)
tree2c5466e01de852178243588dea075efe99676696
parentc68b26369e89ead7511ef113850035775c5d183d (diff)
downloadmusl-e898a790538ba4c1b7fdb0d16e1cc4c98dd84185.tar.gz
musl-e898a790538ba4c1b7fdb0d16e1cc4c98dd84185.tar.xz
musl-e898a790538ba4c1b7fdb0d16e1cc4c98dd84185.zip
fix various bugs in strtold:
0e10000000000000000000000000000000 was setting ERANGE

exponent char e/p was considered part of the match even if not
followed by a valid decimal value

"1e +10" was parsed as "1e+10"

hex digits were misinterpreted as 0..5 instead of 10..15
-rw-r--r--src/stdlib/strtold.c23
1 files changed, 13 insertions, 10 deletions
diff --git a/src/stdlib/strtold.c b/src/stdlib/strtold.c
index 73f2b082..ec464c15 100644
--- a/src/stdlib/strtold.c
+++ b/src/stdlib/strtold.c
@@ -2,6 +2,11 @@
 #include <errno.h>
 #include <ctype.h>
 
+static int valid_exp(const unsigned char *s)
+{
+	return isdigit(*s) || ((s[0]=='+'||s[0]=='-') && isdigit(s[1]));
+}
+
 long double strtold(const char *s1, char **p)
 {
 	const unsigned char *s = (void *)s1;
@@ -11,6 +16,7 @@ long double strtold(const char *s1, char **p)
 	int nonzero = 0;
 	int radix = '.';
 	long e;
+	int saved_errno = errno;
 
 	if (!p) p = (char **)&s1;
 
@@ -41,26 +47,23 @@ long double strtold(const char *s1, char **p)
 		/* We have a real hex float */
 		s += 2;
 		for (; isxdigit(*s); s++) {
-			x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a');
+			x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
 			if (*s!='0') nonzero=1;
 		}
 		if (*s == radix) {
 			frac = 1.0/16.0;
 			for (s++; isxdigit(*s); s++) {
-				x += frac * (isdigit(*s)?*s-'0':(*s|32)-'a');
+				x += frac * (isdigit(*s)?*s-'0':(*s|32)-'a'+10);
 				frac *= 1.0/16.0;
 				if (*s!='0') nonzero=1;
 			}
 		}
-		if ((*s|32) == 'p') {
+		if ((*s|32) == 'p' && valid_exp(s+1)) {
 			e = strtol((void *)(s+1), (void *)&s, 10);
 			for (; e>0; e--) x *= 2.0;
 			for (; e<0; e++) x *= 0.5;
 		}
-		if ((nonzero && !x) || !(1.0/x))
-			errno = ERANGE;
-		*p = (char *)s;
-		return sign ? -x : x;
+		goto finish;
 	}
 
 	/* Mantissa must be non-degenerate */
@@ -81,13 +84,13 @@ long double strtold(const char *s1, char **p)
 			if (*s!='0') nonzero=1;
 		}
 	}
-	if ((*s|32)=='e') {
+	if ((*s|32)=='e' && valid_exp(s+1)) {
 		e = strtol((void *)++s, (void *)&s, 10);
 		for (; e>0; e--) x *= 10.0;
 		for (; e<0; e++) x /= 10.0;
 	}
-	if ((nonzero && !x) || !(1.0/x))
-		errno = ERANGE;
+finish:
+	errno = ((nonzero && !x) || !(1.0/x)) ? ERANGE : saved_errno;
 	*p = (char*)s;
 	return sign ? -x : x;
 }