From e82a1473dac1c7068f5d30f34bc07360ab70881d Mon Sep 17 00:00:00 2001
From: Jakub Filak <jfilak@redhat.com>
Date: Thu, 4 Jun 2015 16:53:18 +0200
Subject: [PATCH] lib: add utility functions parsing numbers
The x* variants dies on errors but we do not want to do that in some
cases.
Signed-off-by: Jakub Filak <jfilak@redhat.com>
---
src/include/internal_libreport.h | 6 +++
src/lib/xatonum.c | 95 ++++++++++++++++++++++++++-------
tests/xfuncs.at | 110 +++++++++++++++++++++++++++++++++++++++
3 files changed, 192 insertions(+), 19 deletions(-)
diff --git a/src/include/internal_libreport.h b/src/include/internal_libreport.h
index 25af681..4f8ea26 100644
--- a/src/include/internal_libreport.h
+++ b/src/include/internal_libreport.h
@@ -241,8 +241,12 @@ const uint8_t *str_to_sha1(uint8_t result[SHA1_RESULT_LEN], const char *str);
const char *str_to_sha1str(char result[SHA1_RESULT_LEN*2 + 1], const char *str);
+#define try_atou libreport_try_atou
+int try_atou(const char *numstr, unsigned *value);
#define xatou libreport_xatou
unsigned xatou(const char *numstr);
+#define try_atoi libreport_try_atoi
+int try_atoi(const char *numstr, int *value);
#define xatoi libreport_xatoi
int xatoi(const char *numstr);
/* Using xatoi() instead of naive atoi() is not always convenient -
@@ -252,6 +256,8 @@ int xatoi(const char *numstr);
* It should really be named xatoi_nonnegative (since it allows 0),
* but that would be too long.
*/
+#define try_atoi_positive libreport_try_atoi_positive
+int try_atoi_positive(const char *numstr, int *value);
#define xatoi_positive libreport_xatoi_positive
int xatoi_positive(const char *numstr);
diff --git a/src/lib/xatonum.c b/src/lib/xatonum.c
index 71b0247..510d766 100644
--- a/src/lib/xatonum.c
+++ b/src/lib/xatonum.c
@@ -22,44 +22,101 @@
*/
#include "internal_libreport.h"
-unsigned xatou(const char *numstr)
+int try_atou(const char *numstr, unsigned *value)
{
+ int ret = 0;
unsigned long r;
int old_errno;
char *e;
+ old_errno = errno;
if (*numstr < '0' || *numstr > '9')
- goto inval;
+ {
+ ret = -EINVAL;
+ goto finito;
+ }
- old_errno = errno;
errno = 0;
r = strtoul(numstr, &e, 10);
- if (errno || numstr == e || *e != '\0' || r > UINT_MAX)
- goto inval; /* error / no digits / illegal trailing chars */
+ if (errno || numstr == e || *e != '\0')
+ {
+ ret = errno != 0 ? -errno : -EINVAL; /* error / no digits / illegal trailing chars */
+ goto finito;
+ }
+
+ /* check range */
+ if (r > UINT_MAX)
+ {
+ /* In this case errno is probably 0, because UINT_MAX < ULONG_MAX, thus
+ * strtoul should not return an error */
+ ret = -ERANGE;
+ goto finito;
+ }
+
+ *value = r;
+
+finito:
errno = old_errno; /* Ok. So restore errno. */
- return r;
+ return ret;
+}
+
+unsigned xatou(const char *numstr)
+{
+ unsigned value = (unsigned)-1;
+
+ if (try_atou(numstr, &value) != 0)
+ error_msg_and_die("expected number in range <0, %d>: '%s'", UINT_MAX, numstr);
+
+ return value;
+}
-inval:
- error_msg_and_die("invalid number '%s'", numstr);
+int try_atoi_positive(const char *numstr, int *value)
+{
+ unsigned tmp;
+ int r = try_atou(numstr, &tmp);
+ if (r != 0)
+ return r;
+
+ if (tmp > (unsigned)INT_MAX)
+ return -ERANGE;
+
+ *value = (int)tmp;
+ return 0;
}
int xatoi_positive(const char *numstr)
{
- unsigned r = xatou(numstr);
- if (r > (unsigned)INT_MAX)
- error_msg_and_die("invalid number '%s'", numstr);
- return r;
+ int value = INT_MIN;
+
+ if (try_atoi_positive(numstr, &value) != 0)
+ error_msg_and_die("expected number in range <0, %d>: '%s'", INT_MAX, numstr);
+
+ return value;
+}
+
+int try_atoi(const char *numstr, int *value)
+{
+ if (*numstr != '-')
+ return try_atoi_positive(numstr, value);
+
+ unsigned tmp;
+ int r = try_atou(numstr + 1, &tmp);
+ if (r < 0)
+ return r;
+
+ if (tmp > (unsigned)INT_MAX + 1)
+ return -ERANGE;
+
+ *value = - (int)tmp;
+ return 0;
}
int xatoi(const char *numstr)
{
- unsigned r;
+ int value = INT_MIN;
- if (*numstr != '-')
- return xatoi_positive(numstr);
+ if (try_atoi(numstr, &value))
+ error_msg_and_die("expected number in range <%d, %d>: '%s'", INT_MIN, INT_MAX, numstr);
- r = xatou(numstr + 1);
- if (r > (unsigned)INT_MAX + 1)
- error_msg_and_die("invalid number '%s'", numstr);
- return - (int)r;
+ return (int)value;
}
diff --git a/tests/xfuncs.at b/tests/xfuncs.at
index d7e947a..dbcc602 100644
--- a/tests/xfuncs.at
+++ b/tests/xfuncs.at
@@ -54,3 +54,113 @@ int main(void)
}
]])
+
+## ------------- ##
+## parse_numbers ##
+## ------------- ##
+
+AT_TESTFUN([parse_numbers],
+[[#include "internal_libreport.h"
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+int main(void)
+{
+ g_verbose=3;
+
+ /* 1. not a number - ERROR
+ * 2. a number with an alpha suffix - ERROR
+ * 3. out of range number - ERROR
+ * a. max + 1 - ERROR
+ * b. min - 1 - ERROR
+ * c. max - OK
+ * d. min - OK
+ */
+
+ {
+ unsigned uint_value = 12345;
+ assert(try_atou("foo", &uint_value) != 0);
+ assert(uint_value == 12345);
+
+ assert(try_atou("foo54321", &uint_value) != 0);
+ assert(uint_value == 12345);
+
+ char buf[sizeof(unsigned long) * 3 + 1];
+
+ snprintf(buf, sizeof(buf), "%lu", 1LU + UINT_MAX);
+ assert(try_atou(buf, &uint_value) != 0 || !"Above UINT_MAX");
+ assert(uint_value == 12345);
+
+ assert(try_atou("-1", &uint_value) != 0);
+ assert(uint_value == 12345);
+
+ snprintf(buf, sizeof(buf), "%lu", (long unsigned)UINT_MAX);
+ assert(try_atou(buf, &uint_value) == 0);
+ assert(uint_value == UINT_MAX);
+ assert(xatou(buf) == UINT_MAX);
+
+ assert(try_atou("0", &uint_value) == 0);
+ assert(uint_value == 0);
+ assert(xatou("0") == 0);
+ }
+
+ {
+ int int_value = 12345;
+ assert(try_atoi("foo", &int_value) != 0);
+ assert(int_value == 12345);
+
+ assert(try_atoi("foo54321", &int_value) != 0);
+ assert(int_value == 12345);
+
+ char buf[sizeof(long) * 3 + 1];
+
+ snprintf(buf, sizeof(buf), "%ld", 1L + INT_MAX);
+ assert(try_atoi(buf, &int_value) != 0 || !"Parse INT_MAX+1");
+ assert(int_value == 12345 || !"Above INT_MAX");
+
+ snprintf(buf, sizeof(buf), "%ld", -1L + INT_MIN);
+ assert(try_atoi(buf, &int_value) != 0 || !"Parse INT_MIN-1");
+ assert(int_value == 12345 || !"Belove INT_MIN");
+
+ snprintf(buf, sizeof(buf), "%ld", (long unsigned)INT_MAX);
+ assert(try_atoi(buf, &int_value) == 0 || !"Parse INT_MAX");
+ assert(int_value == INT_MAX);
+ assert(xatoi(buf) == INT_MAX);
+
+ snprintf(buf, sizeof(buf), "%ld", (long unsigned)INT_MIN);
+ assert(try_atoi(buf, &int_value) == 0 || !"Parse INT_MIN");
+ assert(int_value == INT_MIN);
+ assert(xatoi(buf) == INT_MIN);
+ }
+
+ {
+ int positive_value = 12345;
+ assert(try_atoi_positive("foo", &positive_value) != 0);
+ assert(positive_value == 12345);
+
+ assert(try_atoi_positive("foo54321", &positive_value) != 0);
+ assert(positive_value == 12345);
+
+ char buf[sizeof(long) * 3 + 1];
+
+ snprintf(buf, sizeof(buf), "%ld", 1L + INT_MAX);
+ assert(try_atoi_positive(buf, &positive_value) != 0);
+ assert(positive_value == 12345 || !"Above INT_MAX");
+
+ assert(try_atoi_positive("-1", &positive_value) != 0);
+ assert(positive_value == 12345 || !"After -1");
+
+ snprintf(buf, sizeof(buf), "%ld", (long unsigned)INT_MAX);
+ assert(try_atoi_positive(buf, &positive_value) == 0 || !"Parse INT_MAX");
+ assert(positive_value == INT_MAX);
+ assert(xatoi_positive(buf) == INT_MAX);
+
+ assert(try_atoi_positive("0", &positive_value) == 0);
+ assert(positive_value == 0);
+ assert(xatoi_positive("0") == 0);
+ }
+
+ return 0;
+}
+]])
--
2.1.0