Commit 4110fc8444080aa50449630b634d7337c30924b8

Edward Thomson 2017-12-23T23:30:29

Merge pull request #4285 from pks-t/pks/patches-with-whitespace patch_parse: fix parsing unquoted filenames with spaces

diff --git a/src/patch_parse.c b/src/patch_parse.c
index 48afcc1..27c01e9 100644
--- a/src/patch_parse.c
+++ b/src/patch_parse.c
@@ -53,11 +53,9 @@ static int header_path_len(git_patch_parse_ctx *ctx)
 	return len;
 }
 
-static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx)
+static int parse_header_path_buf(git_buf *path, git_patch_parse_ctx *ctx, size_t path_len)
 {
-	int path_len, error = 0;
-
-	path_len = header_path_len(ctx);
+	int error;
 
 	if ((error = git_buf_put(path, ctx->parse_ctx.line, path_len)) < 0)
 		goto done;
@@ -81,7 +79,7 @@ done:
 static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
 {
 	git_buf path = GIT_BUF_INIT;
-	int error = parse_header_path_buf(&path, ctx);
+	int error = parse_header_path_buf(&path, ctx, header_path_len(ctx));
 
 	*out = git_buf_detach(&path);
 
@@ -91,13 +89,33 @@ static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
 static int parse_header_git_oldpath(
 	git_patch_parsed *patch, git_patch_parse_ctx *ctx)
 {
-	return parse_header_path(&patch->old_path, ctx);
+	git_buf old_path = GIT_BUF_INIT;
+	int error;
+
+	if ((error = parse_header_path_buf(&old_path, ctx, ctx->parse_ctx.line_len - 1)) <  0)
+		goto out;
+
+	patch->old_path = git_buf_detach(&old_path);
+
+out:
+	git_buf_free(&old_path);
+	return error;
 }
 
 static int parse_header_git_newpath(
 	git_patch_parsed *patch, git_patch_parse_ctx *ctx)
 {
-	return parse_header_path(&patch->new_path, ctx);
+	git_buf new_path = GIT_BUF_INIT;
+	int error;
+
+	if ((error = parse_header_path_buf(&new_path, ctx, ctx->parse_ctx.line_len - 1)) <  0)
+		goto out;
+
+	patch->new_path = git_buf_detach(&new_path);
+
+out:
+	git_buf_free(&new_path);
+	return error;
 }
 
 static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx)
@@ -213,7 +231,7 @@ static int parse_header_rename(
 {
 	git_buf path = GIT_BUF_INIT;
 
-	if (parse_header_path_buf(&path, ctx) < 0)
+	if (parse_header_path_buf(&path, ctx, header_path_len(ctx)) < 0)
 		return -1;
 
 	/* Note: the `rename from` and `rename to` lines include the literal
@@ -303,6 +321,22 @@ static int parse_header_start(git_patch_parsed *patch, git_patch_parse_ctx *ctx)
 		return git_parse_err("corrupt new path in git diff header at line %"PRIuZ,
 			ctx->parse_ctx.line_num);
 
+	/*
+	 * We cannot expect to be able to always parse paths correctly at this
+	 * point. Due to the possibility of unquoted names, whitespaces in
+	 * filenames and custom prefixes we have to allow that, though, and just
+	 * proceeed here. We then hope for the "---" and "+++" lines to fix that
+	 * for us.
+	 */
+	if (!git_parse_ctx_contains(&ctx->parse_ctx, "\n", 1)) {
+		git_parse_advance_chars(&ctx->parse_ctx, ctx->parse_ctx.line_len - 1);
+
+		git__free(patch->header_old_path);
+		patch->header_old_path = NULL;
+		git__free(patch->header_new_path);
+		patch->header_new_path = NULL;
+	}
+
 	return 0;
 }
 
diff --git a/tests/patch/parse.c b/tests/patch/parse.c
index 8350ac2..a40ad7b 100644
--- a/tests/patch/parse.c
+++ b/tests/patch/parse.c
@@ -102,3 +102,9 @@ void test_patch_parse__invalid_patches_fails(void)
 		strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
 }
 
+void test_patch_parse__files_with_whitespaces_succeeds(void)
+{
+	git_patch *patch;
+	cl_git_pass(git_patch_from_buffer(&patch, PATCH_NAME_WHITESPACE, strlen(PATCH_NAME_WHITESPACE), NULL));
+	git_patch_free(patch);
+}
diff --git a/tests/patch/patch_common.h b/tests/patch/patch_common.h
index a20ebd6..e838e60 100644
--- a/tests/patch/patch_common.h
+++ b/tests/patch/patch_common.h
@@ -575,6 +575,16 @@
 	"+added line with no nl\n" \
 	"\\ No newline at end of file\n"
 
+#define PATCH_NAME_WHITESPACE \
+	"diff --git a/file with spaces.txt b/file with spaces.txt\n" \
+	"index 9432026..83759c0 100644\n" \
+	"--- a/file with spaces.txt\n" \
+	"+++ b/file with spaces.txt\n" \
+	"@@ -0,3 +0,2 @@\n" \
+	" and this\n" \
+	"-is additional context\n" \
+	" below it!\n" \
+
 #define PATCH_CORRUPT_GIT_HEADER \
 	"diff --git a/file.txt\n" \
 	"index 9432026..0f39b9a 100644\n" \