mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
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:
58
include/lib/util_parse.h
Normal file
58
include/lib/util_parse.h
Normal 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
220
libutil/util_parse.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user