Skip to content

Commit e96b536

Browse files
authored
Merge pull request #10149 from sylvestre/ls-followup
ls: some improvements after #9431
2 parents 2dcda50 + f07f5b9 commit e96b536

1 file changed

Lines changed: 133 additions & 84 deletions

File tree

src/uu/ls/src/colors.rs

Lines changed: 133 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
// file that was distributed with this source code.
55
use super::PathData;
66
use lscolors::{Indicator, LsColors, Style};
7+
use std::borrow::Cow;
78
use std::collections::HashMap;
89
use std::env;
910
use std::ffi::OsString;
@@ -18,6 +19,17 @@ const ANSI_RESET: &str = "\x1b[0m";
1819
const ANSI_CLEAR_EOL: &str = "\x1b[K";
1920
const EMPTY_STYLE: &str = "\x1b[m";
2021

22+
#[cfg(unix)]
23+
mod mode {
24+
// Unix file mode bits
25+
pub const SETUID: u32 = 0o4000;
26+
pub const SETGID: u32 = 0o2000;
27+
pub const EXECUTABLE: u32 = 0o0111;
28+
pub const STICKY_OTHER_WRITABLE: u32 = 0o1002;
29+
pub const OTHER_WRITABLE: u32 = 0o0002;
30+
pub const STICKY: u32 = 0o1000;
31+
}
32+
2133
enum RawIndicatorStyle {
2234
Empty,
2335
Code(Indicator),
@@ -129,14 +141,11 @@ impl<'a> StyleManager<'a> {
129141
indicator: Indicator,
130142
style_code: &mut String,
131143
) {
132-
if !self.indicator_codes.contains_key(&indicator) {
133-
return;
134-
}
135-
style_code.push_str(self.reset(!self.initial_reset_is_done));
136-
style_code.push_str(ANSI_CSI);
137-
if let Some(raw) = self.indicator_codes.get(&indicator) {
144+
if let Some(raw) = self.indicator_codes.get(&indicator).cloned() {
138145
debug_assert!(!raw.is_empty());
139-
style_code.push_str(raw);
146+
style_code.push_str(self.reset(!self.initial_reset_is_done));
147+
style_code.push_str(ANSI_CSI);
148+
style_code.push_str(&raw);
140149
style_code.push_str(ANSI_SGR_END);
141150
}
142151
}
@@ -355,98 +364,138 @@ impl<'a> StyleManager<'a> {
355364
};
356365

357366
if file_type.is_symlink() {
358-
let orphan_enabled = self.has_indicator_style(Indicator::OrphanedSymbolicLink);
359-
let missing_enabled = self.has_indicator_style(Indicator::MissingFile);
360-
let needs_target_state = self.ln_color_from_target || orphan_enabled;
361-
let target_missing = needs_target_state && !entry_exists();
362-
363-
if target_missing {
364-
let orphan_raw = self.indicator_codes.get(&Indicator::OrphanedSymbolicLink);
365-
let orphan_raw_is_empty = orphan_raw.is_some_and(|value| value.is_empty());
366-
if orphan_enabled && (!orphan_raw_is_empty || self.ln_color_from_target) {
367-
return Some(Indicator::OrphanedSymbolicLink);
368-
}
369-
if self.ln_color_from_target && missing_enabled {
370-
return Some(Indicator::MissingFile);
371-
}
372-
}
373-
if self.has_indicator_style(Indicator::SymbolicLink) {
374-
return Some(Indicator::SymbolicLink);
375-
}
376-
return None;
367+
return self.indicator_for_symlink(&mut entry_exists);
377368
}
378369

379370
if self.has_indicator_style(Indicator::MissingFile) && !entry_exists() {
380371
return Some(Indicator::MissingFile);
381372
}
382373

383374
if file_type.is_file() {
384-
#[cfg(unix)]
385-
if self.needs_file_metadata() {
386-
if let Some(metadata) = path.metadata() {
387-
let mode = metadata.mode();
388-
if self.has_indicator_style(Indicator::Setuid) && mode & 0o4000 != 0 {
389-
return Some(Indicator::Setuid);
390-
}
391-
if self.has_indicator_style(Indicator::Setgid) && mode & 0o2000 != 0 {
392-
return Some(Indicator::Setgid);
393-
}
394-
if self.has_indicator_style(Indicator::ExecutableFile) && mode & 0o0111 != 0 {
395-
return Some(Indicator::ExecutableFile);
396-
}
397-
if self.has_indicator_style(Indicator::MultipleHardLinks)
398-
&& metadata.nlink() > 1
399-
{
400-
return Some(Indicator::MultipleHardLinks);
401-
}
402-
}
403-
}
375+
self.indicator_for_file(path)
376+
} else if file_type.is_dir() {
377+
self.indicator_for_directory(path)
378+
} else {
379+
self.indicator_for_special_file(file_type)
380+
}
381+
}
404382

405-
if self.has_indicator_style(Indicator::RegularFile) {
406-
return Some(Indicator::RegularFile);
383+
fn indicator_for_symlink(&self, entry_exists: &mut dyn FnMut() -> bool) -> Option<Indicator> {
384+
let orphan_enabled = self.has_indicator_style(Indicator::OrphanedSymbolicLink);
385+
let missing_enabled = self.has_indicator_style(Indicator::MissingFile);
386+
let needs_target_state = self.ln_color_from_target || orphan_enabled;
387+
let target_missing = needs_target_state && !entry_exists();
388+
389+
if target_missing {
390+
let orphan_raw = self.indicator_codes.get(&Indicator::OrphanedSymbolicLink);
391+
let orphan_raw_is_empty = orphan_raw.is_some_and(|value| value.is_empty());
392+
if orphan_enabled && (!orphan_raw_is_empty || self.ln_color_from_target) {
393+
return Some(Indicator::OrphanedSymbolicLink);
407394
}
408-
} else if file_type.is_dir() {
409-
#[cfg(unix)]
410-
if self.needs_dir_metadata() {
411-
if let Some(metadata) = path.metadata() {
412-
let mode = metadata.mode();
413-
if self.has_indicator_style(Indicator::StickyAndOtherWritable)
414-
&& mode & 0o1002 == 0o1002
415-
{
416-
return Some(Indicator::StickyAndOtherWritable);
417-
}
418-
if self.has_indicator_style(Indicator::OtherWritable) && mode & 0o0002 != 0 {
419-
return Some(Indicator::OtherWritable);
420-
}
421-
if self.has_indicator_style(Indicator::Sticky) && mode & 0o1000 != 0 {
422-
return Some(Indicator::Sticky);
423-
}
424-
}
395+
if self.ln_color_from_target && missing_enabled {
396+
return Some(Indicator::MissingFile);
425397
}
398+
}
399+
if self.has_indicator_style(Indicator::SymbolicLink) {
400+
return Some(Indicator::SymbolicLink);
401+
}
402+
None
403+
}
426404

427-
if self.has_indicator_style(Indicator::Directory) {
428-
return Some(Indicator::Directory);
429-
}
430-
} else {
431-
#[cfg(unix)]
432-
{
433-
if file_type.is_fifo() && self.has_indicator_style(Indicator::FIFO) {
434-
return Some(Indicator::FIFO);
405+
#[cfg(unix)]
406+
fn indicator_for_file(&self, path: &PathData) -> Option<Indicator> {
407+
if self.needs_file_metadata() {
408+
if let Some(metadata) = path.metadata() {
409+
let mode = metadata.mode();
410+
if self.has_indicator_style(Indicator::Setuid) && mode & mode::SETUID != 0 {
411+
return Some(Indicator::Setuid);
412+
}
413+
if self.has_indicator_style(Indicator::Setgid) && mode & mode::SETGID != 0 {
414+
return Some(Indicator::Setgid);
415+
}
416+
if self.has_indicator_style(Indicator::ExecutableFile)
417+
&& mode & mode::EXECUTABLE != 0
418+
{
419+
return Some(Indicator::ExecutableFile);
435420
}
436-
if file_type.is_socket() && self.has_indicator_style(Indicator::Socket) {
437-
return Some(Indicator::Socket);
421+
if self.has_indicator_style(Indicator::MultipleHardLinks) && metadata.nlink() > 1 {
422+
return Some(Indicator::MultipleHardLinks);
438423
}
439-
if file_type.is_block_device() && self.has_indicator_style(Indicator::BlockDevice) {
440-
return Some(Indicator::BlockDevice);
424+
}
425+
}
426+
427+
if self.has_indicator_style(Indicator::RegularFile) {
428+
Some(Indicator::RegularFile)
429+
} else {
430+
None
431+
}
432+
}
433+
434+
#[cfg(not(unix))]
435+
fn indicator_for_file(&self, _path: &PathData) -> Option<Indicator> {
436+
if self.has_indicator_style(Indicator::RegularFile) {
437+
Some(Indicator::RegularFile)
438+
} else {
439+
None
440+
}
441+
}
442+
443+
#[cfg(unix)]
444+
fn indicator_for_directory(&self, path: &PathData) -> Option<Indicator> {
445+
if self.needs_dir_metadata() {
446+
if let Some(metadata) = path.metadata() {
447+
let mode = metadata.mode();
448+
if self.has_indicator_style(Indicator::StickyAndOtherWritable)
449+
&& mode & mode::STICKY_OTHER_WRITABLE == mode::STICKY_OTHER_WRITABLE
450+
{
451+
return Some(Indicator::StickyAndOtherWritable);
441452
}
442-
if file_type.is_char_device()
443-
&& self.has_indicator_style(Indicator::CharacterDevice)
453+
if self.has_indicator_style(Indicator::OtherWritable)
454+
&& mode & mode::OTHER_WRITABLE != 0
444455
{
445-
return Some(Indicator::CharacterDevice);
456+
return Some(Indicator::OtherWritable);
457+
}
458+
if self.has_indicator_style(Indicator::Sticky) && mode & mode::STICKY != 0 {
459+
return Some(Indicator::Sticky);
446460
}
447461
}
448462
}
449463

464+
if self.has_indicator_style(Indicator::Directory) {
465+
Some(Indicator::Directory)
466+
} else {
467+
None
468+
}
469+
}
470+
471+
#[cfg(not(unix))]
472+
fn indicator_for_directory(&self, _path: &PathData) -> Option<Indicator> {
473+
if self.has_indicator_style(Indicator::Directory) {
474+
Some(Indicator::Directory)
475+
} else {
476+
None
477+
}
478+
}
479+
480+
#[cfg(unix)]
481+
fn indicator_for_special_file(&self, file_type: &std::fs::FileType) -> Option<Indicator> {
482+
if file_type.is_fifo() && self.has_indicator_style(Indicator::FIFO) {
483+
return Some(Indicator::FIFO);
484+
}
485+
if file_type.is_socket() && self.has_indicator_style(Indicator::Socket) {
486+
return Some(Indicator::Socket);
487+
}
488+
if file_type.is_block_device() && self.has_indicator_style(Indicator::BlockDevice) {
489+
return Some(Indicator::BlockDevice);
490+
}
491+
if file_type.is_char_device() && self.has_indicator_style(Indicator::CharacterDevice) {
492+
return Some(Indicator::CharacterDevice);
493+
}
494+
None
495+
}
496+
497+
#[cfg(not(unix))]
498+
fn indicator_for_special_file(&self, _file_type: &std::fs::FileType) -> Option<Indicator> {
450499
None
451500
}
452501

@@ -730,22 +779,22 @@ fn parse_indicator_codes() -> (HashMap<Indicator, String>, bool) {
730779
}
731780
continue;
732781
}
733-
indicator_codes.insert(indicator, canonicalize_indicator_value(value));
782+
indicator_codes.insert(indicator, canonicalize_indicator_value(value).into_owned());
734783
}
735784
}
736785
}
737786

738787
(indicator_codes, ln_color_from_target)
739788
}
740789

741-
fn canonicalize_indicator_value(value: &str) -> String {
790+
fn canonicalize_indicator_value(value: &str) -> Cow<'_, str> {
742791
if value.len() == 1 && value.chars().all(|c| c.is_ascii_digit()) {
743792
let mut canonical = String::with_capacity(2);
744793
canonical.push('0');
745794
canonical.push_str(value);
746-
canonical
795+
Cow::Owned(canonical)
747796
} else {
748-
value.to_string()
797+
Cow::Borrowed(value)
749798
}
750799
}
751800

0 commit comments

Comments
 (0)