Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/find/matchers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ use ls::Ls;
use std::{
error::Error,
fs::{File, Metadata},
io::{stderr, Write},
path::{Path, PathBuf},
str::FromStr,
time::SystemTime,
Expand Down Expand Up @@ -534,13 +535,40 @@ fn build_matcher_tree(
return Err(From::from(format!("missing argument to {}", args[i])));
}
i += 1;
// GNU find warns when a -name/-iname pattern contains a directory

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it really need a 6 lines comment ? please make it shorter

// separator: -name matches basenames only, so such a pattern can
// never match and the user likely meant -wholename. See issue #783.
// A pattern made up only of '/' (e.g. `-name /`) is left alone: `/`
// is a valid basename for the root entry, so warning would be a
// false alarm (GNU itself miscategorises this — bug #62227).
if args[i].contains('/') && args[i].chars().any(|c| c != '/') {
writeln!(
&mut stderr(),
"find: warning: '{}' matches against basenames only, but the given pattern contains a directory separator ('/'), thus the expression will evaluate to false all the time. Did you mean '-wholename'?",
args[i - 1]
)
.unwrap();
}
Some(NameMatcher::new(args[i], args[i - 1].starts_with("-i")).into_box())
}
"-path" | "-ipath" | "-wholename" | "-iwholename" => {
if i >= args.len() - 1 {
return Err(From::from(format!("missing argument to {}", args[i])));
}
i += 1;
// GNU find warns when a -path/-wholename pattern ends with '/':
// a trailing separator can never match a real path. See issue #783.
// As with -name, a pattern of only '/' is exempt: `-path /` can
// legitimately match the root entry.
if args[i].ends_with('/') && args[i].chars().any(|c| c != '/') {
writeln!(
&mut stderr(),
"find: warning: {} {} will not match anything because it ends with /.",
args[i - 1],
args[i]
)
.unwrap();
}
Some(PathMatcher::new(args[i], args[i - 1].starts_with("-i")).into_box())
}
"-readable" => Some(AccessMatcher::Readable.into_box()),
Expand Down
60 changes: 60 additions & 0 deletions tests/test_find.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1614,3 +1614,63 @@ fn find_exits_cleanly_on_broken_pipe() {
"find panicked instead of exiting cleanly on a broken pipe:\n{stderr}"
);
}

// GNU find emits a warning when a -name/-iname pattern contains a directory
// separator (it matches basenames only, so the pattern can never match) and
// when a -path/-wholename pattern ends with '/'. find should warn on stderr
// but still exit successfully. See issue #783.
#[test]
fn name_pattern_with_separator_warns() {
ucmd()
.args(&["-name", "a/b"])
.succeeds()
.stderr_contains("'-name' matches against basenames only")
.stderr_contains("directory separator ('/')")
.stderr_contains("Did you mean '-wholename'?");

ucmd()
.args(&["-iname", "a/b"])
.succeeds()
.stderr_contains("'-iname' matches against basenames only");
}

#[test]
fn path_pattern_ending_with_separator_warns() {
ucmd()
.args(&["-path", "a/"])
.succeeds()
.stderr_contains("-path a/ will not match anything because it ends with /.");

ucmd()
.args(&["-wholename", "a/"])
.succeeds()
.stderr_contains("-wholename a/ will not match anything because it ends with /.");

ucmd()
.args(&["-ipath", "a/"])
.succeeds()
.stderr_contains("-ipath a/ will not match anything because it ends with /.");
}

#[test]
fn name_pattern_without_separator_does_not_warn() {
ucmd().args(&["-name", "a.txt"]).succeeds().no_stderr();
ucmd().args(&["-path", "a.txt"]).succeeds().no_stderr();
ucmd().args(&["-wholename", "a.txt"]).succeeds().no_stderr();
}

// A pattern made up only of '/' (e.g. `-name /` or `-path /`) is a legitimate
// way to match the root entry, so it must NOT trigger the separator warning
// (GNU itself gets this wrong — bug #62227). Covers the existing `find_slashes`
// behavior.
#[test]
fn all_slash_pattern_does_not_warn() {
ucmd()
.args(&["///", "-maxdepth", "0", "-name", "/"])
.succeeds()
.no_stderr();
ucmd()
.args(&["/", "-maxdepth", "0", "-path", "/"])
.succeeds()
.no_stderr();
}
Loading