Skip to content

Commit b089328

Browse files
Erik Aignerpks-t
authored andcommitted
patch_parse: ensure valid patch output with EOFNL
1 parent 3f855fe commit b089328

File tree

4 files changed

+58
-16
lines changed

4 files changed

+58
-16
lines changed

src/apply.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,21 @@ static int apply_hunk(
199199

200200
for (i = 0; i < hunk->line_count; i++) {
201201
size_t linenum = hunk->line_start + i;
202-
git_diff_line *line = git_array_get(patch->lines, linenum);
202+
git_diff_line *line = git_array_get(patch->lines, linenum), *prev;
203203

204204
if (!line) {
205205
error = apply_err("preimage does not contain line %"PRIuZ, linenum);
206206
goto done;
207207
}
208208

209209
switch (line->origin) {
210+
case GIT_DIFF_LINE_CONTEXT_EOFNL:
211+
case GIT_DIFF_LINE_DEL_EOFNL:
212+
case GIT_DIFF_LINE_ADD_EOFNL:
213+
prev = i ? git_array_get(patch->lines, i - 1) : NULL;
214+
if (prev && prev->content[prev->content_len - 1] == '\n')
215+
prev->content_len -= 1;
216+
break;
210217
case GIT_DIFF_LINE_CONTEXT:
211218
if ((error = git_vector_insert(&preimage.lines, line)) < 0 ||
212219
(error = git_vector_insert(&postimage.lines, line)) < 0)

src/patch_generate.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -836,7 +836,7 @@ static int patch_generated_line_cb(
836836
{
837837
git_patch_generated *patch = payload;
838838
git_patch_hunk *hunk;
839-
git_diff_line *line;
839+
git_diff_line *line;
840840

841841
GIT_UNUSED(delta);
842842
GIT_UNUSED(hunk_);

src/patch_parse.c

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,14 @@ static int parse_hunk_header(
524524
return -1;
525525
}
526526

527+
static int eof_for_origin(int origin) {
528+
if (origin == GIT_DIFF_LINE_ADDITION)
529+
return GIT_DIFF_LINE_ADD_EOFNL;
530+
if (origin == GIT_DIFF_LINE_DELETION)
531+
return GIT_DIFF_LINE_DEL_EOFNL;
532+
return GIT_DIFF_LINE_CONTEXT_EOFNL;
533+
}
534+
527535
static int parse_hunk_body(
528536
git_patch_parsed *patch,
529537
git_patch_hunk *hunk,
@@ -534,6 +542,7 @@ static int parse_hunk_body(
534542

535543
int oldlines = hunk->hunk.old_lines;
536544
int newlines = hunk->hunk.new_lines;
545+
int last_origin = 0;
537546

538547
for (;
539548
ctx->parse_ctx.remain_len > 1 &&
@@ -584,8 +593,13 @@ static int parse_hunk_body(
584593
* the "\ No newline at end of file" marker. Do not
585594
* verify its format, as it may be localized.
586595
*/
587-
if (!oldlines)
588-
continue;
596+
if (!oldlines) {
597+
prefix = 0;
598+
origin = eof_for_origin(last_origin);
599+
old_lineno = -1;
600+
new_lineno = -1;
601+
break;
602+
}
589603
/* fall through */
590604

591605
default:
@@ -607,6 +621,8 @@ static int parse_hunk_body(
607621
line->new_lineno = new_lineno;
608622

609623
hunk->line_count++;
624+
625+
last_origin = origin;
610626
}
611627

612628
if (oldlines || newlines) {
@@ -617,7 +633,7 @@ static int parse_hunk_body(
617633
}
618634

619635
/*
620-
* Handle "\ No newline at end of file". Only expect the leading
636+
* Handle "\ No newline at end of file". Only expect the leading
621637
* backslash, though, because the rest of the string could be
622638
* localized. Because `diff` optimizes for the case where you
623639
* want to apply the patch by hand.
@@ -628,11 +644,24 @@ static int parse_hunk_body(
628644
line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);
629645

630646
if (line->content_len < 1) {
631-
error = git_parse_err("cannot trim trailing newline of empty line");
647+
error = git_parse_err("last line has no trailing newline");
632648
goto done;
633649
}
634650

635-
line->content_len--;
651+
line = git_array_alloc(patch->base.lines);
652+
GIT_ERROR_CHECK_ALLOC(line);
653+
654+
memset(line, 0x0, sizeof(git_diff_line));
655+
656+
line->content = ctx->parse_ctx.line;
657+
line->content_len = ctx->parse_ctx.line_len;
658+
line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
659+
line->origin = eof_for_origin(last_origin);
660+
line->num_lines = 1;
661+
line->old_lineno = -1;
662+
line->new_lineno = -1;
663+
664+
hunk->line_count++;
636665

637666
git_parse_advance_line(&ctx->parse_ctx);
638667
}

tests/patch/parse.c

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ static void ensure_patch_validity(git_patch *patch)
2727
cl_assert_equal_i(0, delta->new_file.size);
2828
}
2929

30+
static void ensure_identical_patch_inout(const char *content) {
31+
git_buf buf = GIT_BUF_INIT;
32+
git_patch *patch;
33+
34+
cl_git_pass(git_patch_from_buffer(&patch, content, strlen(content), NULL));
35+
cl_git_pass(git_patch_to_buf(&buf, patch));
36+
cl_assert_equal_strn(git_buf_cstr(&buf), content, strlen(content));
37+
38+
git_patch_free(patch);
39+
git_buf_dispose(&buf);
40+
}
41+
3042
void test_patch_parse__original_to_change_middle(void)
3143
{
3244
git_patch *patch;
@@ -104,21 +116,15 @@ void test_patch_parse__invalid_patches_fails(void)
104116

105117
void test_patch_parse__no_newline_at_end_of_new_file(void)
106118
{
107-
git_patch *patch;
108-
cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL, strlen(PATCH_APPEND_NO_NL), NULL));
109-
git_patch_free(patch);
119+
ensure_identical_patch_inout(PATCH_APPEND_NO_NL);
110120
}
111121

112122
void test_patch_parse__no_newline_at_end_of_old_file(void)
113123
{
114-
git_patch *patch;
115-
cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL_IN_OLD_FILE, strlen(PATCH_APPEND_NO_NL_IN_OLD_FILE), NULL));
116-
git_patch_free(patch);
124+
ensure_identical_patch_inout(PATCH_APPEND_NO_NL_IN_OLD_FILE);
117125
}
118126

119127
void test_patch_parse__files_with_whitespaces_succeeds(void)
120128
{
121-
git_patch *patch;
122-
cl_git_pass(git_patch_from_buffer(&patch, PATCH_NAME_WHITESPACE, strlen(PATCH_NAME_WHITESPACE), NULL));
123-
git_patch_free(patch);
129+
ensure_identical_patch_inout(PATCH_NAME_WHITESPACE);
124130
}

0 commit comments

Comments
 (0)