]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
strbuf: Add `strbuf_print_restricted`.
authorFlorian Forster <octo@collectd.org>
Thu, 21 Dec 2023 10:38:20 +0000 (11:38 +0100)
committerFlorian Forster <octo@collectd.org>
Thu, 21 Dec 2023 11:59:59 +0000 (12:59 +0100)
This new function is similar to `strbuf_print_escaped` but differs in two
important aspects:

*    `strbuf_print_restricted` expects a list of acceptable characters,
    i.e. an allow list. `strbuf_print_escaped` expects a deny list.
*    `strbuf_print_restricted` *replaces* characters not in the allow list.
     `strbuf_print_escaped` adds an escape character in front of the
    denied character.

src/utils/strbuf/strbuf.c
src/utils/strbuf/strbuf.h
src/utils/strbuf/strbuf_test.c

index 3e4db07a436672187a2d859ad1ef0b598571e87a..6f1a9f06a928b6b65ad738447b8722719059e958 100644 (file)
@@ -289,3 +289,46 @@ int strbuf_print_escaped(strbuf_t *buf, char const *s, char const *need_escape,
 
   return 0;
 }
+
+int strbuf_print_restricted(strbuf_t *buf, char const *s, char const *accept,
+                            char replace_char) {
+  if (buf == NULL || s == NULL || accept == NULL || accept[0] == 0 || replace_char == 0) {
+    return EINVAL;
+  }
+  if (strchr(accept, replace_char) == NULL) {
+    return EINVAL;
+  }
+
+  size_t s_len = strlen(s);
+  if (s_len == 0) {
+    return strbuf_print(buf, s);
+  }
+
+  while (s_len > 0) {
+    size_t valid_len = strspn(s, accept);
+    if (valid_len == s_len) {
+      return strbuf_print(buf, s);
+    }
+    if (valid_len != 0) {
+      int status = strbuf_printn(buf, s, valid_len);
+      if (status != 0) {
+        return status;
+      }
+
+      s += valid_len;
+      s_len -= valid_len;
+      continue;
+    }
+
+    char tmp[] = {replace_char, 0};
+    int status = strbuf_print(buf, tmp);
+    if (status != 0) {
+      return status;
+    }
+
+    s++;
+    s_len--;
+  }
+
+  return 0;
+}
index c3a95fae2b31d830786f5b7b28e73056c5f8940f..f836287fee230fb434645281718d28da1f7aa8c5 100644 (file)
@@ -104,4 +104,10 @@ int strbuf_printn(strbuf_t *buf, char const *s, size_t n);
 int strbuf_print_escaped(strbuf_t *buf, char const *s, char const *need_escape,
                          char escape_char);
 
+/* strbuf_print_restricted adds a copy of "s" to the buffer, that only consists
+ * of characters in "accept". All other characters are replaced with
+ * "replace_char". "replace_char" has to be in "accept". */
+int strbuf_print_restricted(strbuf_t *buf, char const *s, char const *accept,
+                            char replace_char);
+
 #endif
index cd25b93cffbad742d1f55f18187291399e367422..9078b4bcd725dd4886fdeca7da99ffd2e8a2c535 100644 (file)
@@ -177,6 +177,100 @@ DEF_TEST(print_escaped) {
   return 0;
 }
 
+DEF_TEST(print_restricted) {
+  struct {
+    char const *name;
+    char const *s;
+    char const *accept;
+    char replace_char;
+    char const *want;
+    int want_err;
+  } cases[] = {
+      {
+          .name = "no replacement",
+          .s = "normal string",
+          .accept = "abcdefghijklmnopqrstuvwxyz ",
+          .replace_char = ' ',
+          .want = "normal string",
+      },
+      {
+          .name = "single replacement",
+          .s = "normal string",
+          .accept = "abcdefghijklmnopqrstuvwxyz_",
+          .replace_char = '_',
+          .want = "normal_string",
+      },
+      {
+          .name = "double replacement",
+          .s = "normal, string",
+          .accept = "abcdefghijklmnopqrstuvwxyz_",
+          .replace_char = '_',
+          .want = "normal__string",
+      },
+      {
+          .name = "empty string",
+          .s = "",
+          .accept = "abcdefghijklmnopqrstuvwxyz_",
+          .replace_char = '_',
+          .want = "",
+      },
+      {
+          .name = "s is NULL",
+          .s = NULL,
+          .accept = "abcdefghijklmnopqrstuvwxyz_",
+          .replace_char = '_',
+          .want_err = EINVAL,
+      },
+      {
+          .name = "accept is empty",
+          .s = "normal string",
+          .accept = "",
+          .replace_char = '_',
+          .want_err = EINVAL,
+      },
+      {
+          .name = "accept is NULL",
+          .s = "normal string",
+          .accept = NULL,
+          .replace_char = '_',
+          .want_err = EINVAL,
+      },
+      {
+          .name = "replace char is not in accept",
+          .s = "normal string",
+          .accept = "abcdefghijklmnopqrstuvwxyz_",
+          .replace_char = '@',
+          .want_err = EINVAL,
+      },
+      {
+          .name = "replace char is zero",
+          .s = "normal string",
+          .accept = "abcdefghijklmnopqrstuvwxyz",
+          .replace_char = 0,
+          .want_err = EINVAL,
+      },
+  };
+
+  for (size_t i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
+    printf("# Case %zu: %s\n", i, cases[i].name);
+    strbuf_t buf = STRBUF_CREATE;
+
+    EXPECT_EQ_INT(cases[i].want_err,
+                  strbuf_print_restricted(&buf, cases[i].s, cases[i].accept,
+                                          cases[i].replace_char));
+    EXPECT_EQ_STR(cases[i].want, buf.ptr);
+    if (cases[i].want_err == 0) {
+      /* The string in buf has to have the same length as "s" and must entirely
+       * consist of characters in "accept". */
+      EXPECT_EQ_UINT64(strlen(cases[i].s), strspn(buf.ptr, cases[i].accept));
+    }
+
+    STRBUF_DESTROY(buf);
+  }
+
+  return 0;
+}
+
 int main(int argc, char **argv) /* {{{ */
 {
   RUN_TEST(dynamic_heap);
@@ -185,6 +279,7 @@ int main(int argc, char **argv) /* {{{ */
   RUN_TEST(fixed_stack);
   RUN_TEST(static_stack);
   RUN_TEST(print_escaped);
+  RUN_TEST(print_restricted);
 
   END_TEST;
 } /* }}} int main */