From 5cdfc57b20ce27c779c08ccda8faf32d24c21e9f Mon Sep 17 00:00:00 2001 From: fixfon <12512757+fixfon@users.noreply.github.com> Date: Sat, 24 Oct 2026 23:06:05 +0300 Subject: [PATCH] ls: match glob patterns in the TERM list The entries in the TERMS list are glob patterns, but ls only honored an exact name or a trailing *. Patterns such as *color* and con[0-9]*x[0-9]* never matched, so TERM=konsole-256color got no color. Fixes #9098 --- src/uu/ls/src/config.rs | 13 +++++-------- tests/by-util/test_ls.rs | 36 +++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/src/uu/ls/src/config.rs b/src/uu/ls/src/config.rs index 29f4d9415af..1ec120ddeff 100644 --- a/src/uu/ls/src/config.rs +++ b/src/uu/ls/src/config.rs @@ -420,15 +420,12 @@ fn is_color_compatible_term() -> bool { let term = std::env::var_os("TERM"); let colorterm = std::env::var_os("COLORTERM"); - // Search function in the TERM struct to manage the wildcards + // Entries in TERMS are glob patterns (`*color*`, `con[0-9]*x[0-9]*`), not only `prefix*` let term_matches = |term: &OsStr| -> bool { - uucore::colors::TERMS.iter().any(|&pattern| { - term == pattern - || (pattern.ends_with('*') - && term - .as_encoded_bytes() - .starts_with(&pattern.as_bytes()[..pattern.len() - 1])) - }) + let term = term.to_string_lossy(); + uucore::colors::TERMS + .iter() + .any(|pattern| parse_glob::from_str(pattern).is_ok_and(|glob| glob.matches(&term))) }; match (term, colorterm) { diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 6662c427590..9688b034560 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline onetwo somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired tmpfs mdir COLORTERM mexe bcdef mfoo timefile +// spell-checker:ignore (words) READMECAREFULLY birthtime doesntexist oneline onetwo somebackup lrwx somefile somegroup somehiddenbackup somehiddenfile tabsize aaaaaaaa bbbb cccc dddddddd ncccc neee naaaaa nbcdef nfffff dired subdired tmpfs mdir COLORTERM mexe bcdef mfoo timefile konsole // spell-checker:ignore (words) fakeroot setcap drwxr bcdlps mdangling mentry awith acolons Nofile NOTCAPABLE iproduct newfstatat isox #![allow( @@ -7097,6 +7097,40 @@ fn test_term_colorterm() { ); } +#[test] +fn test_term_glob_patterns() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.mkdir("dir"); + + for (term, colored) in [ + ("xterm-256color", true), + ("konsole", true), + ("dumb", false), + ("konsole-256color", true), + ("linux-256color", true), + ("konsole-direct", true), + ("con80x25", true), + ] { + let result = scene + .ucmd() + .arg("--color=always") + .env("LS_COLORS", "") + .env("TERM", term) + .succeeds(); + let expected = if colored { + "\\u{1b}[0m\\u{1b}[01;34mdir\\u{1b}[0m" + } else { + "dir" + }; + assert_eq!( + result.stdout_str().trim().escape_default().to_string(), + expected, + "TERM={term}" + ); + } +} + #[cfg(all(unix, not(target_vendor = "apple")))] #[test] fn test_acl_display() {