Improve str_to_int() et al

Always set *afterp to point to the first invalid character
even if conversion fails
This commit is contained in:
Andrew Bettison 2013-10-21 16:17:33 +10:30
parent 291a631095
commit 95e45f452e
2 changed files with 21 additions and 19 deletions

31
str.c
View File

@ -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;

9
str.h
View File

@ -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 <andrew@servalproject.com>
*/