Merge pull request #6124 from csware/config-parsing Config parsing
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
diff --git a/src/config_parse.c b/src/config_parse.c
index 3159cbe..0693136 100644
--- a/src/config_parse.c
+++ b/src/config_parse.c
@@ -36,7 +36,7 @@ static int strip_comments(char *line, int in_quotes)
char *ptr;
for (ptr = line; *ptr; ++ptr) {
- if (ptr[0] == '"' && ptr > line && ptr[-1] != '\\')
+ if (ptr[0] == '"' && ((ptr > line && ptr[-1] != '\\') || ptr == line))
quote_count++;
if ((ptr[0] == ';' || ptr[0] == '#') &&
@@ -325,7 +325,7 @@ done:
return 0;
}
-static int parse_multiline_variable(git_config_parser *reader, git_str *value, int in_quotes)
+static int parse_multiline_variable(git_config_parser *reader, git_str *value, int in_quotes, size_t *line_len)
{
int quote_count;
bool multiline = true;
@@ -338,6 +338,10 @@ static int parse_multiline_variable(git_config_parser *reader, git_str *value, i
git_parse_advance_line(&reader->ctx);
line = git__strndup(reader->ctx.line, reader->ctx.line_len);
GIT_ERROR_CHECK_ALLOC(line);
+ if (GIT_ADD_SIZET_OVERFLOW(line_len, *line_len, reader->ctx.line_len)) {
+ error = -1;
+ goto out;
+ }
/*
* We've reached the end of the file, there is no continuation.
@@ -415,7 +419,7 @@ static int parse_name(
return 0;
}
-static int parse_variable(git_config_parser *reader, char **var_name, char **var_value)
+static int parse_variable(git_config_parser *reader, char **var_name, char **var_value, size_t *line_len)
{
const char *value_start = NULL;
char *line = NULL, *name = NULL, *value = NULL;
@@ -449,7 +453,7 @@ static int parse_variable(git_config_parser *reader, char **var_name, char **var
git_str_attach(&multi_value, value, 0);
value = NULL;
- if (parse_multiline_variable(reader, &multi_value, quote_count % 2) < 0 ||
+ if (parse_multiline_variable(reader, &multi_value, quote_count % 2, line_len) < 0 ||
git_str_oom(&multi_value)) {
error = -1;
git_str_dispose(&multi_value);
@@ -554,7 +558,7 @@ int git_config_parse(
break;
default: /* assume variable declaration */
- if ((result = parse_variable(parser, &var_name, &var_value)) == 0 && on_variable) {
+ if ((result = parse_variable(parser, &var_name, &var_value, &line_len)) == 0 && on_variable) {
result = on_variable(parser, current_section, var_name, var_value, line_start, line_len, payload);
git__free(var_name);
git__free(var_value);
diff --git a/tests/config/read.c b/tests/config/read.c
index 3c96f85..a2e668c 100644
--- a/tests/config/read.c
+++ b/tests/config/read.c
@@ -209,6 +209,21 @@ void test_config_read__symbol_headers(void)
{
git_config *cfg;
cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config20")));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "valid.[subsection].something"));
+ cl_assert_equal_s("a", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "sec.[subsec]/child.parent"));
+ cl_assert_equal_s("grand", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "sec2.[subsec2]/child2.type"));
+ cl_assert_equal_s("dvcs", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "sec3.escape\"quote.vcs"));
+ cl_assert_equal_s("git", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "sec4.escaping\\slash.lib"));
+ cl_assert_equal_s("git2", buf.ptr);
+ git_buf_dispose(&buf);
git_config_free(cfg);
}
@@ -219,6 +234,19 @@ void test_config_read__multiline_multiple_quoted_comment_chars(void)
git_config_free(cfg);
}
+void test_config_read__multiline_multiple_quoted_quote_at_beginning_of_line(void)
+{
+ git_config* cfg;
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config22")));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "alias.m"));
+ cl_assert_equal_s("cmd ;; ;; bar", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "alias.m2"));
+ cl_assert_equal_s("'; ; something '", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
void test_config_read__header_in_last_line(void)
{
git_config *cfg;
diff --git a/tests/config/write.c b/tests/config/write.c
index 4ee8383..9d8c3fe 100644
--- a/tests/config/write.c
+++ b/tests/config/write.c
@@ -8,6 +8,7 @@ void test_config_write__initialize(void)
cl_fixture_sandbox("config/config9");
cl_fixture_sandbox("config/config15");
cl_fixture_sandbox("config/config17");
+ cl_fixture_sandbox("config/config22");
}
void test_config_write__cleanup(void)
@@ -15,6 +16,7 @@ void test_config_write__cleanup(void)
cl_fixture_cleanup("config9");
cl_fixture_cleanup("config15");
cl_fixture_cleanup("config17");
+ cl_fixture_cleanup("config22");
}
void test_config_write__replace_value(void)
@@ -743,3 +745,20 @@ void test_config_write__preserve_case(void)
git_config_free(cfg);
}
+
+void test_config_write__write_config_file_with_multi_line_value(void)
+{
+ git_config* cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config22"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "alias.m"));
+ cl_assert_equal_s("cmd ;; ;; bar", buf.ptr);
+ cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "alias.m"));
+ cl_assert_equal_s("cmd ;; ;; bar", buf.ptr);
+ git_buf_dispose(&buf);
+
+ git_config_free(cfg);
+}
diff --git a/tests/resources/config/config22 b/tests/resources/config/config22
new file mode 100644
index 0000000..2a8e528
--- /dev/null
+++ b/tests/resources/config/config22
@@ -0,0 +1,10 @@
+[alias]
+ m = cmd \
+";;" \
+";;" \
+bar
+ m2 = '\
+";" \
+";" \
+something \
+'