Skip to content

Commit

Permalink
Fixed -lname and -iname logic differences with the GNU version.
Browse files Browse the repository at this point in the history
  • Loading branch information
hanbings committed Jul 31, 2024
1 parent 23675ab commit ca126d5
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 26 deletions.
51 changes: 31 additions & 20 deletions src/find/matchers/lname.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,25 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

use super::glob::Pattern;
use super::{Matcher, MatcherIO};
use std::io::{stderr, Write};
use std::path::PathBuf;

use walkdir::DirEntry;

use super::glob::Pattern;
use super::{Matcher, MatcherIO};

fn read_link_target(file_info: &DirEntry) -> Option<PathBuf> {
fn read_link_target(file_info: &DirEntry, follow: bool) -> Option<PathBuf> {
match file_info.path().read_link() {
Ok(target) => Some(target),
Ok(target) => {
if follow {
return None;
}

Some(target)
}
Err(err) => {
if follow && err.kind() == std::io::ErrorKind::NotFound {
return None;
}

// If it's not a symlink, then it's not an error that should be
// shown.
if err.kind() != std::io::ErrorKind::InvalidInput {
Expand All @@ -27,48 +33,43 @@ fn read_link_target(file_info: &DirEntry) -> Option<PathBuf> {
)
.unwrap();
}

None
}
}
}

/// This matcher makes a comparison of the link target against a shell wildcard
/// pattern. See `glob::Pattern` for details on the exact syntax.
pub struct LinkNameMatcher {
pattern: Pattern,
follow: bool,
}

impl LinkNameMatcher {
pub fn new(pattern_string: &str, caseless: bool) -> LinkNameMatcher {
pub fn new(pattern_string: &str, caseless: bool, follow: bool) -> LinkNameMatcher {
let pattern = Pattern::new(pattern_string, caseless);
Self { pattern }
Self { pattern, follow }
}
}

impl Matcher for LinkNameMatcher {
fn matches(&self, file_info: &DirEntry, _: &mut MatcherIO) -> bool {
if let Some(target) = read_link_target(file_info) {
if let Some(target) = read_link_target(file_info, self.follow) {
self.pattern.matches(&target.to_string_lossy())
} else {
false
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::find::matchers::tests::get_dir_entry_for;
use crate::find::tests::FakeDependencies;

use std::io::ErrorKind;

#[cfg(unix)]
use std::os::unix::fs::symlink;
#[cfg(windows)]
use std::os::windows::fs::symlink_file;

fn create_file_link() {
#[cfg(unix)]
if let Err(e) = symlink("abbbc", "test_data/links/link-f") {
Expand All @@ -86,13 +87,12 @@ mod tests {
);
}
}

#[test]
fn matches_against_link_target() {
create_file_link();

let link_f = get_dir_entry_for("test_data/links", "link-f");
let matcher = LinkNameMatcher::new("ab?bc", false);
let matcher = LinkNameMatcher::new("ab?bc", false, false);
let deps = FakeDependencies::new();
assert!(matcher.matches(&link_f, &mut deps.new_matcher_io()));
}
Expand All @@ -102,8 +102,19 @@ mod tests {
create_file_link();

let link_f = get_dir_entry_for("test_data/links", "link-f");
let matcher = LinkNameMatcher::new("AbB?c", true);
let matcher = LinkNameMatcher::new("AbB?c", true, false);
let deps = FakeDependencies::new();
assert!(matcher.matches(&link_f, &mut deps.new_matcher_io()));
}

#[test]
fn matches_with_follow_option() {
create_file_link();

let link_f = get_dir_entry_for("test_data/links", "link-f");
let matcher = LinkNameMatcher::new("ab?bc", false, true);
let deps = FakeDependencies::new();
// This matcher always returns false when the follow option is enabled.
assert!(!matcher.matches(&link_f, &mut deps.new_matcher_io()));
}
}
10 changes: 5 additions & 5 deletions src/find/matchers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,10 @@ fn build_matcher_tree(
return Err(From::from(format!("missing argument to {}", args[i])));
}
i += 1;
Some(LinkNameMatcher::new(args[i], args[i - 1].starts_with("-i")).into_box())
Some(
LinkNameMatcher::new(args[i], args[i - 1].starts_with("-i"), config.follow)
.into_box(),
)
}
"-name" | "-iname" => {
if i >= args.len() - 1 {
Expand Down Expand Up @@ -713,10 +716,7 @@ fn build_matcher_tree(
// the file that a symbolic link points to rather than the link itself.
//
// 5. causes the -lname and -ilname predicates always to return false.
// links: https://www.gnu.org/software/findutils/manual/html_node/find_html/Symbolic-Links.html
// The -lname and -iname parameters differ between the GNU version and the GNU documentation.
// The current implementation follows the actual behavior of the GNU version.
// which means that the specified directories are still traversed.
// (unless they happen to match broken symbolic links)
config.follow = true;
config.no_leaf_dirs = true;
Some(TrueMatcher.into_box())
Expand Down
8 changes: 7 additions & 1 deletion src/find/matchers/type_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,13 @@ impl Matcher for TypeMatcher {
// `file_info.path()` will always return the underlying file.
let path = file_info.path();
match path.metadata() {
Ok(metadata) => metadata.file_type(),
Ok(metadata) => {
if self.follow_ignore_l_option {
return false;
}

metadata.file_type()
}
Err(e) => {
if self.follow_ignore_l_option && e.kind() == ErrorKind::NotFound {
return true;
Expand Down

0 comments on commit ca126d5

Please sign in to comment.