diff --git a/include/lib/util_parse.h b/include/lib/util_parse.h new file mode 100644 index 00000000..dc5dd4bb --- /dev/null +++ b/include/lib/util_parse.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: MIT */ +/* + * util - Utility function library + * + * String parsing utility functions + * + * Copyright IBM Corp. 2026 + */ + +#ifndef LIB_UTIL_PARSE_H +#define LIB_UTIL_PARSE_H + +#include +#include + +struct util_range { + size_t start; + size_t end; +}; + +/* + * Parse a boolean input string into a boolean value + * Accepts: "0"/"1", "n"/"y", "no"/"yes", "f"/"t", "false"/"true", "off"/"on" + * Case-insensitive + * @param input: Input string to parse + * @return: 1 for true, 0 for false, -EINVAL for invalid input + */ +int util_parse_bool(const char *input); + +/* + * Parse byte sizes with optional unit suffixes + * Supports: K/KiB, M/MiB, G/GiB, T/TiB, P/PiB, E/EiB + * K/M/G/T/P/E use 1000-based multipliers + * KiB/MiB/GiB/TiB/PiB/EiB use 1024-based multipliers + * @param input: Input string to parse + * @param bytes: Pointer to store parsed byte size + * @return: 0 on success, negative error code on failure + */ +int util_parse_byte_size(const char *input, size_t *bytes); + +/* + * Parse numeric ranges in the format "start-end" + * @param input: Input string to parse (format: "start-end") + * @param range: Pointer to util_range struct to store result + * @return: 0 on success, negative error code on failure + */ +int util_parse_range(const char *input, struct util_range *range); + +/* + * Parse integers with support for different bases + * Supports: decimal, hex (0x prefix), binary (0b prefix), octal (0o prefix) + * @param input: Input string to parse + * @param value: Pointer to store parsed integer value + * @return: 0 on success, negative error code on failure + */ +int util_parse_int(const char *input, size_t *value); + +#endif diff --git a/libutil/util_parse.c b/libutil/util_parse.c new file mode 100644 index 00000000..fae9c95d --- /dev/null +++ b/libutil/util_parse.c @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: MIT */ +/* + * util - Utility function library + * + * String parsing utility functions + * + * Copyright IBM Corp. 2026 + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "lib/util_libc.h" +#include "lib/util_parse.h" +#include "lib/util_str.h" +#include "lib/zt_common.h" + +enum size_unit { + UNIT_BYTES = 0, + UNIT_KIB, /* 1024 */ + UNIT_K, /* 1000 */ + UNIT_MIB, /* 1024^2 */ + UNIT_M, /* 1000^2 */ + UNIT_GIB, /* 1024^3 */ + UNIT_G, /* 1000^3 */ + UNIT_TIB, /* 1024^4 */ + UNIT_T, /* 1000^4 */ + UNIT_PIB, /* 1024^5 */ + UNIT_P, /* 1000^5 */ + UNIT_EIB, /* 1024^6 */ + UNIT_E, /* 1000^6 */ +}; + +struct unit_mapping { + const char *suffix; + enum size_unit unit; + uint64_t multiplier; +}; + +static const struct unit_mapping unit_table[] = { + { "EiB", UNIT_EIB, 1024ULL * 1024 * 1024 * 1024 * 1024 * 1024 }, + { "E", UNIT_E, 1000ULL * 1000 * 1000 * 1000 * 1000 * 1000 }, + { "PiB", UNIT_PIB, 1024ULL * 1024 * 1024 * 1024 * 1024 }, + { "P", UNIT_P, 1000ULL * 1000 * 1000 * 1000 * 1000 }, + { "TiB", UNIT_TIB, 1024ULL * 1024 * 1024 * 1024 }, + { "T", UNIT_T, 1000ULL * 1000 * 1000 * 1000 }, + { "GiB", UNIT_GIB, 1024ULL * 1024 * 1024 }, + { "G", UNIT_G, 1000ULL * 1000 * 1000 }, + { "MiB", UNIT_MIB, 1024ULL * 1024 }, + { "M", UNIT_M, 1000ULL * 1000 }, + { "KiB", UNIT_KIB, 1024ULL }, + { "K", UNIT_K, 1000ULL }, + { "", UNIT_BYTES, 1ULL } +}; + +static const char *const bool_false_values[] = { "0", "n", "no", "f", "false", "off" }; +static const char *const bool_true_values[] = { "1", "y", "yes", "t", "true", "on" }; + +/* Ensure both arrays have the same size for consistency */ +STATIC_ASSERT(ARRAY_SIZE(bool_false_values) == ARRAY_SIZE(bool_true_values)); + +static const struct unit_mapping *find_unit_suffix(const char *suffix) +{ + size_t i; + + if (!suffix || *suffix == '\0') + return &unit_table[ARRAY_SIZE(unit_table) - 1]; + for (i = 0; i < ARRAY_SIZE(unit_table); i++) { + if (strcasecmp(suffix, unit_table[i].suffix) == 0) + return &unit_table[i]; + } + return NULL; +} + +/** + * util_parse_bool - Parse a boolean value from a string + * @input: Input string to parse + * + * Returns: 1 for true, 0 for false, -EINVAL on error + */ +int util_parse_bool(const char *input) +{ + size_t i; + + if (!input) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(bool_false_values); i++) { + if (strcasecmp(input, bool_false_values[i]) == 0) + return 0; + } + + for (i = 0; i < ARRAY_SIZE(bool_true_values); i++) { + if (strcasecmp(input, bool_true_values[i]) == 0) + return 1; + } + + return -EINVAL; +} + +int util_parse_byte_size(const char *input, size_t *bytes) +{ + const struct unit_mapping *unit; + uint64_t multiplier = 1; + unsigned long value; + char *endptr; + + if (!input || !bytes) + return -EINVAL; + + errno = 0; + value = strtoul(input, &endptr, 10); + if (errno == ERANGE) + return -ERANGE; + if (errno != 0) + return -errno; + if (endptr == input) + return -EINVAL; + + if (*endptr != '\0') { + unit = find_unit_suffix(endptr); + if (!unit) + return -EINVAL; + multiplier = unit->multiplier; + } + + if (value > SIZE_MAX / multiplier) + return -ERANGE; + + *bytes = (size_t)(value * multiplier); + return 0; +} + +int util_parse_range(const char *input, struct util_range *range) +{ + char *start_str, *end_str; + char *input_copy; + char *dash_pos; + int ret = 0; + + if (!input || !range) + return -EINVAL; + + input_copy = util_strdup(input); + + dash_pos = strchr(input_copy, '-'); + if (!dash_pos) { + ret = -EINVAL; + goto out; + } + + *dash_pos = '\0'; + start_str = input_copy; + end_str = dash_pos + 1; + + ret = util_parse_int(start_str, &range->start); + if (ret) + goto out; + + ret = util_parse_int(end_str, &range->end); + if (ret) + goto out; + + if (range->start > range->end) { + ret = -ERANGE; + goto out; + } + +out: + free(input_copy); + return ret; +} + +int util_parse_int(const char *input, size_t *value) +{ + const char *num_start, *rest; + char *endptr = NULL; + int base = 0; + + if (!input || !value) + return -EINVAL; + if (*input == '\0') + return -EINVAL; + + num_start = input; + + /* Determine base from prefix */ + /* Supports: 0x (hex), 0b (binary), 0o/0 (octal), decimal */ + rest = util_startswith_no_case(input, "0b"); + if (rest) { + base = 2; + num_start = rest; + if (*num_start == '\0') + return -EINVAL; + } else { + rest = util_startswith_no_case(input, "0o"); + if (rest) { + base = 8; + num_start = rest; + if (*num_start == '\0') + return -EINVAL; + } + /* base=0: strtoull handles 0x (hex) and 0 (octal) */ + } + + errno = 0; + *value = strtoull(num_start, &endptr, base); + + if (errno == ERANGE) + return -ERANGE; + if (!endptr || endptr == num_start || *endptr != '\0') + return -EINVAL; + + return 0; +}