summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buffer.c34
-rw-r--r--src/buffer.h19
-rw-r--r--tests-clar/core/buffer.c47
3 files changed, 100 insertions, 0 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 04aaec3df..d16b17160 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -141,6 +141,40 @@ int git_buf_puts(git_buf *buf, const char *string)
return git_buf_put(buf, string, strlen(string));
}
+int git_buf_puts_escaped(
+ git_buf *buf, const char *string, const char *esc_chars, const char *esc_with)
+{
+ const char *scan = string;
+ size_t total = 0, esc_with_len = strlen(esc_with);
+
+ while (*scan) {
+ size_t count = strcspn(scan, esc_chars);
+ total += count + 1 + esc_with_len;
+ scan += count + 1;
+ }
+
+ ENSURE_SIZE(buf, buf->size + total + 1);
+
+ for (scan = string; *scan; ) {
+ size_t count = strcspn(scan, esc_chars);
+
+ memmove(buf->ptr + buf->size, scan, count);
+ scan += count;
+ buf->size += count;
+
+ if (*scan) {
+ memmove(buf->ptr + buf->size, esc_with, esc_with_len);
+ buf->size += esc_with_len;
+
+ memmove(buf->ptr + buf->size, scan, 1);
+ scan += 1;
+ buf->size += 1;
+ }
+ }
+
+ return 0;
+}
+
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
int len;
diff --git a/src/buffer.h b/src/buffer.h
index 50c75f64e..75f3b0e4f 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -91,6 +91,18 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
/**
+ * Copy string into buf prefixing every character that is contained in the
+ * esc_chars string with the esc_with string.
+ */
+int git_buf_puts_escaped(
+ git_buf *buf, const char *string, const char *esc_chars, const char *esc_with);
+
+GIT_INLINE(int) git_buf_puts_escape_regex(git_buf *buf, const char *string)
+{
+ return git_buf_puts_escaped(buf, string, "^.[]$()|*+?{}\\", "\\");
+}
+
+/**
* Join two strings as paths, inserting a slash between as needed.
* @return 0 on success, -1 on failure
*/
@@ -121,6 +133,13 @@ GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
return idx;
}
+GIT_INLINE(ssize_t) git_buf_rfind(git_buf *buf, char ch)
+{
+ ssize_t idx = (ssize_t)buf->size - 1;
+ while (idx >= 0 && buf->ptr[idx] != ch) idx--;
+ return idx;
+}
+
/* Remove whitespace from the end of the buffer */
void git_buf_rtrim(git_buf *buf);
diff --git a/tests-clar/core/buffer.c b/tests-clar/core/buffer.c
index 6a718f459..996cb7bcb 100644
--- a/tests-clar/core/buffer.c
+++ b/tests-clar/core/buffer.c
@@ -611,3 +611,50 @@ void test_core_buffer__11(void)
git_buf_free(&a);
}
+
+void test_core_buffer__rfind_variants(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ ssize_t len;
+
+ cl_git_pass(git_buf_sets(&a, "/this/is/it/"));
+
+ len = (ssize_t)git_buf_len(&a);
+
+ cl_assert(git_buf_rfind(&a, '/') == len - 1);
+ cl_assert(git_buf_rfind_next(&a, '/') == len - 4);
+
+ cl_assert(git_buf_rfind(&a, 'i') == len - 3);
+ cl_assert(git_buf_rfind_next(&a, 'i') == len - 3);
+
+ cl_assert(git_buf_rfind(&a, 'h') == 2);
+ cl_assert(git_buf_rfind_next(&a, 'h') == 2);
+
+ cl_assert(git_buf_rfind(&a, 'q') == -1);
+ cl_assert(git_buf_rfind_next(&a, 'q') == -1);
+
+ git_buf_clear(&a);
+}
+
+void test_core_buffer__puts_escaped(void)
+{
+ git_buf a = GIT_BUF_INIT;
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "", ""));
+ cl_assert_equal_s("this is a test", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "t", "\\"));
+ cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_puts_escaped(&a, "this is a test", "i ", "__"));
+ cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
+
+ git_buf_clear(&a);
+ cl_git_pass(git_buf_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
+ cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);
+
+ git_buf_free(&a);
+}