diff --git a/Userland/Utilities/cp.cpp b/Userland/Utilities/cp.cpp index 643e40a5ee..87f62c63c5 100644 --- a/Userland/Utilities/cp.cpp +++ b/Userland/Utilities/cp.cpp @@ -20,6 +20,7 @@ ErrorOr serenity_main(Main::Arguments arguments) auto preserve = FileSystem::PreserveMode::Nothing; bool recursion_allowed = false; bool verbose = false; + bool interactive = false; Vector sources; ByteString destination; @@ -56,6 +57,7 @@ ErrorOr serenity_main(Main::Arguments arguments) }, Core::ArgsParser::OptionHideMode::None, }); + args_parser.add_option(interactive, "Prompt before overwriting files", "interactive", 'i'); 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'); @@ -72,10 +74,63 @@ ErrorOr serenity_main(Main::Arguments arguments) bool destination_is_existing_dir = FileSystem::is_directory(destination); for (auto& source : sources) { + // FIXME: May be formatted wrong if destination is a directory with a trailing slash, e.g. if called as such: + // cp source dest/ + // ... `destination_path` may be formatted as `dest//source`, which isn't strictly correct. auto destination_path = destination_is_existing_dir ? ByteString::formatted("{}/{}", destination, LexicalPath::basename(source)) : destination; + if (interactive && FileSystem::exists(destination_path)) { + bool overwrite = false; + while (true) { + warn("cp: overwrite '{}'? ", destination_path); + fflush(stdout); + + char* line = nullptr; + + size_t n = 0; + ssize_t size = getline(&line, &n, stdin); + ScopeGuard guard([line] { free(line); }); + + // Strip trailing newline. + if (line[size - 1] == '\n') { + --size; + } + + if (size == 1) { + if (*line == 'y') { + overwrite = true; + break; + } + if (*line == 'n') { + break; + } + } else if (size == 2) { + if ("no"sv.compare(StringView { line, static_cast(size) }) == 0) { + break; + } + } else if (size == 3) { + if ("yes"sv.compare(StringView { line, static_cast(size) }) == 0) { + overwrite = true; + break; + } + } + } + + if (verbose) { + if (!overwrite) { + warnln("cp: skipping {}", destination_path); + continue; + } + warnln("cp: overwriting {}", destination_path); + } + + if (!overwrite) { + continue; + } + } + auto result = FileSystem::copy_file_or_directory( destination_path, source, recursion_allowed ? FileSystem::RecursionMode::Allowed : FileSystem::RecursionMode::Disallowed,