summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Hofstetter <daniel.hofstetter@42dh.com>2023-06-02 11:41:58 +0200
committerGitHub <noreply@github.com>2023-06-02 11:41:58 +0200
commitd2bf531a7b13885f5035cde1c1c123c29a287e27 (patch)
tree2693647ec53f8af122523df8bd17f5086c9869f9
parent0b4a91744d73daea683a1385d160f281b361e242 (diff)
parent20ce7accf250b07a4e0fe2f1bfe29f6363581cd6 (diff)
Merge pull request #4925 from sylvestre/cp-i
cp: -i prompts in the right place and other improv
-rw-r--r--src/uu/cp/src/cp.rs40
-rw-r--r--tests/by-util/test_cp.rs30
2 files changed, 51 insertions, 19 deletions
diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs
index 513fb8380..e8552a179 100644
--- a/src/uu/cp/src/cp.rs
+++ b/src/uu/cp/src/cp.rs
@@ -43,7 +43,8 @@ use uucore::fs::{
};
use uucore::update_control::{self, UpdateMode};
use uucore::{
- crash, format_usage, help_about, help_section, help_usage, prompt_yes, show_error, show_warning,
+ crash, format_usage, help_about, help_section, help_usage, prompt_yes, show_error,
+ show_warning, util_name,
};
use crate::copydir::copy_directory;
@@ -1102,23 +1103,21 @@ fn preserve_hardlinks(
}
/// When handling errors, we don't always want to show them to the user. This function handles that.
-/// If the error is printed, returns true, false otherwise.
-fn show_error_if_needed(error: &Error) -> bool {
+fn show_error_if_needed(error: &Error) {
match error {
// When using --no-clobber, we don't want to show
// an error message
- Error::NotAllFilesCopied => (),
+ Error::NotAllFilesCopied => {
+ // Need to return an error code
+ }
Error::Skipped => {
// touch a b && echo "n"|cp -i a b && echo $?
// should return an error from GNU 9.2
- return true;
}
_ => {
show_error!("{}", error);
- return true;
}
}
- false
}
/// Copy all `sources` to `target`. Returns an
@@ -1175,9 +1174,8 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
options,
&mut symlinked_files,
) {
- if show_error_if_needed(&error) {
- non_fatal_errors = true;
- }
+ show_error_if_needed(&error);
+ non_fatal_errors = true;
}
}
seen_sources.insert(source);
@@ -1254,13 +1252,23 @@ fn copy_source(
}
impl OverwriteMode {
- fn verify(&self, path: &Path) -> CopyResult<()> {
+ fn verify(&self, path: &Path, verbose: bool) -> CopyResult<()> {
match *self {
- Self::NoClobber => Err(Error::NotAllFilesCopied),
+ Self::NoClobber => {
+ if verbose {
+ println!("skipped {}", path.quote());
+ } else {
+ eprintln!("{}: not replacing {}", util_name(), path.quote());
+ }
+ Err(Error::NotAllFilesCopied)
+ }
Self::Interactive(_) => {
if prompt_yes!("overwrite {}?", path.quote()) {
Ok(())
} else {
+ if verbose {
+ println!("skipped {}", path.quote());
+ }
Err(Error::Skipped)
}
}
@@ -1468,7 +1476,7 @@ fn handle_existing_dest(
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
}
- options.overwrite.verify(dest)?;
+ options.overwrite.verify(dest, options.verbose)?;
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
if let Some(backup_path) = backup_path {
@@ -1826,7 +1834,7 @@ fn copy_helper(
File::create(dest).context(dest.display().to_string())?;
} else if source_is_fifo && options.recursive && !options.copy_contents {
#[cfg(unix)]
- copy_fifo(dest, options.overwrite)?;
+ copy_fifo(dest, options.overwrite, options.verbose)?;
} else if source_is_symlink {
copy_link(source, dest, symlinked_files)?;
} else {
@@ -1851,9 +1859,9 @@ fn copy_helper(
// "Copies" a FIFO by creating a new one. This workaround is because Rust's
// built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390).
#[cfg(unix)]
-fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> {
+fn copy_fifo(dest: &Path, overwrite: OverwriteMode, verbose: bool) -> CopyResult<()> {
if dest.exists() {
- overwrite.verify(dest)?;
+ overwrite.verify(dest, verbose)?;
fs::remove_file(dest)?;
}
diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs
index 1feeb0ede..7f153343d 100644
--- a/tests/by-util/test_cp.rs
+++ b/tests/by-util/test_cp.rs
@@ -457,6 +457,29 @@ fn test_cp_arg_interactive_update() {
}
#[test]
+#[cfg(not(target_os = "android"))]
+fn test_cp_arg_interactive_verbose() {
+ let (at, mut ucmd) = at_and_ucmd!();
+ at.touch("a");
+ at.touch("b");
+ ucmd.args(&["-vi", "a", "b"])
+ .pipe_in("N\n")
+ .fails()
+ .stdout_is("skipped 'b'\n");
+}
+
+#[test]
+#[cfg(not(target_os = "android"))]
+fn test_cp_arg_interactive_verbose_clobber() {
+ let (at, mut ucmd) = at_and_ucmd!();
+ at.touch("a");
+ at.touch("b");
+ ucmd.args(&["-vin", "a", "b"])
+ .fails()
+ .stdout_is("skipped 'b'\n");
+}
+
+#[test]
#[cfg(target_os = "linux")]
fn test_cp_arg_link() {
use std::os::linux::fs::MetadataExt;
@@ -487,7 +510,8 @@ fn test_cp_arg_no_clobber() {
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.arg("--no-clobber")
- .succeeds();
+ .fails()
+ .stderr_contains("not replacing");
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
}
@@ -498,7 +522,7 @@ fn test_cp_arg_no_clobber_inferred_arg() {
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE)
.arg("--no-clob")
- .succeeds();
+ .fails();
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
}
@@ -525,7 +549,7 @@ fn test_cp_arg_no_clobber_twice() {
.arg("--no-clobber")
.arg("source.txt")
.arg("dest.txt")
- .succeeds();
+ .fails();
assert_eq!(at.read("source.txt"), "some-content");
// Should be empty as the "no-clobber" should keep