@@ -256,68 +256,107 @@ impl LsColors {
256
256
self . style_for_path_with_metadata ( path, metadata. as_ref ( ) )
257
257
}
258
258
259
- /// Get the ANSI style for a path, given the corresponding `Metadata` struct.
260
- ///
261
- /// *Note:* The `Metadata` struct must have been acquired via `Path::symlink_metadata` in
262
- /// order to colorize symbolic links correctly.
263
- pub fn style_for_path_with_metadata < P : AsRef < Path > > (
264
- & self ,
265
- path : P ,
266
- metadata : Option < & std:: fs:: Metadata > ,
267
- ) -> Option < & Style > {
268
- if let Some ( metadata) = metadata {
269
- if metadata. is_dir ( ) {
270
- return self . style_for_indicator ( Indicator :: Directory ) ;
271
- }
259
+ /// Check if an indicator has an associated color.
260
+ fn has_color_for ( & self , indicator : Indicator ) -> bool {
261
+ self . indicator_mapping . contains_key ( & indicator)
262
+ }
272
263
273
- if metadata. file_type ( ) . is_symlink ( ) {
274
- // This works because `Path::exists` traverses symlinks.
275
- if path. as_ref ( ) . exists ( ) {
276
- return self . style_for_indicator ( Indicator :: SymbolicLink ) ;
264
+ /// Get the indicator type for a path with corresponding metadata.
265
+ fn indicator_for ( & self , path : & Path , metadata : Option < & std:: fs:: Metadata > ) -> Indicator {
266
+ if let Some ( metadata) = metadata {
267
+ let file_type = metadata. file_type ( ) ;
268
+
269
+ if file_type. is_file ( ) {
270
+ let mode = crate :: fs:: mode ( metadata) ;
271
+ let nlink = crate :: fs:: nlink ( metadata) ;
272
+
273
+ if self . has_color_for ( Indicator :: Setuid ) && mode & 0o4000 != 0 {
274
+ Indicator :: Setuid
275
+ } else if self . has_color_for ( Indicator :: Setgid ) && mode & 0o2000 != 0 {
276
+ Indicator :: Setgid
277
+ } else if self . has_color_for ( Indicator :: ExecutableFile ) && mode & 0o0111 != 0 {
278
+ Indicator :: ExecutableFile
279
+ } else if self . has_color_for ( Indicator :: MultipleHardLinks ) && nlink > 1 {
280
+ Indicator :: MultipleHardLinks
277
281
} else {
278
- return self . style_for_indicator ( Indicator :: OrphanedSymbolicLink ) ;
282
+ Indicator :: RegularFile
279
283
}
280
- }
281
-
282
- # [ cfg ( unix ) ]
283
- {
284
- use std :: os :: unix :: fs :: FileTypeExt ;
285
-
286
- let filetype = metadata . file_type ( ) ;
287
- if filetype . is_fifo ( ) {
288
- return self . style_for_indicator ( Indicator :: FIFO ) ;
289
- }
290
- if filetype . is_socket ( ) {
291
- return self . style_for_indicator ( Indicator :: Socket ) ;
284
+ } else if file_type . is_dir ( ) {
285
+ let mode = crate :: fs :: mode ( metadata ) ;
286
+
287
+ if self . has_color_for ( Indicator :: StickyAndOtherWritable ) && mode & 0o1002 == 0o1002
288
+ {
289
+ Indicator :: StickyAndOtherWritable
290
+ } else if self . has_color_for ( Indicator :: OtherWritable ) && mode & 0o0002 != 0 {
291
+ Indicator :: OtherWritable
292
+ } else if self . has_color_for ( Indicator :: Sticky ) && mode & 0o1000 != 0 {
293
+ Indicator :: Sticky
294
+ } else {
295
+ Indicator :: Directory
292
296
}
293
- if filetype. is_block_device ( ) {
294
- return self . style_for_indicator ( Indicator :: BlockDevice ) ;
297
+ } else if file_type. is_symlink ( ) {
298
+ // This works because `Path::exists` traverses symlinks.
299
+ if self . has_color_for ( Indicator :: OrphanedSymbolicLink ) && !path. exists ( ) {
300
+ return Indicator :: OrphanedSymbolicLink ;
295
301
}
296
- if filetype. is_char_device ( ) {
297
- return self . style_for_indicator ( Indicator :: CharacterDevice ) ;
302
+
303
+ Indicator :: SymbolicLink
304
+ } else {
305
+ #[ cfg( unix) ]
306
+ {
307
+ use std:: os:: unix:: fs:: FileTypeExt ;
308
+
309
+ if file_type. is_fifo ( ) {
310
+ return Indicator :: FIFO ;
311
+ }
312
+ if file_type. is_socket ( ) {
313
+ return Indicator :: Socket ;
314
+ }
315
+ if file_type. is_block_device ( ) {
316
+ return Indicator :: BlockDevice ;
317
+ }
318
+ if file_type. is_char_device ( ) {
319
+ return Indicator :: CharacterDevice ;
320
+ }
298
321
}
299
- }
300
322
301
- if crate :: fs :: is_executable ( & metadata ) {
302
- return self . style_for_indicator ( Indicator :: ExecutableFile ) ;
323
+ // Treat files of unknown type as errors
324
+ Indicator :: MissingFile
303
325
}
326
+ } else {
327
+ // Default to a regular file, so we still try the suffix map when no metadata is available
328
+ Indicator :: RegularFile
304
329
}
330
+ }
305
331
306
- // Note: using '.to_str()' here means that filename
307
- // matching will not work with invalid-UTF-8 paths.
308
- let filename = path. as_ref ( ) . file_name ( ) ?. to_str ( ) ?. to_ascii_lowercase ( ) ;
309
-
310
- // We need to traverse LS_COLORS from back to front
311
- // to be consistent with `ls`:
312
- for ( suffix, style) in self . suffix_mapping . iter ( ) . rev ( ) {
313
- // Note: For some reason, 'ends_with' is much
314
- // slower if we omit `.as_str()` here:
315
- if filename. ends_with ( suffix. as_str ( ) ) {
316
- return Some ( style) ;
332
+ /// Get the ANSI style for a path, given the corresponding `Metadata` struct.
333
+ ///
334
+ /// *Note:* The `Metadata` struct must have been acquired via `Path::symlink_metadata` in
335
+ /// order to colorize symbolic links correctly.
336
+ pub fn style_for_path_with_metadata < P : AsRef < Path > > (
337
+ & self ,
338
+ path : P ,
339
+ metadata : Option < & std:: fs:: Metadata > ,
340
+ ) -> Option < & Style > {
341
+ let indicator = self . indicator_for ( path. as_ref ( ) , metadata) ;
342
+
343
+ if indicator == Indicator :: RegularFile {
344
+ // Note: using '.to_str()' here means that filename
345
+ // matching will not work with invalid-UTF-8 paths.
346
+ let filename = path. as_ref ( ) . file_name ( ) ?. to_str ( ) ?. to_ascii_lowercase ( ) ;
347
+
348
+ // We need to traverse LS_COLORS from back to front
349
+ // to be consistent with `ls`:
350
+ for ( suffix, style) in self . suffix_mapping . iter ( ) . rev ( ) {
351
+ // Note: For some reason, 'ends_with' is much
352
+ // slower if we omit `.as_str()` here:
353
+ if filename. ends_with ( suffix. as_str ( ) ) {
354
+ return Some ( style) ;
355
+ }
317
356
}
318
357
}
319
358
320
- None
359
+ self . style_for_indicator ( indicator )
321
360
}
322
361
323
362
/// Get ANSI styles for each component of a given path. Components already include the path
@@ -337,13 +376,27 @@ impl LsColors {
337
376
/// For example, the style for `mi` (missing file) falls back to `or` (orphaned symbolic link)
338
377
/// if it has not been specified explicitly.
339
378
pub fn style_for_indicator ( & self , indicator : Indicator ) -> Option < & Style > {
340
- match indicator {
341
- Indicator :: MissingFile => self
342
- . indicator_mapping
343
- . get ( & Indicator :: MissingFile )
344
- . or_else ( || self . indicator_mapping . get ( & Indicator :: OrphanedSymbolicLink ) ) ,
345
- _ => self . indicator_mapping . get ( & indicator) ,
346
- }
379
+ self . indicator_mapping
380
+ . get ( & indicator)
381
+ . or_else ( || {
382
+ self . indicator_mapping . get ( & match indicator {
383
+ Indicator :: Setuid
384
+ | Indicator :: Setgid
385
+ | Indicator :: ExecutableFile
386
+ | Indicator :: MultipleHardLinks => Indicator :: RegularFile ,
387
+
388
+ Indicator :: StickyAndOtherWritable
389
+ | Indicator :: OtherWritable
390
+ | Indicator :: Sticky => Indicator :: Directory ,
391
+
392
+ Indicator :: OrphanedSymbolicLink => Indicator :: SymbolicLink ,
393
+
394
+ Indicator :: MissingFile => Indicator :: OrphanedSymbolicLink ,
395
+
396
+ _ => indicator,
397
+ } )
398
+ } )
399
+ . or_else ( || self . indicator_mapping . get ( & Indicator :: Normal ) )
347
400
}
348
401
}
349
402
@@ -512,6 +565,63 @@ mod tests {
512
565
assert_eq ! ( Some ( Color :: Yellow ) , style_missing. foreground) ;
513
566
}
514
567
568
+ #[ cfg( unix) ]
569
+ #[ test]
570
+ fn style_for_setid ( ) {
571
+ use std:: fs:: { set_permissions, Permissions } ;
572
+ use std:: os:: unix:: fs:: PermissionsExt ;
573
+
574
+ let tmp_dir = temp_dir ( ) ;
575
+ let tmp_file = create_file ( tmp_dir. path ( ) . join ( "setid" ) ) ;
576
+ let perms = Permissions :: from_mode ( 0o6750 ) ;
577
+ set_permissions ( & tmp_file, perms) . unwrap ( ) ;
578
+
579
+ let suid_style = get_default_style ( & tmp_file) . unwrap ( ) ;
580
+ assert_eq ! ( Some ( Color :: Red ) , suid_style. background) ;
581
+
582
+ let lscolors = LsColors :: from_string ( "su=0" ) ;
583
+ let sgid_style = lscolors. style_for_path ( & tmp_file) . unwrap ( ) ;
584
+ assert_eq ! ( Some ( Color :: Yellow ) , sgid_style. background) ;
585
+ }
586
+
587
+ #[ cfg( unix) ]
588
+ #[ test]
589
+ fn style_for_multi_hard_links ( ) {
590
+ let tmp_dir = temp_dir ( ) ;
591
+ let tmp_file = create_file ( tmp_dir. path ( ) . join ( "file1" ) ) ;
592
+ std:: fs:: hard_link ( & tmp_file, tmp_dir. path ( ) . join ( "file2" ) ) . unwrap ( ) ;
593
+
594
+ let lscolors = LsColors :: from_string ( "mh=35" ) ;
595
+ let style = lscolors. style_for_path ( & tmp_file) . unwrap ( ) ;
596
+ assert_eq ! ( Some ( Color :: Magenta ) , style. foreground) ;
597
+ }
598
+
599
+ #[ cfg( unix) ]
600
+ #[ test]
601
+ fn style_for_sticky_other_writable ( ) {
602
+ use std:: fs:: { set_permissions, Permissions } ;
603
+ use std:: os:: unix:: fs:: PermissionsExt ;
604
+
605
+ let tmp_root = temp_dir ( ) ;
606
+ let tmp_dir = create_dir ( tmp_root. path ( ) . join ( "test-dir" ) ) ;
607
+ let perms = Permissions :: from_mode ( 0o1777 ) ;
608
+ set_permissions ( & tmp_dir, perms) . unwrap ( ) ;
609
+
610
+ let so_style = get_default_style ( & tmp_dir) . unwrap ( ) ;
611
+ assert_eq ! ( Some ( Color :: Black ) , so_style. foreground) ;
612
+ assert_eq ! ( Some ( Color :: Green ) , so_style. background) ;
613
+
614
+ let lscolors1 = LsColors :: from_string ( "tw=0" ) ;
615
+ let ow_style = lscolors1. style_for_path ( & tmp_dir) . unwrap ( ) ;
616
+ assert_eq ! ( Some ( Color :: Blue ) , ow_style. foreground) ;
617
+ assert_eq ! ( Some ( Color :: Green ) , ow_style. background) ;
618
+
619
+ let lscolors2 = LsColors :: from_string ( "tw=0:ow=0" ) ;
620
+ let st_style = lscolors2. style_for_path ( & tmp_dir) . unwrap ( ) ;
621
+ assert_eq ! ( Some ( Color :: White ) , st_style. foreground) ;
622
+ assert_eq ! ( Some ( Color :: Blue ) , st_style. background) ;
623
+ }
624
+
515
625
#[ test]
516
626
fn style_for_path_components ( ) {
517
627
use std:: ffi:: OsString ;
0 commit comments