From e838b6c8cc2f6153187cabc8e3e55611c108c997 Mon Sep 17 00:00:00 2001 From: Shannon Booth Date: Fri, 7 Jul 2023 00:34:59 +1200 Subject: [PATCH] patch: Add the beginnings of a patch utility This is still very bare bones, and there is _much_ more to still handle. However, this implements enough functionality to parse a single unified patch read from stdin, and apply it to a file. --- Userland/Utilities/CMakeLists.txt | 3 +- Userland/Utilities/patch.cpp | 60 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 Userland/Utilities/patch.cpp diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index 6b0447f889..6c2c30f898 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -3,7 +3,7 @@ list(APPEND SPECIAL_TARGETS test install) list(APPEND REQUIRED_TARGETS arp base64 basename cat chmod chown clear comm cp cut date dd df diff dirname dmesg du echo env expr false file find grep groups head host hostname id ifconfig kill killall ln logout ls mkdir mount mv network-settings nproc - pgrep pidof ping pkill pmap ps readlink realpath reboot rm rmdir sed route seq shutdown sleep sort stat stty su tail test + patch pgrep pidof ping pkill pmap ps readlink realpath reboot rm rmdir sed route seq shutdown sleep sort stat stty su tail test touch tr true umount uname uniq uptime w wc which whoami xargs yes ) list(APPEND RECOMMENDED_TARGETS @@ -123,6 +123,7 @@ target_link_libraries(notify PRIVATE LibGfx LibGUI) target_link_libraries(open PRIVATE LibDesktop LibFileSystem) target_link_libraries(passwd PRIVATE LibCrypt) target_link_libraries(paste PRIVATE LibGUI) +target_link_libraries(patch PRIVATE LibDiff LibFileSystem) target_link_libraries(pdf PRIVATE LibGfx LibPDF) target_link_libraries(pgrep PRIVATE LibRegex) target_link_libraries(pixelflut PRIVATE LibImageDecoderClient LibIPC LibGfx) diff --git a/Userland/Utilities/patch.cpp b/Userland/Utilities/patch.cpp new file mode 100644 index 0000000000..a3261b1ca2 --- /dev/null +++ b/Userland/Utilities/patch.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, Shannon Booth + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +static ErrorOr do_patch(StringView path_of_file_to_patch, Diff::Patch const& patch) +{ + auto file_to_patch = TRY(Core::File::open(path_of_file_to_patch, Core::File::OpenMode::Read)); + auto content = TRY(file_to_patch->read_until_eof()); + auto lines = StringView(content).lines(); + + // Apply patch to a temporary file in case one or more of the hunks fails. + char tmp_output[] = "/tmp/patch.XXXXXX"; + auto tmp_file = TRY(Core::File::adopt_fd(TRY(Core::System::mkstemp(tmp_output)), Core::File::OpenMode::ReadWrite)); + + TRY(Diff::apply_patch(*tmp_file, lines, patch)); + + return FileSystem::move_file(path_of_file_to_patch, StringView { tmp_output, sizeof(tmp_output) }); +} + +ErrorOr serenity_main(Main::Arguments arguments) +{ + Core::ArgsParser args_parser; + args_parser.parse(arguments); + + auto input = TRY(Core::File::standard_input()); + + auto patch_content = TRY(input->read_until_eof()); + + // FIXME: Support multiple patches in the patch file. + Diff::Parser parser(patch_content); + Diff::Patch patch; + patch.header = TRY(parser.parse_header()); + patch.hunks = TRY(parser.parse_hunks()); + + // FIXME: Support adding/removing a file, and asking for file to patch as fallback otherwise. + StringView to_patch; + if (FileSystem::is_regular_file(patch.header.old_file_path)) { + to_patch = patch.header.old_file_path; + } else if (FileSystem::is_regular_file(patch.header.new_file_path)) { + to_patch = patch.header.new_file_path; + } else { + warnln("Unable to determine file to patch"); + return 1; + } + + outln("patching file {}", to_patch); + TRY(do_patch(to_patch, patch)); + + return 0; +}