Skip to content

Commit

Permalink
realpath: add -L and -P command-line arguments
Browse files Browse the repository at this point in the history
Add the `-L` (logical) and `-P` (physical) command-line arguments to
`realpath`. The `-L` option makes the program resolve ".." components
before symbolic links. The `-P` option makes the program resolve
symbolic links as they are encountered.

This also changes the default behavior of `realpath` from the `-L`
behavior to the `-P` behavior. This matches the behavior of GNU
`realpath`.
  • Loading branch information
jfinkels committed Jul 28, 2021
1 parent bc16746 commit 14977c8
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 6 deletions.
36 changes: 30 additions & 6 deletions src/uu/realpath/src/realpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ static ABOUT: &str = "print the resolved path";
static OPT_QUIET: &str = "quiet";
static OPT_STRIP: &str = "strip";
static OPT_ZERO: &str = "zero";
static OPT_LOGICAL: &str = "logical";
static OPT_PHYSICAL: &str = "physical";

static ARG_FILES: &str = "files";

Expand All @@ -42,9 +44,13 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let strip = matches.is_present(OPT_STRIP);
let zero = matches.is_present(OPT_ZERO);
let quiet = matches.is_present(OPT_QUIET);
let logical = matches.is_present(OPT_LOGICAL);
let physical = matches.is_present(OPT_PHYSICAL);
// If neither `-P` nor `-L` are present, use the default behavior `-P`.
let logical = logical && !physical;
let mut retcode = 0;
for path in &paths {
if let Err(e) = resolve_path(path, strip, zero) {
if let Err(e) = resolve_path(path, strip, zero, logical) {
if !quiet {
show_error!("{}: {}", e, path.display());
}
Expand All @@ -58,6 +64,20 @@ pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.version(crate_version!())
.about(ABOUT)
.arg(
Arg::with_name(OPT_LOGICAL)
.short("L")
.long(OPT_LOGICAL)
.help("resolve '..' components before symlinks")
.takes_value(false),
)
.arg(
Arg::with_name(OPT_PHYSICAL)
.short("P")
.long(OPT_PHYSICAL)
.help("resolve symlinks as encountered (default)")
.takes_value(false),
)
.arg(
Arg::with_name(OPT_QUIET)
.short("q")
Expand Down Expand Up @@ -88,21 +108,25 @@ pub fn uu_app() -> App<'static, 'static> {
/// Resolve a path to an absolute form and print it.
///
/// If `strip` is `true`, then this function does not attempt to resolve
/// symbolic links in the path. If `zero` is `true`, then this function
/// prints the path followed by the null byte (`'\0'`) instead of a
/// newline character (`'\n'`).
/// symbolic links in the path.
///
/// If `zero` is `true`, then this function prints the path followed by
/// the null byte (`'\0'`) instead of a newline character (`'\n'`).
///
/// If `logical` is `true`, then this function resolves ".." components
/// before symbolic links.
///
/// # Errors
///
/// This function returns an error if there is a problem resolving
/// symbolic links.
fn resolve_path(p: &Path, strip: bool, zero: bool) -> std::io::Result<()> {
fn resolve_path(p: &Path, strip: bool, zero: bool, logical: bool) -> std::io::Result<()> {
let mode = if strip {
CanonicalizeMode::None
} else {
CanonicalizeMode::Normal
};
let abs = canonicalize(p, mode, true)?;
let abs = canonicalize(p, mode, logical)?;
let line_ending = if zero { '\0' } else { '\n' };
print!("{}{}", abs.display(), line_ending);
Ok(())
Expand Down
51 changes: 51 additions & 0 deletions tests/by-util/test_realpath.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,54 @@ fn test_realpath_file_and_links_strip_zero() {
.succeeds()
.stdout_contains("bar\u{0}");
}

#[test]
fn test_logical() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

at.mkdir("dir1");
at.mkdir("dir1/dir2");
at.symlink_dir("dir1/dir2", "link_to_dir2");

let expected = format!("{}\n", at.as_string());
scene
.ucmd()
.args(&["-L", "link_to_dir2/.."])
.succeeds()
.stdout_is(expected);
}

#[test]
fn test_physical() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

at.mkdir("dir1");
at.mkdir("dir1/dir2");
at.symlink_dir("dir1/dir2", "link_to_dir2");

let expected = format!("{}/dir1\n", at.as_string());
scene
.ucmd()
.args(&["-P", "link_to_dir2/.."])
.succeeds()
.stdout_is(expected);
}

#[test]
fn test_physical_default() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;

at.mkdir("dir1");
at.mkdir("dir1/dir2");
at.symlink_dir("dir1/dir2", "link_to_dir2");

let expected = format!("{}/dir1\n", at.as_string());
scene
.ucmd()
.arg("link_to_dir2/..")
.succeeds()
.stdout_is(expected);
}

0 comments on commit 14977c8

Please sign in to comment.