From 629fe49d2d0a5a4ef949eb987920fe7b3f379ba9 Mon Sep 17 00:00:00 2001
From: "Deven T. Corzine" <deven@ties.org>
Date: Sun, 2 Mar 2025 01:43:08 -0500
Subject: [PATCH 1/2] xargs: Add -N (or --newline) option for
 newline-terminated input.

This new -N (or --newline) option is a convenience alias for -d'\n' to
process newline-terminated input, such as the output from "find -print".
Using "find -print0" is still better, but this option can also be useful
for working with other tools which don't offer null-terminated output.

* xargs/xargs.c: Add -N (or --newline) option for newline-terminated input.
* xargs/testsuite/xargs.gnu/newline.exp: Test case for -N option.
* xargs/testsuite/xargs.gnu/newline.xo: Expected output of test case.
* xargs/xargs.1: Document -N (--newline) option in man page.
* doc/find.texi: Document -N (--newline) option in texinfo documentation.
* NEWS: Add description of -N (--newline) option.
---
 NEWS                                  |  8 ++++++
 doc/find.texi                         | 40 ++++++++++++++++++++-------
 xargs/testsuite/xargs.gnu/newline.exp |  1 +
 xargs/testsuite/xargs.gnu/newline.xo  |  5 ++++
 xargs/xargs.1                         | 38 +++++++++++++++++++------
 xargs/xargs.c                         | 15 ++++++++--
 6 files changed, 86 insertions(+), 21 deletions(-)
 create mode 100644 xargs/testsuite/xargs.gnu/newline.exp
 create mode 100644 xargs/testsuite/xargs.gnu/newline.xo

diff --git a/NEWS b/NEWS
index 5b32c9fc..f03bd09d 100644
--- a/NEWS
+++ b/NEWS
@@ -24,6 +24,14 @@ GNU findutils NEWS - User visible changes.      -*- outline -*- (allout)
   argument of the command to be run.  While POSIX allows this for -exec, this is
   deemed insecure as an attacker could influence which files could be found.
 
+** New features in xargs
+
+  Added a new convenience option -N (--newline) as an alias for -d'\n' for
+  newline-terminated input.  This can be used with "find -print" output for
+  path names containing spaces (but not newlines), although "find -print0"
+  remains preferable.  This option can also be useful in conjunction with
+  other tools which don't offer null-terminated output like "find -print0".
+
 ** Documentation Changes
 
   The forthcoming Issue 8 of the POSIX standard will standardise "find
diff --git a/doc/find.texi b/doc/find.texi
index d441e01c..5e835736 100644
--- a/doc/find.texi
+++ b/doc/find.texi
@@ -1426,9 +1426,16 @@ files that contain the string, rather than the lines that contain it.
 The string argument (@samp{thing}) is actually a regular expression,
 so it can contain metacharacters.  This method can be refined a little
 by using the @samp{-r} option to make @code{xargs} not run @code{grep}
-if @code{find} produces no output, and using the @code{find} action
-@samp{-print0} and the @code{xargs} option @samp{-0} to avoid
-misinterpreting files whose names contain spaces:
+if @code{find} produces no output, and using the @code{xargs} option
+@samp{-N} or the @code{find} action @samp{-print0} with the
+@code{xargs} option @samp{-0} to avoid misinterpreting filenames
+containing spaces:
+
+@example
+find . -name '*.[ch]' -print | xargs -r -N grep -l thing
+@end example
+
+or better yet:
 
 @example
 find . -name '*.[ch]' -print0 | xargs -r -0 grep -l thing
@@ -2508,7 +2515,9 @@ and even newlines, it is not safe to process them using @code{xargs}
 in its default mode of operation.  But since most files' names do not
 contain blanks, this problem occurs only infrequently.  If you are
 only searching through files that you know have safe names, then you
-need not be concerned about it.
+need not be concerned about it.  (The @samp{-N} or @samp{--newline}
+option of @code{xargs} may be useful for filenames which contain
+spaces but not newlines.)
 
 Error messages issued by @code{find} and @code{locate} quote unusual
 characters in file names in order to prevent unwanted changes in the
@@ -3911,22 +3920,22 @@ rejecting this as unrecognized option.
 
 @item --arg-file@r{=@var{inputfile}}
 @itemx -a @r{@var{inputfile}}
