From 84825b161ba5d18da4142893b9789b3fc71284d9 Mon Sep 17 00:00:00 2001
From: Karel Zak <kzak@redhat.com>
Date: Tue, 22 Jun 2021 14:20:42 +0200
Subject: [PATCH] include/strutils: cleanup strto..() functions

* add ul_strtos64() and ul_strtou64()
* add simple test

Addresses: https://github.com/karelzak/util-linux/issues/1358
Signed-off-by: Karel Zak <kzak@redhat.com>

Upstream-Backport: [https://github.com/util-linux/util-linux/commit/84825b161ba5d18da4142893b9789b3fc71284d9]
Signed-off-by: Ranjitsinh Rathod <ranjitsinh.rathod@kpit.com>

---
 include/strutils.h |   3 +
 lib/strutils.c     | 174 ++++++++++++++++++++++++++-------------------
 2 files changed, 105 insertions(+), 72 deletions(-)

diff --git a/include/strutils.h b/include/strutils.h
index e75a2f0e17..389e849905 100644
--- a/include/strutils.h
+++ b/include/strutils.h
@@ -19,6 +19,9 @@ extern int parse_size(const char *str, uintmax_t *res, int *power);
 extern int strtosize(const char *str, uintmax_t *res);
 extern uintmax_t strtosize_or_err(const char *str, const char *errmesg);
 
+extern int ul_strtos64(const char *str, int64_t *num, int base);
+extern int ul_strtou64(const char *str, uint64_t *num, int base);
+
 extern int16_t strtos16_or_err(const char *str, const char *errmesg);
 extern uint16_t strtou16_or_err(const char *str, const char *errmesg);
 extern uint16_t strtox16_or_err(const char *str, const char *errmesg);
diff --git a/lib/strutils.c b/lib/strutils.c
index ee2c835495..d9976dca70 100644
--- a/lib/strutils.c
+++ b/lib/strutils.c
@@ -319,39 +319,80 @@ char *strndup(const char *s, size_t n)
 }
 #endif
 
-static uint32_t _strtou32_or_err(const char *str, const char *errmesg, int base);
-static uint64_t _strtou64_or_err(const char *str, const char *errmesg, int base);
+/*
+ * convert strings to numbers; returns <0 on error, and 0 on success
+ */
+int ul_strtos64(const char *str, int64_t *num, int base)
+{
+	char *end = NULL;
 
-int16_t strtos16_or_err(const char *str, const char *errmesg)
+	errno = 0;
+	if (str == NULL || *str == '\0')
+		return -EINVAL;
+	*num = (int64_t) strtoimax(str, &end, base);
+
+	if (errno || str == end || (end && *end))
+		return -EINVAL;
+	return 0;
+}
+
+int ul_strtou64(const char *str, uint64_t *num, int base)
 {
-	int32_t num = strtos32_or_err(str, errmesg);
+	char *end = NULL;
 
-	if (num < INT16_MIN || num > INT16_MAX) {
-		errno = ERANGE;
-		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
-	}
-	return num;
+	errno = 0;
+	if (str == NULL || *str == '\0')
+		return -EINVAL;
+	*num = (uint64_t) strtoumax(str, &end, base);
+
+	if (errno || str == end || (end && *end))
+		return -EINVAL;
+	return 0;
 }
 
-static uint16_t _strtou16_or_err(const char *str, const char *errmesg, int base)
+/*
+ * Covert strings to numbers and print message on error.
+ *
+ * Note that hex functions (strtox..()) returns unsigned numbers, if you need
+ * something else then use ul_strtos64(s, &n, 16).
+ */
+int64_t strtos64_or_err(const char *str, const char *errmesg)
 {
-	uint32_t num = _strtou32_or_err(str, errmesg, base);
+	int64_t num = 0;
 
-	if (num > UINT16_MAX) {
-		errno = ERANGE;
-		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	if (ul_strtos64(str, &num, 10) != 0) {
+		if (errno == ERANGE)
+			err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
 	}
 	return num;
 }
 
-uint16_t strtou16_or_err(const char *str, const char *errmesg)
+uint64_t strtou64_or_err(const char *str, const char *errmesg)
 {
-	return _strtou16_or_err(str, errmesg, 10);
+	uint64_t num = 0;
+
+	if (ul_strtou64(str, &num, 10)) {
+		if (errno == ERANGE)
+			err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	}
+	return num;
 }
 
-uint16_t strtox16_or_err(const char *str, const char *errmesg)
+uint64_t strtox64_or_err(const char *str, const char *errmesg)
 {
-	return _strtou16_or_err(str, errmesg, 16);
+	uint64_t num = 0;
+
+	if (ul_strtou64(str, &num, 16)) {
+		if (errno == ERANGE)
+			err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+
+		errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	}
+	return num;
 }
 
 int32_t strtos32_or_err(const char *str, const char *errmesg)
@@ -365,9 +406,9 @@ int32_t strtos32_or_err(const char *str, const char *errmesg)
 	return num;
 }
 
