From 625c3f2330739e90b107a32fe3fe51e4c2d2a4b3 Mon Sep 17 00:00:00 2001 From: James Robson Date: Sun, 29 Aug 2021 15:08:45 +0100 Subject: [PATCH] Add -e/-m to realpath --- src/uu/realpath/src/realpath.rs | 39 +++++++++++++++++++++++++++--- tests/by-util/test_realpath.rs | 43 +++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/src/uu/realpath/src/realpath.rs b/src/uu/realpath/src/realpath.rs index cbe1d40da..451253d50 100644 --- a/src/uu/realpath/src/realpath.rs +++ b/src/uu/realpath/src/realpath.rs @@ -21,6 +21,8 @@ static OPT_STRIP: &str = "strip"; static OPT_ZERO: &str = "zero"; static OPT_PHYSICAL: &str = "physical"; static OPT_LOGICAL: &str = "logical"; +const OPT_CANONICALIZE_MISSING: &str = "canonicalize-missing"; +const OPT_CANONICALIZE_EXISTING: &str = "canonicalize-existing"; static ARG_FILES: &str = "files"; @@ -45,9 +47,16 @@ pub fn uumain(args: impl uucore::Args) -> i32 { let zero = matches.is_present(OPT_ZERO); let quiet = matches.is_present(OPT_QUIET); let logical = matches.is_present(OPT_LOGICAL); + let can_mode = if matches.is_present(OPT_CANONICALIZE_EXISTING) { + MissingHandling::Existing + } else if matches.is_present(OPT_CANONICALIZE_MISSING) { + MissingHandling::Missing + } else { + MissingHandling::Normal + }; let mut retcode = 0; for path in &paths { - if let Err(e) = resolve_path(path, strip, zero, logical) { + if let Err(e) = resolve_path(path, strip, zero, logical, can_mode) { if !quiet { show_error!("{}: {}", e, path.display()); } @@ -92,6 +101,24 @@ pub fn uu_app() -> App<'static, 'static> { .overrides_with_all(&[OPT_STRIP, OPT_LOGICAL]) .help("resolve symlinks as encountered (default)"), ) + .arg( + Arg::with_name(OPT_CANONICALIZE_EXISTING) + .short("e") + .long(OPT_CANONICALIZE_EXISTING) + .help( + "canonicalize by following every symlink in every component of the \ + given name recursively, all components must exist", + ), + ) + .arg( + Arg::with_name(OPT_CANONICALIZE_MISSING) + .short("m") + .long(OPT_CANONICALIZE_MISSING) + .help( + "canonicalize by following every symlink in every component of the \ + given name recursively, without requirements on components existence", + ), + ) .arg( Arg::with_name(ARG_FILES) .multiple(true) @@ -112,7 +139,13 @@ pub fn uu_app() -> App<'static, 'static> { /// /// This function returns an error if there is a problem resolving /// symbolic links. -fn resolve_path(p: &Path, strip: bool, zero: bool, logical: bool) -> std::io::Result<()> { +fn resolve_path( + p: &Path, + strip: bool, + zero: bool, + logical: bool, + can_mode: MissingHandling, +) -> std::io::Result<()> { let resolve = if strip { ResolveMode::None } else if logical { @@ -120,7 +153,7 @@ fn resolve_path(p: &Path, strip: bool, zero: bool, logical: bool) -> std::io::Re } else { ResolveMode::Physical }; - let abs = canonicalize(p, MissingHandling::Normal, resolve)?; + let abs = canonicalize(p, can_mode, resolve)?; let line_ending = if zero { '\0' } else { '\n' }; print!("{}{}", abs.display(), line_ending); diff --git a/tests/by-util/test_realpath.rs b/tests/by-util/test_realpath.rs index 7b6da5d36..72bf5b6ea 100644 --- a/tests/by-util/test_realpath.rs +++ b/tests/by-util/test_realpath.rs @@ -1,5 +1,9 @@ use crate::common::util::*; +use std::path::Path; + +static GIBBERISH: &str = "supercalifragilisticexpialidocious"; + #[test] fn test_realpath_current_directory() { let (at, mut ucmd) = at_and_ucmd!(); @@ -159,3 +163,42 @@ fn test_realpath_loop() { .succeeds() .stdout_only(at.plus_as_string("2\n")); } + +#[test] +fn test_realpath_default_allows_final_non_existent() { + let p = Path::new("").join(GIBBERISH); + let (at, mut ucmd) = at_and_ucmd!(); + let expect = path_concat!(at.root_dir_resolved(), p.to_str().unwrap()) + "\n"; + ucmd.arg(p.as_os_str()).succeeds().stdout_only(expect); +} + +#[test] +fn test_realpath_default_forbids_non_final_non_existent() { + let p = Path::new("").join(GIBBERISH).join(GIBBERISH); + new_ucmd!().arg(p.to_str().unwrap()).fails(); +} + +#[test] +fn test_realpath_existing() { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.arg("-e") + .arg(".") + .succeeds() + .stdout_only(at.plus_as_string(&format!("{}\n", at.root_dir_resolved()))); +} + +#[test] +fn test_realpath_existing_error() { + new_ucmd!().arg("-e").arg(GIBBERISH).fails(); +} + +#[test] +fn test_realpath_missing() { + let p = Path::new("").join(GIBBERISH).join(GIBBERISH); + let (at, mut ucmd) = at_and_ucmd!(); + let expect = path_concat!(at.root_dir_resolved(), p.to_str().unwrap()) + "\n"; + ucmd.arg("-m") + .arg(p.as_os_str()) + .succeeds() + .stdout_only(expect); +}