Index: uspace/lib/c/test/main.c
===================================================================
--- uspace/lib/c/test/main.c	(revision 88e7dc53faf2ba5c873e4ef7f71b0f2bac0efbc3)
+++ uspace/lib/c/test/main.c	(revision 9259d203a46c750aab526dff7ff95b01e17364f4)
@@ -53,4 +53,5 @@
 PCUT_IMPORT(str);
 PCUT_IMPORT(string);
+PCUT_IMPORT(strtol);
 PCUT_IMPORT(table);
 PCUT_IMPORT(uuid);
Index: uspace/lib/c/test/stdlib.c
===================================================================
--- uspace/lib/c/test/stdlib.c	(revision 88e7dc53faf2ba5c873e4ef7f71b0f2bac0efbc3)
+++ uspace/lib/c/test/stdlib.c	(revision 9259d203a46c750aab526dff7ff95b01e17364f4)
@@ -63,31 +63,4 @@
 }
 
-/** atoi function */
-PCUT_TEST(atoi)
-{
-	int i;
-
-	i = atoi(" \t42");
-	PCUT_ASSERT_TRUE(i == 42);
-}
-
-/** atol function */
-PCUT_TEST(atol)
-{
-	long li;
-
-	li = atol(" \t42");
-	PCUT_ASSERT_TRUE(li == 42);
-}
-
-/** atoll function */
-PCUT_TEST(atoll)
-{
-	long long lli;
-
-	lli = atoll(" \t42");
-	PCUT_ASSERT_TRUE(lli == 42);
-}
-
 /** strtold function */
 #include <stdio.h>
@@ -101,95 +74,4 @@
 	printf("ld=%.10lf\n", (double)ld);
 	PCUT_ASSERT_TRUE(ld == 42.0);
-}
-
-/** strtol function */
-PCUT_TEST(strtol)
-{
-	long li;
-	char *ep;
-
-	li = strtol(" \t42x", &ep, 10);
-	PCUT_ASSERT_TRUE(li == 42);
-	PCUT_ASSERT_TRUE(*ep == 'x');
-}
-
-/** strtol function with auto-detected base 10 */
-PCUT_TEST(strtol_dec_auto)
-{
-	long li;
-	char *ep;
-
-	li = strtol(" \t42x", &ep, 0);
-	PCUT_ASSERT_TRUE(li == 42);
-	PCUT_ASSERT_TRUE(*ep == 'x');
-}
-
-/** strtol function with octal number */
-PCUT_TEST(strtol_oct)
-{
-	long li;
-	char *ep;
-
-	li = strtol(" \t052x", &ep, 8);
-	PCUT_ASSERT_TRUE(li == 052);
-	PCUT_ASSERT_TRUE(*ep == 'x');
-}
-
-/** strtol function with octal number with prefix */
-#include <stdio.h>
-PCUT_TEST(strtol_oct_prefix)
-{
-	long li;
-	char *ep;
-
-	li = strtol(" \t052x", &ep, 0);
-	printf("li=%ld (0%lo)\n", li, li);
-	PCUT_ASSERT_TRUE(li == 052);
-	PCUT_ASSERT_TRUE(*ep == 'x');
-}
-
-/** strtol function with hex number */
-PCUT_TEST(strtol_hex)
-{
-	long li;
-	char *ep;
-
-	li = strtol(" \t2ax", &ep, 16);
-	PCUT_ASSERT_TRUE(li == 0x2a);
-	PCUT_ASSERT_TRUE(*ep == 'x');
-}
-
-/** strtol function with hex number with hex prefix */
-PCUT_TEST(strtol_hex_prefixed)
-{
-	long li;
-	char *ep;
-
-	li = strtol(" \t0x2ax", &ep, 0);
-	PCUT_ASSERT_TRUE(li == 0x2a);
-	PCUT_ASSERT_TRUE(*ep == 'x');
-}
-
-/** strtol function with base 16 and number with 0x prefix */
-PCUT_TEST(strtol_base16_prefix)
-{
-	long li;
-	char *ep;
-
-	li = strtol(" \t0x1y", &ep, 16);
-	printf("li=%ld\n", li);
-	PCUT_ASSERT_TRUE(li == 1);
-	PCUT_ASSERT_TRUE(*ep == 'y');
-}
-
-/** strtol function with base 36 number */
-PCUT_TEST(strtol_base36)
-{
-	long li;
-	char *ep;
-
-	li = strtol(" \tz1.", &ep, 36);
-	PCUT_ASSERT_TRUE(li == 35 * 36 + 1);
-	PCUT_ASSERT_TRUE(*ep == '.');
 }
 
