util: add a strv_for_each helper function
authorPeter Hutterer <peter.hutterer@who-t.net>
Tue, 3 Sep 2024 04:31:02 +0000 (14:31 +1000)
committerMarge Bot <emma+marge@anholt.net>
Thu, 5 Sep 2024 13:54:07 +0000 (13:54 +0000)
Part-of: <https://gitlab.freedesktop.org/libinput/libinput/-/merge_requests/1038>

src/util-strings.c
src/util-strings.h
test/test-utils.c

index 1f9b8a748422d902752da2428253384a0e2cba67..8732cc5e6c3923bf27171fb3b73c5f9f7cc77a94 100644 (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
index b09168152c3f855a0edf9ccdf2119fff2330fb05..85e9081dbb0af953521cf03adc43a2d464a3baf3 100644 (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;
index 8ac0bd664a30e894b2e30b3c43c001aff07b73b5..ee9cc606aa0d569119906e889e75b871ab4b8f3e 100644 (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);