Index: uspace/lib/c/generic/ctype.c
===================================================================
--- uspace/lib/c/generic/ctype.c	(revision a4cf3126a037f037c410df0a162e56534e940f0f)
+++ uspace/lib/c/generic/ctype.c	(revision 5acf533f5ecb8a32938f2f277f8c704de0c8dce2)
@@ -90,5 +90,4 @@
 	case '\v':
 		return 1;
-		break;
 	default:
 		return 0;
Index: uspace/lib/c/generic/str.c
===================================================================
--- uspace/lib/c/generic/str.c	(revision a4cf3126a037f037c410df0a162e56534e940f0f)
+++ uspace/lib/c/generic/str.c	(revision 5acf533f5ecb8a32938f2f277f8c704de0c8dce2)
@@ -1471,408 +1471,4 @@
 	*end = '\0';
 	return start;
-}
-
-/** Convert string to uint64_t (internal variant).
- *
- * @param nptr   Pointer to string.
- * @param endptr Pointer to the first invalid character is stored here.
- * @param base   Zero or number between 2 and 36 inclusive.
- * @param neg    Indication of unary minus is stored here.
- * @apram result Result of the conversion.
- *
- * @return EOK if conversion was successful.
- *
- */
-static errno_t str_uint(const char *nptr, char **endptr, unsigned int base,
-    bool *neg, uint64_t *result)
-{
-	assert(endptr != NULL);
-	assert(neg != NULL);
-	assert(result != NULL);
-
-	*neg = false;
-	const char *str = nptr;
-
-	/* Ignore leading whitespace */
-	while (isspace(*str))
-		str++;
-
-	if (*str == '-') {
-		*neg = true;
-		str++;
-	} else if (*str == '+')
-		str++;
-
-	if (base == 0) {
-		/* Decode base if not specified */
-		base = 10;
-
-		if (*str == '0') {
-			base = 8;
-			str++;
-
-			switch (*str) {
-			case 'b':
-			case 'B':
-				base = 2;
-				str++;
-				break;
-			case 'o':
-			case 'O':
-				base = 8;
-				str++;
-				break;
-			case 'd':
-			case 'D':
-			case 't':
-			case 'T':
-				base = 10;
-				str++;
-				break;
-			case 'x':
-			case 'X':
-				base = 16;
-				str++;
-				break;
-			default:
-				str--;
-			}
-		}
-	} else {
-		/* Check base range */
-		if ((base < 2) || (base > 36)) {
-			*endptr = (char *) str;
-			return EINVAL;
-		}
-	}
-
-	*result = 0;
-	const char *startstr = str;
-
-	while (*str != 0) {
-		unsigned int digit;
-
-		if ((*str >= 'a') && (*str <= 'z'))
-			digit = *str - 'a' + 10;
-		else if ((*str >= 'A') && (*str <= 'Z'))
-			digit = *str - 'A' + 10;
-		else if ((*str >= '0') && (*str <= '9'))
-			digit = *str - '0';
-		else
-			break;
-
-		if (digit >= base)
-			break;
-
-		uint64_t prev = *result;
-		*result = (*result) * base + digit;
-
-		if (*result < prev) {
-			/* Overflow */
-			*endptr = (char *) str;
-			return EOVERFLOW;
-		}
-
-		str++;
-	}
-
-	if (str == startstr) {
-		/*
-		 * No digits were decoded => first invalid character is
-		 * the first character of the string.
-		 */
-		str = nptr;
-	}
-
-	*endptr = (char *) str;
-
-	if (str == nptr)
-		return EINVAL;
-
-	return EOK;
-}
-
-/** Convert string to uint8_t.
- *
- * @param nptr   Pointer to string.
- * @param endptr If not NULL, pointer to the first invalid character
- *               is stored here.
- * @param base   Zero or number between 2 and 36 inclusive.
- * @param strict Do not allow any trailing characters.
- * @param result Result of the conversion.
- *
- * @return EOK if conversion was successful.
- *
- */
-errno_t str_uint8_t(const char *nptr, const char **endptr, unsigned int base,
-    bool strict, uint8_t *result)
-{
-	assert(result != NULL);
-
-	bool neg;
-	char *lendptr;
-	uint64_t res;
-	errno_t ret = str_uint(nptr, &lendptr, base, &neg, &res);
-
-	if (endptr != NULL)
-		*endptr = (char *) lendptr;
-
-	if (ret != EOK)
-		return ret;
-
-	/* Do not allow negative values */
-	if (neg)
-		return EINVAL;
-
-	/*
-	 * Check whether we are at the end of
-	 * the string in strict mode
-	 */
-	if ((strict) && (*lendptr != 0))
-		return EINVAL;
-
-	/* Check for overflow */
-	uint8_t _res = (uint8_t) res;
-	if (_res != res)
-		return EOVERFLOW;
-
-	*result = _res;
-
-	return EOK;
-}
-
-/** Convert string to uint16_t.
- *
- * @param nptr   Pointer to string.
- * @param endptr If not NULL, pointer to the first invalid character
- *               is stored here.
- * @param base   Zero or number between 2 and 36 inclusive.
- * @param strict Do not allow any trailing characters.
- * @param result Result of the conversion.
- *
- * @return EOK if conversion was successful.
- *
- */
-errno_t str_uint16_t(const char *nptr, const char **endptr, unsigned int base,
-    bool strict, uint16_t *result)
-{
-	assert(result != NULL);
-
-	bool neg;
-	char *lendptr;
-	uint64_t res;
-	errno_t ret = str_uint(nptr, &lendptr, base, &neg, &res);
-
-	if (endptr != NULL)
-		*endptr = (char *) lendptr;
-
-	if (ret != EOK)
-		return ret;
-
-	/* Do not allow negative values */
-	if (neg)
-		return EINVAL;
-
-	/*
-	 * Check whether we are at the end of
-	 * the string in strict mode
-	 */
-	if ((strict) && (*lendptr != 0))
-		return EINVAL;
-
-	/* Check for overflow */
-	uint16_t _res = (uint16_t) res;
-	if (_res != res)
-		return EOVERFLOW;
-
-	*result = _res;
-
-	return EOK;
-}
-
-/** Convert string to uint32_t.
- *
- * @param nptr   Pointer to string.
- * @param endptr If not NULL, pointer to the first invalid character
- *               is stored here.
- * @param base   Zero or number between 2 and 36 inclusive.
- * @param strict Do not allow any trailing characters.
- * @param result Result of the conversion.
- *
- * @return EOK if conversion was successful.
- *
- */
-errno_t str_uint32_t(const char *nptr, const char **endptr, unsigned int base,
-    bool strict, uint32_t *result)
-{
-	assert(result != NULL);
-
-	bool neg;
-	char *lendptr;
-	uint64_t res;
-	errno_t ret = str_uint(nptr, &lendptr, base, &neg, &res);
-
-	if (endptr != NULL)
-		*endptr = (char *) lendptr;
-
-	if (ret != EOK)
-		return ret;
-
-	/* Do not allow negative values */
-	if (neg)
-		return EINVAL;
-
-	/*
-	 * Check whether we are at the end of
-	 * the string in strict mode
-	 */
-	if ((strict) && (*lendptr != 0))
-		return EINVAL;
-
-	/* Check for overflow */
-	uint32_t _res = (uint32_t) res;
-	if (_res != res)
-		return EOVERFLOW;
-
-	*result = _res;
-
-	return EOK;
-}
-
-/** Convert string to uint64_t.
- *
- * @param nptr   Pointer to string.
- * @param endptr If not NULL, pointer to the first invalid character
- *               is stored here.
- * @param base   Zero or number between 2 and 36 inclusive.
- * @param strict Do not allow any trailing characters.
- * @param result Result of the conversion.
- *
- * @return EOK if conversion was successful.
- *
- */
-errno_t str_uint64_t(const char *nptr, const char **endptr, unsigned int base,
-    bool strict, uint64_t *result)
-{
-	assert(result != NULL);
-
-	bool neg;
-	char *lendptr;
-	errno_t ret = str_uint(nptr, &lendptr, base, &neg, result);
-
-	if (endptr != NULL)
-		*endptr = (char *) lendptr;
-
-	if (ret != EOK)
-		return ret;
-
-	/* Do not allow negative values */
-	if (neg)
-		return EINVAL;
-
-	/*
-	 * Check whether we are at the end of
-	 * the string in strict mode
-	 */
-	if ((strict) && (*lendptr != 0))
-		return EINVAL;
-
-	return EOK;
-}
-
-/** Convert string to int64_t.
- *
- * @param nptr   Pointer to string.
- * @param endptr If not NULL, pointer to the first invalid character
- *               is stored here.
- * @param base   Zero or number between 2 and 36 inclusive.
- * @param strict Do not allow any trailing characters.
- * @param result Result of the conversion.
- *
- * @return EOK if conversion was successful.
- *
- */
-int str_int64_t(const char *nptr, const char **endptr, unsigned int base,
-    bool strict, int64_t *result)
-{
-	assert(result != NULL);
-
-	bool neg;
-	char *lendptr;
-	uint64_t unsigned_result;
-	int ret = str_uint(nptr, &lendptr, base, &neg, &unsigned_result);
-
-	if (endptr != NULL)
-		*endptr = (char *) lendptr;
-
-	if (ret != EOK)
-		return ret;
-
-	/* Do not allow negative values */
-	if (neg) {
-		if (unsigned_result == UINT64_MAX)
-			return EINVAL;
-
-		*result = -(int64_t) unsigned_result;
-	} else
-		*result = unsigned_result;
-
-	/*
-	 * Check whether we are at the end of
-	 * the string in strict mode
-	 */
-	if ((strict) && (*lendptr != 0))
-		return EINVAL;
-
-	return EOK;
-}
-
-/** Convert string to size_t.
- *
- * @param nptr   Pointer to string.
- * @param endptr If not NULL, pointer to the first invalid character
- *               is stored here.
- * @param base   Zero or number between 2 and 36 inclusive.
- * @param strict Do not allow any trailing characters.
- * @param result Result of the conversion.
- *
- * @return EOK if conversion was successful.
- *
- */
-errno_t str_size_t(const char *nptr, const char **endptr, unsigned int base,
-    bool strict, size_t *result)
-{
-	assert(result != NULL);
-
-	bool neg;
-	char *lendptr;
-	uint64_t res;
-	errno_t ret = str_uint(nptr, &lendptr, base, &neg, &res);
-
-	if (endptr != NULL)
-		*endptr = (char *) lendptr;
-
-	if (ret != EOK)
-		return ret;
-
-	/* Do not allow negative values */
-	if (neg)
-		return EINVAL;
-
-	/*
-	 * Check whether we are at the end of
-	 * the string in strict mode
-	 */
-	if ((strict) && (*lendptr != 0))
-		return EINVAL;
-
-	/* Check for overflow */
-	size_t _res = (size_t) res;
-	if (_res != res)
-		return EOVERFLOW;
-
-	*result = _res;
-
-	return EOK;
 }
 
