From 97b7dcf32e0da8565615972904623a3ca630eb12 Mon Sep 17 00:00:00 2001 From: Karel Zak Date: Thu, 24 Jun 2021 16:36:58 +0200 Subject: [PATCH] include/strutils: consolidate string to number conversion Signed-off-by: Karel Zak --- include/strutils.h | 23 +++--- lib/strutils.c | 173 ++++++++++++++++++++------------------------- 2 files changed, 91 insertions(+), 105 deletions(-) diff --git a/include/strutils.h b/include/strutils.h index b905847b3..6e95707ea 100644 --- a/include/strutils.h +++ b/include/strutils.h @@ -21,18 +21,23 @@ extern uintmax_t strtosize_or_err(const char *str, const char *errmesg); extern int ul_strtos64(const char *str, int64_t *num, int base); extern int ul_strtou64(const char *str, uint64_t *num, int base); +extern int ul_strtos32(const char *str, int32_t *num, int base); +extern int ul_strtou32(const char *str, uint32_t *num, int base); -extern int16_t strtos16_or_err(const char *str, const char *errmesg); -extern uint16_t strtou16_or_err(const char *str, const char *errmesg); -extern uint16_t strtox16_or_err(const char *str, const char *errmesg); +extern int64_t str2num_or_err(const char *str, int base, const char *errmesg, int64_t low, int64_t up); +extern uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up); -extern int32_t strtos32_or_err(const char *str, const char *errmesg); -extern uint32_t strtou32_or_err(const char *str, const char *errmesg); -extern uint32_t strtox32_or_err(const char *str, const char *errmesg); +#define strtos64_or_err(_s, _e) str2num_or_err(_s, 10, _e, 0, 0) +#define strtou64_or_err(_s, _e) str2unum_or_err(_s, 10, _e, 0) +#define strtox64_or_err(_s, _e) str2unum_or_err(_s, 16, _e, 0) -extern int64_t strtos64_or_err(const char *str, const char *errmesg); -extern uint64_t strtou64_or_err(const char *str, const char *errmesg); -extern uint64_t strtox64_or_err(const char *str, const char *errmesg); +#define strtos32_or_err(_s, _e) (int32_t) str2num_or_err(_s, 10, _e, INT32_MIN, INT32_MAX) +#define strtou32_or_err(_s, _e) (uint32_t) str2unum_or_err(_s, 10, _e, UINT32_MAX) +#define strtox32_or_err(_s, _e) (uint32_t) str2unum_or_err(_s, 16, _e, UINT32_MAX) + +#define strtos16_or_err(_s, _e) (int16_t) str2num_or_err(_s, 10, _e, INT16_MIN, INT16_MAX) +#define strtou16_or_err(_s, _e) (uint16_t) str2unum_or_err(_s, 10, _e, UINT16_MAX) +#define strtox16_or_err(_s, _e) (uint16_t) str2unum_or_err(_s, 16, _e, UINT16_MAX) extern double strtod_or_err(const char *str, const char *errmesg); extern long double strtold_or_err(const char *str, const char *errmesg); diff --git a/lib/strutils.c b/lib/strutils.c index 9fb53a1e2..096aaf533 100644 --- a/lib/strutils.c +++ b/lib/strutils.c @@ -339,128 +339,96 @@ int ul_strtos64(const char *str, int64_t *num, int base) int ul_strtou64(const char *str, uint64_t *num, int base) { char *end = NULL; + int64_t tmp; errno = 0; if (str == NULL || *str == '\0') return -EINVAL; - *num = (uint64_t) strtoumax(str, &end, base); + + /* we need to ignore negative numbers, note that for invalid negative + * number strtoimax() returns negative number too, so we do not + * need to check errno here */ + tmp = (int64_t) strtoimax(str, &end, base); + if (tmp < 0) + errno = ERANGE; + else { + errno = 0; + *num = strtoumax(str, &end, base); + } if (errno || str == end || (end && *end)) return -EINVAL; return 0; } +int ul_strtos32(const char *str, int32_t *num, int base) +{ + int64_t tmp; + int rc; + + rc = ul_strtos64(str, &tmp, base); + if (rc == 0 && (tmp < INT32_MIN || tmp > INT32_MAX)) + rc = -(errno = ERANGE); + if (rc == 0) + *num = (int32_t) tmp; + return rc; +} + +int ul_strtou32(const char *str, uint32_t *num, int base) +{ + uint64_t tmp; + int rc; + + rc = ul_strtou64(str, &tmp, base); + if (rc == 0 && tmp > UINT32_MAX) + rc = -(errno = ERANGE); + if (rc == 0) + *num = (uint32_t) tmp; + return rc; +} + /* - * Covert strings to numbers and print message on error. + * Covert strings to numbers in defined range and print message on error. * - * Note that hex functions (strtox..()) returns unsigned numbers, if you need - * something else then use ul_strtos64(s, &n, 16). + * These functions are used when we read input from users (getopt() etc.). It's + * better to consolidate the code and keep it all based on 64-bit numbers then + * implement it for 32 and 16-bit numbers too. */ -int64_t strtos64_or_err(const char *str, const char *errmesg) +int64_t str2num_or_err(const char *str, int base, const char *errmesg, + int64_t low, int64_t up) { int64_t num = 0; + int rc; - if (ul_strtos64(str, &num, 10) != 0) { + rc = ul_strtos64(str, &num, base); + if (rc == 0 && ((low && num < low) || (up && num > up))) + rc = -(errno = ERANGE); + + if (rc) { if (errno == ERANGE) err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); } return num; } -uint64_t strtou64_or_err(const char *str, const char *errmesg) +uint64_t str2unum_or_err(const char *str, int base, const char *errmesg, uint64_t up) { uint64_t num = 0; + int rc; - if (ul_strtou64(str, &num, 10)) { + rc = ul_strtou64(str, &num, base); + if (rc == 0 && (up && num > up)) + rc = -(errno = ERANGE); + + if (rc) { if (errno == ERANGE) err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); } return num; } -uint64_t strtox64_or_err(const char *str, const char *errmesg) -{ - uint64_t num = 0; - - if (ul_strtou64(str, &num, 16)) { - if (errno == ERANGE) - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - - errx(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; -} - -int32_t strtos32_or_err(const char *str, const char *errmesg) -{ - int64_t num = strtos64_or_err(str, errmesg); - - if (num < INT32_MIN || num > INT32_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; -} - -uint32_t strtou32_or_err(const char *str, const char *errmesg) -{ - uint64_t num = strtou64_or_err(str, errmesg); - - if (num > UINT32_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; -} - -uint32_t strtox32_or_err(const char *str, const char *errmesg) -{ - uint64_t num = strtox64_or_err(str, errmesg); - - if (num > UINT32_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; -} - -int16_t strtos16_or_err(const char *str, const char *errmesg) -{ - int64_t num = strtos64_or_err(str, errmesg); - - if (num < INT16_MIN || num > INT16_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; -} - -uint16_t strtou16_or_err(const char *str, const char *errmesg) -{ - uint64_t num = strtou64_or_err(str, errmesg); - - if (num > UINT16_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; -} - -uint16_t strtox16_or_err(const char *str, const char *errmesg) -{ - uint64_t num = strtox64_or_err(str, errmesg); - - if (num > UINT16_MAX) { - errno = ERANGE; - err(STRTOXX_EXIT_CODE, "%s: '%s'", errmesg, str); - } - return num; -} - double strtod_or_err(const char *str, const char *errmesg) { double num; @@ -1231,20 +1199,33 @@ int main(int argc, char *argv[]) } else if (argc == 3 && strcmp(argv[1], "--normalize") == 0) { return test_strutils_normalize(argc - 1, argv + 1); - } else if (argc == 3 && strcmp(argv[1], "--str2num") == 0) { - uint64_t n; - if (ul_strtou64(argv[2], &n, 10) == 0) { - printf("'%s' --> %ju\n", argv[2], (uintmax_t) n); - return EXIT_SUCCESS; - } + } else if (argc == 3 && strcmp(argv[1], "--strtos64") == 0) { + printf("'%s'-->%jd\n", argv[2], strtos64_or_err(argv[2], "strtos64 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtou64") == 0) { + printf("'%s'-->%ju\n", argv[2], strtou64_or_err(argv[2], "strtou64 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtos32") == 0) { + printf("'%s'-->%d\n", argv[2], strtos32_or_err(argv[2], "strtos32 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtou32") == 0) { + printf("'%s'-->%u\n", argv[2], strtou32_or_err(argv[2], "strtou32 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtos16") == 0) { + printf("'%s'-->%hd\n", argv[2], strtos16_or_err(argv[2], "strtos16 failed")); + return EXIT_SUCCESS; + } else if (argc == 3 && strcmp(argv[1], "--strtou16") == 0) { + printf("'%s'-->%hu\n", argv[2], strtou16_or_err(argv[2], "strtou16 failed")); + return EXIT_SUCCESS; + } else { fprintf(stderr, "usage: %1$s --size [suffix]\n" " %1$s --cmp-paths \n" " %1$s --strdup-member \n" " %1$s --stralnumcmp \n" " %1$s --normalize \n" - " %1$s --num2num \n", + " %1$s --strto{s,u}{16,32,64} \n", argv[0]); exit(EXIT_FAILURE); }