util: add a strv_for_each helper function

Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1038>
This commit is contained in:
Peter Hutterer 2024-09-03 14:31:02 +10:00 committed by Marge Bot
parent 910d59e836
commit ea7ad8d25c
3 changed files with 102 additions and 0 deletions

View file

@ -24,6 +24,8 @@
#include "config.h"
#include <stdint.h>
#include "util-strings.h"
/**
@ -199,6 +201,38 @@ strv_join(char **strv, const char *joiner)
return str;
}
/**
* Iterate through strv, calling func with each string and its respective index.
* Iteration stops successfully after max elements or at the last element,
* whichever occurs first.
*
* If func returns non-zero, iteration stops and strv_for_each returns
* that value.
*
* @return zero on success, otherwise the error returned by the callback
*/
int strv_for_each_n(const char **strv, size_t max, strv_foreach_callback_t func, void *data)
{
for (size_t i = 0; i < max && strv && strv[i]; i++) {
int ret = func(strv[i], i, data);
if (ret)
return ret;
}
return 0;
}
/**
* Iterate through strv, calling func with each string and its respective index.
* If func returns non-zero, iteration stops and strv_for_each returns
* that value.
*
* @return zero on success, otherwise the error returned by the callback
*/
int strv_for_each(const char **strv, strv_foreach_callback_t func, void *data)
{
return strv_for_each_n(strv, SIZE_MAX, func, data);
}
/**
* Return a pointer to the basename within filename.
* If the filename the empty string or a directory (i.e. the last char of

View file

@ -266,6 +266,10 @@ char **strv_from_argv(int argc, char **argv);
char **strv_from_string(const char *in, const char *separator, size_t *num_elements);
char *strv_join(char **strv, const char *joiner);
typedef int (*strv_foreach_callback_t)(const char *str, size_t index, void *data);
int strv_for_each(const char **strv, strv_foreach_callback_t func, void *data);
int strv_for_each_n(const char **strv, size_t max, strv_foreach_callback_t func, void *data);
static inline void
strv_free(char **strv) {
char **s = strv;

View file

@ -1120,6 +1120,69 @@ START_TEST(strsplit_test)
}
END_TEST
struct strv_test_data {
const char *terminate_at;
unsigned char bitmask[1];
};
static int strv_test_set_bitmask(const char *str, size_t index, void *data)
{
struct strv_test_data *td = data;
if (streq(str, td->terminate_at))
return index + 1;
set_bit(td->bitmask, index);
return 0;
}
START_TEST(strv_for_each_test)
{
struct test_data {
const char *terminator;
int index;
unsigned int bitmask;
} test_data[] = {
{ "one", 1, 0x0 },
{ "two", 2, 0x1 },
{ "three", 3, 0x3 },
{ "four", 4, 0x7 },
{ "five", 5, 0xf },
{ "does-not-exist", 0, 0x1f },
{ NULL, 0, 0x1f },
{ NULL, 0 },
};
const char *array[] = { "one", "two", "three", "four", "five", NULL };
struct test_data *t = test_data;
while (t->terminator || t->bitmask) {
const int max = 3;
struct strv_test_data td = {
.terminate_at = t->terminator,
.bitmask = { 0 },
};
int rc = strv_for_each(array, strv_test_set_bitmask, &td);
ck_assert_int_eq(rc, t->index);
ck_assert_int_eq(td.bitmask[0], t->bitmask);
struct strv_test_data tdmax = {
.terminate_at = t->terminator,
.bitmask = { 0 },
};
rc = strv_for_each_n(array, max, strv_test_set_bitmask, &tdmax);
if (max < t->index)
ck_assert_int_eq(rc, 0);
else
ck_assert_int_eq(rc, t->index);
ck_assert_int_eq(tdmax.bitmask[0], t->bitmask & ((1 << max) - 1));
t++;
}
}
START_TEST(double_array_from_string_test)
{
struct double_array_from_string_test {
@ -1653,6 +1716,7 @@ litest_utils_suite(void)
tcase_add_test(tc, safe_atou_base_8_test);
tcase_add_test(tc, safe_atod_test);
tcase_add_test(tc, strsplit_test);
tcase_add_test(tc, strv_for_each_test);
tcase_add_test(tc, double_array_from_string_test);
tcase_add_test(tc, strargv_test);
tcase_add_test(tc, kvsplit_double_test);