From e649c8fa79794843656493c96d8c44913c44d3a3 Mon Sep 17 00:00:00 2001 From: "J.W" Date: Sun, 15 Oct 2017 01:57:15 +0800 Subject: [PATCH] Use a uniform output format for searching ../ closes #107, fixes #82 by the way --- src/fshelper/mod.rs | 2 +- src/main.rs | 55 ++++++++++++----------- src/output.rs | 6 ++- src/walk.rs | 8 +--- tests/tests.rs | 103 ++++++++++++++++++++++++++++++++++++-------- 5 files changed, 120 insertions(+), 54 deletions(-) diff --git a/src/fshelper/mod.rs b/src/fshelper/mod.rs index c6f38f5..3e30c83 100644 --- a/src/fshelper/mod.rs +++ b/src/fshelper/mod.rs @@ -10,7 +10,7 @@ pub fn path_relative_from(path: &Path, base: &Path) -> Option { if path.is_absolute() { Some(PathBuf::from(path)) } else { - None + Some(PathBuf::from(base.join(path))) } } else { let mut ita = path.components(); diff --git a/src/main.rs b/src/main.rs index 3d1435c..df56c8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,7 @@ mod walk; use std::env; use std::error::Error; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::sync::Arc; use std::time; @@ -35,26 +35,16 @@ fn main() { let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern); // Get the current working directory - let current_dir_buf = match env::current_dir() { - Ok(cd) => cd, - Err(_) => error("Error: could not get current directory."), - }; - let current_dir = current_dir_buf.as_path(); + let current_dir = Path::new("."); + if !current_dir.is_dir() { + error("Error: could not get current directory."); + } // Get the root directory for the search - let mut root_dir_is_absolute = false; - let root_dir_buf = if let Some(rd) = matches.value_of("path") { - let path = Path::new(rd); - - root_dir_is_absolute = path.is_absolute(); - - fshelper::absolute_path(path).unwrap_or_else(|_| { - error(&format!("Error: could not find directory '{}'.", rd)) - }) - } else { - current_dir_buf.clone() + let mut root_dir_buf = match matches.value_of("path") { + Some(path) => PathBuf::from(path), + None => current_dir.to_path_buf(), }; - if !root_dir_buf.is_dir() { error(&format!( "Error: '{}' is not a directory.", @@ -62,6 +52,15 @@ fn main() { )); } + let path_display = if matches.is_present("absolute-path") || root_dir_buf.is_absolute() { + PathDisplay::Absolute + } else { + PathDisplay::Relative + }; + + if path_display == PathDisplay::Absolute && root_dir_buf.is_relative() { + root_dir_buf = fshelper::absolute_path(root_dir_buf.as_path()).unwrap(); + } let root_dir = root_dir_buf.as_path(); // The search will be case-sensitive if the command line flag is set or @@ -109,11 +108,7 @@ fn main() { .value_of("max-buffer-time") .and_then(|n| u64::from_str_radix(n, 10).ok()) .map(time::Duration::from_millis), - path_display: if matches.is_present("absolute-path") || root_dir_is_absolute { - PathDisplay::Absolute - } else { - PathDisplay::Relative - }, + path_display: path_display, ls_colors: ls_colors, file_type: match matches.value_of("file-type") { Some("f") | Some("file") => FileType::RegularFile, @@ -127,17 +122,21 @@ fn main() { }), }; - let root = Path::new(ROOT_DIR); - let base = match config.path_display { - PathDisplay::Relative => current_dir, - PathDisplay::Absolute => root, + // If base_dir is ROOT_DIR, then root_dir must be absolute. + // Otherwise root_dir/entry cannot be turned into an existing relative path from base_dir. + // + // We utilize ROOT_DIR to avoid resolving parent directories the root_dir. + let base_dir_buf = match config.path_display { + PathDisplay::Relative => current_dir.to_path_buf(), + PathDisplay::Absolute => PathBuf::from(ROOT_DIR), }; + let base_dir = base_dir_buf.as_path(); match RegexBuilder::new(pattern) .case_insensitive(!config.case_sensitive) .dot_matches_new_line(true) .build() { - Ok(re) => walk::scan(root_dir, Arc::new(re), base, Arc::new(config)), + Ok(re) => walk::scan(root_dir, Arc::new(re), base_dir, Arc::new(config)), Err(err) => error(err.description()), } } diff --git a/src/output.rs b/src/output.rs index a32c4d3..aa246ea 100644 --- a/src/output.rs +++ b/src/output.rs @@ -11,7 +11,11 @@ use std::os::unix::fs::PermissionsExt; use ansi_term; pub fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) { - let path_full = base.join(entry); + let path_full = if !entry.as_os_str().is_empty() { + base.join(entry) + } else { + base.to_path_buf() + }; let path_to_print = if config.path_display == PathDisplay::Absolute { &path_full diff --git a/src/walk.rs b/src/walk.rs index a5666db..03ca003 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -2,7 +2,7 @@ use internal::{error, FdOptions}; use fshelper; use output; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::sync::Arc; use std::sync::mpsc::channel; use std::thread; @@ -153,15 +153,11 @@ pub fn scan(root: &Path, pattern: Arc, base: &Path, config: Arc p, None => error("Error: could not get relative path for directory entry."), }; - if path_rel_buf == PathBuf::new() { - path_rel_buf.push("."); - } - // TODO: take care of the unwrap call tx_thread.send(path_rel_buf.to_owned()).unwrap() }); diff --git a/tests/tests.rs b/tests/tests.rs index dfd02fb..676c312 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -4,6 +4,20 @@ mod testenv; use testenv::TestEnv; +fn get_absolute_root_path(env: &TestEnv) -> String { + let path = env.root() + .canonicalize() + .expect("absolute path") + .to_str() + .expect("string") + .to_string(); + + #[cfg(windows)] + let path = path.trim_left_matches(r"\\?\").to_string(); + + path +} + /// Simple tests #[test] fn test_simple() { @@ -62,21 +76,21 @@ fn test_explicit_root_path() { "one/two", &["foo", "../../"], "../../a.foo - ../b.foo - c.foo - C.Foo2 - three/d.foo - three/directory_foo", + ../../one/b.foo + ../../one/two/c.foo + ../../one/two/C.Foo2 + ../../one/two/three/d.foo + ../../one/two/three/directory_foo", ); te.assert_output_subdirectory( "one/two/three", &["", ".."], - ". - ../c.foo + "../c.foo ../C.Foo2 - d.foo - directory_foo", + ../three + ../three/d.foo + ../three/directory_foo", ); } @@ -314,15 +328,7 @@ fn test_max_depth() { fn test_absolute_path() { let te = TestEnv::new(); - let abs_path = te.root() - .canonicalize() - .expect("absolute path") - .to_str() - .expect("string") - .to_string(); - - #[cfg(windows)] - let abs_path = abs_path.trim_left_matches(r"\\?\"); + let abs_path = get_absolute_root_path(&te); te.assert_output( &["--absolute-path"], @@ -416,3 +422,64 @@ fn test_extension() { te.assert_output(&["--extension", "foo2"], "one/two/C.Foo2"); } + +/// Symlinks misc +#[test] +fn test_symlink() { + let te = TestEnv::new(); + + let abs_path = get_absolute_root_path(&te); + + // From: http://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html + // The getcwd() function shall place an absolute pathname of the current working directory in + // the array pointed to by buf, and return buf. The pathname shall contain no components that + // are dot or dot-dot, or are symbolic links. + // + // Symlinks on Unix are aliases to real paths, only has one redirection. + // + // Symlinks on Windows can refer to symlinks, and are resolved after logical step "..". + + let parent_parent = if cfg!(windows) { ".." } else { "../.." }; + te.assert_output_subdirectory( + "symlink", + &["", &parent_parent], + &format!( + "{dir}/a.foo + {dir}/one + {dir}/one/b.foo + {dir}/one/two + {dir}/one/two/c.foo + {dir}/one/two/C.Foo2 + {dir}/one/two/three + {dir}/one/two/three/d.foo + {dir}/one/two/three/directory_foo + {dir}/symlink", + dir = &parent_parent + ), + ); + + te.assert_output_subdirectory( + "symlink", + &["--absolute-path"], + &format!( + "{abs_path}/one/two/c.foo + {abs_path}/one/two/C.Foo2 + {abs_path}/one/two/three + {abs_path}/one/two/three/d.foo + {abs_path}/one/two/three/directory_foo", + abs_path = abs_path + ), + ); + + te.assert_output( + &["", &format!("{abs_path}/symlink", abs_path = abs_path)], + &format!( + "{abs_path}/symlink/c.foo + {abs_path}/symlink/C.Foo2 + {abs_path}/symlink/three + {abs_path}/symlink/three/d.foo + {abs_path}/symlink/three/directory_foo", + abs_path = abs_path + ), + ); +}