浏览代码

cp: Add `--interactive`/`-i` flag

- Add interactive flag which tells `cp` to prompt before overwriting
  files
- Accept 'yes', 'no', 'n', and 'y' as input, all other inputs will
  result in the prompt being shown again
Erick Howard 1 年之前
父节点
当前提交
3e0d1becd4
共有 1 个文件被更改,包括 55 次插入0 次删除
  1. 55 0
      Userland/Utilities/cp.cpp

+ 55 - 0
Userland/Utilities/cp.cpp

@@ -20,6 +20,7 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
     auto preserve = FileSystem::PreserveMode::Nothing;
     bool recursion_allowed = false;
     bool verbose = false;
+    bool interactive = false;
     Vector<StringView> sources;
     ByteString destination;
 
@@ -56,6 +57,7 @@ ErrorOr<int> 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<int> 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<unsigned long>(size) }) == 0) {
+                        break;
+                    }
+                } else if (size == 3) {
+                    if ("yes"sv.compare(StringView { line, static_cast<unsigned long>(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,