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/abs.c4
-rw-r--r--src/stdlib/atof.c6
-rw-r--r--src/stdlib/atoi.c15
-rw-r--r--src/stdlib/atol.c16
-rw-r--r--src/stdlib/atoll.c16
-rw-r--r--src/stdlib/bsearch.c20
-rw-r--r--src/stdlib/div.c6
-rw-r--r--src/stdlib/frexp.c23
-rw-r--r--src/stdlib/frexpf.c23
-rw-r--r--src/stdlib/frexpl.c25
-rw-r--r--src/stdlib/imaxabs.c6
-rw-r--r--src/stdlib/imaxdiv.c6
-rw-r--r--src/stdlib/labs.c4
-rw-r--r--src/stdlib/ldiv.c6
-rw-r--r--src/stdlib/llabs.c4
-rw-r--r--src/stdlib/lldiv.c6
-rw-r--r--src/stdlib/qsort.c50
-rw-r--r--src/stdlib/strtod.c6
-rw-r--r--src/stdlib/strtof.c6
-rw-r--r--src/stdlib/strtoimax.c25
-rw-r--r--src/stdlib/strtol.c17
-rw-r--r--src/stdlib/strtold.c93
-rw-r--r--src/stdlib/strtoll.c17
-rw-r--r--src/stdlib/strtoul.c14
-rw-r--r--src/stdlib/strtoull.c14
-rw-r--r--src/stdlib/strtoumax.c123
-rw-r--r--src/stdlib/wcstoimax.c24
-rw-r--r--src/stdlib/wcstol.c18
-rw-r--r--src/stdlib/wcstoll.c18
-rw-r--r--src/stdlib/wcstoul.c15
-rw-r--r--src/stdlib/wcstoull.c15
-rw-r--r--src/stdlib/wcstoumax.c47
32 files changed, 688 insertions, 0 deletions
diff --git a/src/stdlib/abs.c b/src/stdlib/abs.c
new file mode 100644
index 00000000..4806d629
--- /dev/null
+++ b/src/stdlib/abs.c
@@ -0,0 +1,4 @@
+int abs(int a)
+{
+	return a>0 ? a : -a;
+}
diff --git a/src/stdlib/atof.c b/src/stdlib/atof.c
new file mode 100644
index 00000000..f7fcd826
--- /dev/null
+++ b/src/stdlib/atof.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+double atof(const char *s)
+{
+	return strtod(s, 0);
+}
diff --git a/src/stdlib/atoi.c b/src/stdlib/atoi.c
new file mode 100644
index 00000000..648b154f
--- /dev/null
+++ b/src/stdlib/atoi.c
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+#include <ctype.h>
+
+int atoi(const char *s)
+{
+	int n=0, neg=0;
+	while (isspace(*s)) s++;
+	switch (*s) {
+	case '-': neg=1;
+	case '+': s++;
+	}
+	while (isdigit(*s))
+		n = 10*n + *s++ - '0';
+	return neg ? -n : n;
+}
diff --git a/src/stdlib/atol.c b/src/stdlib/atol.c
new file mode 100644
index 00000000..9c91bba9
--- /dev/null
+++ b/src/stdlib/atol.c
@@ -0,0 +1,16 @@
+#include <stdlib.h>
+#include <ctype.h>
+
+long atol(const char *s)
+{
+	long n=0;
+	int neg=0;
+	while (isspace(*s)) s++;
+	switch (*s) {
+	case '-': neg=1;
+	case '+': s++;
+	}
+	while (isdigit(*s))
+		n = 10*n + *s++ - '0';
+	return neg ? -n : n;
+}
diff --git a/src/stdlib/atoll.c b/src/stdlib/atoll.c
new file mode 100644
index 00000000..0e03e0a1
--- /dev/null
+++ b/src/stdlib/atoll.c
@@ -0,0 +1,16 @@
+#include <stdlib.h>
+#include <ctype.h>
+
+long long atoll(const char *s)
+{
+	long long n=0;
+	int neg=0;
+	while (isspace(*s)) s++;
+	switch (*s) {
+	case '-': neg=1;
+	case '+': s++;
+	}
+	while (isdigit(*s))
+		n = 10*n + *s++ - '0';
+	return neg ? -n : n;
+}
diff --git a/src/stdlib/bsearch.c b/src/stdlib/bsearch.c
new file mode 100644
index 00000000..61d89367
--- /dev/null
+++ b/src/stdlib/bsearch.c
@@ -0,0 +1,20 @@
+#include <stdlib.h>
+
+void *bsearch(const void *key, const void *base, size_t nel, size_t width, int (*cmp)(const void *, const void *))
+{
+	void *try;
+	int sign;
+	while (nel > 0) {
+		try = (char *)base + width*(nel/2);
+		sign = cmp(key, try);
+		if (!sign) return try;
+		else if (nel == 1) break;
+		else if (sign < 0)
+			nel /= 2;
+		else {
+			base = try;
+			nel -= nel/2;
+		}
+	}
+	return NULL;
+}
diff --git a/src/stdlib/div.c b/src/stdlib/div.c
new file mode 100644
index 00000000..e42c1f14
--- /dev/null
+++ b/src/stdlib/div.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+div_t div(int num, int den)
+{
+	return (div_t){ num/den, num%den };
+}
diff --git a/src/stdlib/frexp.c b/src/stdlib/frexp.c
new file mode 100644
index 00000000..ae82cb30
--- /dev/null
+++ b/src/stdlib/frexp.c
@@ -0,0 +1,23 @@
+#include <math.h>
+#include <inttypes.h>
+
+double frexp(double x, int *e)
+{
+	union { double d; uint64_t i; } y = { x };
+	int ee = y.i>>52 & 0x7ff;
+
+	if (!ee) {
+		if (x) {
+			x = frexp(x*0x1p64, e);
+			*e -= 64;
+		} else *e = 0;
+		return x;
+	} else if (ee == 0x7ff) {
+		return x;
+	}
+
+	*e = ee - 0x3fe;
+	y.i &= 0x800fffffffffffffull;
+	y.i |= 0x3fe0000000000000ull;
+	return y.d;
+}
diff --git a/src/stdlib/frexpf.c b/src/stdlib/frexpf.c
new file mode 100644
index 00000000..ee5e910a
--- /dev/null
+++ b/src/stdlib/frexpf.c
@@ -0,0 +1,23 @@
+#include <math.h>
+#include <inttypes.h>
+
+float frexpf(float x, int *e)
+{
+	union { float f; uint32_t i; } y = { x };
+	int ee = y.i>>23 & 0xff;
+
+	if (!ee) {
+		if (x) {
+			x = frexpf(x*0x1p64, e);
+			*e -= 64;
+		} else *e = 0;
+		return x;
+	} else if (ee == 0xff) {
+		return x;
+	}
+
+	*e = ee - 0x7e;
+	y.i &= 0x807ffffful;
+	y.i |= 0x3f000000ul;
+	return y.f;
+}
diff --git a/src/stdlib/frexpl.c b/src/stdlib/frexpl.c
new file mode 100644
index 00000000..ecfff007
--- /dev/null
+++ b/src/stdlib/frexpl.c
@@ -0,0 +1,25 @@
+#include <math.h>
+#include <inttypes.h>
+
+/* This version is for 80-bit little endian long double */
+
+long double frexpl(long double x, int *e)
+{
+	union { long double ld; uint16_t hw[5]; } y = { x };
+	int ee = y.hw[4]&0x7fff;
+
+	if (!ee) {
+		if (x) {
+			x = frexpl(x*0x1p64, e);
+			*e -= 64;
+		} else *e = 0;
+		return x;
+	} else if (ee == 0x7fff) {
+		return x;
+	}
+
+	*e = ee - 0x3ffe;
+	y.hw[4] &= 0x8000;
+	y.hw[4] |= 0x3ffe;
+	return y.ld;
+}
diff --git a/src/stdlib/imaxabs.c b/src/stdlib/imaxabs.c
new file mode 100644
index 00000000..81001819
--- /dev/null
+++ b/src/stdlib/imaxabs.c
@@ -0,0 +1,6 @@
+#include <inttypes.h>
+
+intmax_t imaxabs(intmax_t a)
+{
+	return a>0 ? a : -a;
+}
diff --git a/src/stdlib/imaxdiv.c b/src/stdlib/imaxdiv.c
new file mode 100644
index 00000000..b2ce821f
--- /dev/null
+++ b/src/stdlib/imaxdiv.c
@@ -0,0 +1,6 @@
+#include <inttypes.h>
+
+imaxdiv_t imaxdiv(intmax_t num, intmax_t den)
+{
+	return (imaxdiv_t){ num/den, num%den };
+}
diff --git a/src/stdlib/labs.c b/src/stdlib/labs.c
new file mode 100644
index 00000000..675b95b8
--- /dev/null
+++ b/src/stdlib/labs.c
@@ -0,0 +1,4 @@
+long labs(long a)
+{
+	return a>0 ? a : -a;
+}
diff --git a/src/stdlib/ldiv.c b/src/stdlib/ldiv.c
new file mode 100644
index 00000000..36eb960b
--- /dev/null
+++ b/src/stdlib/ldiv.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+ldiv_t ldiv(long num, long den)
+{
+	return (ldiv_t){ num/den, num%den };
+}
diff --git a/src/stdlib/llabs.c b/src/stdlib/llabs.c
new file mode 100644
index 00000000..bec4a03d
--- /dev/null
+++ b/src/stdlib/llabs.c
@@ -0,0 +1,4 @@
+long long llabs(long long a)
+{
+	return a>0 ? a : -a;
+}
diff --git a/src/stdlib/lldiv.c b/src/stdlib/lldiv.c
new file mode 100644
index 00000000..7aaf7a0e
--- /dev/null
+++ b/src/stdlib/lldiv.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+lldiv_t lldiv(long long num, long long den)
+{
+	return (lldiv_t){ num/den, num%den };
+}
diff --git a/src/stdlib/qsort.c b/src/stdlib/qsort.c
new file mode 100644
index 00000000..f5bf3d02
--- /dev/null
+++ b/src/stdlib/qsort.c
@@ -0,0 +1,50 @@
+#include <stdlib.h>
+#include <string.h>
+
+/* A simple heap sort implementation.. only in-place O(nlogn) sort I know. */
+
+#define MIN(a, b) ((a)<(b) ? (a) : (b))
+
+static void swap(char *a, char *b, size_t len)
+{
+	char tmp[256];
+	size_t l;
+	while (len) {
+		l = MIN(sizeof tmp, len);
+		memcpy(tmp, a, l);
+		memcpy(a, b, l);
+		memcpy(b, tmp, l);
+		a += l;
+		b += l;
+		len -= l;
+	}
+}
+
+static void sift(char *base, size_t root, size_t nel, size_t width, int (*cmp)(const void *, const void *))
+{
+	size_t max;
+
+	while (2*root <= nel) {
+		max = 2*root;
+		if (max < nel && cmp(base+max*width, base+(max+1)*width) < 0)
+			max++;
+		if (cmp(base+root*width, base+max*width) < 0) {
+			swap(base+root*width, base+max*width, width);
+			root = max;
+		} else break;
+	}
+}
+
+void qsort(void *_base, size_t nel, size_t width, int (*cmp)(const void *, const void *))
+{
+	char *base = _base;
+	size_t i;
+
+	if (!nel) return;
+	for (i=(nel+1)/2; i; i--)
+		sift(base, i-1, nel-1, width, cmp);
+	for (i=nel-1; i; i--) {
+		swap(base, base+i*width, width);
+		sift(base, 0, i-1, width, cmp);
+	}
+}
diff --git a/src/stdlib/strtod.c b/src/stdlib/strtod.c
new file mode 100644
index 00000000..388058fe
--- /dev/null
+++ b/src/stdlib/strtod.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+double strtod(const char *s, char **p)
+{
+	return strtold(s, p);
+}
diff --git a/src/stdlib/strtof.c b/src/stdlib/strtof.c
new file mode 100644
index 00000000..07b32df4
--- /dev/null
+++ b/src/stdlib/strtof.c
@@ -0,0 +1,6 @@
+#include <stdlib.h>
+
+float strtof(const char *s, char **p)
+{
+	return strtold(s, p);
+}
diff --git a/src/stdlib/strtoimax.c b/src/stdlib/strtoimax.c
new file mode 100644
index 00000000..19691091
--- /dev/null
+++ b/src/stdlib/strtoimax.c
@@ -0,0 +1,25 @@
+#include <inttypes.h>
+#include <errno.h>
+#include <ctype.h>
+
+intmax_t strtoimax(const char *s1, char **p, int base)
+{
+	const unsigned char *s = s1;
+	int sign = 0;
+	uintmax_t x;
+
+	/* Initial whitespace */
+	for (; isspace(*s); s++);
+
+	/* Optional sign */
+	if (*s == '-') sign = *s++;
+	else if (*s == '+') s++;
+
+	x = strtoumax(s, p, base);
+	if (x > INTMAX_MAX) {
+		if (!sign || -x != INTMAX_MIN)
+			errno = ERANGE;
+		return sign ? INTMAX_MIN : INTMAX_MAX;
+	}
+	return sign ? -x : x;
+}
diff --git a/src/stdlib/strtol.c b/src/stdlib/strtol.c
new file mode 100644
index 00000000..ace820af
--- /dev/null
+++ b/src/stdlib/strtol.c
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+
+long strtol(const char *s, char **p, int base)
+{
+	intmax_t x = strtoimax(s, p, base);
+	if (x > LONG_MAX) {
+		errno = ERANGE;
+		return LONG_MAX;
+	} else if (x < LONG_MIN) {
+		errno = ERANGE;
+		return LONG_MIN;
+	}
+	return x;
+}
diff --git a/src/stdlib/strtold.c b/src/stdlib/strtold.c
new file mode 100644
index 00000000..54f80469
--- /dev/null
+++ b/src/stdlib/strtold.c
@@ -0,0 +1,93 @@
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+
+long double strtold(const char *s1, char **p)
+{
+	const unsigned char *s = s1;
+	long double x = 0;
+	long double frac;
+	int sign = 0;
+	int nonzero = 0;
+	int radix = '.';
+	long e;
+
+	if (!p) p = (char **)&s1;
+
+	/* Initial whitespace */
+	for (; isspace(*s); s++);
+
+	/* Optional sign */
+	if (*s == '-') sign = *s++;
+	else if (*s == '+') s++;
+
+	/* Handle infinities and NaNs. */
+	if ((s[0]|32)=='i' && (s[1]|32)=='n' && (s[2]|32)=='f') {
+		*p = (char *)s + 3;
+		return sign ? -1.0/0.0 : 1.0/0.0;
+	} else if ((s[0]|32)=='n' && (s[1]|32)=='a' && (s[2]|32)=='n') {
+		*p = (char *)s + 3;
+		return 0.0/0.0;
+	}
+
+	/* Possible hex float */
+	if (s[0]=='0' && (s[1]|32)=='x') {
+		/* Mantissa must be non-degenerate */
+		if (!isxdigit(s[2]) && (s[2]!=radix || !isxdigit(s[3]))) {
+			/* Decimal float 0, 'x' extraneous */
+			*p = (char *)++s;
+			return 0;
+		}
+		/* We have a real hex float */
+		s += 2;
+		for (; isxdigit(*s); s++) {
+			x = 16*x + (isdigit(*s)?*s-'0':(*s|32)-'a');
+			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');
+				frac *= 1.0/16.0;
+				if (*s!='0') nonzero=1;
+			}
+		}
+		if ((*s|32) == 'p') {
+			e = strtol(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;
+	}
+
+	/* Mantissa must be non-degenerate */
+	if (!isdigit(s[0]) && (s[0]!=radix || !isdigit(s[1]))) {
+		*p = (char *)s1;
+		return 0;
+	}
+
+	for (; isdigit(*s); s++) {
+		x = 10*x + *s-'0';
+		if (*s!='0') nonzero=1;
+	}
+	if (*s == radix) {
+		frac = 10.0;
+		for (s++; isdigit(*s); s++) {
+			x += (*s-'0') / frac;
+			frac *= 10.0;
+			if (*s!='0') nonzero=1;
+		}
+	}
+	if ((*s|32)=='e') {
+		e = strtol(++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;
+	*p = (char*)s;
+	return sign ? -x : x;
+}
diff --git a/src/stdlib/strtoll.c b/src/stdlib/strtoll.c
new file mode 100644
index 00000000..9ab66fd9
--- /dev/null
+++ b/src/stdlib/strtoll.c
@@ -0,0 +1,17 @@
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+
+long long strtoll(const char *s, char **p, int base)
+{
+	intmax_t x = strtoimax(s, p, base);
+	if (x > LLONG_MAX) {
+		errno = ERANGE;
+		return LLONG_MAX;
+	} else if (x < LLONG_MIN) {
+		errno = ERANGE;
+		return LLONG_MIN;
+	}
+	return x;
+}
diff --git a/src/stdlib/strtoul.c b/src/stdlib/strtoul.c
new file mode 100644
index 00000000..951d5e8c
--- /dev/null
+++ b/src/stdlib/strtoul.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+
+unsigned long strtoul(const char *s, char **p, int base)
+{
+	uintmax_t x = strtoumax(s, p, base);
+	if (x > ULONG_MAX) {
+		errno = ERANGE;
+		return ULONG_MAX;
+	}
+	return x;
+}
diff --git a/src/stdlib/strtoull.c b/src/stdlib/strtoull.c
new file mode 100644
index 00000000..20aa7bde
--- /dev/null
+++ b/src/stdlib/strtoull.c
@@ -0,0 +1,14 @@
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+
+unsigned long long strtoull(const char *s, char **p, int base)
+{
+	uintmax_t x = strtoumax(s, p, base);
+	if (x > ULLONG_MAX) {
+		errno = ERANGE;
+		return ULLONG_MAX;
+	}
+	return x;
+}
diff --git a/src/stdlib/strtoumax.c b/src/stdlib/strtoumax.c
new file mode 100644
index 00000000..a529f6e8
--- /dev/null
+++ b/src/stdlib/strtoumax.c
@@ -0,0 +1,123 @@
+#include <inttypes.h>
+#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,
+};
+
+uintmax_t strtoumax(const char *s1, char **p, int base)
+{
+	const unsigned char *s = 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++;
+
+	/* 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 ((unsigned)base-2 > 36-2 || digits[*s]>=base) {
+		*p = (char *)s1;
+		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];
+		}
+	}
+
+	*p = (char *)s;
+	return sign ? -x : x;
+
+overflow:
+	for (; digits[*s] < base; s++);
+	*p = (char *)s;
+	errno = ERANGE;
+	return UINTMAX_MAX;
+}
diff --git a/src/stdlib/wcstoimax.c b/src/stdlib/wcstoimax.c
new file mode 100644
index 00000000..861fcb54
--- /dev/null
+++ b/src/stdlib/wcstoimax.c
@@ -0,0 +1,24 @@
+#include <wchar.h>
+#include <inttypes.h>
+#include <errno.h>
+
+intmax_t wcstoimax(const wchar_t *s, wchar_t **p, int base)
+{
+	int sign = 0;
+	uintmax_t x;
+
+	/* Initial whitespace */
+	for (; iswspace(*s); s++);
+
+	/* Optional sign */
+	if (*s == '-') sign = *s++;
+	else if (*s == '+') s++;
+
+	x = wcstoumax(s, p, base);
+	if (x > INTMAX_MAX) {
+		if (!sign || -x != INTMAX_MIN)
+			errno = ERANGE;
+		return sign ? INTMAX_MIN : INTMAX_MAX;
+	}
+	return sign ? -x : x;
+}
diff --git a/src/stdlib/wcstol.c b/src/stdlib/wcstol.c
new file mode 100644
index 00000000..aad62e5b
--- /dev/null
+++ b/src/stdlib/wcstol.c
@@ -0,0 +1,18 @@
+#include <wchar.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+
+long wcstol(const wchar_t *s, wchar_t **p, int base)
+{
+	intmax_t x = wcstoimax(s, p, base);
+	if (x > LONG_MAX) {
+		errno = ERANGE;
+		return LONG_MAX;
+	} else if (x < LONG_MIN) {
+		errno = ERANGE;
+		return LONG_MIN;
+	}
+	return x;
+}
diff --git a/src/stdlib/wcstoll.c b/src/stdlib/wcstoll.c
new file mode 100644
index 00000000..ddfea74b
--- /dev/null
+++ b/src/stdlib/wcstoll.c
@@ -0,0 +1,18 @@
+#include <wchar.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+
+long long wcstoll(const wchar_t *s, wchar_t **p, int base)
+{
+	intmax_t x = wcstoimax(s, p, base);
+	if (x > LLONG_MAX) {
+		errno = ERANGE;
+		return LLONG_MAX;
+	} else if (x < LLONG_MIN) {
+		errno = ERANGE;
+		return LLONG_MIN;
+	}
+	return x;
+}
diff --git a/src/stdlib/wcstoul.c b/src/stdlib/wcstoul.c
new file mode 100644
index 00000000..e39faafe
--- /dev/null
+++ b/src/stdlib/wcstoul.c
@@ -0,0 +1,15 @@
+#include <wchar.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+
+unsigned long wcstoul(const wchar_t *s, wchar_t **p, int base)
+{
+	uintmax_t x = wcstoumax(s, p, base);
+	if (x > ULONG_MAX) {
+		errno = ERANGE;
+		return ULONG_MAX;
+	}
+	return x;
+}
diff --git a/src/stdlib/wcstoull.c b/src/stdlib/wcstoull.c
new file mode 100644
index 00000000..e324dfb2
--- /dev/null
+++ b/src/stdlib/wcstoull.c
@@ -0,0 +1,15 @@
+#include <wchar.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <limits.h>
+
+unsigned long long wcstoull(const wchar_t *s, wchar_t **p, int base)
+{
+	uintmax_t x = wcstoumax(s, p, base);
+	if (x > ULLONG_MAX) {
+		errno = ERANGE;
+		return ULLONG_MAX;
+	}
+	return x;
+}
diff --git a/src/stdlib/wcstoumax.c b/src/stdlib/wcstoumax.c
new file mode 100644
index 00000000..a8f4680f
--- /dev/null
+++ b/src/stdlib/wcstoumax.c
@@ -0,0 +1,47 @@
+#include <wchar.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <errno.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;
+
+	if (!p) p = (wchar_t **)&s;
+
+	if (base && (unsigned)base-2 > 36-2) {
+		*p = (wchar_t *)s;
+		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) < 0) return 0;
+	buf[sizeof buf-1]=0;
+
+	/* 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++);
+		*p = (wchar_t *)s;
+	}
+
+	return strtoumax(buf, 0, base);
+}