git_buf_quote: quote ugly characters
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
diff --git a/src/buffer.c b/src/buffer.c
index c2a54a5..31341c4 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 2be299b..cdfca6d 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 ef26116..ed5021e 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\"");
}