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; const char *end = str;
errno = 0; errno = 0;
long value = strtol(str, (char**)&end, base); long value = strtol(str, (char**)&end, base);
if (errno == ERANGE || end == str || value > INT_MAX || value < INT_MIN)
return 0;
if (afterp) if (afterp)
*afterp = end; *afterp = end;
else if (*end) if (errno == ERANGE || end == str || value > INT_MAX || value < INT_MIN || isdigit(*end) || (!afterp && *end))
return 0; return 0;
if (result) if (result)
*result = value; *result = value;
@ -253,11 +251,9 @@ int str_to_uint(const char *str, int base, unsigned *result, const char **afterp
const char *end = str; const char *end = str;
errno = 0; errno = 0;
unsigned long value = strtoul(str, (char**)&end, base); unsigned long value = strtoul(str, (char**)&end, base);
if (errno == ERANGE || end == str || value > UINT_MAX)
return 0;
if (afterp) if (afterp)
*afterp = end; *afterp = end;
else if (*end) if (errno == ERANGE || end == str || value > UINT_MAX || isdigit(*end) || (!afterp && *end))
return 0; return 0;
if (result) if (result)
*result = value; *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; const char *end = str;
errno = 0; errno = 0;
long long value = strtoll(str, (char**)&end, base); long long value = strtoll(str, (char**)&end, base);
if (errno == ERANGE || end == str)
return 0;
if (afterp) if (afterp)
*afterp = end; *afterp = end;
else if (*end) if (errno == ERANGE || end == str || isdigit(*end) || (!afterp && *end))
return 0; return 0;
if (result) if (result)
*result = value; *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; const char *end = str;
errno = 0; errno = 0;
unsigned long long value = strtoull(str, (char**)&end, base); unsigned long long value = strtoull(str, (char**)&end, base);
if (errno == ERANGE || end == str)
return 0;
if (afterp) if (afterp)
*afterp = end; *afterp = end;
else if (*end) if (errno == ERANGE || end == str || isdigit(*end) || (!afterp && *end))
return 0; return 0;
if (result) if (result)
*result = value; *result = value;
@ -334,8 +326,11 @@ int str_to_int64_scaled(const char *str, int base, int64_t *result, const char *
{ {
int64_t value; int64_t value;
const char *end = str; 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; return 0;
}
value *= scale_factor(end, &end); value *= scale_factor(end, &end);
if (afterp) if (afterp)
*afterp = end; *afterp = end;
@ -350,8 +345,11 @@ int str_to_uint64_scaled(const char *str, int base, uint64_t *result, const char
{ {
uint64_t value; uint64_t value;
const char *end = str; 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; return 0;
}
value *= scale_factor(end, &end); value *= scale_factor(end, &end);
if (afterp) if (afterp)
*afterp = end; *afterp = end;
@ -386,8 +384,11 @@ int str_to_uint64_interval_ms(const char *str, int64_t *result, const char **aft
return 0; return 0;
const char *end = str; const char *end = str;
unsigned long long value = strtoull(str, (char**)&end, 10) * precision; unsigned long long value = strtoull(str, (char**)&end, 10) * precision;
if (end == str) if (end == str) {
if (afterp)
*afterp = end;
return 0; return 0;
}
if (end[0] == '.' && isdigit(end[1])) { if (end[0] == '.' && isdigit(end[1])) {
++end; ++end;
unsigned factor; 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 /* Parse a string as an integer in ASCII radix notation in the given 'base' (eg, base=10 means
* decimal). * decimal).
* *
* Return 1 if a valid integer was parsed, storing the value in *result (unless result is NULL) and * 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 (unless afterp is NULL, in * storing a pointer to the immediately succeeding character in *afterp. If afterp is NULL then
* which case returns 1 only if the immediately succeeding character is a nul '\0'). Returns 0 * returns 0 unless the immediately succeeding character is a NUL '\0'. If no integer is parsed or
* otherwise, leaving *result and *afterp unchanged. * 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> * @author Andrew Bettison <andrew@servalproject.com>
*/ */