From bb0feebddc7d60afefc62a6e245a532cf5252296 Mon Sep 17 00:00:00 2001 From: Tim Schumacher Date: Tue, 12 Jul 2022 23:57:14 +0200 Subject: [PATCH] cp: Implement the granular `--preserve` flag --- Userland/Utilities/cp.cpp | 43 +++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/Userland/Utilities/cp.cpp b/Userland/Utilities/cp.cpp index ce7fef9c75..781b33b18d 100644 --- a/Userland/Utilities/cp.cpp +++ b/Userland/Utilities/cp.cpp @@ -17,7 +17,7 @@ ErrorOr serenity_main(Main::Arguments arguments) TRY(Core::System::pledge("stdio rpath wpath cpath fattr chown")); bool link = false; - bool preserve = false; + auto preserve = Core::File::PreserveMode::Nothing; bool recursion_allowed = false; bool verbose = false; Vector sources; @@ -25,7 +25,37 @@ ErrorOr serenity_main(Main::Arguments arguments) Core::ArgsParser args_parser; args_parser.add_option(link, "Link files instead of copying", "link", 'l'); - args_parser.add_option(preserve, "Preserve time, UID/GID and file permissions", nullptr, 'p'); + args_parser.add_option({ + Core::ArgsParser::OptionArgumentMode::Optional, + "Preserve a selection of mode, ownership and timestamps. Defaults to all three if the option is present but no list is given.", + "preserve", + 'p', + "attributes", + [&preserve](char const* s) { + if (!s) { + preserve = Core::File::PreserveMode::Permissions | Core::File::PreserveMode::Ownership | Core::File::PreserveMode::Timestamps; + return true; + } + + bool values_ok = true; + + StringView { s, strlen(s) }.for_each_split_view(',', false, [&](StringView value) { + if (value == "mode"sv) { + preserve |= Core::File::PreserveMode::Permissions; + } else if (value == "ownership"sv) { + preserve |= Core::File::PreserveMode::Ownership; + } else if (value == "timestamps"sv) { + preserve |= Core::File::PreserveMode::Timestamps; + } else { + warnln("cp: Unknown or unimplemented --preserve attribute: '{}'", value); + values_ok = false; + } + }); + + return values_ok; + }, + Core::ArgsParser::OptionHideMode::None, + }); args_parser.add_option(recursion_allowed, "Copy directories recursively", "recursive", 'R'); args_parser.add_option(recursion_allowed, "Same as -R", nullptr, 'r'); args_parser.add_option(verbose, "Verbose", "verbose", 'v'); @@ -33,7 +63,7 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_positional_argument(destination, "Destination file path", "destination"); args_parser.parse(arguments); - if (preserve) { + if (has_flag(preserve, Core::File::PreserveMode::Permissions)) { umask(0); } else { TRY(Core::System::pledge("stdio rpath wpath cpath fattr")); @@ -41,11 +71,6 @@ ErrorOr serenity_main(Main::Arguments arguments) bool destination_is_existing_dir = Core::File::is_directory(destination); - auto preserve_mode = Core::File::PreserveMode::Nothing; - - if (preserve) - preserve_mode = Core::File::PreserveMode::Permissions | Core::File::PreserveMode::Ownership | Core::File::PreserveMode::Timestamps; - for (auto& source : sources) { auto destination_path = destination_is_existing_dir ? String::formatted("{}/{}", destination, LexicalPath::basename(source)) @@ -56,7 +81,7 @@ ErrorOr serenity_main(Main::Arguments arguments) recursion_allowed ? Core::File::RecursionMode::Allowed : Core::File::RecursionMode::Disallowed, link ? Core::File::LinkMode::Allowed : Core::File::LinkMode::Disallowed, Core::File::AddDuplicateFileMarker::No, - preserve_mode); + preserve); if (result.is_error()) { if (result.error().tried_recursing)