Index: uspace/lib/c/generic/strtol.c
===================================================================
--- uspace/lib/c/generic/strtol.c	(revision a4cf3126a037f037c410df0a162e56534e940f0f)
+++ uspace/lib/c/generic/strtol.c	(revision 5acf533f5ecb8a32938f2f277f8c704de0c8dce2)
@@ -44,6 +44,11 @@
 #include <stdbool.h>
 #include <stdlib.h>
-
-// TODO: unit tests
+#include <str.h>
+
+// FIXME: The original HelenOS functions return EOVERFLOW instead
+//        of ERANGE. It's a pointless distinction from standard functions,
+//        so we should change that. Beware the callers though.
+
+// TODO: more unit tests
 
 static inline int _digit_value(int c)
@@ -69,10 +74,57 @@
 }
 
+static inline int _prefixbase(const char *restrict *nptrptr, bool nonstd)
+{
+	const char *nptr = *nptrptr;
+
+	if (nptr[0] != '0')
+		return 10;
+
+	if (nptr[1] == 'x' || nptr[1] == 'X') {
+		if (_digit_value(nptr[2]) < 16) {
+			*nptrptr += 2;
+			return 16;
+		}
+	}
+
+	if (nonstd) {
+		switch (nptr[1]) {
+		case 'b':
+		case 'B':
+			if (_digit_value(nptr[2]) < 2) {
+				*nptrptr += 2;
+				return 2;
+			}
+			break;
+		case 'o':
+		case 'O':
+			if (_digit_value(nptr[2]) < 8) {
+				*nptrptr += 2;
+				return 8;
+			}
+			break;
+		case 'd':
+		case 'D':
+		case 't':
+		case 'T':
+			if (_digit_value(nptr[2]) < 10) {
+				*nptrptr += 2;
+				return 10;
+			}
+			break;
+		}
+	}
+
+	return 8;
+}
+
 static inline uintmax_t _strtoumax(
     const char *restrict nptr, char **restrict endptr, int base,
-    bool *restrict sgn)
+    bool *restrict sgn, errno_t *err, bool nonstd)
 {
 	assert(nptr != NULL);
 	assert(sgn != NULL);
+
+	const char *first = nptr;
 
 	/* Skip leading whitespace. */
@@ -96,29 +148,36 @@
 	/* Figure out the base. */
 
-	if (base == 0) {
-		if (*nptr == '0') {
-			if (tolower(nptr[1]) == 'x') {
-				/* 0x... is hex. */
-				base = 16;
-				nptr += 2;
-			} else {
-				/* 0... is octal. */
-				base = 8;
-			}
-		} else {
-			/* Anything else is decimal by default. */
-			base = 10;
-		}
-	} else if (base == 16) {
-		/* Allow hex number to be prefixed with "0x". */
-		if (nptr[0] == '0' && tolower(nptr[1]) == 'x') {
+	if (base == 0)
+		base = _prefixbase(&nptr, nonstd);
+
+	if (base == 16 && !nonstd) {
+		/*
+		 * Standard strto* functions allow hexadecimal prefix to be
+		 * present when base is explicitly set to 16.
+		 * Our nonstandard str_* functions don't allow it.
+		 * I don't know if that is intended, just matching the original
+		 * functionality here.
+		 */
+
+		if (nptr[0] == '0' && (nptr[1] == 'x' || nptr[1] == 'X') &&
+		    _digit_value(nptr[2]) < base)
 			nptr += 2;
-		}
-	} else if (base < 0 || base == 1 || base > 36) {
-		errno = EINVAL;
+	}
+
+	if (base < 2 || base > 36) {
+		*err = EINVAL;
 		return 0;
 	}
 
-	/* Read the value. */
+	/* Must be at least one digit. */
+
+	if (_digit_value(*nptr) >= base) {
+		/* No digits on input. */
+		if (endptr != NULL)
+			*endptr = (char *) first;
+		return 0;
+	}
+
+	/* Read the value.  */
 
 	uintmax_t result = 0;
@@ -127,9 +186,8 @@
 
 	while (digit = _digit_value(*nptr), digit < base) {
-
 		if (result > max ||
 		    __builtin_add_overflow(result * base, digit, &result)) {
 
-			errno = ERANGE;
+			*err = nonstd ? EOVERFLOW : ERANGE;
 			result = UINTMAX_MAX;
 			break;
@@ -145,4 +203,6 @@
 		 * Move the pointer to the end of the number,
 		 * in case it isn't there already.
+		 * This can happen when the number has legal formatting,
+		 * but is out of range of the target type.
 		 */
 		while (_digit_value(*nptr) < base) {
@@ -157,8 +217,8 @@
 
 static inline intmax_t _strtosigned(const char *nptr, char **endptr, int base,
-    intmax_t min, intmax_t max)
+    intmax_t min, intmax_t max, errno_t *err, bool nonstd)
 {
 	bool sgn = false;
-	uintmax_t number = _strtoumax(nptr, endptr, base, &sgn);
+	uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err, nonstd);
 
 	if (number > (uintmax_t) max) {
@@ -167,5 +227,5 @@
 		}
 
-		errno = ERANGE;
+		*err = nonstd ? EOVERFLOW : ERANGE;
 		return (sgn ? min : max);
 	}
@@ -175,24 +235,21 @@
 
 static inline uintmax_t _strtounsigned(const char *nptr, char **endptr, int base,
-    uintmax_t max)
+    uintmax_t max, errno_t *err, bool nonstd)
 {
 	bool sgn = false;
-	uintmax_t number = _strtoumax(nptr, endptr, base, &sgn);
-
-	if (sgn) {
-		if (number == 0) {
-			return 0;
-		} else {
-			errno = ERANGE;
-			return max;
-		}
+	uintmax_t number = _strtoumax(nptr, endptr, base, &sgn, err, nonstd);
+
+	if (nonstd && sgn) {
+		/* Do not allow negative values */
+		*err = EINVAL;
+		return 0;
 	}
 
 	if (number > max) {
-		errno = ERANGE;
+		*err = nonstd ? EOVERFLOW : ERANGE;
 		return max;
 	}
 
-	return number;
+	return (sgn ? -number : number);
 }
 
@@ -212,5 +269,5 @@
 long strtol(const char *nptr, char **endptr, int base)
 {
-	return _strtosigned(nptr, endptr, base, LONG_MIN, LONG_MAX);
+	return _strtosigned(nptr, endptr, base, LONG_MIN, LONG_MAX, &errno, false);
 }
 
@@ -230,25 +287,25 @@
 unsigned long strtoul(const char *nptr, char **endptr, int base)
 {
-	return _strtounsigned(nptr, endptr, base, ULONG_MAX);
+	return _strtounsigned(nptr, endptr, base, ULONG_MAX, &errno, false);
 }
 
 long long strtoll(const char *nptr, char **endptr, int base)
 {
-	return _strtosigned(nptr, endptr, base, LLONG_MIN, LLONG_MAX);
+	return _strtosigned(nptr, endptr, base, LLONG_MIN, LLONG_MAX, &errno, false);
 }
 
 unsigned long long strtoull(const char *nptr, char **endptr, int base)
 {
-	return _strtounsigned(nptr, endptr, base, ULLONG_MAX);
+	return _strtounsigned(nptr, endptr, base, ULLONG_MAX, &errno, false);
 }
 
 intmax_t strtoimax(const char *nptr, char **endptr, int base)
 {
-	return _strtosigned(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX);
+	return _strtosigned(nptr, endptr, base, INTMAX_MIN, INTMAX_MAX, &errno, false);
 }
 
 uintmax_t strtoumax(const char *nptr, char **endptr, int base)
 {
-	return _strtounsigned(nptr, endptr, base, UINTMAX_MAX);
+	return _strtounsigned(nptr, endptr, base, UINTMAX_MAX, &errno, false);
 }
 
@@ -268,4 +325,214 @@
 }
 
+/** Convert string to uint8_t.
+ *
+ * @param nptr   Pointer to string.
+ * @param endptr If not NULL, pointer to the first invalid character
+ *               is stored here.
+ * @param base   Zero or number between 2 and 36 inclusive.
+ * @param strict Do not allow any trailing characters.
+ * @param result Result of the conversion.
+ *
+ * @return EOK if conversion was successful.
+ *
+ */
+errno_t str_uint8_t(const char *nptr, const char **endptr, unsigned int base,
+    bool strict, uint8_t *result)
+{
+	assert(result != NULL);
+
+	errno_t rc = EOK;
+	char *lendptr = (char *) nptr;
+
+	uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT8_MAX, &rc, true);
+
+	if (endptr)
+		*endptr = lendptr;
+
+	if (rc != EOK)
+		return rc;
+
+	if (strict && *lendptr != '\0')
+		return EINVAL;
+
+	*result = r;
+	return EOK;
+}
+
+/** Convert string to uint16_t.
+ *
+ * @param nptr   Pointer to string.
+ * @param endptr If not NULL, pointer to the first invalid character
+ *               is stored here.
+ * @param base   Zero or number between 2 and 36 inclusive.
+ * @param strict Do not allow any trailing characters.
+ * @param result Result of the conversion.
+ *
+ * @return EOK if conversion was successful.
+ *
+ */
+errno_t str_uint16_t(const char *nptr, const char **endptr, unsigned int base,
+    bool strict, uint16_t *result)
+{
+	assert(result != NULL);
+
+	errno_t rc = EOK;
+	char *lendptr = (char *) nptr;
+
+	uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT16_MAX, &rc, true);
+
+	if (endptr)
+		*endptr = lendptr;
+
+	if (rc != EOK)
+		return rc;
+
+	if (strict && *lendptr != '\0')
+		return EINVAL;
+
+	*result = r;
+	return EOK;
+}
+
+/** Convert string to uint32_t.
+ *
+ * @param nptr   Pointer to string.
+ * @param endptr If not NULL, pointer to the first invalid character
+ *               is stored here.
+ * @param base   Zero or number between 2 and 36 inclusive.
+ * @param strict Do not allow any trailing characters.
+ * @param result Result of the conversion.
+ *
+ * @return EOK if conversion was successful.
+ *
+ */
+errno_t str_uint32_t(const char *nptr, const char **endptr, unsigned int base,
+    bool strict, uint32_t *result)
+{
+	assert(result != NULL);
+
+	errno_t rc = EOK;
+	char *lendptr = (char *) nptr;
+
+	uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT32_MAX, &rc, true);
+
+	if (endptr)
+		*endptr = lendptr;
+
+	if (rc != EOK)
+		return rc;
+
+	if (strict && *lendptr != '\0')
+		return EINVAL;
+
+	*result = r;
+	return EOK;
+}
+
+/** Convert string to uint64_t.
+ *
+ * @param nptr   Pointer to string.
+ * @param endptr If not NULL, pointer to the first invalid character
+ *               is stored here.
+ * @param base   Zero or number between 2 and 36 inclusive.
+ * @param strict Do not allow any trailing characters.
+ * @param result Result of the conversion.
+ *
+ * @return EOK if conversion was successful.
+ *
+ */
+errno_t str_uint64_t(const char *nptr, const char **endptr, unsigned int base,
+    bool strict, uint64_t *result)
+{
+	assert(result != NULL);
+
+	errno_t rc = EOK;
+	char *lendptr = (char *) nptr;
+
+	uintmax_t r = _strtounsigned(nptr, &lendptr, base, UINT64_MAX, &rc, true);
+
+	if (endptr)
+		*endptr = lendptr;
+
+	if (rc != EOK)
+		return rc;
+
+	if (strict && *lendptr != '\0')
+		return EINVAL;
+
+	*result = r;
+	return EOK;
+}
+
+/** Convert string to int64_t.
+ *
+ * @param nptr   Pointer to string.
+ * @param endptr If not NULL, pointer to the first invalid character
+ *               is stored here.
+ * @param base   Zero or number between 2 and 36 inclusive.
+ * @param strict Do not allow any trailing characters.
+ * @param result Result of the conversion.
+ *
+ * @return EOK if conversion was successful.
+ *
+ */
+errno_t str_int64_t(const char *nptr, const char **endptr, unsigned int base,
+    bool strict, int64_t *result)
+{
+	assert(result != NULL);
+
+	errno_t rc = EOK;
+	char *lendptr = (char *) nptr;
+
+	intmax_t r = _strtosigned(nptr, &lendptr, base, INT64_MIN, INT64_MAX, &rc, true);
+
+	if (endptr)
+		*endptr = lendptr;
+
+	if (rc != EOK)
+		return rc;
+
+	if (strict && *lendptr != '\0')
+		return EINVAL;
+
+	*result = r;
+	return EOK;
+}
+
+/** Convert string to size_t.
+ *
+ * @param nptr   Pointer to string.
+ * @param endptr If not NULL, pointer to the first invalid character
+ *               is stored here.
+ * @param base   Zero or number between 2 and 36 inclusive.
+ * @param strict Do not allow any trailing characters.
+ * @param result Result of the conversion.
+ *
+ * @return EOK if conversion was successful.
+ *
+ */
+errno_t str_size_t(const char *nptr, const char **endptr, unsigned int base,
+    bool strict, size_t *result)
+{
+	assert(result != NULL);
+
+	errno_t rc = EOK;
+	char *lendptr = (char *) nptr;
+
+	uintmax_t r = _strtounsigned(nptr, &lendptr, base, SIZE_MAX, &rc, true);
+
+	if (endptr)
+		*endptr = lendptr;
+
+	if (rc != EOK)
+		return rc;
+
+	if (strict && *lendptr != '\0')
+		return EINVAL;
+
+	*result = r;
+	return EOK;
+}
+
 /** @}
  */
