mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
Merge pull request #7089 from sylvestre/dircolors
dircolors: fix empty COLORTERM matching with ?* pattern
This commit is contained in:
commit
d7800b5c88
2 changed files with 86 additions and 50 deletions
|
@ -3,12 +3,11 @@
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) clrtoeol dircolors eightbit endcode fnmatch leftcode multihardlink rightcode setenv sgid suid colorterm
|
// spell-checker:ignore (ToDO) clrtoeol dircolors eightbit endcode fnmatch leftcode multihardlink rightcode setenv sgid suid colorterm disp
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
//use std::io::IsTerminal;
|
|
||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
@ -360,8 +359,6 @@ enum ParseState {
|
||||||
}
|
}
|
||||||
|
|
||||||
use uucore::{format_usage, parse_glob};
|
use uucore::{format_usage, parse_glob};
|
||||||
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
|
||||||
fn parse<T>(user_input: T, fmt: &OutputFmt, fp: &str) -> Result<String, String>
|
fn parse<T>(user_input: T, fmt: &OutputFmt, fp: &str) -> Result<String, String>
|
||||||
where
|
where
|
||||||
T: IntoIterator,
|
T: IntoIterator,
|
||||||
|
@ -372,10 +369,12 @@ where
|
||||||
|
|
||||||
result.push_str(&prefix);
|
result.push_str(&prefix);
|
||||||
|
|
||||||
|
// Get environment variables once at the start
|
||||||
let term = env::var("TERM").unwrap_or_else(|_| "none".to_owned());
|
let term = env::var("TERM").unwrap_or_else(|_| "none".to_owned());
|
||||||
let term = term.as_str();
|
let colorterm = env::var("COLORTERM").unwrap_or_default();
|
||||||
|
|
||||||
let mut state = ParseState::Global;
|
let mut state = ParseState::Global;
|
||||||
|
let mut saw_colorterm_match = false;
|
||||||
|
|
||||||
for (num, line) in user_input.into_iter().enumerate() {
|
for (num, line) in user_input.into_iter().enumerate() {
|
||||||
let num = num + 1;
|
let num = num + 1;
|
||||||
|
@ -395,52 +394,38 @@ where
|
||||||
num
|
num
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let lower = key.to_lowercase();
|
|
||||||
if lower == "term" || lower == "colorterm" {
|
|
||||||
if term.fnmatch(val) {
|
|
||||||
state = ParseState::Matched;
|
|
||||||
} else if state != ParseState::Matched {
|
|
||||||
state = ParseState::Pass;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if state == ParseState::Matched {
|
|
||||||
// prevent subsequent mismatched TERM from
|
|
||||||
// cancelling the input
|
|
||||||
state = ParseState::Continue;
|
|
||||||
}
|
|
||||||
if state != ParseState::Pass {
|
|
||||||
let search_key = lower.as_str();
|
|
||||||
|
|
||||||
if key.starts_with('.') {
|
let lower = key.to_lowercase();
|
||||||
if *fmt == OutputFmt::Display {
|
match lower.as_str() {
|
||||||
result.push_str(format!("\x1b[{val}m*{key}\t{val}\x1b[0m\n").as_str());
|
"term" => {
|
||||||
} else {
|
if term.fnmatch(val) {
|
||||||
result.push_str(format!("*{key}={val}:").as_str());
|
state = ParseState::Matched;
|
||||||
}
|
} else if state == ParseState::Global {
|
||||||
} else if key.starts_with('*') {
|
state = ParseState::Pass;
|
||||||
if *fmt == OutputFmt::Display {
|
}
|
||||||
result.push_str(format!("\x1b[{val}m{key}\t{val}\x1b[0m\n").as_str());
|
}
|
||||||
} else {
|
"colorterm" => {
|
||||||
result.push_str(format!("{key}={val}:").as_str());
|
// For COLORTERM ?*, only match if COLORTERM is non-empty
|
||||||
}
|
let matches = if val == "?*" {
|
||||||
} else if lower == "options" || lower == "color" || lower == "eightbit" {
|
!colorterm.is_empty()
|
||||||
// Slackware only. Ignore
|
|
||||||
} else if let Some((_, s)) = FILE_ATTRIBUTE_CODES
|
|
||||||
.iter()
|
|
||||||
.find(|&&(key, _)| key == search_key)
|
|
||||||
{
|
|
||||||
if *fmt == OutputFmt::Display {
|
|
||||||
result.push_str(format!("\x1b[{val}m{s}\t{val}\x1b[0m\n").as_str());
|
|
||||||
} else {
|
|
||||||
result.push_str(format!("{s}={val}:").as_str());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return Err(format!(
|
colorterm.fnmatch(val)
|
||||||
"{}:{}: unrecognized keyword {}",
|
};
|
||||||
fp.maybe_quote(),
|
if matches {
|
||||||
num,
|
state = ParseState::Matched;
|
||||||
key
|
saw_colorterm_match = true;
|
||||||
));
|
} else if !saw_colorterm_match && state == ParseState::Global {
|
||||||
|
state = ParseState::Pass;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if state == ParseState::Matched {
|
||||||
|
// prevent subsequent mismatched TERM from
|
||||||
|
// cancelling the input
|
||||||
|
state = ParseState::Continue;
|
||||||
|
}
|
||||||
|
if state != ParseState::Pass {
|
||||||
|
append_entry(&mut result, fmt, key, &lower, val)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,6 +440,46 @@ where
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn append_entry(
|
||||||
|
result: &mut String,
|
||||||
|
fmt: &OutputFmt,
|
||||||
|
key: &str,
|
||||||
|
lower: &str,
|
||||||
|
val: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
if key.starts_with(['.', '*']) {
|
||||||
|
let entry = if key.starts_with('.') {
|
||||||
|
format!("*{key}")
|
||||||
|
} else {
|
||||||
|
key.to_string()
|
||||||
|
};
|
||||||
|
let disp = if *fmt == OutputFmt::Display {
|
||||||
|
format!("\x1b[{val}m{entry}\t{val}\x1b[0m\n")
|
||||||
|
} else {
|
||||||
|
format!("{entry}={val}:")
|
||||||
|
};
|
||||||
|
result.push_str(&disp);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
match lower {
|
||||||
|
"options" | "color" | "eightbit" => Ok(()), // Slackware only, ignore
|
||||||
|
_ => {
|
||||||
|
if let Some((_, s)) = FILE_ATTRIBUTE_CODES.iter().find(|&&(key, _)| key == lower) {
|
||||||
|
let disp = if *fmt == OutputFmt::Display {
|
||||||
|
format!("\x1b[{val}m{s}\t{val}\x1b[0m\n")
|
||||||
|
} else {
|
||||||
|
format!("{s}={val}:")
|
||||||
|
};
|
||||||
|
result.push_str(&disp);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(format!("unrecognized keyword {key}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Escape single quotes because they are not allowed between single quotes in shell code, and code
|
/// Escape single quotes because they are not allowed between single quotes in shell code, and code
|
||||||
/// enclosed by single quotes is what is returned by `parse()`.
|
/// enclosed by single quotes is what is returned by `parse()`.
|
||||||
///
|
///
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//
|
//
|
||||||
// For the full copyright and license information, please view the LICENSE
|
// For the full copyright and license information, please view the LICENSE
|
||||||
// file that was distributed with this source code.
|
// file that was distributed with this source code.
|
||||||
// spell-checker:ignore overridable
|
// spell-checker:ignore overridable colorterm
|
||||||
use crate::common::util::TestScenario;
|
use crate::common::util::TestScenario;
|
||||||
|
|
||||||
use dircolors::{guess_syntax, OutputFmt, StrUtils};
|
use dircolors::{guess_syntax, OutputFmt, StrUtils};
|
||||||
|
@ -253,3 +253,14 @@ fn test_repeated() {
|
||||||
new_ucmd!().arg(arg).arg(arg).succeeds().no_stderr();
|
new_ucmd!().arg(arg).arg(arg).succeeds().no_stderr();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_colorterm_empty_with_wildcard() {
|
||||||
|
new_ucmd!()
|
||||||
|
.env("COLORTERM", "")
|
||||||
|
.pipe_in("COLORTERM ?*\nowt 40;33\n")
|
||||||
|
.args(&["-b", "-"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_is("LS_COLORS='';\nexport LS_COLORS\n")
|
||||||
|
.no_stderr();
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue