diff --git a/src/applier.cpp b/src/applier.cpp index 3262d4f..decc1dd 100644 --- a/src/applier.cpp +++ b/src/applier.cpp @@ -88,6 +88,9 @@ static LineNumber write_define_hunk(LineWriter& output, const Hunk& hunk, const for (const auto& patch_line : hunk.lines) { if (patch_line.operation == ' ') { + if (line_number >= lines.size()) + continue; + const auto& line = lines.at(line_number); ++line_number; if (define_state != DefineState::Outside) { @@ -105,6 +108,9 @@ static LineNumber write_define_hunk(LineWriter& output, const Hunk& hunk, const } output << patch_line.line; } else if (patch_line.operation == '-') { + if (line_number >= lines.size()) + continue; + const auto& line = lines.at(line_number); ++line_number; @@ -136,12 +142,16 @@ static LineNumber write_hunk(LineWriter& output, const Hunk& hunk, const Locatio for (const auto& patch_line : hunk.lines) { if (patch_line.operation == ' ') { + if (line_number >= lines.size()) + continue; + output << lines.at(line_number); ++line_number; } else if (patch_line.operation == '+') { output << patch_line.line; } else if (patch_line.operation == '-') { - ++line_number; + if (line_number < lines.size()) + ++line_number; } } diff --git a/src/locator.cpp b/src/locator.cpp index 95c1884..752ab22 100644 --- a/src/locator.cpp +++ b/src/locator.cpp @@ -105,7 +105,8 @@ Location locate_hunk(const std::vector& content, const Hunk& hunk, bool ig if (hunk.old_file_range.number_of_lines == 0) { if (hunk.old_file_range.start_line == 0 && !content.empty()) return {}; - return { offset_guess, 0, 0 }; + const auto end = static_cast(content.size()); + return { std::min(std::max(offset_guess, 0), end), 0, 0 }; } LineNumber patch_prefix_content = 0; diff --git a/tests/test_applier.cpp b/tests/test_applier.cpp index 997c14e..b6b79e3 100644 --- a/tests/test_applier.cpp +++ b/tests/test_applier.cpp @@ -90,6 +90,88 @@ int another() EXPECT_FILE_EQ("a.rej", patch); } +static void write_context_past_end_of_file_patch() +{ + Patch::File file("diff.patch", std::ios_base::out); + file << R"(--- a ++++ a +@@ -1,5 +1,5 @@ + a +-b ++B + c + ZZZ + QQQ +)"; + file.close(); +} + +static void write_three_line_file() +{ + Patch::File file("a", std::ios_base::out); + file << "a\nb\nc\n"; + file.close(); +} + +COMPAT_TEST(applier_insertion_past_end_of_file) +{ + { + Patch::File file("diff.patch", std::ios_base::out); + file << R"(--- a ++++ a +@@ -100,0 +100,1 @@ ++inserted +)"; + file.close(); + } + + write_three_line_file(); + + Process process(patch_path, { patch_path, "--force", "-i", "diff.patch", nullptr }); + + EXPECT_EQ(process.stdout_data(), "patching file a\n"); + EXPECT_EQ(process.stderr_data(), ""); + EXPECT_EQ(process.return_code(), 0); + EXPECT_FILE_EQ("a", "a\nb\nc\ninserted\n"); +} + +COMPAT_TEST(applier_fuzz_matches_context_past_end_of_file) +{ + write_context_past_end_of_file_patch(); + write_three_line_file(); + + Process process(patch_path, { patch_path, "--force", "--fuzz=3", "-i", "diff.patch", nullptr }); + + EXPECT_EQ(process.stdout_data(), R"(patching file a +Hunk #1 succeeded at 1 with fuzz 2. +)"); + EXPECT_EQ(process.stderr_data(), ""); + EXPECT_EQ(process.return_code(), 0); + EXPECT_FILE_EQ("a", "a\nB\nc\n"); +} + +COMPAT_TEST(applier_fuzz_matches_context_past_end_of_file_with_define) +{ + write_context_past_end_of_file_patch(); + write_three_line_file(); + + Process process(patch_path, { patch_path, "--force", "--fuzz=3", "-D", "FOO", "-i", "diff.patch", nullptr }); + + EXPECT_EQ(process.stdout_data(), R"(patching file a +Hunk #1 succeeded at 1 with fuzz 2. +)"); + EXPECT_EQ(process.stderr_data(), ""); + EXPECT_EQ(process.return_code(), 0); + EXPECT_FILE_EQ("a", R"(a +#ifndef FOO +b +#else +B +#endif +c +)"); +} + COMPAT_TEST(applier_end_of_file_hunk_requires_fuzz_when_no_longer_at_end) { const std::string patch = R"(--- a