44// file that was distributed with this source code.
55use super :: PathData ;
66use lscolors:: { Indicator , LsColors , Style } ;
7+ use std:: borrow:: Cow ;
78use std:: collections:: HashMap ;
89use std:: env;
910use std:: ffi:: OsString ;
@@ -18,6 +19,17 @@ const ANSI_RESET: &str = "\x1b[0m";
1819const ANSI_CLEAR_EOL : & str = "\x1b [K" ;
1920const 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+
2133enum 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