From 95e45f452e69d9f26d541f272106c47806c85bb7 Mon Sep 17 00:00:00 2001 From: Andrew Bettison Date: Mon, 21 Oct 2013 16:17:33 +1030 Subject: [PATCH] Improve str_to_int() et al Always set *afterp to point to the first invalid character even if conversion fails --- str.c | 31 ++++++++++++++++--------------- str.h | 9 +++++---- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/str.c b/str.c index 11d8d906..7ce43f82 100644 --- a/str.c +++ b/str.c @@ -235,11 +235,9 @@ int str_to_int(const char *str, int base, int *result, const char **afterp) const char *end = str; errno = 0; long value = strtol(str, (char**)&end, base); - if (errno == ERANGE || end == str || value > INT_MAX || value < INT_MIN) - return 0; if (afterp) *afterp = end; - else if (*end) + if (errno == ERANGE || end == str || value > INT_MAX || value < INT_MIN || isdigit(*end) || (!afterp && *end)) return 0; if (result) *result = value; @@ -253,11 +251,9 @@ int str_to_uint(const char *str, int base, unsigned *result, const char **afterp const char *end = str; errno = 0; unsigned long value = strtoul(str, (char**)&end, base); - if (errno == ERANGE || end == str || value > UINT_MAX) - return 0; if (afterp) *afterp = end; - else if (*end) + if (errno == ERANGE || end == str || value > UINT_MAX || isdigit(*end) || (!afterp && *end)) return 0; if (result) *result = value; @@ -271,11 +267,9 @@ int str_to_int64(const char *str, int base, int64_t *result, const char **afterp const char *end = str; errno = 0; long long value = strtoll(str, (char**)&end, base); - if (errno == ERANGE || end == str) - return 0; if (afterp) *afterp = end; - else if (*end) + if (errno == ERANGE || end == str || isdigit(*end) || (!afterp && *end)) return 0; if (result) *result = value; @@ -289,11 +283,9 @@ int str_to_uint64(const char *str, int base, uint64_t *result, const char **afte const char *end = str; errno = 0; unsigned long long value = strtoull(str, (char**)&end, base); - if (errno == ERANGE || end == str) - return 0; if (afterp) *afterp = end; - else if (*end) + if (errno == ERANGE || end == str || isdigit(*end) || (!afterp && *end)) return 0; if (result) *result = value; @@ -334,8 +326,11 @@ int str_to_int64_scaled(const char *str, int base, int64_t *result, const char * { int64_t value; const char *end = str; - if (!str_to_int64(str, base, &value, &end)) + if (!str_to_int64(str, base, &value, &end)) { + if (afterp) + *afterp = end; return 0; + } value *= scale_factor(end, &end); if (afterp) *afterp = end; @@ -350,8 +345,11 @@ int str_to_uint64_scaled(const char *str, int base, uint64_t *result, const char { uint64_t value; const char *end = str; - if (!str_to_uint64(str, base, &value, &end)) + if (!str_to_uint64(str, base, &value, &end)) { + if (afterp) + *afterp = end; return 0; + } value *= scale_factor(end, &end); if (afterp) *afterp = end; @@ -386,8 +384,11 @@ int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **aft return 0; const char *end = str; unsigned long long value = strtoull(str, (char**)&end, 10) * precision; - if (end == str) + if (end == str) { + if (afterp) + *afterp = end; return 0; + } if (end[0] == '.' && isdigit(end[1])) { ++end; unsigned factor; diff --git a/str.h b/str.h index a2ece4eb..d14b7f47 100644 --- a/str.h +++ b/str.h @@ -199,10 +199,11 @@ char *str_str(char *haystack, const char *needle, int haystack_len); /* Parse a string as an integer in ASCII radix notation in the given 'base' (eg, base=10 means * decimal). * - * Return 1 if a valid integer was parsed, storing the value in *result (unless result is NULL) and - * storing a pointer to the immediately succeeding character in *afterp (unless afterp is NULL, in - * which case returns 1 only if the immediately succeeding character is a nul '\0'). Returns 0 - * otherwise, leaving *result and *afterp unchanged. + * Returns 1 if a valid integer is parsed, storing the value in *result (unless result is NULL) and + * storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then + * returns 0 unless the immediately succeeding character is a NUL '\0'. If no integer is parsed or + * if the integer overflows (too many digits), then returns 0, leaving *result unchanged and setting + * setting *afterp to point to the character where parsing failed. * * @author Andrew Bettison */