Commit 72946881b598c133ff1d522d06c083690e260947

Carlos Martín Nieto 2011-04-04T15:26:43

config: support multiline values If a variable value has the traditional continuation character (\) as the last non-space character in the line, then we continue reading the value on the next line. Using more than two lines is also supported. Signed-off-by: Carlos Martín Nieto <cmn@elego.de>

diff --git a/src/config.c b/src/config.c
index 8c509be..0052c65 100644
--- a/src/config.c
+++ b/src/config.c
@@ -924,10 +924,85 @@ static int config_parse(git_config *cfg_file)
 	return error;
 }
 
+static int is_multiline_var(const char *str)
+{
+	char *end = strrchr(str, '\0') - 1;
+
+	while (isspace(*end))
+		--end;
+
+	return *end == '\\';
+}
+
+static int parse_multiline_variable(git_config *cfg, const char *first, char **out)
+{
+	char *line = NULL, *end;
+	int error = GIT_SUCCESS, len, ret;
+	char *buf;
+
+	/* Check that the next line exists */
+	line = cfg_readline(cfg);
+	if (line == NULL)
+		return GIT_ENOMEM;
+
+	/* We've reached the end of the file, there is input missing */
+	if (line[0] == '\0') {
+		error = GIT_EOBJCORRUPTED;
+		goto out;
+	}
+
+	strip_comments(line);
+
+	/* If it was just a comment, pretend it didn't exist */
+	if (line[0] == '\0') {
+		error = parse_multiline_variable(cfg, first, out);
+		goto out;
+	}
+
+	/* Find the continuation character '\' and strip the whitespace */
+	end = strrchr(first, '\\');
+	while (isspace(end[-1]))
+		--end;
+
+	*end = '\0'; /* Terminate the string here */
+
+	len = strlen(first) + strlen(line) + 2;
+	buf = git__malloc(len);
+	if (buf == NULL) {
+		error = GIT_ENOMEM;
+		goto out;
+	}
+
+	ret = snprintf(buf, len, "%s %s", first, line);
+	if (ret < 0) {
+		error = GIT_EOSERR;
+		free(buf);
+		goto out;
+	}
+
+	/*
+	 * If we need to continue reading the next line, pretend
+	 * everything we've read up to now was in one line and call
+	 * ourselves.
+	 */
+	if (is_multiline_var(buf)) {
+		char *final_val;
+		error = parse_multiline_variable(cfg, buf, &final_val);
+		free(buf);
+		buf = final_val;
+	}
+
+	*out = buf;
+
+ out:
+	free(line);
+	return error;
+}
+
 static int parse_variable(git_config *cfg, char **var_name, char **var_value)
 {
 	char *tmp;
-
+	int error = GIT_SUCCESS;
 	const char *var_end = NULL;
 	const char *value_start = NULL;
 	char *line;
@@ -950,36 +1025,43 @@ static int parse_variable(git_config *cfg, char **var_name, char **var_value)
 		while (isspace(var_end[0]));
 	}
 
+	tmp = strndup(line, var_end - line + 1);
+	if (tmp == NULL) {
+		error = GIT_ENOMEM;
+		goto out;
+	}
+
+	*var_name = tmp;
+
+	/*
+	 * Now, let's try to parse the value
+	 */
 	if (value_start != NULL) {
 
 		while (isspace(value_start[0]))
 			value_start++;
 
 		if (value_start[0] == '\0')
-			goto error;
-	}
-
-	tmp = strndup(line, var_end - line + 1);
-	if (tmp == NULL)
-		return GIT_ENOMEM;
+			goto out;
 
-	*var_name = tmp;
+		if (is_multiline_var(value_start)) {
+			error = parse_multiline_variable(cfg, value_start, var_value);
+			if (error < GIT_SUCCESS)
+				free(*var_name);
+			goto out;
+		}
 
-	if (value_start != NULL) {
 		tmp = strdup(value_start);
 		if (tmp == NULL) {
 			free(*var_name);
-			return GIT_ENOMEM;
+			error = GIT_ENOMEM;
+			goto out;
 		}
 
 		*var_value = tmp;
 	}
 
+ out:
 	free(line);
-
-	return GIT_SUCCESS;
-
-error:
-	free(line);
-	return GIT_EOBJCORRUPTED;
+	return error;
 }