diff --git a/src/main.rs b/src/main.rs index aec9a63..62f5d41 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,6 +25,7 @@ mod app; mod exec; mod internal; mod output; +mod utils; mod walk; #[cfg(windows)] @@ -152,7 +153,7 @@ fn main() { .collect(), }, extensions: matches.values_of("extension").map(|exts| { - exts.map(|e| e.trim_left_matches('.').to_lowercase()) + exts.map(|e| String::from(".") + &e.trim_left_matches('.')) .collect() }), command, diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..f2c3926 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,45 @@ +// Copyright (c) 2017 fd developers +// Licensed under the Apache License, Version 2.0 +// +// or the MIT license , +// at your option. All files in the project carrying such +// notice may not be copied, modified, or distributed except +// according to those terms. + +use std::path::Path; +use std::iter::Iterator; + +/// Determine if an os string ends with any of the given extensions (case insensitive). +pub fn path_has_any_extension<'a, I>(path: &Path, exts: I) -> bool +where + I: 'a + Iterator + Clone, +{ + // TODO: remove these two lines when we drop support for Rust version < 1.23. + #[allow(unused_imports)] + use std::ascii::AsciiExt; + + if let Some(ref name) = path.file_name() { + if let Some(ref name_str) = name.to_str() { + exts.clone().any(|x| { + let mut it = name_str.chars().rev(); + + if x.chars() + .rev() + .zip(&mut it) + .all(|(a, b)| a.eq_ignore_ascii_case(&b)) + { + match it.next() { + Some('/') | None => false, + _ => true, + } + } else { + false + } + }) + } else { + false + } + } else { + false + } +} diff --git a/src/walk.rs b/src/walk.rs index 8f18bf8..121d3a8 100644 --- a/src/walk.rs +++ b/src/walk.rs @@ -11,6 +11,7 @@ extern crate ctrlc; use exec; use fshelper; use internal::{error, FdOptions, EXITCODE_SIGINT, MAX_BUFFER_LENGTH}; +use utils::path_has_any_extension; use output; use std::process; @@ -214,10 +215,7 @@ pub fn scan(path_vec: &[PathBuf], pattern: Arc, config: Arc) { // Filter out unwanted extensions. if let Some(ref filter_exts) = config.extensions { - let entry_ext = entry_path - .extension() - .map(|e| e.to_string_lossy().to_lowercase()); - if entry_ext.map_or(true, |ext| !filter_exts.contains(&ext)) { + if !path_has_any_extension(entry_path, filter_exts.iter()) { return ignore::WalkState::Continue; } } diff --git a/tests/tests.rs b/tests/tests.rs index 3e80786..2e7e2b8 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -530,6 +530,28 @@ fn test_extension() { te.assert_output(&["--extension", ".foo", "a"], "a.foo"); te.assert_output(&["--extension", "foo2"], "one/two/C.Foo2"); + + let te2 = TestEnv::new(&[], &["spam.bar.baz", "egg.bar.baz", "yolk.bar.baz.sig"]); + + te2.assert_output( + &["--extension", ".bar.baz"], + "spam.bar.baz + egg.bar.baz", + ); + + te2.assert_output(&["--extension", "sig"], "yolk.bar.baz.sig"); + + te2.assert_output(&["--extension", "bar.baz.sig"], "yolk.bar.baz.sig"); + + let te3 = TestEnv::new(&[], &["latin1.e\u{301}xt", "smiley.☻"]); + + te3.assert_output(&["--extension", "☻"], "smiley.☻"); + + te3.assert_output(&["--extension", ".e\u{301}xt"], "latin1.e\u{301}xt"); + + let te4 = TestEnv::new(&[], &[".hidden", "test.hidden"]); + + te4.assert_output(&["--hidden", "--extension", ".hidden"], "test.hidden"); } /// Symlinks misc