mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
Add functions to parse boolean values, sizes, ranges, and numbers. Declarations are in util_parse.h for use across all s390-tools. Signed-off-by: Wisdom Erhimwionsobo <werh29@linux.ibm.com> Co-developed-by: Dean Doyle <ddoyle@linux.ibm.com> Signed-off-by: Dean Doyle <ddoyle@linux.ibm.com> Reviewed-by: Jan Höppner <hoeppner@linux.ibm.com> Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
221 lines
4.6 KiB
C
221 lines
4.6 KiB
C
/* SPDX-License-Identifier: MIT */
|
|
/*
|
|
* util - Utility function library
|
|
*
|
|
* String parsing utility functions
|
|
*
|
|
* Copyright IBM Corp. 2026
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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;
|
|
}
|