1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

dircolors should use the datastructures when printing

This commit is contained in:
Sylvestre Ledru 2023-12-02 12:57:38 +01:00
parent 0e8c171c80
commit 5d19f79cd0
2 changed files with 134 additions and 74 deletions

View file

@ -12,7 +12,7 @@ use std::io::{BufRead, BufReader};
use std::path::Path; use std::path::Path;
use clap::{crate_version, Arg, ArgAction, Command}; 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::display::Quotable;
use uucore::error::{UResult, USimpleError, UUsageError}; use uucore::error::{UResult, USimpleError, UUsageError};
use uucore::{help_about, help_section, help_usage}; 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::<Vec<String>>()
.join("\n"),
_ => {
// Existing logic for other formats
FILE_TYPES
.iter()
.map(|&(_, v1, v2)| format!("{}={}", v1, v2))
.collect::<Vec<String>>()
.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] #[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> { pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args.collect_ignore(); let args = args.collect_ignore();
@ -126,7 +209,12 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let result; let result;
if files.is_empty() { 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 { } else if files.len() > 1 {
return Err(UUsageError::new( return Err(UUsageError::new(
1, 1,
@ -287,12 +375,9 @@ where
{ {
// 1790 > $(dircolors | wc -m) // 1790 > $(dircolors | wc -m)
let mut result = String::with_capacity(1790); let mut result = String::with_capacity(1790);
match fmt { let (prefix, suffix) = get_colors_format_strings(&fmt);
OutputFmt::Shell => result.push_str("LS_COLORS='"),
OutputFmt::CShell => result.push_str("setenv LS_COLORS '"), result.push_str(&prefix);
OutputFmt::Display => (),
OutputFmt::Unknown => unreachable!(),
}
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 term = term.as_str();
@ -331,6 +416,7 @@ where
state = ParseState::Continue; state = ParseState::Continue;
} }
if state != ParseState::Pass { if state != ParseState::Pass {
let search_key = lower.as_str();
if key.starts_with('.') { if key.starts_with('.') {
if *fmt == OutputFmt::Display { if *fmt == OutputFmt::Display {
result.push_str(format!("\x1b[{val}m*{key}\t{val}\x1b[0m\n").as_str()); 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" { } else if lower == "options" || lower == "color" || lower == "eightbit" {
// Slackware only. Ignore // 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 { if *fmt == OutputFmt::Display {
result.push_str(format!("\x1b[{val}m{s}\t{val}\x1b[0m\n").as_str()); result.push_str(format!("\x1b[{val}m{s}\t{val}\x1b[0m\n").as_str());
} else { } else {
@ -363,15 +452,11 @@ where
} }
} }
match fmt { if fmt == &OutputFmt::Display {
OutputFmt::Shell => result.push_str("';\nexport LS_COLORS"), // remove latest "\n"
OutputFmt::CShell => result.push('\''), result.pop();
OutputFmt::Display => {
// remove latest "\n"
result.pop();
}
OutputFmt::Unknown => unreachable!(),
} }
result.push_str(&suffix);
Ok(result) Ok(result)
} }

View file

@ -4,19 +4,15 @@
// file that was distributed with this source code. // file that was distributed with this source code.
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::HashMap;
/* The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the /* The keywords COLOR, OPTIONS, and EIGHTBIT (honored by the
* slackware version of dircolors) are recognized but ignored. * slackware version of dircolors) are recognized but ignored.
* Global config options can be specified before TERM or COLORTERM entries * Global config options can be specified before TERM or COLORTERM entries
* below are TERM or COLORTERM entries, which can be glob patterns, which * below are TERM or COLORTERM entries, which can be glob patterns, which
* restrict following config to systems with matching environment variables. * restrict following config to systems with matching environment variables.
* COLORTERM ?*
*/ */
pub static TERMS: Lazy<Vec<&str>> = Lazy::new(|| {
pub static TERMS: Lazy<HashMap<&str, &str>> = Lazy::new(|| { vec![
let mut m = HashMap::new();
[
"Eterm", "Eterm",
"ansi", "ansi",
"*color*", "*color*",
@ -43,11 +39,6 @@ pub static TERMS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
"vt100", "vt100",
"xterm*", "xterm*",
] ]
.iter()
.for_each(|&term| {
m.insert(term, "");
});
m
}); });
/* /*
@ -64,34 +55,27 @@ pub static TERMS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
#NORMAL 00 # no color code at all #NORMAL 00 # no color code at all
#FILE 00 # regular file: use no color at all #FILE 00 # regular file: use no color at all
*/ */
// FILE_TYPES with Lazy initialization pub static FILE_TYPES: Lazy<Vec<(&'static str, &'static str, &'static str)>> = Lazy::new(|| {
pub static FILE_TYPES: Lazy<HashMap<&str, &str>> = Lazy::new(|| { vec![
let mut m = HashMap::new(); ("RESET", "rs", "0"), // reset to "normal" color
[ ("DIR", "di", "01;34"), // directory
("RESET", "0"), // reset to "normal" color ("LINK", "ln", "01;36"), // symbolic link
("DIR", "01;34"), // directory ("MULTIHARDLINK", "mh", "00"), // regular file with more than one link
("LINK", "01;36"), // symbolic link ("FIFO", "pi", "40;33"), // pipe
("MULTIHARDLINK", "00"), // regular file with more than one link ("SOCK", "so", "01;35"), // socket
("FIFO", "40;33"), // pipe ("DOOR", "do", "01;35"), // door
("SOCK", "01;35"), // socket ("BLK", "bd", "40;33;01"), // block device driver
("DOOR", "01;35"), // door ("CHR", "cd", "40;33;01"), // character device driver
("BLK", "40;33;01"), // block device driver ("ORPHAN", "or", "40;31;01"), // symlink to nonexistent file, or non-stat'able file
("CHR", "40;33;01"), // character device driver ("MISSING", "mi", "00"), // ... and the files they point to
("ORPHAN", "40;31;01"), // symlink to nonexistent file, or non-stat'able file ("SETUID", "su", "37;41"), // file that is setuid (u+s)
("MISSING", "00"), // ... and the files they point to ("SETGID", "sg", "30;43"), // file that is setgid (g+s)
("SETUID", "37;41"), // file that is setuid (u+s) ("CAPABILITY", "ca", "00"), // file with capability
("SETGID", "30;43"), // file that is setgid (g+s) ("STICKY_OTHER_WRITABLE", "tw", "30;42"), // dir that is sticky and other-writable (+t,o+w)
("CAPABILITY", "00"), // file with capability ("OTHER_WRITABLE", "ow", "34;42"), // dir that is other-writable (o+w) and not sticky
("STICKY_OTHER_WRITABLE", "30;42"), // dir that is sticky and other-writable (+t,o+w) ("STICKY", "st", "37;44"), // dir with the sticky bit set (+t) and not other-writable
("OTHER_WRITABLE", "34;42"), // dir that is other-writable (o+w) and not sticky ("EXEC", "ex", "01;32"), // files with execute permission
("STICKY", "37;44"), // dir with the sticky bit set (+t) and not other-writable
("EXEC", "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<HashMap<&str, &str>> = Lazy::new(|| {
# to color below. Put the extension, a space, and the color init string. # to color below. Put the extension, a space, and the color init string.
# (and any comments you want to add after a '#') # (and any comments you want to add after a '#')
*/ */
pub static FILE_COLORS: Lazy<HashMap<&str, &str>> = Lazy::new(|| { pub static FILE_COLORS: Lazy<Vec<(&str, &str)>> = Lazy::new(|| {
let mut m = HashMap::new(); vec![
[ /*
// Executables (Windows) // Executables (Windows)
(".cmd", "01;32"), (".cmd", "01;32"),
(".exe", "01;32"), (".exe", "01;32"),
@ -109,7 +93,7 @@ pub static FILE_COLORS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
(".btm", "01;32"), (".btm", "01;32"),
(".bat", "01;32"), (".bat", "01;32"),
(".sh", "01;32"), (".sh", "01;32"),
(".csh", "01;32"), (".csh", "01;32"),*/
// Archives or compressed // Archives or compressed
(".tar", "01;31"), (".tar", "01;31"),
(".tgz", "01;31"), (".tgz", "01;31"),
@ -207,6 +191,7 @@ pub static FILE_COLORS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
(".yuv", "01;35"), (".yuv", "01;35"),
(".cgm", "01;35"), (".cgm", "01;35"),
(".emf", "01;35"), (".emf", "01;35"),
// https://wiki.xiph.org/MIME_Types_and_File_Extensions
(".ogv", "01;35"), (".ogv", "01;35"),
(".ogx", "01;35"), (".ogx", "01;35"),
// Audio formats // Audio formats
@ -222,13 +207,14 @@ pub static FILE_COLORS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
(".ogg", "00;36"), (".ogg", "00;36"),
(".ra", "00;36"), (".ra", "00;36"),
(".wav", "00;36"), (".wav", "00;36"),
// https://wiki.xiph.org/MIME_Types_and_File_Extensions
(".oga", "00;36"), (".oga", "00;36"),
(".opus", "00;36"), (".opus", "00;36"),
(".spx", "00;36"), (".spx", "00;36"),
(".xspf", "00;36"), (".xspf", "00;36"),
// Backup files // Backup files
("*~", "00;90"), ("~", "00;90"),
("*#", "00;90"), ("#", "00;90"),
(".bak", "00;90"), (".bak", "00;90"),
(".old", "00;90"), (".old", "00;90"),
(".orig", "00;90"), (".orig", "00;90"),
@ -245,16 +231,10 @@ pub static FILE_COLORS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
(".rpmorig", "00;90"), (".rpmorig", "00;90"),
(".rpmsave", "00;90"), (".rpmsave", "00;90"),
] ]
.iter()
.for_each(|&(k, v)| {
m.insert(k, v);
});
m
}); });
pub static FILE_ATTRIBUTE_CODES: Lazy<HashMap<&str, &str>> = Lazy::new(|| { pub static FILE_ATTRIBUTE_CODES: Lazy<Vec<(&str, &str)>> = Lazy::new(|| {
let mut m = HashMap::new(); vec![
[
("normal", "no"), ("normal", "no"),
("norm", "no"), ("norm", "no"),
("file", "fi"), ("file", "fi"),
@ -293,9 +273,4 @@ pub static FILE_ATTRIBUTE_CODES: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
("multihardlink", "mh"), ("multihardlink", "mh"),
("clrtoeol", "cl"), ("clrtoeol", "cl"),
] ]
.iter()
.for_each(|&(k, v)| {
m.insert(k, v);
});
m
}); });