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;
+}
* 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
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;
}
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);
RUN_TEST(dynamic_stack);
RUN_TEST(fixed_stack);
RUN_TEST(static_stack);
+ RUN_TEST(print_escaped);
END_TEST;
} /* }}} int main */