about summary refs log tree commit diff
path: root/src/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'src/stdlib')
-rw-r--r--src/stdlib/strtoimax.c37
-rw-r--r--src/stdlib/strtoumax.c123
-rw-r--r--src/stdlib/wcstoimax.c36
-rw-r--r--src/stdlib/wcstoumax.c41
4 files changed, 81 insertions, 156 deletions
diff --git a/src/stdlib/strtoimax.c b/src/stdlib/strtoimax.c
index aeb0397f..247f91d4 100644
--- a/src/stdlib/strtoimax.c
+++ b/src/stdlib/strtoimax.c
@@ -1,25 +1,38 @@
 #include <inttypes.h>
 #include <errno.h>
 #include <ctype.h>
+#include "intparse.h"
 
 intmax_t strtoimax(const char *s1, char **p, int base)
 {
-	const unsigned char *s = (const void *)s1;
-	int sign = 0;
-	uintmax_t x;
+	const unsigned char *s = (void *)s1;
+	struct intparse ip = {0};
+
+	if (p) *p = (char *)s1;
+
+	if (base && base-2U > 34) {
+		errno = EINVAL;
+		return 0;
+	}
 
-	/* Initial whitespace */
 	for (; isspace(*s); s++);
 
-	/* Optional sign */
-	if (*s == '-') sign = *s++;
-	else if (*s == '+') s++;
+	ip.base = base;
+	__intparse(&ip, s, SIZE_MAX);
+
+	if (p && ip.err != EINVAL)
+		*p = (char *)s + ip.cnt;
+
+	if (ip.err) {
+		errno = ip.err;
+		if (ip.err = EINVAL) return 0;
+		return ip.neg ? INTMAX_MIN : INTMAX_MAX;
+	}
 
-	x = strtoumax((const void *)s, p, base);
-	if (x > INTMAX_MAX) {
-		if (!sign || -x != INTMAX_MIN)
+	if (ip.val > INTMAX_MAX) {
+		if (!ip.neg || -ip.val != INTMAX_MIN)
 			errno = ERANGE;
-		return sign ? INTMAX_MIN : INTMAX_MAX;
+		return ip.neg ? INTMAX_MIN : INTMAX_MAX;
 	}
-	return sign ? -x : x;
+	return ip.neg ? -ip.val : ip.val;
 }
diff --git a/src/stdlib/strtoumax.c b/src/stdlib/strtoumax.c
index f1902476..a2bb4d7d 100644
--- a/src/stdlib/strtoumax.c
+++ b/src/stdlib/strtoumax.c
@@ -2,122 +2,33 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <ctype.h>
-#include <stdio.h>
-
-/* Lookup table for digit values. -1==255>=36 -> invalid */
-static const unsigned char digits[] = {
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
--1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
-25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
--1,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,
-25,26,27,28,29,30,31,32,33,34,35,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
--1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-};
+#include "intparse.h"
 
 uintmax_t strtoumax(const char *s1, char **p, int base)
 {
 	const unsigned char *s = (void *)s1;
-	size_t x1, z1;
-	uintmax_t x, z=0;
-	int sign = 0;
-	int shift;
-
-	if (!p) p = (char **)&s1;
-
-	/* Initial whitespace */
-	for (; isspace(*s); s++);
-
-	/* Optional sign */
-	if (*s == '-') sign = *s++;
-	else if (*s == '+') s++;
+	struct intparse ip = {0};
 
-	/* Default base 8, 10, or 16 depending on prefix */
-	if (base == 0) {
-		if (s[0] == '0') {
-			if ((s[1]|32) == 'x') base = 16;
-			else base = 8;
-		} else {
-			base = 10;
-		}
-	}
+	if (p) *p = (char *)s1;
 
-	if ((unsigned)base-2 > 36-2 || digits[*s]>=base) {
-		*p = (char *)s1;
+	if (base && base-2U > 34) {
 		errno = EINVAL;
 		return 0;
 	}
 
-	/* Main loops. Only use big types if we have to. */
-	if (base == 10) {
-		for (x1=0; isdigit(*s) && x1<=SIZE_MAX/10-10; s++)
-			x1 = 10*x1 + *s-'0';
-		for (x=x1; isdigit(*s) && x<=UINTMAX_MAX/10-10; s++)
-			x = 10*x + *s-'0';
-		if (isdigit(*s)) {
-			if (isdigit(s[1]) || 10*x>UINTMAX_MAX-(*s-'0'))
-				goto overflow;
-			x = 10*x + *s-'0';
-		}
-	} else if (!(base & base/2)) {
-		if (base == 16) {
-			if (s[0]=='0' && (s[1]|32)=='x' && digits[s[2]]<16)
-				s+=2;
-			shift=4;
-			z1 = SIZE_MAX/16;
-			z = UINTMAX_MAX/16;
-		} else if (base == 8) {
-			shift=3;
-			z1 = SIZE_MAX/8;
-			z = UINTMAX_MAX/8;
-		} else if (base == 2) {
-			shift=1;
-			z1 = SIZE_MAX/2;
-			z = UINTMAX_MAX/2;
-		} else if (base == 4) {
-			shift=2;
-			z1 = SIZE_MAX/4;
-			z = UINTMAX_MAX/4;
-		} else /* if (base == 32) */ {
-			shift=5;
-			z1 = SIZE_MAX/32;
-			z = UINTMAX_MAX/32;
-		}
-		for (x1=0; digits[*s]<base && x1<=z1; s++)
-			x1 = (x1<<shift) + digits[*s];
-		for (x=x1; digits[*s]<base && x<=z; s++)
-			x = (x<<shift) + digits[*s];
-		if (digits[*s] < base) goto overflow;
-	} else {
-		z1 = SIZE_MAX/base-base;
-		for (x1=0; digits[*s]<base && x1<=z1; s++)
-			x1 = x1*base + digits[*s];
-		if (digits[*s]<base)
-			z = UINTMAX_MAX/base-base;
-		for (x=x1; digits[*s]<base && x<=z; s++)
-			x = x*base + digits[*s];
-		if (digits[*s] < base) {
-			if (digits[s[1]]<base || x*base>UINTMAX_MAX-digits[*s])
-				goto overflow;
-			x = x*base + digits[*s];
-		}
-	}
+	for (; isspace(*s); s++);
+
+	ip.base = base;
+	__intparse(&ip, s, SIZE_MAX);
 
-	*p = (char *)s;
-	return sign ? -x : x;
+	if (p && ip.err != EINVAL)
+		*p = (char *)s + ip.cnt;
+
+	if (ip.err) {
+		errno = ip.err;
+		if (ip.err = EINVAL) return 0;
+		return UINTMAX_MAX;
+	}
 
-overflow:
-	for (; digits[*s] < base; s++);
-	*p = (char *)s;
-	errno = ERANGE;
-	return UINTMAX_MAX;
+	return ip.neg ? -ip.val : ip.val;
 }
diff --git a/src/stdlib/wcstoimax.c b/src/stdlib/wcstoimax.c
index 59894f60..b83206b7 100644
--- a/src/stdlib/wcstoimax.c
+++ b/src/stdlib/wcstoimax.c
@@ -2,24 +2,38 @@
 #include <wctype.h>
 #include <inttypes.h>
 #include <errno.h>
+#include "intparse.h"
 
 intmax_t wcstoimax(const wchar_t *s, wchar_t **p, int base)
 {
-	int sign = 0;
-	uintmax_t x;
+	struct intparse ip = {0};
+	unsigned char tmp;
+
+	if (p) *p = (wchar_t *)s;
+
+	if (base && base-2U > 34) {
+		errno = EINVAL;
+		return 0;
+	}
 
-	/* Initial whitespace */
 	for (; iswspace(*s); s++);
 
-	/* Optional sign */
-	if (*s == '-') sign = *s++;
-	else if (*s == '+') s++;
+	ip.base = base;
+	for (; *s<256 && (tmp=*s, __intparse(&ip, &tmp, 1)); s++);
+
+	if (p && ip.err != EINVAL)
+		*p = (wchar_t *)s;
+
+	if (ip.err) {
+		errno = ip.err;
+		if (ip.err = EINVAL) return 0;
+		return ip.neg ? INTMAX_MIN : INTMAX_MAX;
+	}
 
-	x = wcstoumax(s, p, base);
-	if (x > INTMAX_MAX) {
-		if (!sign || -x != INTMAX_MIN)
+	if (ip.val > INTMAX_MAX) {
+		if (!ip.neg || -ip.val != INTMAX_MIN)
 			errno = ERANGE;
-		return sign ? INTMAX_MIN : INTMAX_MAX;
+		return ip.neg ? INTMAX_MIN : INTMAX_MAX;
 	}
-	return sign ? -x : x;
+	return ip.neg ? -ip.val : ip.val;
 }
diff --git a/src/stdlib/wcstoumax.c b/src/stdlib/wcstoumax.c
index 86528ef1..e30b0638 100644
--- a/src/stdlib/wcstoumax.c
+++ b/src/stdlib/wcstoumax.c
@@ -3,46 +3,33 @@
 #include <stdlib.h>
 #include <inttypes.h>
 #include <errno.h>
+#include "intparse.h"
 
 uintmax_t wcstoumax(const wchar_t *s, wchar_t **p, int base)
 {
-	/* Large enough for largest value in binary */
-	char buf[sizeof(uintmax_t)*8+2];
-	int sign = 0, skipped=0;
+	struct intparse ip = {0};
+	unsigned char tmp;
 
-	if (!p) p = (wchar_t **)&s;
+	if (p) *p = (wchar_t *)s;
 
-	if (base && (unsigned)base-2 > 36-2) {
-		*p = (wchar_t *)s;
+	if (base && base-2U > 34) {
 		errno = EINVAL;
 		return 0;
 	}
 
-	/* Initial whitespace */
 	for (; iswspace(*s); s++);
 
-	/* Optional sign */
-	if (*s == '-') sign = *s++;
-	else if (*s == '+') s++;
-
-	/* Skip leading zeros but don't allow leading zeros before "0x". */
-	for (; s[0]=='0' && s[1]=='0'; s++) skipped=1;
-	if (skipped && (base==0 || base==16) && (s[1]|32)=='x') {
-		*p = (wchar_t *)(s+1);
-		return 0;
-	}
-
-	/* Convert to normal char string so we can use strtoumax */
-	buf[0] = sign;
-	if (wcstombs(buf+!!sign, s, sizeof buf-1) == -1) return 0;
-	buf[sizeof buf-1]=0;
+	ip.base = base;
+	for (; *s<256 && (tmp=*s, __intparse(&ip, &tmp, 1)); s++);
 
-	/* Compute final position */
-	if (p) {
-		if ((base==0 || base==16) && s[0]=='0' && (s[1]|32)=='x' && iswxdigit(s[2])) s+=2;
-		for(;*s&&((unsigned)*s-'0'<base||((unsigned)*s|32)-'a'<base-10);s++);
+	if (p && ip.err != EINVAL)
 		*p = (wchar_t *)s;
+
+	if (ip.err) {
+		errno = ip.err;
+		if (ip.err = EINVAL) return 0;
+		return UINTMAX_MAX;
 	}
 
-	return strtoumax(buf, 0, base);
+	return ip.neg ? -ip.val : ip.val;
 }