str: introduce git_str_shellquote

Introduce a helper method to quote a string in a shellsafe manner.  This
wraps the entire buffer in single quotes, escaping single-quotes and
exclamation points.
This commit is contained in:
Edward Thomson
2025-02-18 14:15:34 +00:00
parent f327ee09f2
commit 31fa5617eb
3 changed files with 72 additions and 0 deletions

View File

@@ -1415,3 +1415,40 @@ done:
git_str_dispose(&replaced);
return error;
}
int git_str_shellquote(git_str *buf)
{
git_str quoted = GIT_STR_INIT;
size_t i;
int error = 0;
ENSURE_SIZE(&quoted, buf->size);
git_str_putc(&quoted, '\'');
for (i = 0; i < buf->size; i++) {
switch (buf->ptr[i]) {
case '\'':
case '!':
git_str_puts(&quoted, "'\\");
git_str_putc(&quoted, buf->ptr[i]);
git_str_putc(&quoted, '\'');
break;
default:
git_str_putc(&quoted, buf->ptr[i]);
}
}
git_str_putc(&quoted, '\'');
if (git_str_oom(&quoted)) {
error = -1;
goto done;
}
git_str_swap(&quoted, buf);
done:
git_str_dispose(&quoted);
return error;
}

View File

@@ -366,4 +366,10 @@ int git_str_replace(
const char *replacements[][2],
size_t replacements_len);
/**
* Quote for shell safety. Wrap the given buffer in single quotes,
* escaping any single quotes and exclamation points.
*/
int git_str_shellquote(git_str *buf);
#endif

View File

@@ -64,3 +64,32 @@ void test_str_basic__replace(void)
git_str_dispose(&buf);
}
void test_str_basic__shellquote(void)
{
git_str buf = GIT_BUF_INIT;
cl_git_pass(git_str_puts(&buf, "filename"));
cl_git_pass(git_str_shellquote(&buf));
cl_assert_equal_s("\'filename\'", buf.ptr);
git_str_clear(&buf);
cl_git_pass(git_str_puts(&buf, "file name"));
cl_git_pass(git_str_shellquote(&buf));
cl_assert_equal_s("\'file name\'", buf.ptr);
git_str_clear(&buf);
cl_git_pass(git_str_puts(&buf, "file\'name"));
cl_git_pass(git_str_shellquote(&buf));
cl_assert_equal_s("\'file\'\\\'\'name\'", buf.ptr);
git_str_clear(&buf);
cl_git_pass(git_str_puts(&buf, "file!name"));
cl_git_pass(git_str_shellquote(&buf));
cl_assert_equal_s("\'file\'\\!\'name\'", buf.ptr);
git_str_dispose(&buf);
}