libutil: Add utility parsing functions

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>
This commit is contained in:
Wisdom
2025-08-06 12:04:45 +01:00
committed by Jan Höppner
parent aa29a5f1f9
commit d2efda1ac2
2 changed files with 278 additions and 0 deletions

58
include/lib/util_parse.h Normal file
View File

@@ -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 <stdbool.h>
#include <stddef.h>
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

220
libutil/util_parse.c Normal file
View File

@@ -0,0 +1,220 @@
/* 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;
}