From 9e1398b164f55238990907f63dfdef60588d9b24 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sat, 7 Nov 2020 21:23:57 +0100 Subject: Prepare rred binary for external usage Merging patches is a bit of non-trivial code we have for client-side work, but as we support also server-side merging we can export this functionality so that server software can reuse it. Note that this just cleans up and makes rred behave a bit more like all our other binaries by supporting setting configuration at runtime and supporting --help and --version. If you can make due without this, the now advertised functionality is provided already in earlier versions. --- apt-private/private-cmndline.cc | 11 ++- apt-private/private-cmndline.h | 1 + apt-private/private-main.cc | 1 + doc/examples/configure-index | 2 + methods/CMakeLists.txt | 2 + methods/rred.cc | 131 ++++++++++++++++++---------- test/integration/test-00-commands-have-help | 6 +- test/integration/test-method-rred | 37 ++++++++ 8 files changed, 144 insertions(+), 47 deletions(-) diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc index bcafe785b..b6c5ca90a 100644 --- a/apt-private/private-cmndline.cc +++ b/apt-private/private-cmndline.cc @@ -359,6 +359,13 @@ static bool addArgumentsAPT(std::vector &Args, char const * c addArg(0, "with-source", "APT::Sources::With::", CommandLine::HasArg); + return true; +} + /*}}}*/ +static bool addArgumentsRred(std::vector &Args, char const * const /*Cmd*/)/*{{{*/ +{ + addArg('t', nullptr, "Rred::T", 0); + addArg('f', nullptr, "Rred::F", 0); return true; } /*}}}*/ @@ -384,6 +391,7 @@ std::vector getCommandArgs(APT_CMD const Program, char const case APT_CMD::APT_INTERNAL_SOLVER: addArgumentsAPTInternalSolver(Args, Cmd); break; case APT_CMD::APT_MARK: addArgumentsAPTMark(Args, Cmd); break; case APT_CMD::APT_SORTPKG: addArgumentsAPTSortPkgs(Args, Cmd); break; + case APT_CMD::RRED: addArgumentsRred(Args, Cmd); break; } // options without a command @@ -441,11 +449,12 @@ static bool ShowCommonHelp(APT_CMD const Binary, CommandLine &CmdL, std::vector< case APT_CMD::APT_INTERNAL_SOLVER: cmd = nullptr; break; case APT_CMD::APT_MARK: cmd = "apt-mark(8)"; break; case APT_CMD::APT_SORTPKG: cmd = "apt-sortpkgs(1)"; break; + case APT_CMD::RRED: cmd = nullptr; break; } if (cmd != nullptr) ioprintf(std::cout, _("See %s for more information about the available commands."), cmd); if (Binary != APT_CMD::APT_DUMP_SOLVER && Binary != APT_CMD::APT_INTERNAL_SOLVER && - Binary != APT_CMD::APT_INTERNAL_PLANNER) + Binary != APT_CMD::APT_INTERNAL_PLANNER && Binary != APT_CMD::RRED) std::cout << std::endl << _("Configuration options and syntax is detailed in apt.conf(5).\n" "Information about how to configure sources can be found in sources.list(5).\n" diff --git a/apt-private/private-cmndline.h b/apt-private/private-cmndline.h index 37fe2c91a..22e25d280 100644 --- a/apt-private/private-cmndline.h +++ b/apt-private/private-cmndline.h @@ -23,6 +23,7 @@ enum class APT_CMD { APT_SORTPKG, APT_DUMP_SOLVER, APT_INTERNAL_PLANNER, + RRED, }; struct aptDispatchWithHelp { diff --git a/apt-private/private-main.cc b/apt-private/private-main.cc index e9f65bd83..d4ce0ab2a 100644 --- a/apt-private/private-main.cc +++ b/apt-private/private-main.cc @@ -33,6 +33,7 @@ void InitLocale(APT_CMD const binary) /*{{{*/ case APT_CMD::APT_HELPER: case APT_CMD::APT_GET: case APT_CMD::APT_MARK: + case APT_CMD::RRED: textdomain("apt"); break; case APT_CMD::APT_EXTRACTTEMPLATES: diff --git a/doc/examples/configure-index b/doc/examples/configure-index index d9f269344..ee031c8b4 100644 --- a/doc/examples/configure-index +++ b/doc/examples/configure-index @@ -830,6 +830,8 @@ dir::filelistdir ""; dir::dpkg::tupletable ""; dir::dpkg::triplettable ""; dir::dpkg::cputable ""; +Rred::t ""; +Rred::f ""; APT::Internal::OpProgress::Absolute ""; APT::Color ""; diff --git a/methods/CMakeLists.txt b/methods/CMakeLists.txt index d575382f7..a5a360217 100644 --- a/methods/CMakeLists.txt +++ b/methods/CMakeLists.txt @@ -23,6 +23,8 @@ target_include_directories(http PRIVATE $<$:${SYSTEMD_INC target_link_libraries(http ${GNUTLS_LIBRARIES} $<$:${SYSTEMD_LIBRARIES}>) target_link_libraries(ftp ${GNUTLS_LIBRARIES}) +target_link_libraries(rred apt-private) + # Install the library install(TARGETS file copy store gpgv cdrom http ftp rred rsh mirror RUNTIME DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/apt/methods) diff --git a/methods/rred.cc b/methods/rred.cc index 949fa4773..b7825721c 100644 --- a/methods/rred.cc +++ b/methods/rred.cc @@ -15,6 +15,8 @@ #include #include +#include + #include #include #include @@ -33,6 +35,33 @@ #define BLOCK_SIZE (512*1024) +static bool ShowHelp(CommandLine &) +{ + std::cout << + "Usage: rred [options] -t input output patch-1 … patch-N\n" + " rred [options] -f patch-1 … patch-N < input > output\n" + " rred [options] patch-1 … patch-N > merged-patch\n" + "\n" + "The main use of this binary is by APTs acquire system, a mode reached\n" + "by calling it without any arguments and driven via messages on stdin.\n" + "\n" + "For the propose of testing as well as simpler direct usage the above\n" + "mentioned modes to work with \"reversed restricted ed\" patches as well.\n" + "\n" + "The arguments used above are:\n" + "* input: denotes a file you want to patch.\n" + "* output: a file you want to store the patched content in.\n" + "* patch-1 … patch-N: One or more files containing a patch.\n" + "* merged-patch: All changes by patch-1 … patch-N in one patch.\n" + "\n" + "This rred supports the commands 'a', 'c' and 'd', both single as well\n" + "as multi line. Other commands are not supported (hence 'restricted').\n" + "The command to patch the last line must appear first in the patch\n" + "(hence 'reversed'). Such a patch can e.g. be produced with 'diff --ed'.\n" + ; + return true; +} + class MemBlock { char *start; size_t size; @@ -727,62 +756,74 @@ class RredMethod : public aptMethod { } }; -int main(int argc, char **argv) +static std::vector GetCommands() { - int i; - bool just_diff = true; - bool test = false; - Patch patch; - - if (argc <= 1) { + return {{nullptr, nullptr, nullptr}}; +} +int main(int argc, const char *argv[]) +{ + if (argc <= 1) return RredMethod().Run(); + + CommandLine CmdL; + auto const Cmds = ParseCommandLine(CmdL, APT_CMD::RRED, &_config, nullptr, argc, argv, &ShowHelp, &GetCommands); + + FileFd input, output; + unsigned int argi = 0; + auto const argmax = CmdL.FileSize(); + bool const quiet = _config->FindI("quiet", 0) >= 2; + + bool just_diff = false; + if (_config->FindB("Rred::T", false)) + { + if (argmax < 3) + { + std::cerr << "E: Not enough filenames given on the command line for mode 't'\n"; + return 101; + } + if (not quiet) + std::clog << "Patching " << CmdL.FileList[0] << " into " << CmdL.FileList[1] << "\n"; + input.Open(CmdL.FileList[0], FileFd::ReadOnly,FileFd::Extension); + output.Open(CmdL.FileList[1], FileFd::WriteOnly | FileFd::Create | FileFd::Empty | FileFd::BufferedWrite, FileFd::Extension); + argi = 2; + } + else + { + output.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite); + if (_config->FindB("Rred::F", false)) + input.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly); + else + just_diff = true; } - // Usage: rred -t input output diff ... - if (argc > 1 && strcmp(argv[1], "-t") == 0) { - // Read config files so we see compressors. - pkgInitConfig(*_config); - just_diff = false; - test = true; - i = 4; - } else if (argc > 1 && strcmp(argv[1], "-f") == 0) { - just_diff = false; - i = 2; - } else { - i = 1; + if (argi + 1 > argmax) + { + std::cerr << "E: At least one patch needs to be given on the command line\n"; + return 101; } - for (; i < argc; i++) { - FileFd p; - if (p.Open(argv[i], FileFd::ReadOnly) == false) { + Patch merged_patch; + for (; argi < argmax; ++argi) + { + FileFd patch; + if (not patch.Open(CmdL.FileList[argi], FileFd::ReadOnly)) + { _error->DumpErrors(std::cerr); - exit(1); + return 1; } - if (patch.read_diff(p, NULL) == false) + if (not merged_patch.read_diff(patch, nullptr)) { _error->DumpErrors(std::cerr); - exit(2); + return 2; } } - if (test) { - FileFd out, inp; - std::cerr << "Patching " << argv[2] << " into " << argv[3] << "\n"; - inp.Open(argv[2], FileFd::ReadOnly,FileFd::Extension); - out.Open(argv[3], FileFd::WriteOnly | FileFd::Create | FileFd::Empty | FileFd::BufferedWrite, FileFd::Extension); - patch.apply_against_file(out, inp); - out.Close(); - } else if (just_diff) { - FileFd out; - out.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::Create); - patch.write_diff(out); - out.Close(); - } else { - FileFd out, inp; - out.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::Create | FileFd::BufferedWrite); - inp.OpenDescriptor(STDIN_FILENO, FileFd::ReadOnly); - patch.apply_against_file(out, inp); - out.Close(); - } - return 0; + if (just_diff) + merged_patch.write_diff(output); + else + merged_patch.apply_against_file(output, input); + + output.Close(); + input.Close(); + return DispatchCommandLine(CmdL, {}); } diff --git a/test/integration/test-00-commands-have-help b/test/integration/test-00-commands-have-help index 4a0cc64d4..f91238d5d 100755 --- a/test/integration/test-00-commands-have-help +++ b/test/integration/test-00-commands-have-help @@ -49,7 +49,11 @@ for CMD in 'apt-cache' 'apt-cdrom' 'apt-config' \ checkoptions "$cmd" done -for CMD in 'apt-dump-solver' 'apt-internal-solver' 'apt-internal-planner'; do + +rred() { + runapt "${METHODSDIR}/rred" "$@" +} +for CMD in 'apt-dump-solver' 'apt-internal-solver' 'apt-internal-planner' 'rred'; do checkoptions "$(echo "$CMD" | tr -d '-')" done diff --git a/test/integration/test-method-rred b/test/integration/test-method-rred index 5a885e9d2..00b4b7c0b 100755 --- a/test/integration/test-method-rred +++ b/test/integration/test-method-rred @@ -195,3 +195,40 @@ Package: supercoolstuff failrred 'Wrong order of commands' '7d 17d' failrred 'End before start' '7,6d' + +# deal correctly with patch merging +mergepatches() { + testsuccess runapt "${METHODSDIR}/rred" Packages.ed-* + cp -a rootdir/tmp/testsuccess.output patch.ed + testfileequal 'patch.ed' "$1" +} +createpatch() { + echo "$2" +} + +createpatch 'Change dog to cat + kitties' '19c + And a cat! + +Package: extra-kittens +Version: unavailable +Description: fix later +.' > Packages.ed-0 +createpatch 'Remove more stuff and fix later' '23d, +6d' > Packages.ed-1 +createpatch 'Remove (old) dog paragraph' '10,19d' > Packages.ed-2 +mergepatches '11,19c +Package: extra-kittens +Version: unavailable +. +6d' +testrred 'Apply' 'merged patch' "$(cat patch.ed)" 'Package: coolstuff +Version: 0.8.15 +Description: collection of stuff + A lot, too much to iterate all, but at least this: + - stuff + - even more stuff + . + And a cow. + +Package: extra-kittens +Version: unavailable' -- cgit v1.2.3-70-g09d2