@@ -251,68 +251,106 @@ impl LsColors {
251251 self . style_for_path_with_metadata ( path, metadata. as_ref ( ) )
252252 }
253253
254- /// Get the ANSI style for a path, given the corresponding `Metadata` struct.
255- ///
256- /// *Note:* The `Metadata` struct must have been acquired via `Path::symlink_metadata` in
257- /// order to colorize symbolic links correctly.
258- pub fn style_for_path_with_metadata < P : AsRef < Path > > (
259- & self ,
260- path : P ,
261- metadata : Option < & std:: fs:: Metadata > ,
262- ) -> Option < & Style > {
263- if let Some ( metadata) = metadata {
264- if metadata. is_dir ( ) {
265- return self . style_for_indicator ( Indicator :: Directory ) ;
266- }
254+ /// Check if an indicator has an associated color.
255+ fn has_color_for ( & self , indicator : Indicator ) -> bool {
256+ self . indicator_mapping . contains_key ( & indicator)
257+ }
267258
268- if metadata. file_type ( ) . is_symlink ( ) {
269- // This works because `Path::exists` traverses symlinks.
270- if path. as_ref ( ) . exists ( ) {
271- return self . style_for_indicator ( Indicator :: SymbolicLink ) ;
259+ /// Get the indicator type for a path with corresponding metadata.
260+ fn indicator_for ( & self , path : & Path , metadata : Option < & std:: fs:: Metadata > ) -> Indicator {
261+ if let Some ( metadata) = metadata {
262+ let file_type = metadata. file_type ( ) ;
263+
264+ if file_type. is_file ( ) {
265+ let mode = crate :: fs:: mode ( metadata) ;
266+ let nlink = crate :: fs:: nlink ( metadata) ;
267+
268+ if self . has_color_for ( Indicator :: Setuid ) && mode & 0o4000 != 0 {
269+ Indicator :: Setuid
270+ } else if self . has_color_for ( Indicator :: Setgid ) && mode & 0o2000 != 0 {
271+ Indicator :: Setgid
272+ } else if self . has_color_for ( Indicator :: ExecutableFile ) && mode & 0o0111 != 0 {
273+ Indicator :: ExecutableFile
274+ } else if self . has_color_for ( Indicator :: MultipleHardLinks ) && nlink > 1 {
275+ Indicator :: MultipleHardLinks
272276 } else {
273- return self . style_for_indicator ( Indicator :: OrphanedSymbolicLink ) ;
274- }
275- }
276-
277- #[ cfg( unix) ]
278- {
279- use std:: os:: unix:: fs:: FileTypeExt ;
280-
281- let filetype = metadata. file_type ( ) ;
282- if filetype. is_fifo ( ) {
283- return self . style_for_indicator ( Indicator :: FIFO ) ;
277+ Indicator :: RegularFile
284278 }
285- if filetype. is_socket ( ) {
286- return self . style_for_indicator ( Indicator :: Socket ) ;
279+ } else if file_type. is_dir ( ) {
280+ let mode = crate :: fs:: mode ( metadata) ;
281+
282+ if self . has_color_for ( Indicator :: StickyAndOtherWritable ) && mode & 0o1002 == 0o1002 {
283+ Indicator :: StickyAndOtherWritable
284+ } else if self . has_color_for ( Indicator :: OtherWritable ) && mode & 0o0002 != 0 {
285+ Indicator :: OtherWritable
286+ } else if self . has_color_for ( Indicator :: Sticky ) && mode & 0o1000 != 0 {
287+ Indicator :: Sticky
288+ } else {
289+ Indicator :: Directory
287290 }
288- if filetype. is_block_device ( ) {
289- return self . style_for_indicator ( Indicator :: BlockDevice ) ;
291+ } else if file_type. is_symlink ( ) {
292+ // This works because `Path::exists` traverses symlinks.
293+ if self . has_color_for ( Indicator :: OrphanedSymbolicLink ) && !path. exists ( ) {
294+ return Indicator :: OrphanedSymbolicLink ;
290295 }
291- if filetype. is_char_device ( ) {
292- return self . style_for_indicator ( Indicator :: CharacterDevice ) ;
296+
297+ Indicator :: SymbolicLink
298+ } else {
299+ #[ cfg( unix) ]
300+ {
301+ use std:: os:: unix:: fs:: FileTypeExt ;
302+
303+ if file_type. is_fifo ( ) {
304+ return Indicator :: FIFO ;
305+ }
306+ if file_type. is_socket ( ) {
307+ return Indicator :: Socket ;
308+ }
309+ if file_type. is_block_device ( ) {
310+ return Indicator :: BlockDevice ;
311+ }
312+ if file_type. is_char_device ( ) {
313+ return Indicator :: CharacterDevice ;
314+ }
293315 }
294- }
295316
296- if crate :: fs :: is_executable ( & metadata ) {
297- return self . style_for_indicator ( Indicator :: ExecutableFile ) ;
317+ // Treat files of unknown type as errors
318+ Indicator :: MissingFile
298319 }
320+ } else {
321+ // Default to a regular file, so we still try the suffix map when no metadata is available
322+ Indicator :: RegularFile
299323 }
324+ }
300325
301- // Note: using '.to_str()' here means that filename
302- // matching will not work with invalid-UTF-8 paths.
303- let filename = path. as_ref ( ) . file_name ( ) ?. to_str ( ) ?. to_ascii_lowercase ( ) ;
304-
305- // We need to traverse LS_COLORS from back to front
306- // to be consistent with `ls`:
307- for ( suffix, style) in self . suffix_mapping . iter ( ) . rev ( ) {
308- // Note: For some reason, 'ends_with' is much
309- // slower if we omit `.as_str()` here:
310- if filename. ends_with ( suffix. as_str ( ) ) {
311- return Some ( style) ;
326+ /// Get the ANSI style for a path, given the corresponding `Metadata` struct.
327+ ///
328+ /// *Note:* The `Metadata` struct must have been acquired via `Path::symlink_metadata` in
329+ /// order to colorize symbolic links correctly.
330+ pub fn style_for_path_with_metadata < P : AsRef < Path > > (
331+ & self ,
332+ path : P ,
333+ metadata : Option < & std:: fs:: Metadata > ,
334+ ) -> Option < & Style > {
335+ let indicator = self . indicator_for ( path. as_ref ( ) , metadata) ;
336+
337+ if indicator == Indicator :: RegularFile {
338+ // Note: using '.to_str()' here means that filename
339+ // matching will not work with invalid-UTF-8 paths.
340+ let filename = path. as_ref ( ) . file_name ( ) ?. to_str ( ) ?. to_ascii_lowercase ( ) ;
341+
342+ // We need to traverse LS_COLORS from back to front
343+ // to be consistent with `ls`:
344+ for ( suffix, style) in self . suffix_mapping . iter ( ) . rev ( ) {
345+ // Note: For some reason, 'ends_with' is much
346+ // slower if we omit `.as_str()` here:
347+ if filename. ends_with ( suffix. as_str ( ) ) {
348+ return Some ( style) ;
349+ }
312350 }
313351 }
314352
315- None
353+ self . style_for_indicator ( indicator )
316354 }
317355
318356 /// Get ANSI styles for each component of a given path. Components already include the path
@@ -332,13 +370,27 @@ impl LsColors {
332370 /// For example, the style for `mi` (missing file) falls back to `or` (orphaned symbolic link)
333371 /// if it has not been specified explicitly.
334372 pub fn style_for_indicator ( & self , indicator : Indicator ) -> Option < & Style > {
335- match indicator {
336- Indicator :: MissingFile => self
337- . indicator_mapping
338- . get ( & Indicator :: MissingFile )
339- . or_else ( || self . indicator_mapping . get ( & Indicator :: OrphanedSymbolicLink ) ) ,
340- _ => self . indicator_mapping . get ( & indicator) ,
341- }
373+ self . indicator_mapping
374+ . get ( & indicator)
375+ . or_else ( || {
376+ self . indicator_mapping . get ( & match indicator {
377+ Indicator :: Setuid
378+ | Indicator :: Setgid
379+ | Indicator :: ExecutableFile
380+ | Indicator :: MultipleHardLinks => Indicator :: RegularFile ,
381+
382+ Indicator :: StickyAndOtherWritable
383+ | Indicator :: OtherWritable
384+ | Indicator :: Sticky => Indicator :: Directory ,
385+
386+ Indicator :: OrphanedSymbolicLink => Indicator :: SymbolicLink ,
387+
388+ Indicator :: MissingFile => Indicator :: OrphanedSymbolicLink ,
389+
390+ _ => indicator,
391+ } )
392+ } )
393+ . or_else ( || self . indicator_mapping . get ( & Indicator :: Normal ) )
342394 }
343395}
344396
0 commit comments