Index: uspace/lib/c/test/strtol.c
===================================================================
--- uspace/lib/c/test/strtol.c	(revision a4e78743a4f4b5720e7ef6c7386d40d69e40fc9b)
+++ uspace/lib/c/test/strtol.c	(revision a4e78743a4f4b5720e7ef6c7386d40d69e40fc9b)
@@ -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);
