Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/uu/realpath/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ realpath-help-strip = Only strip '.' and '..' components, but don't resolve symb
realpath-help-zero = Separate output filenames with \0 rather than newline
realpath-help-logical = resolve '..' components before symlinks
realpath-help-physical = resolve symlinks as encountered (default)
realpath-help-canonicalize = all but the last component must exist (default)
realpath-help-canonicalize-existing = canonicalize by following every symlink in every component of the given name recursively, all components must exist
realpath-help-canonicalize-missing = canonicalize by following every symlink in every component of the given name recursively, without requirements on components existence
realpath-help-relative-to = print the resolved path relative to DIR
Expand Down
1 change: 1 addition & 0 deletions src/uu/realpath/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ realpath-help-strip = Supprimer uniquement les composants '.' et '..', mais ne p
realpath-help-zero = Séparer les noms de fichiers de sortie avec \0 plutôt qu'avec un saut de ligne
realpath-help-logical = résoudre les composants '..' avant les liens symboliques
realpath-help-physical = résoudre les liens symboliques rencontrés (par défaut)
realpath-help-canonicalize = tous les composants sauf le dernier doivent exister (par défaut)
realpath-help-canonicalize-existing = canonicaliser en suivant récursivement chaque lien symbolique dans chaque composant du nom donné, tous les composants doivent exister
realpath-help-canonicalize-missing = canonicaliser en suivant récursivement chaque lien symbolique dans chaque composant du nom donné, sans exigences sur l'existence des composants
realpath-help-relative-to = afficher le chemin résolu relativement à RÉP
Expand Down
21 changes: 18 additions & 3 deletions src/uu/realpath/src/realpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const OPT_ZERO: &str = "zero";
const OPT_PHYSICAL: &str = "physical";
const OPT_LOGICAL: &str = "logical";
const OPT_CANONICALIZE_MISSING: &str = "canonicalize-missing";
const OPT_CANONICALIZE: &str = "canonicalize";
const OPT_CANONICALIZE_EXISTING: &str = "canonicalize-existing";
const OPT_RELATIVE_TO: &str = "relative-to";
const OPT_RELATIVE_BASE: &str = "relative-base";
Expand Down Expand Up @@ -86,11 +87,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let line_ending = LineEnding::from_zero_flag(matches.get_flag(OPT_ZERO));
let quiet = matches.get_flag(OPT_QUIET);
let logical = matches.get_flag(OPT_LOGICAL);
let can_mode = if matches.get_flag(OPT_CANONICALIZE_EXISTING) {
MissingHandling::Existing
} else if matches.get_flag(OPT_CANONICALIZE_MISSING) {
let can_mode = if matches.get_flag(OPT_CANONICALIZE_MISSING) {
MissingHandling::Missing
} else if matches.get_flag(OPT_CANONICALIZE_EXISTING) {
// -e: all components must exist
// Despite the name, MissingHandling::Existing requires all components to exist
MissingHandling::Existing
} else {
// Default behavior (same as -E): all but last component must exist
// MissingHandling::Normal allows the final component to not exist
MissingHandling::Normal
};
let resolve_mode = if strip {
Expand Down Expand Up @@ -164,17 +169,27 @@ pub fn uu_app() -> Command {
.help(translate!("realpath-help-physical"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_CANONICALIZE)
.short('E')
.long(OPT_CANONICALIZE)
.overrides_with_all([OPT_CANONICALIZE_EXISTING, OPT_CANONICALIZE_MISSING])
.help(translate!("realpath-help-canonicalize"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_CANONICALIZE_EXISTING)
.short('e')
.long(OPT_CANONICALIZE_EXISTING)
.overrides_with_all([OPT_CANONICALIZE, OPT_CANONICALIZE_MISSING])
.help(translate!("realpath-help-canonicalize-existing"))
.action(ArgAction::SetTrue),
)
.arg(
Arg::new(OPT_CANONICALIZE_MISSING)
.short('m')
.long(OPT_CANONICALIZE_MISSING)
.overrides_with_all([OPT_CANONICALIZE, OPT_CANONICALIZE_EXISTING])
.help(translate!("realpath-help-canonicalize-missing"))
.action(ArgAction::SetTrue),
)
Expand Down
62 changes: 62 additions & 0 deletions tests/by-util/test_realpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -505,3 +505,65 @@ fn test_realpath_empty_string() {
.fails()
.code_is(1);
}

#[test]
fn test_realpath_canonicalize_options() {
// Test that default, -E, and --canonicalize all allow nonexistent final component
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.mkdir("existing_dir");

let test_cases = [
vec![], // default behavior
vec!["-E"], // explicit -E flag
vec!["--canonicalize"], // --canonicalize long form
];

#[cfg(windows)]
let expected_path = "existing_dir\\nonexistent";
#[cfg(not(windows))]
let expected_path = "existing_dir/nonexistent";

for args in test_cases {
let mut ucmd = scene.ucmd();
for arg in args {
ucmd.arg(arg);
}
ucmd.arg("existing_dir/nonexistent")
.succeeds()
.stdout_contains(expected_path);
}
}

#[test]
fn test_realpath_canonicalize_vs_existing() {
// Test difference between -E and -e, and option overrides
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.mkdir("existing_dir");

let test_cases = [
(vec!["-E"], true), // -E should succeed with nonexistent final component
(vec!["-e"], false), // -e should fail with nonexistent final component
(vec!["-e", "-E"], true), // -E should override -e
];

#[cfg(windows)]
let expected_path = "existing_dir\\nonexistent";
#[cfg(not(windows))]
let expected_path = "existing_dir/nonexistent";

for (args, should_succeed) in test_cases {
let mut ucmd = scene.ucmd();
for arg in args {
ucmd.arg(arg);
}
ucmd.arg("existing_dir/nonexistent");

if should_succeed {
ucmd.succeeds().stdout_contains(expected_path);
} else {
ucmd.fails();
}
}
}
Loading