@@ -21,6 +21,8 @@ mod options {
2121
2222 pub const ARG_FILE : & str = "arg-file" ;
2323 pub const DELIMITER : & str = "delimiter" ;
24+ pub const EOF : & str = "eof" ;
25+ pub const EOF_E : & str = "eof-E" ;
2426 pub const EXIT : & str = "exit" ;
2527 pub const MAX_ARGS : & str = "max-args" ;
2628 pub const MAX_CHARS : & str = "max-chars" ;
@@ -44,6 +46,7 @@ struct Options {
4446 null : bool ,
4547 replace : Option < String > ,
4648 verbose : bool ,
49+ eof_delimiter : Option < String > ,
4750}
4851
4952#[ derive( Debug , PartialEq , Eq ) ]
@@ -637,6 +640,39 @@ where
637640 }
638641}
639642
643+ struct EofArgumentReader {
644+ reader : Box < dyn ArgumentReader > ,
645+ eof_delimiter : OsString ,
646+ eof_found : bool ,
647+ }
648+
649+ impl EofArgumentReader {
650+ fn new ( reader : Box < dyn ArgumentReader > , eof_delimiter : & String ) -> Self {
651+ Self {
652+ reader,
653+ eof_delimiter : eof_delimiter. into ( ) ,
654+ eof_found : false ,
655+ }
656+ }
657+ }
658+
659+ impl ArgumentReader for EofArgumentReader {
660+ fn next ( & mut self ) -> io:: Result < Option < Argument > > {
661+ Ok ( if self . eof_found {
662+ None
663+ } else {
664+ self . reader . next ( ) ?. and_then ( |arg| {
665+ if arg. arg == self . eof_delimiter {
666+ self . eof_found = true ;
667+ None
668+ } else {
669+ Some ( arg)
670+ }
671+ } )
672+ } )
673+ }
674+ }
675+
640676#[ derive( Debug ) ]
641677enum XargsError {
642678 ArgumentTooLarge ,
@@ -952,6 +988,27 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
952988 . overrides_with ( options:: REPLACE )
953989 . value_parser ( clap:: value_parser!( String ) ) ,
954990 )
991+ . arg (
992+ Arg :: new ( options:: EOF )
993+ . short ( 'E' )
994+ . num_args ( 1 )
995+ . value_name ( "eof-string" )
996+ . help (
997+ "If specified, stop processing the input upon reaching an input \
998+ item that matches eof-string",
999+ )
1000+ . value_parser ( clap:: value_parser!( String ) ) ,
1001+ )
1002+ . arg (
1003+ Arg :: new ( options:: EOF_E )
1004+ . short ( 'e' )
1005+ . long ( options:: EOF )
1006+ . num_args ( 0 ..=1 )
1007+ . value_name ( "eof-string" )
1008+ . help ( "Alias for -E" )
1009+ . overrides_with ( options:: EOF )
1010+ . value_parser ( clap:: value_parser!( String ) ) ,
1011+ )
9551012 . try_get_matches_from ( args) ;
9561013
9571014 let matches = match matches {
@@ -988,6 +1045,13 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
9881045 } )
9891046 } ) ,
9901047 verbose : matches. get_flag ( options:: VERBOSE ) ,
1048+ eof_delimiter : [ options:: EOF_E , options:: EOF ] . iter ( ) . find_map ( |& option| {
1049+ matches. contains_id ( option) . then ( || {
1050+ matches
1051+ . get_one :: < String > ( option)
1052+ . map_or_else ( || "{}" . to_string ( ) , std:: borrow:: ToOwned :: to_owned)
1053+ } )
1054+ } ) ,
9911055 } ;
9921056
9931057 let ( max_args, max_lines, replace, delimiter) = normalize_options ( & options, & matches) ;
@@ -1026,12 +1090,16 @@ fn do_xargs(args: &[&str]) -> Result<CommandResult, XargsError> {
10261090 Box :: new ( io:: stdin ( ) )
10271091 } ;
10281092
1029- let args: Box < dyn ArgumentReader > = if let Some ( delimiter) = delimiter {
1093+ let mut args: Box < dyn ArgumentReader > = if let Some ( delimiter) = delimiter {
10301094 Box :: new ( ByteDelimitedArgumentReader :: new ( args_file, delimiter) )
10311095 } else {
10321096 Box :: new ( WhitespaceDelimitedArgumentReader :: new ( args_file) )
10331097 } ;
10341098
1099+ if let Some ( eof_delimiter) = options. eof_delimiter {
1100+ args = Box :: new ( EofArgumentReader :: new ( args, & eof_delimiter) ) ;
1101+ }
1102+
10351103 let result = process_input (
10361104 & builder_options,
10371105 args,
@@ -1293,6 +1361,47 @@ mod tests {
12931361 assert_eq ! ( reader. next( ) . unwrap( ) , None ) ;
12941362 }
12951363
1364+ #[ test]
1365+ fn test_eof_argument_reader ( ) {
1366+ let filter = String :: from ( "def" ) ;
1367+
1368+ let reader = WhitespaceDelimitedArgumentReader :: new ( ChunkReader :: new ( vec ! [ Chunk :: Data (
1369+ b"abc def ghi" ,
1370+ ) ] ) ) ;
1371+ let mut wrapper = EofArgumentReader :: new ( Box :: new ( reader) , & filter) ;
1372+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "abc" ) ) ;
1373+ assert_eq ! ( wrapper. next( ) . unwrap( ) , None ) ;
1374+ assert_eq ! ( wrapper. next( ) . unwrap( ) , None ) ;
1375+
1376+ let reader = WhitespaceDelimitedArgumentReader :: new ( ChunkReader :: new ( vec ! [ Chunk :: Data (
1377+ b"abc define undef undefined ghi" ,
1378+ ) ] ) ) ;
1379+ let mut wrapper = EofArgumentReader :: new ( Box :: new ( reader) , & filter) ;
1380+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "abc" ) ) ;
1381+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "define" ) ) ;
1382+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "undef" ) ) ;
1383+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "undefined" ) ) ;
1384+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "ghi" ) ) ;
1385+ assert_eq ! ( wrapper. next( ) . unwrap( ) , None ) ;
1386+
1387+ let reader = WhitespaceDelimitedArgumentReader :: new ( ChunkReader :: new ( vec ! [
1388+ Chunk :: Data ( b"abc " ) ,
1389+ Chunk :: Error ( io:: ErrorKind :: Interrupted ) ,
1390+ Chunk :: Data ( b"deF " ) ,
1391+ Chunk :: Error ( io:: ErrorKind :: BrokenPipe ) ,
1392+ Chunk :: Data ( b"ghi " ) ,
1393+ Chunk :: Data ( b"def " ) ,
1394+ Chunk :: Error ( io:: ErrorKind :: BrokenPipe ) ,
1395+ ] ) ) ;
1396+ let mut wrapper = EofArgumentReader :: new ( Box :: new ( reader) , & filter) ;
1397+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "abc" ) ) ;
1398+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "deF" ) ) ;
1399+ assert ! ( wrapper. next( ) . err( ) . unwrap( ) . kind( ) == io:: ErrorKind :: BrokenPipe ) ;
1400+ assert_eq ! ( wrapper. next( ) . unwrap( ) . unwrap( ) , make_arg_soft( "ghi" ) ) ;
1401+ assert_eq ! ( wrapper. next( ) . unwrap( ) , None ) ;
1402+ assert_eq ! ( wrapper. next( ) . unwrap( ) , None ) ;
1403+ }
1404+
12961405 #[ test]
12971406 fn test_byte_delimited_reader ( ) {
12981407 let mut reader = ByteDelimitedArgumentReader :: new (
0 commit comments