-static uint32_t _strtou32_or_err(const char *str, const char *errmesg, int base)
+uint32_t strtou32_or_err(const char *str, const char *errmesg)
 {
-	uint64_t num = _strtou64_or_err(str, errmesg, base);
+	uint64_t num = strtou64_or_err(str, errmesg);
 
 	if (num > UINT32_MAX) {
 		errno = ERANGE;
@@ -376,66 +417,48 @@ static uint32_t _strtou32_or_err(const char *str, const char *errmesg, int base)
 	return num;
 }
 
-uint32_t strtou32_or_err(const char *str, const char *errmesg)
-{
-	return _strtou32_or_err(str, errmesg, 10);
-}
-
 uint32_t strtox32_or_err(const char *str, const char *errmesg)
 {
-	return _strtou32_or_err(str, errmesg, 16);
+	uint64_t num = strtox64_or_err(str, errmesg);
+
+	if (num > UINT32_MAX) {
+		errno = ERANGE;
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	}
+	return num;
 }
 
-int64_t strtos64_or_err(const char *str, const char *errmesg)
+int16_t strtos16_or_err(const char *str, const char *errmesg)
 {
-	int64_t num;
-	char *end = NULL;
-
-	errno = 0;
-	if (str == NULL || *str == '\0')
-		goto err;
-	num = strtoimax(str, &end, 10);
-
-	if (errno || str == end || (end && *end))
-		goto err;
+	int64_t num = strtos64_or_err(str, errmesg);
 
-	return num;
-err:
-	if (errno == ERANGE)
+	if (num < INT16_MIN || num > INT16_MAX) {
+		errno = ERANGE;
 		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
-
-	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	}
+	return num;
 }
 
-static uint64_t _strtou64_or_err(const char *str, const char *errmesg, int base)
+uint16_t strtou16_or_err(const char *str, const char *errmesg)
 {
-	uintmax_t num;
-	char *end = NULL;
-
-	errno = 0;
-	if (str == NULL || *str == '\0')
-		goto err;
-	num = strtoumax(str, &end, base);
-
-	if (errno || str == end || (end && *end))
-		goto err;
+	uint64_t num = strtou64_or_err(str, errmesg);
 
-	return num;
-err:
-	if (errno == ERANGE)
+	if (num > UINT16_MAX) {
+		errno = ERANGE;
 		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
-
-	errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	}
+	return num;
 }
 
-uint64_t strtou64_or_err(const char *str, const char *errmesg)
+uint16_t strtox16_or_err(const char *str, const char *errmesg)
 {
-	return _strtou64_or_err(str, errmesg, 10);
-}
+	uint64_t num = strtox64_or_err(str, errmesg);
 
-uint64_t strtox64_or_err(const char *str, const char *errmesg)
-{
-	return _strtou64_or_err(str, errmesg, 16);
+	if (num > UINT16_MAX) {
+		errno = ERANGE;
+		err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str);
+	}
+	return num;
 }
 
 double strtod_or_err(const char *str, const char *errmesg)
@@ -1051,15 +1051,25 @@ static int test_strutils_cmp_paths(int a
 
 int main(int argc, char *argv[])
 {
-	if (argc == 3 && strcmp(argv[1], "--size") == 0)
+	if (argc == 3 && strcmp(argv[1], "--size") == 0) {
 		return test_strutils_sizes(argc - 1, argv + 1);
 
-	else if (argc == 4 && strcmp(argv[1], "--cmp-paths") == 0)
+	} else if (argc == 4 && strcmp(argv[1], "--cmp-paths") == 0) {
 		return test_strutils_cmp_paths(argc - 1, argv + 1);
 
+	} else if (argc == 3 && strcmp(argv[1], "--str2num") == 0) {
+        	uint64_t n;
+
+        	if (ul_strtou64(argv[2], &n, 10) == 0) {
+                	printf("'%s' --> %ju\n", argv[2], (uintmax_t) n);
+                	return EXIT_SUCCESS;
+        	}
+	}
+
 	else {
 		fprintf(stderr, "usage: %1$s --size <number>[suffix]\n"
-				"       %1$s --cmp-paths <path> <path>\n",
+				"       %1$s --cmp-paths <path> <path>\n"
+				"       %1$s --num2num <str>\n",
 				argv[0]);
 		exit(EXIT_FAILURE);
 	}