From d3d95d5ae2c0c06724d040713a04202073114041 Mon Sep 17 00:00:00 2001 From: Edward Thomson Date: Wed, 23 Sep 2015 16:30:48 -0400 Subject: [PATCH] git_buf_quote: quote ugly characters --- src/buffer.c | 66 +++++++++++++++++++++++++++++++++++++++ src/buffer.h | 3 +- tests/buf/quote.c | 78 +++++++++++++++++++++++++++++++---------------- 3 files changed, 120 insertions(+), 27 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index c2a54a5bd..31341c4b5 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -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("ed, '"'); + git_buf_put("ed, 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("ed, '\\'); + git_buf_putc("ed, whitespace[buf->ptr[i] - '\a']); + } + + /* double quote and backslash must be escaped */ + else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') { + git_buf_putc("ed, '\\'); + git_buf_putc("ed, buf->ptr[i]); + } + + /* escape anything unprintable as octal */ + else if (buf->ptr[i] != ' ' && + (buf->ptr[i] < '!' || buf->ptr[i] > '~')) { + git_buf_printf("ed, "\\%03o", buf->ptr[i]); + } + + /* yay, printable! */ + else { + git_buf_putc("ed, buf->ptr[i]); + } + } + + git_buf_putc("ed, '"'); + + if (git_buf_oom("ed)) { + error = -1; + goto done; + } + + git_buf_swap("ed, buf); + +done: + git_buf_free("ed); + return error; +} + /* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */ int git_buf_unquote(git_buf *buf) { diff --git a/src/buffer.h b/src/buffer.h index 2be299b14..cdfca6d99 100644 --- a/src/buffer.h +++ b/src/buffer.h @@ -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 */ diff --git a/tests/buf/quote.c b/tests/buf/quote.c index ef2611663..ed5021e25 100644 --- a/tests/buf/quote.c +++ b/tests/buf/quote.c @@ -1,7 +1,33 @@ #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\""); }