diff --git a/CHANGELOG.md b/CHANGELOG.md index 08e0ed1e8..bd0cd7790 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## Bugfixes - Handle invalid working directories gracefully when using `--full-path`, see #1900 (@Xavrir). +- Normalize `--full-path` matching for relative search paths such as `..`, see #1513 (@leno23). - Fire the "search pattern contains a path separator" diagnostic for any pattern containing `/`, not just patterns that happen to name an existing directory. Preserves the legacy Windows behaviour that also flags native `\` separators when the pattern resolves to a real directory. See #1873. # 10.4.2 diff --git a/src/walk.rs b/src/walk.rs index 018ad2f70..34db3926f 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::ffi::OsStr; use std::io::{self, Write}; use std::mem; -use std::path::PathBuf; +use std::path::{Component, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, MutexGuard}; use std::thread; @@ -13,6 +13,7 @@ use crossbeam_channel::{Receiver, RecvTimeoutError, SendError, Sender, bounded}; use etcetera::BaseStrategy; use ignore::overrides::{Override, OverrideBuilder}; use ignore::{WalkBuilder, WalkParallel, WalkState}; +use normpath::PathExt; use regex::bytes::Regex; use crate::config::Config; @@ -674,7 +675,18 @@ fn search_str_for_entry<'a>( return Cow::Borrowed(entry_path.as_os_str()); } let path = entry_path.strip_prefix(".").unwrap_or(entry_path); - Cow::Owned(cwd.join(path).into()) + let absolute_path = cwd.join(path); + if path + .components() + .any(|component| matches!(component, Component::CurDir | Component::ParentDir)) + { + match absolute_path.normalize() { + Ok(normalized_path) => Cow::Owned(normalized_path.into()), + Err(_) => Cow::Owned(absolute_path.into()), + } + } else { + Cow::Owned(absolute_path.into()) + } } else { match entry_path.file_name() { Some(filename) => Cow::Borrowed(filename), diff --git a/tests/tests.rs b/tests/tests.rs index aa0919ca0..ebbdda80b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -4,7 +4,7 @@ mod testenv; use nix::unistd::{Gid, Group, Uid, User}; use std::fs; use std::io::Write; -use std::path::Path; +use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime}; use test_case::test_case; @@ -1299,6 +1299,21 @@ fn test_normalized_absolute_path() { ); } +/// Full path matching should normalize relative search paths as well. +#[test] +fn test_full_path_normalizes_relative_search_path() { + let (te, abs_path) = get_test_env_with_abs_path(DEFAULT_DIRS, DEFAULT_FILES); + + let target = PathBuf::from(&abs_path).join("one").join("b.foo"); + let pattern = escape(target.to_string_lossy().as_ref()); + + te.assert_output_subdirectory( + "one/two", + &["--full-path", "-t", "f", &pattern, ".."], + "../b.foo", + ); +} + /// File type filter (--type) #[test] fn test_type() {