cp: symlink flags fixing conflicting flag logic to use last flag (#9960)

* Adding overrides_with_all for symlink flags for ordering

* Adding regression tests for symlink flag ordering
This commit is contained in:
Dylan Skelly
2026-01-01 15:04:47 +01:00
committed by GitHub
parent 911bc15f82
commit 9cf774bed8
2 changed files with 74 additions and 2 deletions
+30 -2
View File
@@ -689,7 +689,12 @@ pub fn uu_app() -> Command {
Arg::new(options::NO_DEREFERENCE)
.short('P')
.long(options::NO_DEREFERENCE)
.overrides_with(options::DEREFERENCE)
.overrides_with_all([
options::DEREFERENCE,
options::CLI_SYMBOLIC_LINKS,
options::ARCHIVE,
options::NO_DEREFERENCE_PRESERVE_LINKS,
])
// -d sets this option
.help(translate!("cp-help-no-dereference"))
.action(ArgAction::SetTrue),
@@ -698,13 +703,24 @@ pub fn uu_app() -> Command {
Arg::new(options::DEREFERENCE)
.short('L')
.long(options::DEREFERENCE)
.overrides_with(options::NO_DEREFERENCE)
.overrides_with_all([
options::NO_DEREFERENCE,
options::CLI_SYMBOLIC_LINKS,
options::ARCHIVE,
options::NO_DEREFERENCE_PRESERVE_LINKS,
])
.help(translate!("cp-help-dereference"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::CLI_SYMBOLIC_LINKS)
.short('H')
.overrides_with_all([
options::DEREFERENCE,
options::NO_DEREFERENCE,
options::ARCHIVE,
options::NO_DEREFERENCE_PRESERVE_LINKS,
])
.help(translate!("cp-help-cli-symbolic-links"))
.action(ArgAction::SetTrue),
)
@@ -712,12 +728,24 @@ pub fn uu_app() -> Command {
Arg::new(options::ARCHIVE)
.short('a')
.long(options::ARCHIVE)
.overrides_with_all([
options::DEREFERENCE,
options::NO_DEREFERENCE,
options::CLI_SYMBOLIC_LINKS,
options::NO_DEREFERENCE_PRESERVE_LINKS,
])
.help(translate!("cp-help-archive"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(options::NO_DEREFERENCE_PRESERVE_LINKS)
.short('d')
.overrides_with_all([
options::DEREFERENCE,
options::NO_DEREFERENCE,
options::CLI_SYMBOLIC_LINKS,
options::ARCHIVE,
])
.help(translate!("cp-help-no-dereference-preserve-links"))
.action(ArgAction::SetTrue),
)
+44
View File
@@ -7400,3 +7400,47 @@ fn test_cp_recurse_verbose_output_with_symlink_already_exists() {
.no_stderr()
.stdout_is(output);
}
#[test]
#[cfg(unix)]
fn test_cp_hlp_flag_ordering() {
// GNU cp: "If more than one of -H, -L, and -P is specified, only the final one takes effect"
let (at, mut ucmd) = at_and_ucmd!();
at.touch("file.txt");
at.symlink_file("file.txt", "symlink");
// -HP: P wins, copy symlink as symlink
ucmd.args(&["-HP", "symlink", "dest_hp"]).succeeds();
assert!(at.is_symlink("dest_hp"));
// -PH: H wins, copy target file
let (at, mut ucmd) = at_and_ucmd!();
at.touch("file.txt");
at.symlink_file("file.txt", "symlink");
ucmd.args(&["-PH", "symlink", "dest_ph"]).succeeds();
assert!(!at.is_symlink("dest_ph"));
assert!(at.file_exists("dest_ph"));
}
#[test]
#[cfg(unix)]
fn test_cp_archive_deref_flag_ordering() {
// (flags, expect_symlink): last flag wins; a/d imply -P, H/L dereference
for (flags, expect_symlink) in [
("-Ha", true),
("-aH", false),
("-Hd", true),
("-dH", false),
("-La", true),
("-aL", false),
("-Ld", true),
("-dL", false),
] {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("file.txt");
at.symlink_file("file.txt", "symlink");
let dest = format!("dest{flags}");
ucmd.args(&[flags, "symlink", &dest]).succeeds();
assert_eq!(at.is_symlink(&dest), expect_symlink, "failed for {flags}");
}
}