-Read names from the file @var{inputfile} instead of standard input.
+Read items from the file @var{inputfile} instead of standard input.
 If you use this option, the standard input stream remains unchanged
 when commands are run. Otherwise, stdin is redirected from
 @file{/dev/null}.
 
 @item --null
 @itemx -0
-Input file names are terminated by a null character instead of by
-whitespace, and any quotes and backslash characters are not considered
-special (every character is taken literally).
-Disables the end-of-file string, which is treated like any other argument.
+Input items are terminated by a null character instead of by whitespace,
+and any quotes and backslash characters are not considered special (every
+character is taken literally).  Disables the end-of-file string, which is
+treated like any other argument.
 
 @item --delimiter @var{delim}
 @itemx -d @var{delim}
 
-Input file names are terminated by the specified character @var{delim}
+Input items are terminated by the specified character @var{delim}
 instead of by whitespace, and any quotes and backslash characters are
 not considered special (every character is taken literally).  Disables
 the logical end-of-file marker string, which is treated like any other
@@ -3982,6 +3991,17 @@ of counting the lines.  Implies @samp{-x}.  The @samp{-l} form of this
 option is deprecated in favour of the POSIX-compliant @samp{-L}
 option.
 
+@item --newline
+@itemx -N
+Input items are terminated by newlines, not by whitespace.  Spaces, tabs,
+quotes and backslash are not special (every character is taken literally).
+Disables the end-of-file string, which is treated like any other argument.
+Useful when the input consists of simply newline-separated items (such as
+file names from the find @samp{-print} option which might contain spaces
+or tabs), although it is almost always better to design your program to
+use @samp{--null} where this is possible.  (This option is an alias for
+@samp{-d'\\n'}.)
+
 @item --max-args=@var{max-args}
 @itemx -n @var{max-args}
 Use at most @var{max-args} arguments per command line.  Fewer than
diff --git a/xargs/testsuite/xargs.gnu/newline.exp b/xargs/testsuite/xargs.gnu/newline.exp
new file mode 100644
index 00000000..29318302
--- /dev/null
+++ b/xargs/testsuite/xargs.gnu/newline.exp
@@ -0,0 +1 @@
+xargs_start p {-N -n1} quotes.xi
diff --git a/xargs/testsuite/xargs.gnu/newline.xo b/xargs/testsuite/xargs.gnu/newline.xo
new file mode 100644
index 00000000..031e84fb
--- /dev/null
+++ b/xargs/testsuite/xargs.gnu/newline.xo
@@ -0,0 +1,5 @@
+   this is
+"quoted 	stuff"  
+and \
+an embedded   newline
+with 'single	quotes' as well.
diff --git a/xargs/xargs.1 b/xargs/xargs.1
index d238cfc0..b6fdd1dc 100644
--- a/xargs/xargs.1
+++ b/xargs/xargs.1
@@ -41,6 +41,7 @@ This will normally have significant performance benefits.
 Some commands can usefully be executed in parallel too; see the
 .B \-P
 option.
+
 .P
 Because Unix filenames can contain blanks and newlines, this default
 behaviour is often problematic; filenames containing blanks
@@ -58,7 +59,12 @@ If that program is GNU
 .B find
 for example, the
 .B \-print0
-option does this for you.
+option does this for you.  (The
+.B \-N
+/
+.B \-\-newline
+option may also be useful if filenames contain spaces but not newlines.)
+
 .P
 If any invocation of the command exits with a status of 255,
 .B xargs
@@ -72,7 +78,7 @@ Input items are terminated by a null character instead of by
 whitespace, and the quotes and backslash are not special (every
 character is taken literally).
 Disables the end-of-file string, which is treated like any other argument.
-Useful when input items might contain white space, quote marks, or backslashes.
+Useful when input items might contain whitespace, quote marks, or backslashes.
 The GNU find (and from Issue 8, POSIX) \-print0 option produces input suitable for this mode.
 
 .TP
@@ -100,10 +106,13 @@ every character in the input is taken literally.
 The
 .B \-d
 option disables any end-of-file string, which is treated like any
-other argument.
-You can use this option when the input consists of
-simply newline-separated items, although it is almost always better to
-design your program to use
+other argument.  You can use this option (or
+.B \-N
+/
+.B \-\-newline
+) when the input consists of simply newline-separated items (such as
+file names from the find \-print option which might contain spaces or tabs),
+although it is almost always better to design your program to use
 .B \-\-null
 where this is possible.
 
