]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
strbuf: Implenet "strbuf_printn" and "strbuf_print_escaped".
authorFlorian Forster <octo@collectd.org>
Sun, 21 Jun 2020 19:23:22 +0000 (21:23 +0200)
committerFlorian Forster <octo@google.com>
Tue, 14 Jul 2020 17:18:50 +0000 (19:18 +0200)
src/utils/strbuf/strbuf.c
src/utils/strbuf/strbuf.h
src/utils/strbuf/strbuf_test.c

index 80281715803dd63923cad4c8002520298dd50981..b0b5da186a8a74a7800738d5abf7899c4aad1aa1 100644 (file)
@@ -196,3 +196,82 @@ int strbuf_printf(strbuf_t *buf, char const *format, ...) {
 
   return 0;
 }
+
+int strbuf_printn(strbuf_t *buf, char const *s, size_t n) {
+  if ((buf == NULL) || (s == NULL))
+    return EINVAL;
+  if (n == 0) {
+    return 0;
+  }
+
+  size_t s_len = strnlen(s, n);
+  int status = strbuf_resize(buf, s_len);
+  if (status != 0)
+    return status;
+
+  size_t bytes = strbuf_avail(buf);
+  if (bytes == 0)
+    return ENOSPC;
+
+  if (bytes > s_len)
+    bytes = s_len;
+
+  memmove(buf->ptr + buf->pos, s, bytes);
+  buf->pos += bytes;
+  buf->ptr[buf->pos] = 0;
+
+  return 0;
+}
+
+int strbuf_print_escaped(strbuf_t *buf, char const *s, char const *need_escape,
+                         char escape_char) {
+  if ((buf == NULL) || (s == NULL) || (need_escape == NULL) ||
+      (escape_char == 0)) {
+    return EINVAL;
+  }
+
+  size_t s_len = strlen(s);
+  while (s_len > 0) {
+    size_t valid_len = strcspn(s, need_escape);
+    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;
+    }
+
+    /* Ensure the escape sequence is not truncated. */
+    if (buf->fixed && (strbuf_avail(buf) < 2)) {
+      return 0;
+    }
+
+    char c = s[0];
+    if (escape_char == '\\') {
+      if (c == '\n') {
+        c = 'n';
+      } else if (c == '\r') {
+        c = 'r';
+      } else if (c == '\t') {
+        c = 't';
+      }
+    }
+
+    char tmp[3] = {escape_char, c, 0};
+    int status = strbuf_print(buf, tmp);
+    if (status != 0) {
+      return status;
+    }
+
+    s++;
+    s_len--;
+  }
+
+  return 0;
+}
index 3bdde162f1ef1c6ceb9cfa8e591183c296058569..85664a295ed0bbd1e913edff199eb5e5fbc90e38 100644 (file)
@@ -90,4 +90,16 @@ int strbuf_print(strbuf_t *buf, char const *s);
  * is returned. */
 int strbuf_printf(strbuf_t *buf, char const *format, ...);
 
+/* strbuf_printn adds at most n bytes from "s" to the buffer.  If the size of
+ * the buffer is static and there is no space available in the buffer, ENOSPC
+ * is returned. */
+int strbuf_printn(strbuf_t *buf, char const *s, size_t n);
+
+/* strbuf_print_escaped adds an escaped copy of "s" to the buffer. Each
+ * character in "need_escape" is prefixed by "escape_char". If "escape_char" is
+ * '\' (backslash), newline (\n), cartridge return (\r) and tab (\t) are
+ * handled correctly. */
+int strbuf_print_escaped(strbuf_t *buf, char const *s, char const *need_escape,
+                         char escape_char);
+
 #endif
index b0ecf036b894be933af35c2e0ce9727dac5d4769..cd25b93cffbad742d1f55f18187291399e367422 100644 (file)
@@ -55,6 +55,12 @@ int test_buffer(strbuf_t *buf, bool is_static) {
   want = is_static ? "new cont" : "new content";
   EXPECT_EQ_STR(want, buf->ptr);
 
+  strbuf_reset(buf);
+  CHECK_ZERO(strlen(buf->ptr));
+
+  CHECK_ZERO(strbuf_printn(buf, "foobar", 3));
+  EXPECT_EQ_STR("foo", buf->ptr);
+
   return 0;
 }
 
@@ -115,6 +121,62 @@ DEF_TEST(static_stack) {
   return status;
 }
 
+DEF_TEST(print_escaped) {
+  struct {
+    char const *s;
+    char const *need_escape;
+    char escape_char;
+    char const *want;
+  } cases[] = {
+      {
+          .s = "normal string",
+          .need_escape = "\\\"\n\r\t",
+          .escape_char = '\\',
+          .want = "normal string",
+      },
+      {
+          .s = "\"special\"\n",
+          .need_escape = "\\\"\n\r\t",
+          .escape_char = '\\',
+          .want = "\\\"special\\\"\\n",
+      },
+      {
+          /* string gets truncated */
+          .s = "0123456789ABCDEF",
+          .need_escape = ">",
+          .escape_char = '<',
+          .want = "0123456789ABCDE",
+      },
+      {
+          /* string gets truncated */
+          .s = "0123456789>BCDEF",
+          .need_escape = ">",
+          .escape_char = '<',
+          .want = "0123456789<>BCD",
+      },
+      {
+          /* truncation between escape_char and to-be-escaped char. */
+          .s = "0123456789ABCD>F",
+          .need_escape = ">",
+          .escape_char = '<',
+          .want = "0123456789ABCD",
+      },
+  };
+
+  for (size_t i = 0; i < (sizeof(cases) / sizeof(cases[0])); i++) {
+    char mem[16] = {0};
+    strbuf_t buf = STRBUF_CREATE_STATIC(mem);
+
+    CHECK_ZERO(strbuf_print_escaped(&buf, cases[i].s, cases[i].need_escape,
+                                    cases[i].escape_char));
+    EXPECT_EQ_STR(cases[i].want, buf.ptr);
+
+    STRBUF_DESTROY(buf);
+  }
+
+  return 0;
+}
+
 int main(int argc, char **argv) /* {{{ */
 {
   RUN_TEST(dynamic_heap);
@@ -122,6 +184,7 @@ int main(int argc, char **argv) /* {{{ */
   RUN_TEST(dynamic_stack);
   RUN_TEST(fixed_stack);
   RUN_TEST(static_stack);
+  RUN_TEST(print_escaped);
 
   END_TEST;
 } /* }}} int main */