Index: uspace/lib/c/test/strtol.c
===================================================================
--- uspace/lib/c/test/strtol.c	(revision 9259d203a46c750aab526dff7ff95b01e17364f4)
+++ uspace/lib/c/test/strtol.c	(revision 9259d203a46c750aab526dff7ff95b01e17364f4)
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2018 Jiri Svoboda
+ * 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.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
+ */
+
+/** @addtogroup libc
+ * @{
+ */
+/**
+ * @file
+ * @brief Test string to int conversion functions
+ */
+
+#include <pcut/pcut.h>
+#include <stdlib.h>
+#include <str.h>
+
+PCUT_INIT;
+
+PCUT_TEST_SUITE(strtol);
+
+/** atoi function */
+PCUT_TEST(atoi)
+{
+	int i;
+
+	i = atoi(" \t42");
+	PCUT_ASSERT_TRUE(i == 42);
+}
+
+/** atol function */
+PCUT_TEST(atol)
+{
+	long li;
+
+	li = atol(" \t42");
+	PCUT_ASSERT_TRUE(li == 42);
+}
+
+/** atoll function */
+PCUT_TEST(atoll)
+{
+	long long lli;
+
+	lli = atoll(" \t42");
+	PCUT_ASSERT_TRUE(lli == 42);
+}
+
+/** strtol function */
+PCUT_TEST(strtol)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t42x", &ep, 10);
+	PCUT_ASSERT_TRUE(li == 42);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with auto-detected base 10 */
+PCUT_TEST(strtol_dec_auto)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t42x", &ep, 0);
+	PCUT_ASSERT_TRUE(li == 42);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with octal number */
+PCUT_TEST(strtol_oct)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t052x", &ep, 8);
+	PCUT_ASSERT_TRUE(li == 052);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with octal number with prefix */
+#include <stdio.h>
+PCUT_TEST(strtol_oct_prefix)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t052x", &ep, 0);
+	printf("li=%ld (0%lo)\n", li, li);
+	PCUT_ASSERT_TRUE(li == 052);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with hex number */
+PCUT_TEST(strtol_hex)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t2ax", &ep, 16);
+	PCUT_ASSERT_TRUE(li == 0x2a);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with hex number with hex prefix */
+PCUT_TEST(strtol_hex_prefixed)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t0x2ax", &ep, 0);
+	PCUT_ASSERT_TRUE(li == 0x2a);
+	PCUT_ASSERT_TRUE(*ep == 'x');
+}
+
+/** strtol function with base 16 and number with 0x prefix */
+PCUT_TEST(strtol_base16_prefix)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \t0x1y", &ep, 16);
+	printf("li=%ld\n", li);
+	PCUT_ASSERT_TRUE(li == 1);
+	PCUT_ASSERT_TRUE(*ep == 'y');
+}
+
+/** strtol function with base 36 number */
+PCUT_TEST(strtol_base36)
+{
+	long li;
+	char *ep;
+
+	li = strtol(" \tz1.", &ep, 36);
+	PCUT_ASSERT_TRUE(li == 35 * 36 + 1);
+	PCUT_ASSERT_TRUE(*ep == '.');
+}
+
+PCUT_TEST(str_uint_hex)
+{
+	int base;
+	bool strict;
+	const char *endp;
+	uint32_t result;
+	errno_t rc;
+	const char *input;
+
+	/*
+	 * str_* conversion function do not admit a prefix when base is
+	 * specified.
+	 */
+
+	rc = str_uint32_t(input = "0x10", &endp, base = 0, strict = false, &result);
+	PCUT_ASSERT_INT_EQUALS(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(0x10, result);
+	PCUT_ASSERT_PTR_EQUALS(input + 4, endp);
+
+	rc = str_uint32_t(input = "0x10", &endp, base = 16, strict = false, &result);
+	PCUT_ASSERT_INT_EQUALS(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(0, result);
+	PCUT_ASSERT_PTR_EQUALS(input + 1, endp);
+
+	rc = str_uint32_t(input = "  \t0x10", &endp, base = 0, strict = false, &result);
+	PCUT_ASSERT_INT_EQUALS(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(0x10, result);
+	PCUT_ASSERT_PTR_EQUALS(input + 7, endp);
+
+	rc = str_uint32_t(input = "  \t0x10", &endp, base = 16, strict = false, &result);
+	PCUT_ASSERT_INT_EQUALS(EOK, rc);
+	PCUT_ASSERT_INT_EQUALS(0, result);
+	PCUT_ASSERT_PTR_EQUALS(input + 4, endp);
+}
+
+PCUT_TEST(str_uint_overflow)
+{
+	int base;
+	bool strict;
+	uint64_t result;
+	errno_t rc;
+	const char *input;
+
+	/*
+	 * Naive overflow check will not detect this overflow,
+	 * since the intermediate result is equal to the previous step.
+	 */
+
+	rc = str_uint64_t(input = "0xffffffffffffffffffffffffffffffff", NULL, base = 0, strict = false, &result);
+#if 0
+	/* Buggy result. */
+	PCUT_ASSERT_INT_EQUALS(EOK, rc);
+	PCUT_ASSERT_UINT_EQUALS(0xffffffffffffffff, result);
+#else
+	/* Correct result. */
+	PCUT_ASSERT_INT_EQUALS(EOVERFLOW, rc);
+	PCUT_ASSERT_UINT_EQUALS(UINT64_MAX, result);
+#endif
+
+	/* 3^40 */
+
+	rc = str_uint64_t(input = "1" "0000000000" "0000000000" "0000000000" "0000000000", NULL, base = 3, strict = false, &result);
+	PCUT_ASSERT_INT_EQUALS(EOK, rc);
+	PCUT_ASSERT_UINT_EQUALS(0xa8b8b452291fe821, result);
+
+	/*
+	 * Naive overflow check will not detect this overflow,
+	 * since the intermediate result is greater than the previous step,
+	 * despite overflowing 64 bits.
+	 *
+	 * The input is 3^41 which is greater than 2^64,
+	 * but (3^41 mod 2^64) is still greater than 3^40, so overflow is not
+	 * detected by a naive magnitude check.
+	 */
+
+	rc = str_uint64_t(input = "10" "0000000000" "0000000000" "0000000000" "0000000000", NULL, base = 3, strict = false, &result);
+#if 0
+	/* Buggy result. */
+	PCUT_ASSERT_INT_EQUALS(EOK, rc);
+	/* Correct value is    0x1fa2a1cf67b5fb863 */
+	PCUT_ASSERT_UINT_EQUALS(0xfa2a1cf67b5fb863, result);
+#else
+	/* Correct result. */
+	PCUT_ASSERT_INT_EQUALS(EOVERFLOW, rc);
+	PCUT_ASSERT_UINT_EQUALS(UINT64_MAX, result);
+#endif
+}
+
+PCUT_TEST(strtol_fringe)
+{
+	long output;
+	char *endp;
+	char *endp_unchanged = (char *) "endp_unchanged unique pointer";
+	int errno_unchanged = -1;
+	const char *input;
+	int base;
+
+	/* Variants of plain zero with various bases. */
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = 0);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 1, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = -10);
+	PCUT_ASSERT_INT_EQUALS(EINVAL, errno);
+	PCUT_ASSERT_PTR_EQUALS(endp_unchanged, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = 1);
+	PCUT_ASSERT_INT_EQUALS(EINVAL, errno);
+	PCUT_ASSERT_PTR_EQUALS(endp_unchanged, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	for (base = 2; base <= 36; base++) {
+		endp = endp_unchanged;
+		errno = errno_unchanged;
+		output = strtol(input = "0", &endp, base);
+		PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+		PCUT_ASSERT_PTR_EQUALS(input + 1, endp);
+		PCUT_ASSERT_INT_EQUALS(0, output);
+
+		endp = endp_unchanged;
+		errno = errno_unchanged;
+		output = strtol(input = "1", &endp, base);
+		PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+		PCUT_ASSERT_PTR_EQUALS(input + 1, endp);
+		PCUT_ASSERT_INT_EQUALS(1, output);
+
+		endp = endp_unchanged;
+		errno = errno_unchanged;
+		output = strtol(input = "10", &endp, base);
+		PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+		PCUT_ASSERT_PTR_EQUALS(input + 2, endp);
+		PCUT_ASSERT_INT_EQUALS(base, output);
+
+		endp = endp_unchanged;
+		errno = errno_unchanged;
+		output = strtol(input = "100", &endp, base);
+		PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+		PCUT_ASSERT_PTR_EQUALS(input + 3, endp);
+		PCUT_ASSERT_INT_EQUALS(base * base, output);
+
+		endp = endp_unchanged;
+		errno = errno_unchanged;
+		output = strtol(input = "1000", &endp, base);
+		PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+		PCUT_ASSERT_PTR_EQUALS(input + 4, endp);
+		PCUT_ASSERT_INT_EQUALS(base * base * base, output);
+	}
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = 8);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 1, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = 10);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 1, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = 16);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 1, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = 36);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 1, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = 37);
+	PCUT_ASSERT_INT_EQUALS(EINVAL, errno);
+	PCUT_ASSERT_PTR_EQUALS(endp_unchanged, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "0", &endp, base = 37);
+	PCUT_ASSERT_INT_EQUALS(EINVAL, errno);
+	PCUT_ASSERT_PTR_EQUALS(endp_unchanged, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	/* No valid number */
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "", &endp, base = 0);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "    ", &endp, base = 0);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "    ", &endp, base = 10);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "    x", &endp, base = 0);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "    x0", &endp, base = 0);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "    0x", &endp, base = 0);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 5, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "    0x1", &endp, base = 0);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 7, endp);
+	PCUT_ASSERT_INT_EQUALS(1, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "    0x", &endp, base = 16);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 5, endp);
+	PCUT_ASSERT_INT_EQUALS(0, output);
+
+	endp = endp_unchanged;
+	errno = errno_unchanged;
+	output = strtol(input = "    0x1", &endp, base = 16);
+	PCUT_ASSERT_INT_EQUALS(errno_unchanged, errno);
+	PCUT_ASSERT_PTR_EQUALS(input + 7, endp);
+	PCUT_ASSERT_INT_EQUALS(1, output);
+}
+
+PCUT_EXPORT(strtol);
