@@ -285,6 +285,7 @@ fn list_archive(args: ListCommand) -> anyhow::Result<()> {
285285 hide_control_chars : args. hide_control_chars ,
286286 classify : args. classify ,
287287 format : args. format ,
288+ out_to_stderr : false ,
288289 } ;
289290 let archive = args. file . archive ( ) ;
290291 let files = args. file . files ( ) ;
@@ -363,6 +364,7 @@ pub(crate) struct ListOptions {
363364 pub ( crate ) hide_control_chars : bool ,
364365 pub ( crate ) classify : bool ,
365366 pub ( crate ) format : Option < Format > ,
367+ pub ( crate ) out_to_stderr : bool ,
366368}
367369
368370pub ( crate ) fn run_list_archive < ' a > (
@@ -441,19 +443,31 @@ fn print_entries<'a>(
441443 } )
442444 . collect :: < Vec < _ > > ( ) ;
443445 globs. ensure_all_matched ( ) ?;
446+ let mut stdout = io:: stdout ( ) . lock ( ) ;
447+ let mut stderr = io:: stderr ( ) . lock ( ) ;
448+ let out: & mut dyn Write = if options. out_to_stderr {
449+ & mut stderr
450+ } else {
451+ & mut stdout
452+ } ;
453+
444454 match options. format {
445- Some ( Format :: Line ) => simple_list_entries ( entries, options) ,
446- Some ( Format :: JsonL ) => json_line_entries ( entries) ,
447- Some ( Format :: Table ) => detail_list_entries ( entries, options) ,
448- Some ( Format :: Tree ) => tree_entries ( entries, options) ,
449- Some ( Format :: BsdTar ) => bsd_tar_list_entries ( entries, options) ,
450- None if options. long => detail_list_entries ( entries, options) ,
451- None => simple_list_entries ( entries, options) ,
452- }
455+ Some ( Format :: Line ) => simple_list_entries_to ( entries, & options, out ) ? ,
456+ Some ( Format :: JsonL ) => json_line_entries_to ( entries, out ) ? ,
457+ Some ( Format :: Table ) => detail_list_entries_to ( entries, & options, out ) ? ,
458+ Some ( Format :: Tree ) => tree_entries_to ( entries, & options, out ) ? ,
459+ Some ( Format :: BsdTar ) => bsd_tar_list_entries_to ( entries, & options, out ) ? ,
460+ None if options. long => detail_list_entries_to ( entries, & options, out ) ? ,
461+ None => simple_list_entries_to ( entries, & options, out ) ? ,
462+ } ;
453463 Ok ( ( ) )
454464}
455465
456- fn bsd_tar_list_entries ( entries : Vec < TableRow > , options : ListOptions ) {
466+ fn bsd_tar_list_entries_to (
467+ entries : Vec < TableRow > ,
468+ options : & ListOptions ,
469+ out : & mut dyn Write ,
470+ ) -> io:: Result < ( ) > {
457471 let now = SystemTime :: now ( ) ;
458472 let mut uname_width = 6 ;
459473 let mut gname_width = 6 ;
@@ -486,10 +500,12 @@ fn bsd_tar_list_entries(entries: Vec<TableRow>, options: ListOptions) {
486500
487501 // permission nlink uname gname size mtime name link
488502 // ex: -rw-r--r-- 0 1000 1000 0 Jan 1 1980 f
489- println ! (
503+ writeln ! (
504+ out,
490505 "{perm} {nlink} {uname:<uname_width$} {gname:<gname_width$} {size:8} {mtime} {name}"
491- ) ;
506+ ) ? ;
492507 }
508+ Ok ( ( ) )
493509}
494510
495511fn bsd_tar_time ( now : SystemTime , time : SystemTime ) -> DelayedFormat < StrftimeItems < ' static > > {
@@ -527,17 +543,23 @@ impl<'a> Display for SimpleListDisplay<'a> {
527543 }
528544}
529545
530- fn simple_list_entries ( entries : Vec < TableRow > , options : ListOptions ) {
531- print ! (
532- "{}" ,
533- SimpleListDisplay {
534- entries: & entries,
535- options: & options,
536- }
537- ) ;
546+ fn simple_list_entries_to (
547+ entries : Vec < TableRow > ,
548+ options : & ListOptions ,
549+ out : & mut dyn Write ,
550+ ) -> io:: Result < ( ) > {
551+ let display = SimpleListDisplay {
552+ entries : & entries,
553+ options,
554+ } ;
555+ write ! ( out, "{display}" )
538556}
539557
540- fn detail_list_entries ( entries : impl IntoIterator < Item = TableRow > , options : ListOptions ) {
558+ fn detail_list_entries_to (
559+ entries : impl IntoIterator < Item = TableRow > ,
560+ options : & ListOptions ,
561+ out : & mut dyn Write ,
562+ ) -> io:: Result < ( ) > {
541563 let underline = Color :: new ( "\x1B [4m" , "\x1B [0m" ) ;
542564 let reset = Color :: new ( "\x1B [8m" , "\x1B [0m" ) ;
543565 let header = [
@@ -663,7 +685,7 @@ fn detail_list_entries(entries: impl IntoIterator<Item = TableRow>, options: Lis
663685 Color :: empty ( ) ,
664686 Color :: empty ( ) ,
665687 ) ) ;
666- println ! ( "{table}" ) ;
688+ writeln ! ( out , "{table}" )
667689}
668690
669691const DURATION_SIX_MONTH : Duration = Duration :: from_secs ( 60 * 60 * 24 * 30 * 6 ) ;
@@ -843,7 +865,7 @@ struct XAttr<'a> {
843865 value : String ,
844866}
845867
846- fn json_line_entries ( entries : Vec < TableRow > ) {
868+ fn json_line_entries_to ( entries : Vec < TableRow > , out : & mut dyn Write ) -> anyhow :: Result < ( ) > {
847869 let entries = entries
848870 . par_iter ( )
849871 . map ( |it| {
@@ -899,54 +921,11 @@ fn json_line_entries(entries: Vec<TableRow>) {
899921 } )
900922 . collect :: < Vec < _ > > ( ) ;
901923
902- print ! ( "{}" , JsonLDisplay ( entries) ) ;
903- }
904-
905- struct JsonLDisplay < T > ( Vec < T > ) ;
906-
907- impl < T : serde:: Serialize > Display for JsonLDisplay < T > {
908- #[ inline]
909- fn fmt ( & self , f : & mut Formatter < ' _ > ) -> fmt:: Result {
910- struct FormatterAdapter < ' f , ' a > ( & ' f mut Formatter < ' a > ) ;
911- impl Write for FormatterAdapter < ' _ , ' _ > {
912- #[ inline]
913- fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
914- self . 0
915- . write_str ( unsafe { std:: str:: from_utf8_unchecked ( buf) } )
916- . map_err ( io:: Error :: other) ?;
917- Ok ( buf. len ( ) )
918- }
919-
920- #[ inline]
921- fn flush ( & mut self ) -> io:: Result < ( ) > {
922- Ok ( ( ) )
923- }
924- }
925-
926- impl fmt:: Write for FormatterAdapter < ' _ , ' _ > {
927- #[ inline]
928- fn write_str ( & mut self , s : & str ) -> fmt:: Result {
929- self . 0 . write_str ( s)
930- }
931-
932- #[ inline]
933- fn write_char ( & mut self , c : char ) -> fmt:: Result {
934- self . 0 . write_char ( c)
935- }
936-
937- #[ inline]
938- fn write_fmt ( & mut self , args : fmt:: Arguments < ' _ > ) -> fmt:: Result {
939- self . 0 . write_fmt ( args)
940- }
941- }
942- let mut writer = FormatterAdapter ( f) ;
943- for line in & self . 0 {
944- use core:: fmt:: Write ;
945- serde_json:: to_writer ( & mut writer, & line) . map_err ( |_| fmt:: Error ) ?;
946- writer. write_char ( '\n' ) ?;
947- }
948- Ok ( ( ) )
924+ for line in entries {
925+ serde_json:: to_writer ( & mut * out, & line) ?;
926+ out. write_all ( b"\n " ) ?;
949927 }
928+ Ok ( ( ) )
950929}
951930
952931#[ derive( Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Hash , Debug ) ]
@@ -962,16 +941,20 @@ impl<'s> TreeEntry<'s> {
962941 }
963942}
964943
965- fn tree_entries ( entries : Vec < TableRow > , options : ListOptions ) {
944+ fn tree_entries_to (
945+ entries : Vec < TableRow > ,
946+ options : & ListOptions ,
947+ out : & mut dyn Write ,
948+ ) -> io:: Result < ( ) > {
966949 let entries = entries. iter ( ) . map ( |it| match & it. entry_type {
967950 EntryType :: File ( name) => ( name. as_str ( ) , DataKind :: File ) ,
968951 EntryType :: Directory ( name) => ( name. as_str ( ) , DataKind :: Directory ) ,
969952 EntryType :: SymbolicLink ( name, _) => ( name. as_str ( ) , DataKind :: SymbolicLink ) ,
970953 EntryType :: HardLink ( name, _) => ( name. as_str ( ) , DataKind :: HardLink ) ,
971954 } ) ;
972955 let map = build_tree_map ( entries) ;
973- let tree = build_term_tree ( & map, Cow :: Borrowed ( "" ) , None , DataKind :: Directory , & options) ;
974- println ! ( "{tree}" ) ;
956+ let tree = build_term_tree ( & map, Cow :: Borrowed ( "" ) , None , DataKind :: Directory , options) ;
957+ writeln ! ( out , "{tree}" )
975958}
976959
977960fn build_tree_map < ' s > (
0 commit comments