Commit dff05bc308f40fe327364334eaae412afca8ad61

Sven Strickroth 2021-11-25T11:40:20

Multiline config values not preserved on saving (fixes issue #6088) Signed-off-by: Sven Strickroth <email@cs-ware.de>

diff --git a/src/config_parse.c b/src/config_parse.c
index e96edc3..0693136 100644
--- a/src/config_parse.c
+++ b/src/config_parse.c
@@ -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/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);
+}