From 5d19f79cd0b791bf28c9c8d9d8f4fd61817f6026 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 2 Dec 2023 12:57:38 +0100 Subject: [PATCH] dircolors should use the datastructures when printing --- src/uu/dircolors/src/dircolors.rs | 119 ++++++++++++++++++++++---- src/uucore/src/lib/features/colors.rs | 89 +++++++------------ 2 files changed, 134 insertions(+), 74 deletions(-) diff --git a/src/uu/dircolors/src/dircolors.rs b/src/uu/dircolors/src/dircolors.rs index 58228ddeb..43e35c3d2 100644 --- a/src/uu/dircolors/src/dircolors.rs +++ b/src/uu/dircolors/src/dircolors.rs @@ -12,7 +12,7 @@ use std::io::{BufRead, BufReader}; use std::path::Path; use clap::{crate_version, Arg, ArgAction, Command}; -use uucore::colors::FILE_ATTRIBUTE_CODES; +use uucore::colors::{FILE_ATTRIBUTE_CODES, FILE_COLORS, FILE_TYPES}; use uucore::display::Quotable; use uucore::error::{UResult, USimpleError, UUsageError}; use uucore::{help_about, help_section, help_usage}; @@ -58,6 +58,89 @@ pub fn guess_syntax() -> OutputFmt { } } +fn get_colors_format_strings(fmt: &OutputFmt) -> (String, String) { + let prefix = match fmt { + OutputFmt::Shell => "LS_COLORS='".to_string(), + OutputFmt::CShell => "setenv LS_COLORS '".to_string(), + OutputFmt::Display => String::new(), + OutputFmt::Unknown => unreachable!(), + }; + + let suffix = match fmt { + OutputFmt::Shell => "';\nexport LS_COLORS".to_string(), + OutputFmt::CShell => "'".to_string(), + OutputFmt::Display => String::new(), + OutputFmt::Unknown => unreachable!(), + }; + + (prefix, suffix) +} + +pub fn generate_type_output(fmt: &OutputFmt) -> String { + match fmt { + OutputFmt::Display => FILE_TYPES + .iter() + .map(|&(_, key, val)| format!("\x1b[{}m{}\t{}\x1b[0m", val, key, val)) + .collect::>() + .join("\n"), + _ => { + // Existing logic for other formats + FILE_TYPES + .iter() + .map(|&(_, v1, v2)| format!("{}={}", v1, v2)) + .collect::>() + .join(":") + } + } +} + +enum ExtensionFormat { + StarDot, // Format as ".*ext" + Dot, // Format as ".ext" + NoDot, // Format as "ext" +} + +fn generate_ls_colors(fmt: &OutputFmt, format: ExtensionFormat, sep: &str) -> String { + match fmt { + OutputFmt::Display => { + let mut display_parts = vec![]; + let type_output = generate_type_output(fmt); + display_parts.push(type_output); + for &(extension, code) in FILE_COLORS.iter() { + display_parts.push(format!("\x1b[{}m*{}\t{}\x1b[0m", code, extension, code)); + } + display_parts.join("\n") + } + _ => { + // existing logic for other formats + let mut parts = vec![]; + for &(extension, code) in FILE_COLORS.iter() { + let formatted_extension = match format { + ExtensionFormat::StarDot => format!("*{}", extension), + ExtensionFormat::Dot => extension.to_string(), + ExtensionFormat::NoDot => { + if extension.starts_with('.') { + extension[1..].to_string() + } else { + extension.to_string() + } + } + }; + parts.push(format!("{}={}", formatted_extension, code)); + } + let (prefix, suffix) = get_colors_format_strings(&fmt); + let ls_colors = parts.join(sep); + format!( + "{}{}:{}:{}", + prefix, + generate_type_output(&fmt), + ls_colors, + suffix + ) + } + } +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let args = args.collect_ignore(); @@ -126,7 +209,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let result; if files.is_empty() { - result = parse(INTERNAL_DB.lines(), &out_format, ""); + println!( + "{}", + generate_ls_colors(&out_format, ExtensionFormat::StarDot, ":") + ); + + return Ok(()); } else if files.len() > 1 { return Err(UUsageError::new( 1, @@ -287,12 +375,9 @@ where { // 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 => (), - OutputFmt::Unknown => unreachable!(), - } + let (prefix, suffix) = get_colors_format_strings(&fmt); + + result.push_str(&prefix); let term = env::var("TERM").unwrap_or_else(|_| "none".to_owned()); let term = term.as_str(); @@ -331,6 +416,7 @@ where state = ParseState::Continue; } if state != ParseState::Pass { + let search_key = lower.as_str(); if key.starts_with('.') { if *fmt == OutputFmt::Display { result.push_str(format!("\x1b[{val}m*{key}\t{val}\x1b[0m\n").as_str()); @@ -345,7 +431,10 @@ where } } else if lower == "options" || lower == "color" || lower == "eightbit" { // Slackware only. Ignore - } else if let Some(s) = FILE_ATTRIBUTE_CODES.get(lower.as_str()) { + } 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 { @@ -363,15 +452,11 @@ where } } - match fmt { - OutputFmt::Shell => result.push_str("';\nexport LS_COLORS"), - OutputFmt::CShell => result.push('\''), - OutputFmt::Display => { - // remove latest "\n" - result.pop(); - } - OutputFmt::Unknown => unreachable!(), + if fmt == &OutputFmt::Display { + // remove latest "\n" + result.pop(); } + result.push_str(&suffix); Ok(result) } diff --git a/src/uucore/src/lib/features/colors.rs b/src/uucore/src/lib/features/colors.rs index 69be16ba2..96ca6d456 100644 --- a/src/uucore/src/lib/features/colors.rs +++ b/src/uucore/src/lib/features/colors.rs @@ -4,19 +4,15 @@ // file that was distributed with this source code. use once_cell::sync::Lazy; -use std::collections::HashMap; /* The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the * slackware version of dircolors) are recognized but ignored. * Global config options can be specified before TERM or COLORTERM entries * below are TERM or COLORTERM entries, which can be glob patterns, which * restrict following config to systems with matching environment variables. - * COLORTERM ?* */ - -pub static TERMS: Lazy> = Lazy::new(|| { - let mut m = HashMap::new(); - [ +pub static TERMS: Lazy> = Lazy::new(|| { + vec![ "Eterm", "ansi", "*color*", @@ -43,11 +39,6 @@ pub static TERMS: Lazy> = Lazy::new(|| { "vt100", "xterm*", ] - .iter() - .for_each(|&term| { - m.insert(term, ""); - }); - m }); /* @@ -64,34 +55,27 @@ pub static TERMS: Lazy> = Lazy::new(|| { #NORMAL 00 # no color code at all #FILE 00 # regular file: use no color at all */ -// FILE_TYPES with Lazy initialization -pub static FILE_TYPES: Lazy> = Lazy::new(|| { - let mut m = HashMap::new(); - [ - ("RESET", "0"), // reset to "normal" color - ("DIR", "01;34"), // directory - ("LINK", "01;36"), // symbolic link - ("MULTIHARDLINK", "00"), // regular file with more than one link - ("FIFO", "40;33"), // pipe - ("SOCK", "01;35"), // socket - ("DOOR", "01;35"), // door - ("BLK", "40;33;01"), // block device driver - ("CHR", "40;33;01"), // character device driver - ("ORPHAN", "40;31;01"), // symlink to nonexistent file, or non-stat'able file - ("MISSING", "00"), // ... and the files they point to - ("SETUID", "37;41"), // file that is setuid (u+s) - ("SETGID", "30;43"), // file that is setgid (g+s) - ("CAPABILITY", "00"), // file with capability - ("STICKY_OTHER_WRITABLE", "30;42"), // dir that is sticky and other-writable (+t,o+w) - ("OTHER_WRITABLE", "34;42"), // dir that is other-writable (o+w) and not sticky - ("STICKY", "37;44"), // dir with the sticky bit set (+t) and not other-writable - ("EXEC", "01;32"), // files with execute permission +pub static FILE_TYPES: Lazy> = Lazy::new(|| { + vec![ + ("RESET", "rs", "0"), // reset to "normal" color + ("DIR", "di", "01;34"), // directory + ("LINK", "ln", "01;36"), // symbolic link + ("MULTIHARDLINK", "mh", "00"), // regular file with more than one link + ("FIFO", "pi", "40;33"), // pipe + ("SOCK", "so", "01;35"), // socket + ("DOOR", "do", "01;35"), // door + ("BLK", "bd", "40;33;01"), // block device driver + ("CHR", "cd", "40;33;01"), // character device driver + ("ORPHAN", "or", "40;31;01"), // symlink to nonexistent file, or non-stat'able file + ("MISSING", "mi", "00"), // ... and the files they point to + ("SETUID", "su", "37;41"), // file that is setuid (u+s) + ("SETGID", "sg", "30;43"), // file that is setgid (g+s) + ("CAPABILITY", "ca", "00"), // file with capability + ("STICKY_OTHER_WRITABLE", "tw", "30;42"), // dir that is sticky and other-writable (+t,o+w) + ("OTHER_WRITABLE", "ow", "34;42"), // dir that is other-writable (o+w) and not sticky + ("STICKY", "st", "37;44"), // dir with the sticky bit set (+t) and not other-writable + ("EXEC", "ex", "01;32"), // files with execute permission ] - .iter() - .for_each(|&(k, v)| { - m.insert(k, v); - }); - m }); /* @@ -99,9 +83,9 @@ pub static FILE_TYPES: Lazy> = Lazy::new(|| { # to color below. Put the extension, a space, and the color init string. # (and any comments you want to add after a '#') */ -pub static FILE_COLORS: Lazy> = Lazy::new(|| { - let mut m = HashMap::new(); - [ +pub static FILE_COLORS: Lazy> = Lazy::new(|| { + vec![ + /* // Executables (Windows) (".cmd", "01;32"), (".exe", "01;32"), @@ -109,7 +93,7 @@ pub static FILE_COLORS: Lazy> = Lazy::new(|| { (".btm", "01;32"), (".bat", "01;32"), (".sh", "01;32"), - (".csh", "01;32"), + (".csh", "01;32"),*/ // Archives or compressed (".tar", "01;31"), (".tgz", "01;31"), @@ -207,6 +191,7 @@ pub static FILE_COLORS: Lazy> = Lazy::new(|| { (".yuv", "01;35"), (".cgm", "01;35"), (".emf", "01;35"), + // https://wiki.xiph.org/MIME_Types_and_File_Extensions (".ogv", "01;35"), (".ogx", "01;35"), // Audio formats @@ -222,13 +207,14 @@ pub static FILE_COLORS: Lazy> = Lazy::new(|| { (".ogg", "00;36"), (".ra", "00;36"), (".wav", "00;36"), + // https://wiki.xiph.org/MIME_Types_and_File_Extensions (".oga", "00;36"), (".opus", "00;36"), (".spx", "00;36"), (".xspf", "00;36"), // Backup files - ("*~", "00;90"), - ("*#", "00;90"), + ("~", "00;90"), + ("#", "00;90"), (".bak", "00;90"), (".old", "00;90"), (".orig", "00;90"), @@ -245,16 +231,10 @@ pub static FILE_COLORS: Lazy> = Lazy::new(|| { (".rpmorig", "00;90"), (".rpmsave", "00;90"), ] - .iter() - .for_each(|&(k, v)| { - m.insert(k, v); - }); - m }); -pub static FILE_ATTRIBUTE_CODES: Lazy> = Lazy::new(|| { - let mut m = HashMap::new(); - [ +pub static FILE_ATTRIBUTE_CODES: Lazy> = Lazy::new(|| { + vec![ ("normal", "no"), ("norm", "no"), ("file", "fi"), @@ -293,9 +273,4 @@ pub static FILE_ATTRIBUTE_CODES: Lazy> = Lazy::new(|| { ("multihardlink", "mh"), ("clrtoeol", "cl"), ] - .iter() - .for_each(|&(k, v)| { - m.insert(k, v); - }); - m });