mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
Merge pull request #4925 from sylvestre/cp-i
cp: -i prompts in the right place and other improv
This commit is contained in:
commit
d2bf531a7b
2 changed files with 51 additions and 19 deletions
|
@ -43,7 +43,8 @@ use uucore::fs::{
|
||||||
};
|
};
|
||||||
use uucore::update_control::{self, UpdateMode};
|
use uucore::update_control::{self, UpdateMode};
|
||||||
use uucore::{
|
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;
|
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.
|
/// 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) {
|
||||||
fn show_error_if_needed(error: &Error) -> bool {
|
|
||||||
match error {
|
match error {
|
||||||
// When using --no-clobber, we don't want to show
|
// When using --no-clobber, we don't want to show
|
||||||
// an error message
|
// an error message
|
||||||
Error::NotAllFilesCopied => (),
|
Error::NotAllFilesCopied => {
|
||||||
|
// Need to return an error code
|
||||||
|
}
|
||||||
Error::Skipped => {
|
Error::Skipped => {
|
||||||
// touch a b && echo "n"|cp -i a b && echo $?
|
// touch a b && echo "n"|cp -i a b && echo $?
|
||||||
// should return an error from GNU 9.2
|
// should return an error from GNU 9.2
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
show_error!("{}", error);
|
show_error!("{}", error);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Copy all `sources` to `target`. Returns an
|
/// Copy all `sources` to `target`. Returns an
|
||||||
|
@ -1175,9 +1174,8 @@ fn copy(sources: &[Source], target: &TargetSlice, options: &Options) -> CopyResu
|
||||||
options,
|
options,
|
||||||
&mut symlinked_files,
|
&mut symlinked_files,
|
||||||
) {
|
) {
|
||||||
if show_error_if_needed(&error) {
|
show_error_if_needed(&error);
|
||||||
non_fatal_errors = true;
|
non_fatal_errors = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seen_sources.insert(source);
|
seen_sources.insert(source);
|
||||||
|
@ -1254,13 +1252,23 @@ fn copy_source(
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OverwriteMode {
|
impl OverwriteMode {
|
||||||
fn verify(&self, path: &Path) -> CopyResult<()> {
|
fn verify(&self, path: &Path, verbose: bool) -> CopyResult<()> {
|
||||||
match *self {
|
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(_) => {
|
Self::Interactive(_) => {
|
||||||
if prompt_yes!("overwrite {}?", path.quote()) {
|
if prompt_yes!("overwrite {}?", path.quote()) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
if verbose {
|
||||||
|
println!("skipped {}", path.quote());
|
||||||
|
}
|
||||||
Err(Error::Skipped)
|
Err(Error::Skipped)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1468,7 +1476,7 @@ fn handle_existing_dest(
|
||||||
return Err(format!("{} and {} are the same file", source.quote(), dest.quote()).into());
|
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);
|
let backup_path = backup_control::get_backup_path(options.backup, dest, &options.backup_suffix);
|
||||||
if let Some(backup_path) = backup_path {
|
if let Some(backup_path) = backup_path {
|
||||||
|
@ -1826,7 +1834,7 @@ fn copy_helper(
|
||||||
File::create(dest).context(dest.display().to_string())?;
|
File::create(dest).context(dest.display().to_string())?;
|
||||||
} else if source_is_fifo && options.recursive && !options.copy_contents {
|
} else if source_is_fifo && options.recursive && !options.copy_contents {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
copy_fifo(dest, options.overwrite)?;
|
copy_fifo(dest, options.overwrite, options.verbose)?;
|
||||||
} else if source_is_symlink {
|
} else if source_is_symlink {
|
||||||
copy_link(source, dest, symlinked_files)?;
|
copy_link(source, dest, symlinked_files)?;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1851,9 +1859,9 @@ fn copy_helper(
|
||||||
// "Copies" a FIFO by creating a new one. This workaround is because Rust's
|
// "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).
|
// built-in fs::copy does not handle FIFOs (see rust-lang/rust/issues/79390).
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn copy_fifo(dest: &Path, overwrite: OverwriteMode) -> CopyResult<()> {
|
fn copy_fifo(dest: &Path, overwrite: OverwriteMode, verbose: bool) -> CopyResult<()> {
|
||||||
if dest.exists() {
|
if dest.exists() {
|
||||||
overwrite.verify(dest)?;
|
overwrite.verify(dest, verbose)?;
|
||||||
fs::remove_file(dest)?;
|
fs::remove_file(dest)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -456,6 +456,29 @@ fn test_cp_arg_interactive_update() {
|
||||||
.no_stdout();
|
.no_stdout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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]
|
#[test]
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
fn test_cp_arg_link() {
|
fn test_cp_arg_link() {
|
||||||
|
@ -487,7 +510,8 @@ fn test_cp_arg_no_clobber() {
|
||||||
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
||||||
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
||||||
.arg("--no-clobber")
|
.arg("--no-clobber")
|
||||||
.succeeds();
|
.fails()
|
||||||
|
.stderr_contains("not replacing");
|
||||||
|
|
||||||
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
|
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)
|
ucmd.arg(TEST_HELLO_WORLD_SOURCE)
|
||||||
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
.arg(TEST_HOW_ARE_YOU_SOURCE)
|
||||||
.arg("--no-clob")
|
.arg("--no-clob")
|
||||||
.succeeds();
|
.fails();
|
||||||
|
|
||||||
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n");
|
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("--no-clobber")
|
||||||
.arg("source.txt")
|
.arg("source.txt")
|
||||||
.arg("dest.txt")
|
.arg("dest.txt")
|
||||||
.succeeds();
|
.fails();
|
||||||
|
|
||||||
assert_eq!(at.read("source.txt"), "some-content");
|
assert_eq!(at.read("source.txt"), "some-content");
|
||||||
// Should be empty as the "no-clobber" should keep
|
// Should be empty as the "no-clobber" should keep
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue