Commit d3d95d5a by Edward Thomson Committed by Edward Thomson

git_buf_quote: quote ugly characters

parent 72806f4c
......@@ -858,6 +858,72 @@ int git_buf_splice(
return 0;
}
/* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
int git_buf_quote(git_buf *buf)
{
const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
git_buf quoted = GIT_BUF_INIT;
size_t i = 0;
bool quote = false;
int error = 0;
/* walk to the first char that needs quoting */
if (buf->size && buf->ptr[0] == '!')
quote = true;
for (i = 0; !quote && i < buf->size; i++) {
if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
quote = true;
break;
}
}
if (!quote)
goto done;
git_buf_putc(&quoted, '"');
git_buf_put(&quoted, buf->ptr, i);
for (; i < buf->size; i++) {
/* whitespace - use the map above, which is ordered by ascii value */
if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
git_buf_putc(&quoted, '\\');
git_buf_putc(&quoted, whitespace[buf->ptr[i] - '\a']);
}
/* double quote and backslash must be escaped */
else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
git_buf_putc(&quoted, '\\');
git_buf_putc(&quoted, buf->ptr[i]);
}
/* escape anything unprintable as octal */
else if (buf->ptr[i] != ' ' &&
(buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
git_buf_printf(&quoted, "\\%03o", buf->ptr[i]);
}
/* yay, printable! */
else {
git_buf_putc(&quoted, buf->ptr[i]);
}
}
git_buf_putc(&quoted, '"');
if (git_buf_oom(&quoted)) {
error = -1;
goto done;
}
git_buf_swap(&quoted, buf);
done:
git_buf_free(&quoted);
return error;
}
/* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
int git_buf_unquote(git_buf *buf)
{
......
......@@ -173,9 +173,10 @@ void git_buf_rtrim(git_buf *buf);
int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Unquote a buffer as specified in
/* Quote and unquote a buffer as specified in
* http://marc.info/?l=git&m=112927316408690&w=2
*/
int git_buf_quote(git_buf *buf);
int git_buf_unquote(git_buf *buf);
/* Write data as base64 encoded in buffer */
......
#include "clar_libgit2.h"
#include "buffer.h"
static void expect_pass(const char *expected, const char *quoted)
static void expect_quote_pass(const char *expected, const char *str)
{
git_buf buf = GIT_BUF_INIT;
cl_git_pass(git_buf_puts(&buf, str));
cl_git_pass(git_buf_quote(&buf));
cl_assert_equal_s(expected, git_buf_cstr(&buf));
cl_assert_equal_i(strlen(expected), git_buf_len(&buf));
git_buf_free(&buf);
}
void test_buf_quote__quote_succeeds(void)
{
expect_quote_pass("", "");
expect_quote_pass("foo", "foo");
expect_quote_pass("foo/bar/baz.c", "foo/bar/baz.c");
expect_quote_pass("foo bar", "foo bar");
expect_quote_pass("\"\\\"leading quote\"", "\"leading quote");
expect_quote_pass("\"slash\\\\y\"", "slash\\y");
expect_quote_pass("\"foo\\r\\nbar\"", "foo\r\nbar");
expect_quote_pass("\"foo\\177bar\"", "foo\177bar");
expect_quote_pass("\"foo\\001bar\"", "foo\001bar");
}
static void expect_unquote_pass(const char *expected, const char *quoted)
{
git_buf buf = GIT_BUF_INIT;
......@@ -14,7 +40,7 @@ static void expect_pass(const char *expected, const char *quoted)
git_buf_free(&buf);
}
static void expect_fail(const char *quoted)
static void expect_unquote_fail(const char *quoted)
{
git_buf buf = GIT_BUF_INIT;
......@@ -26,32 +52,32 @@ static void expect_fail(const char *quoted)
void test_buf_quote__unquote_succeeds(void)
{
expect_pass("", "\"\"");
expect_pass(" ", "\" \"");
expect_pass("foo", "\"foo\"");
expect_pass("foo bar", "\"foo bar\"");
expect_pass("foo\"bar", "\"foo\\\"bar\"");
expect_pass("foo\\bar", "\"foo\\\\bar\"");
expect_pass("foo\tbar", "\"foo\\tbar\"");
expect_pass("\vfoo\tbar\n", "\"\\vfoo\\tbar\\n\"");
expect_pass("foo\nbar", "\"foo\\012bar\"");
expect_pass("foo\r\nbar", "\"foo\\015\\012bar\"");
expect_pass("foo\r\nbar", "\"\\146\\157\\157\\015\\012\\142\\141\\162\"");
expect_pass("newline: \n", "\"newline: \\012\"");
expect_unquote_pass("", "\"\"");
expect_unquote_pass(" ", "\" \"");
expect_unquote_pass("foo", "\"foo\"");
expect_unquote_pass("foo bar", "\"foo bar\"");
expect_unquote_pass("foo\"bar", "\"foo\\\"bar\"");
expect_unquote_pass("foo\\bar", "\"foo\\\\bar\"");
expect_unquote_pass("foo\tbar", "\"foo\\tbar\"");
expect_unquote_pass("\vfoo\tbar\n", "\"\\vfoo\\tbar\\n\"");
expect_unquote_pass("foo\nbar", "\"foo\\012bar\"");
expect_unquote_pass("foo\r\nbar", "\"foo\\015\\012bar\"");
expect_unquote_pass("foo\r\nbar", "\"\\146\\157\\157\\015\\012\\142\\141\\162\"");
expect_unquote_pass("newline: \n", "\"newline: \\012\"");
}
void test_buf_quote__unquote_fails(void)
{
expect_fail("no quotes at all");
expect_fail("\"no trailing quote");
expect_fail("no leading quote\"");
expect_fail("\"invalid \\z escape char\"");
expect_fail("\"\\q invalid escape char\"");
expect_fail("\"invalid escape char \\p\"");
expect_fail("\"invalid \\1 escape char \"");
expect_fail("\"invalid \\14 escape char \"");
expect_fail("\"invalid \\411 escape char\"");
expect_fail("\"truncated escape char \\\"");
expect_fail("\"truncated escape char \\0\"");
expect_fail("\"truncated escape char \\01\"");
expect_unquote_fail("no quotes at all");
expect_unquote_fail("\"no trailing quote");
expect_unquote_fail("no leading quote\"");
expect_unquote_fail("\"invalid \\z escape char\"");
expect_unquote_fail("\"\\q invalid escape char\"");
expect_unquote_fail("\"invalid escape char \\p\"");
expect_unquote_fail("\"invalid \\1 escape char \"");
expect_unquote_fail("\"invalid \\14 escape char \"");
expect_unquote_fail("\"invalid \\411 escape char\"");
expect_unquote_fail("\"truncated escape char \\\"");
expect_unquote_fail("\"truncated escape char \\0\"");
expect_unquote_fail("\"truncated escape char \\01\"");
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment