str: allow escaping with prefix and suffix

Allow `git_str_puts_escaped` to take an escaping prefix and an escaping
suffix; this allows for more options, including the ability to better
support escaping executed paths.
This commit is contained in:
Edward Thomson
2025-10-13 22:30:52 +01:00
parent 0618182268
commit d0723d20d7
3 changed files with 42 additions and 12 deletions

View File

@@ -1065,10 +1065,13 @@ int git_str_puts_escaped(
git_str *buf,
const char *string,
const char *esc_chars,
const char *esc_with)
const char *esc_prefix,
const char *esc_suffix)
{
const char *scan;
size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
size_t total = 0, count, alloclen;
size_t esc_prefix_len = esc_prefix ? strlen(esc_prefix) : 0;
size_t esc_suffix_len = esc_suffix ? strlen(esc_suffix) : 0;
if (!string)
return 0;
@@ -1080,7 +1083,7 @@ int git_str_puts_escaped(
scan += count;
/* count run of escaped characters */
count = strspn(scan, esc_chars);
total += count * (esc_len + 1);
total += count * (esc_prefix_len + esc_suffix_len + 1);
scan += count;
}
@@ -1096,13 +1099,22 @@ int git_str_puts_escaped(
buf->size += count;
for (count = strspn(scan, esc_chars); count > 0; --count) {
/* copy escape sequence */
memmove(buf->ptr + buf->size, esc_with, esc_len);
buf->size += esc_len;
/* copy escape prefix sequence */
if (esc_prefix) {
memmove(buf->ptr + buf->size, esc_prefix, esc_prefix_len);
buf->size += esc_prefix_len;
}
/* copy character to be escaped */
buf->ptr[buf->size] = *scan;
buf->size++;
scan++;
/* copy escape suffix sequence */
if (esc_suffix) {
memmove(buf->ptr + buf->size, esc_suffix, esc_suffix_len);
buf->size += esc_suffix_len;
}
}
}

View File

@@ -268,21 +268,23 @@ int git_str_splice(
* @param str String buffer to append data to
* @param string String to escape and append
* @param esc_chars Characters to be escaped
* @param esc_with String to insert in from of each found character
* @param esc_prefix String to insert as prefix of each found character
* @param esc_suffix String to insert as suffix of each found character
* @return 0 on success, <0 on failure (probably allocation problem)
*/
extern int git_str_puts_escaped(
git_str *str,
const char *string,
const char *esc_chars,
const char *esc_with);
const char *esc_prefix,
const char *esc_suffix);
/**
* Append string escaping characters that are regex special
*/
GIT_INLINE(int) git_str_puts_escape_regex(git_str *str, const char *string)
{
return git_str_puts_escaped(str, string, "^.[]$()|*+?{}\\", "\\");
return git_str_puts_escaped(str, string, "^.[]$()|*+?{}\\", "\\", NULL);
}
/**

View File

@@ -691,17 +691,33 @@ void test_gitstr__puts_escaped(void)
git_str a = GIT_STR_INIT;
git_str_clear(&a);
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "", ""));
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "", "", ""));
cl_assert_equal_s("this is a test", a.ptr);
git_str_clear(&a);
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "t", "\\"));
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "", NULL, NULL));
cl_assert_equal_s("this is a test", a.ptr);
git_str_clear(&a);
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "t", "\\", ""));
cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
git_str_clear(&a);
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "i ", "__"));
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "t", "\\", NULL));
cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
git_str_clear(&a);
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "i ", "__", NULL));
cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
git_str_clear(&a);
cl_git_pass(git_str_puts_escaped(&a, "this is a test", "i ", "__", "!!"));
cl_assert_equal_s("th__i!!s__ !!__i!!s__ !!a__ !!test", a.ptr);
git_str_clear(&a);
cl_git_pass(git_str_puts_escaped(&a, "this' is' an' escape! ", "'!", "'\\", "'"));
cl_assert_equal_s("this'\\'' is'\\'' an'\\'' escape'\\!' ", a.ptr);
git_str_clear(&a);
cl_git_pass(git_str_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);