diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 8caff423274..a8f30a1744d 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -11,7 +11,7 @@ extern crate uucore; use clap::{crate_version, Arg, Command}; -use glob::Pattern; +use glob::{MatchOptions, Pattern}; use lscolors::LsColors; use number_prefix::NumberPrefix; use once_cell::unsync::OnceCell; @@ -38,6 +38,7 @@ use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; use unicode_width::UnicodeWidthStr; #[cfg(unix)] use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR}; +use uucore::parse_glob; use uucore::quoting_style::{escape_name, QuotingStyle}; use uucore::{ display::Quotable, @@ -731,7 +732,7 @@ impl Config { } for pattern in options.values_of(options::IGNORE).into_iter().flatten() { - match Pattern::new(pattern) { + match parse_glob::from_str(pattern) { Ok(p) => { ignore_patterns.push(p); } @@ -741,7 +742,7 @@ impl Config { if files == Files::Normal { for pattern in options.values_of(options::HIDE).into_iter().flatten() { - match Pattern::new(pattern) { + match parse_glob::from_str(pattern) { Ok(p) => { ignore_patterns.push(p); } @@ -1822,16 +1823,18 @@ fn should_display(entry: &DirEntry, config: &Config) -> bool { return false; } - // check if explicitly ignored - for pattern in &config.ignore_patterns { - if pattern.matches(entry.file_name().to_str().unwrap()) { - return false; - }; - continue; - } - - // else default to display - true + // check if it is among ignore_patterns + let options = MatchOptions { + // setting require_literal_leading_dot to match behavior in GNU ls + require_literal_leading_dot: true, + require_literal_separator: false, + case_sensitive: true, + }; + let file_name = entry.file_name().into_string().unwrap(); + !config + .ignore_patterns + .iter() + .any(|p| p.matches_with(&file_name, options)) } fn enter_directory( diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index f1f139387cc..01949116a7a 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -2655,6 +2655,69 @@ fn test_ls_ignore_backups() { .stdout_does_not_contain(".somehiddenbackup~"); } +#[test] +fn test_ls_ignore_explicit_period() { + // In ls ignore patterns, leading periods must be explicitly specified + let scene = TestScenario::new(util_name!()); + + let at = &scene.fixtures; + at.touch(".hidden.yml"); + at.touch("regular.yml"); + + scene + .ucmd() + .arg("-a") + .arg("--ignore") + .arg("?hidden.yml") + .succeeds() + .stdout_contains(".hidden.yml") + .stdout_contains("regular.yml"); + + scene + .ucmd() + .arg("-a") + .arg("--ignore") + .arg("*.yml") + .succeeds() + .stdout_contains(".hidden.yml") + .stdout_does_not_contain("regular.yml"); + + // Leading period is explicitly specified + scene + .ucmd() + .arg("-a") + .arg("--ignore") + .arg(".*.yml") + .succeeds() + .stdout_does_not_contain(".hidden.yml") + .stdout_contains("regular.yml"); +} + +#[test] +fn test_ls_ignore_negation() { + let scene = TestScenario::new(util_name!()); + + let at = &scene.fixtures; + at.touch("apple"); + at.touch("boy"); + + scene + .ucmd() + .arg("--ignore") + .arg("[!a]*") + .succeeds() + .stdout_contains("apple") + .stdout_does_not_contain("boy"); + + scene + .ucmd() + .arg("--ignore") + .arg("[^a]*") + .succeeds() + .stdout_contains("apple") + .stdout_does_not_contain("boy"); +} + #[test] fn test_ls_directory() { let scene = TestScenario::new(util_name!());