@@ -184,7 +193,20 @@ The
 option is deprecated since the POSIX standard specifies
 .B \-L
 instead.
+
 .TP
+.B \-N, \-\-newline
+Input items are terminated by newlines, not by whitespace.  Spaces, tabs,
+quotes and backslash are not special (every character is taken literally).
+Disables the end-of-file string, which is treated like any other argument.
+Useful when the input consists of simply newline-separated items (such as
+file names from the find \-print option which might contain spaces or tabs),
+although it is almost always better to design your program to use
+.B \-\-null
+where this is possible.  (This option is an alias for
+.B \-d'\\n'
+.)
+
 .BI \-n " max-args\fR, \fI" "\-\-max\-args" \fR=\fImax-args
 Use at most \fImax-args\fR arguments per command line.
 Fewer than
@@ -378,7 +400,7 @@ because it would not actually conflict.
 .
 .SH "EXAMPLES"
 .nf
-.B find /tmp \-name core \-type f \-print | xargs /bin/rm \-f
+.B find /tmp \-name core \-type f \-print | xargs \-N /bin/rm \-f
 
 .fi
 Find files named
@@ -387,7 +409,7 @@ in or below the directory
 .B /tmp
 and delete them.
 Note that this will work incorrectly if there are
-any filenames containing newlines or spaces.
+any file or directory names containing newlines.
 .P
 .B find /tmp \-name core \-type f \-print0 | xargs \-0 /bin/rm \-f
 
diff --git a/xargs/xargs.c b/xargs/xargs.c
index 517f963c..51a2eefd 100644
--- a/xargs/xargs.c
+++ b/xargs/xargs.c
@@ -154,7 +154,7 @@ static bool print_command = false; /* Option -t */
 static bool query_before_executing = false;
 
 /* The delimiter for input arguments.   This is only consulted if the
- * -0 or -d option had been given.
+ * -0, -d or -N option had been given.
  */
 static char input_delimiter = '\0';
 
@@ -179,6 +179,7 @@ static struct option const longopts[] =
   {"eof", optional_argument, NULL, 'e'},
   {"replace", optional_argument, NULL, 'I'},
   {"max-lines", optional_argument, NULL, 'l'},
+  {"newline", no_argument, NULL, 'N'},
   {"max-args", required_argument, NULL, 'n'},
   {"open-tty", no_argument, NULL, 'o'},
   {"interactive", no_argument, NULL, 'p'},
@@ -518,7 +519,7 @@ main (int argc, char **argv)
       bc_use_sensible_arg_max (&bc_ctl);
     }
 
-  while ((optc = getopt_long (argc, argv, "+0a:E:e::i::I:l::L:n:oprs:txP:d:",
+  while ((optc = getopt_long (argc, argv, "+0a:E:e::i::I:l::L:Nn:oprs:txP:d:",
                               longopts, &option_index)) != -1)
     {
       switch (optc)
@@ -596,6 +597,11 @@ main (int argc, char **argv)
             }
           break;
 
+        case 'N':
+          read_args = read_string;
+          input_delimiter = '\n';
+          break;
+
         case 'n':
           bc_ctl.args_per_exec = parse_num (optarg, 'n', 1L, -1L, 1);
           /* -n excludes -i -l.  */
@@ -714,7 +720,7 @@ main (int argc, char **argv)
   if (eof_str && (read_args == read_string))
     {
       error (0, 0,
-             _("warning: the -E option has no effect if -0 or -d is used.\n"));
+             _("warning: the -E option has no effect if -0, -d or -N is used.\n"));
     }
 
   /* If we had deferred failing due to problems in bc_init_controlinfo (),
@@ -1758,6 +1764,9 @@ usage (int status)
          "                                 command line\n"));
   HTL (_("  -l[MAX-LINES]                similar to -L but defaults to at most one non-\n"
          "                                 blank input line if MAX-LINES is not specified\n"));
+  HTL (_("  -N, --newline                items in input stream are separated by newlines,\n"
+         "                                 not by whitespace; disables quote, backslash\n"
+         "                                 and logical EOF processing (alias for -d'\\n')\n"));
   HTL (_("  -n, --max-args=MAX-ARGS      use at most MAX-ARGS arguments per command line\n"));
   HTL (_("  -o, --open-tty               Reopen stdin as /dev/tty in the child process\n"
          "                                 before executing the command; useful to run an\n"
-- 
2.34.1

