diff --git a/src/uu/dircolors/README.md b/src/uu/dircolors/README.md index 915520d25e3..7a02c0c4ba6 100644 --- a/src/uu/dircolors/README.md +++ b/src/uu/dircolors/README.md @@ -4,6 +4,7 @@ Create the test fixtures by writing the output of the GNU dircolors commands to ``` $ dircolors --print-database > /PATH_TO_COREUTILS/tests/fixtures/dircolors/internal.expected +$ dircolors --print-ls-colors > /PATH_TO_COREUTILS/tests/fixtures/dircolors/ls_colors.expected $ dircolors -b > /PATH_TO_COREUTILS/tests/fixtures/dircolors/bash_def.expected $ dircolors -c > /PATH_TO_COREUTILS/tests/fixtures/dircolors/csh_def.expected ``` diff --git a/src/uu/dircolors/src/dircolors.rs b/src/uu/dircolors/src/dircolors.rs index cc3a9baa692..bd9311ec323 100644 --- a/src/uu/dircolors/src/dircolors.rs +++ b/src/uu/dircolors/src/dircolors.rs @@ -21,6 +21,7 @@ mod options { pub const BOURNE_SHELL: &str = "bourne-shell"; pub const C_SHELL: &str = "c-shell"; pub const PRINT_DATABASE: &str = "print-database"; + pub const PRINT_LS_COLORS: &str = "print-ls-colors"; pub const FILE: &str = "FILE"; } @@ -39,6 +40,7 @@ use self::colors::INTERNAL_DB; pub enum OutputFmt { Shell, CShell, + Display, Unknown, } @@ -76,7 +78,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { // clap provides .conflicts_with / .conflicts_with_all, but we want to // manually handle conflicts so we can match the output of GNU coreutils if (matches.is_present(options::C_SHELL) || matches.is_present(options::BOURNE_SHELL)) - && matches.is_present(options::PRINT_DATABASE) + && (matches.is_present(options::PRINT_DATABASE) + || matches.is_present(options::PRINT_LS_COLORS)) { return Err(UUsageError::new( 1, @@ -85,6 +88,13 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { )); } + if matches.is_present(options::PRINT_DATABASE) && matches.is_present(options::PRINT_LS_COLORS) { + return Err(UUsageError::new( + 1, + "options --print-database and --print-ls-colors are mutually exclusive", + )); + } + if matches.is_present(options::PRINT_DATABASE) { if !files.is_empty() { return Err(UUsageError::new( @@ -100,12 +110,15 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { return Ok(()); } - let mut out_format = OutputFmt::Unknown; - if matches.is_present(options::C_SHELL) { - out_format = OutputFmt::CShell; + let mut out_format = if matches.is_present(options::C_SHELL) { + OutputFmt::CShell } else if matches.is_present(options::BOURNE_SHELL) { - out_format = OutputFmt::Shell; - } + OutputFmt::Shell + } else if matches.is_present(options::PRINT_LS_COLORS) { + OutputFmt::Display + } else { + OutputFmt::Unknown + }; if out_format == OutputFmt::Unknown { match guess_syntax() { @@ -184,6 +197,12 @@ pub fn uu_app<'a>() -> Command<'a> { .help("print the byte counts") .display_order(3), ) + .arg( + Arg::new(options::PRINT_LS_COLORS) + .long("print-ls-colors") + .help("output fully escaped colors for display") + .display_order(4), + ) .arg( Arg::new(options::FILE) .hide(true) @@ -252,6 +271,7 @@ enum ParseState { Continue, Pass, } + use std::collections::HashMap; use uucore::{format_usage, InvalidEncodingHandling}; @@ -260,11 +280,12 @@ where T: IntoIterator, T::Item: Borrow, { - // 1440 > $(dircolors | wc -m) - let mut result = String::with_capacity(1440); + // 1790 > $(dircolors | wc -m) + let mut result = String::with_capacity(1790); match fmt { OutputFmt::Shell => result.push_str("LS_COLORS='"), OutputFmt::CShell => result.push_str("setenv LS_COLORS '"), + OutputFmt::Display => (), _ => unreachable!(), } @@ -343,13 +364,25 @@ where } if state != ParseState::Pass { if key.starts_with('.') { - result.push_str(format!("*{}={}:", key, val).as_str()); + if *fmt == OutputFmt::Display { + result.push_str(format!("\x1b[{1}m*{0}\t{1}\x1b[0m\n", key, val).as_str()); + } else { + result.push_str(format!("*{}={}:", key, val).as_str()); + } } else if key.starts_with('*') { - result.push_str(format!("{}={}:", key, val).as_str()); + if *fmt == OutputFmt::Display { + result.push_str(format!("\x1b[{1}m{0}\t{1}\x1b[0m\n", key, val).as_str()); + } else { + result.push_str(format!("{}={}:", key, val).as_str()); + } } else if lower == "options" || lower == "color" || lower == "eightbit" { // Slackware only. Ignore } else if let Some(s) = table.get(lower.as_str()) { - result.push_str(format!("{}={}:", s, val).as_str()); + if *fmt == OutputFmt::Display { + result.push_str(format!("\x1b[{1}m{0}\t{1}\x1b[0m\n", s, val).as_str()); + } else { + result.push_str(format!("{}={}:", s, val).as_str()); + } } else { return Err(format!( "{}:{}: unrecognized keyword {}", @@ -365,6 +398,10 @@ where match fmt { OutputFmt::Shell => result.push_str("';\nexport LS_COLORS"), OutputFmt::CShell => result.push('\''), + OutputFmt::Display => { + // remove latest "\n" + result.pop(); + } _ => unreachable!(), } diff --git a/tests/by-util/test_dircolors.rs b/tests/by-util/test_dircolors.rs index edbc83bbb17..69109b034dc 100644 --- a/tests/by-util/test_dircolors.rs +++ b/tests/by-util/test_dircolors.rs @@ -61,6 +61,14 @@ fn test_internal_db() { .stdout_is_fixture("internal.expected"); } +#[test] +fn test_ls_colors() { + new_ucmd!() + .arg("--print-ls-colors") + .run() + .stdout_is_fixture("ls_colors.expected"); +} + #[test] fn test_bash_default() { new_ucmd!() @@ -95,6 +103,18 @@ fn test_exclusive_option() { .arg("-cp") .fails() .stderr_contains("mutually exclusive"); + new_ucmd!() + .args(&["-b", "--print-ls-colors"]) + .fails() + .stderr_contains("mutually exclusive"); + new_ucmd!() + .args(&["-c", "--print-ls-colors"]) + .fails() + .stderr_contains("mutually exclusive"); + new_ucmd!() + .args(&["-p", "--print-ls-colors"]) + .fails() + .stderr_contains("mutually exclusive"); } fn test_helper(file_name: &str, term: &str) { diff --git a/tests/fixtures/dircolors/ls_colors.expected b/tests/fixtures/dircolors/ls_colors.expected new file mode 100644 index 00000000000..e68a596215f --- /dev/null +++ b/tests/fixtures/dircolors/ls_colors.expected @@ -0,0 +1,148 @@ +rs 0 +di 01;34 +ln 01;36 +mh 00 +pi 40;33 +so 01;35 +do 01;35 +bd 40;33;01 +cd 40;33;01 +or 40;31;01 +mi 00 +su 37;41 +sg 30;43 +ca 00 +tw 30;42 +ow 34;42 +st 37;44 +ex 01;32 +*.tar 01;31 +*.tgz 01;31 +*.arc 01;31 +*.arj 01;31 +*.taz 01;31 +*.lha 01;31 +*.lz4 01;31 +*.lzh 01;31 +*.lzma 01;31 +*.tlz 01;31 +*.txz 01;31 +*.tzo 01;31 +*.t7z 01;31 +*.zip 01;31 +*.z 01;31 +*.dz 01;31 +*.gz 01;31 +*.lrz 01;31 +*.lz 01;31 +*.lzo 01;31 +*.xz 01;31 +*.zst 01;31 +*.tzst 01;31 +*.bz2 01;31 +*.bz 01;31 +*.tbz 01;31 +*.tbz2 01;31 +*.tz 01;31 +*.deb 01;31 +*.rpm 01;31 +*.jar 01;31 +*.war 01;31 +*.ear 01;31 +*.sar 01;31 +*.rar 01;31 +*.alz 01;31 +*.ace 01;31 +*.zoo 01;31 +*.cpio 01;31 +*.7z 01;31 +*.rz 01;31 +*.cab 01;31 +*.wim 01;31 +*.swm 01;31 +*.dwm 01;31 +*.esd 01;31 +*.avif 01;35 +*.jpg 01;35 +*.jpeg 01;35 +*.mjpg 01;35 +*.mjpeg 01;35 +*.gif 01;35 +*.bmp 01;35 +*.pbm 01;35 +*.pgm 01;35 +*.ppm 01;35 +*.tga 01;35 +*.xbm 01;35 +*.xpm 01;35 +*.tif 01;35 +*.tiff 01;35 +*.png 01;35 +*.svg 01;35 +*.svgz 01;35 +*.mng 01;35 +*.pcx 01;35 +*.mov 01;35 +*.mpg 01;35 +*.mpeg 01;35 +*.m2v 01;35 +*.mkv 01;35 +*.webm 01;35 +*.webp 01;35 +*.ogm 01;35 +*.mp4 01;35 +*.m4v 01;35 +*.mp4v 01;35 +*.vob 01;35 +*.qt 01;35 +*.nuv 01;35 +*.wmv 01;35 +*.asf 01;35 +*.rm 01;35 +*.rmvb 01;35 +*.flc 01;35 +*.avi 01;35 +*.fli 01;35 +*.flv 01;35 +*.gl 01;35 +*.dl 01;35 +*.xcf 01;35 +*.xwd 01;35 +*.yuv 01;35 +*.cgm 01;35 +*.emf 01;35 +*.ogv 01;35 +*.ogx 01;35 +*.aac 00;36 +*.au 00;36 +*.flac 00;36 +*.m4a 00;36 +*.mid 00;36 +*.midi 00;36 +*.mka 00;36 +*.mp3 00;36 +*.mpc 00;36 +*.ogg 00;36 +*.ra 00;36 +*.wav 00;36 +*.oga 00;36 +*.opus 00;36 +*.spx 00;36 +*.xspf 00;36 +*~ 00;90 +*# 00;90 +*.bak 00;90 +*.old 00;90 +*.orig 00;90 +*.part 00;90 +*.rej 00;90 +*.swp 00;90 +*.tmp 00;90 +*.dpkg-dist 00;90 +*.dpkg-old 00;90 +*.ucf-dist 00;90 +*.ucf-new 00;90 +*.ucf-old 00;90 +*.rpmnew 00;90 +*.rpmorig 00;90 +*.rpmsave 00;90