Use a uniform output format for searching ../

closes #107, fixes #82 by the way
This commit is contained in:
J.W 2017-10-15 01:57:15 +08:00 committed by David Peter
parent fe5d8aa29e
commit e649c8fa79
5 changed files with 120 additions and 54 deletions

View file

@ -10,7 +10,7 @@ pub fn path_relative_from(path: &Path, base: &Path) -> Option<PathBuf> {
if path.is_absolute() { if path.is_absolute() {
Some(PathBuf::from(path)) Some(PathBuf::from(path))
} else { } else {
None Some(PathBuf::from(base.join(path)))
} }
} else { } else {
let mut ita = path.components(); let mut ita = path.components();

View file

@ -16,7 +16,7 @@ mod walk;
use std::env; use std::env;
use std::error::Error; use std::error::Error;
use std::path::Path; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use std::time; use std::time;
@ -35,26 +35,16 @@ fn main() {
let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern); let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern);
// Get the current working directory // Get the current working directory
let current_dir_buf = match env::current_dir() { let current_dir = Path::new(".");
Ok(cd) => cd, if !current_dir.is_dir() {
Err(_) => error("Error: could not get current directory."), error("Error: could not get current directory.");
}; }
let current_dir = current_dir_buf.as_path();
// Get the root directory for the search // Get the root directory for the search
let mut root_dir_is_absolute = false; let mut root_dir_buf = match matches.value_of("path") {
let root_dir_buf = if let Some(rd) = matches.value_of("path") { Some(path) => PathBuf::from(path),
let path = Path::new(rd); None => current_dir.to_path_buf(),
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()
}; };
if !root_dir_buf.is_dir() { if !root_dir_buf.is_dir() {
error(&format!( error(&format!(
"Error: '{}' is not a directory.", "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(); let root_dir = root_dir_buf.as_path();
// The search will be case-sensitive if the command line flag is set or // 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") .value_of("max-buffer-time")
.and_then(|n| u64::from_str_radix(n, 10).ok()) .and_then(|n| u64::from_str_radix(n, 10).ok())
.map(time::Duration::from_millis), .map(time::Duration::from_millis),
path_display: if matches.is_present("absolute-path") || root_dir_is_absolute { path_display: path_display,
PathDisplay::Absolute
} else {
PathDisplay::Relative
},
ls_colors: ls_colors, ls_colors: ls_colors,
file_type: match matches.value_of("file-type") { file_type: match matches.value_of("file-type") {
Some("f") | Some("file") => FileType::RegularFile, Some("f") | Some("file") => FileType::RegularFile,
@ -127,17 +122,21 @@ fn main() {
}), }),
}; };
let root = Path::new(ROOT_DIR); // If base_dir is ROOT_DIR, then root_dir must be absolute.
let base = match config.path_display { // Otherwise root_dir/entry cannot be turned into an existing relative path from base_dir.
PathDisplay::Relative => current_dir, //
PathDisplay::Absolute => root, // 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) match RegexBuilder::new(pattern)
.case_insensitive(!config.case_sensitive) .case_insensitive(!config.case_sensitive)
.dot_matches_new_line(true) .dot_matches_new_line(true)
.build() { .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()), Err(err) => error(err.description()),
} }
} }

View file

@ -11,7 +11,11 @@ use std::os::unix::fs::PermissionsExt;
use ansi_term; use ansi_term;
pub fn print_entry(base: &Path, entry: &PathBuf, config: &FdOptions) { 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 { let path_to_print = if config.path_display == PathDisplay::Absolute {
&path_full &path_full

View file

@ -2,7 +2,7 @@ use internal::{error, FdOptions};
use fshelper; use fshelper;
use output; use output;
use std::path::{Path, PathBuf}; use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::channel; use std::sync::mpsc::channel;
use std::thread; use std::thread;
@ -153,15 +153,11 @@ pub fn scan(root: &Path, pattern: Arc<Regex>, base: &Path, config: Arc<FdOptions
if let Some(search_str) = search_str_o { if let Some(search_str) = search_str_o {
pattern.find(&*search_str).map(|_| { pattern.find(&*search_str).map(|_| {
let mut path_rel_buf = match fshelper::path_relative_from(entry_path, &*base) { let path_rel_buf = match fshelper::path_relative_from(entry_path, &*base) {
Some(p) => p, Some(p) => p,
None => error("Error: could not get relative path for directory entry."), 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 // TODO: take care of the unwrap call
tx_thread.send(path_rel_buf.to_owned()).unwrap() tx_thread.send(path_rel_buf.to_owned()).unwrap()
}); });

View file

@ -4,6 +4,20 @@ mod testenv;
use testenv::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 /// Simple tests
#[test] #[test]
fn test_simple() { fn test_simple() {
@ -62,21 +76,21 @@ fn test_explicit_root_path() {
"one/two", "one/two",
&["foo", "../../"], &["foo", "../../"],
"../../a.foo "../../a.foo
../b.foo ../../one/b.foo
c.foo ../../one/two/c.foo
C.Foo2 ../../one/two/C.Foo2
three/d.foo ../../one/two/three/d.foo
three/directory_foo", ../../one/two/three/directory_foo",
); );
te.assert_output_subdirectory( te.assert_output_subdirectory(
"one/two/three", "one/two/three",
&["", ".."], &["", ".."],
". "../c.foo
../c.foo
../C.Foo2 ../C.Foo2
d.foo ../three
directory_foo", ../three/d.foo
../three/directory_foo",
); );
} }
@ -314,15 +328,7 @@ fn test_max_depth() {
fn test_absolute_path() { fn test_absolute_path() {
let te = TestEnv::new(); let te = TestEnv::new();
let abs_path = te.root() let abs_path = get_absolute_root_path(&te);
.canonicalize()
.expect("absolute path")
.to_str()
.expect("string")
.to_string();
#[cfg(windows)]
let abs_path = abs_path.trim_left_matches(r"\\?\");
te.assert_output( te.assert_output(
&["--absolute-path"], &["--absolute-path"],
@ -416,3 +422,64 @@ fn test_extension() {
te.assert_output(&["--extension", "foo2"], "one/two/C.Foo2"); 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
),
);
}