Blob Blame History Raw
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