From 29fa59700f07ce36dda5a5d1be8af47a7f259389 Mon Sep 17 00:00:00 2001
From: Bernhard Voelker <mail@bernhard-voelker.de>
Date: Sun, 4 Jun 2017 20:56:14 +0200
Subject: [PATCH] xargs: add -o, --open-tty option

This option is available in the xargs implementation of FreeBSD, NetBSD,
OpenBSD and in the Apple variant.  Add it for compatibility.

* xargs/xargs.c (open_tty): Add static flag for the new option.
(longopts): Add member.
(main): Handle the 'o' case in the getopt_long() loop.
(prep_child_for_exec): Redirect stdin of the child to /dev/tty when
the -o option is given.  Furthermore, move the just-opened file
descriptor to STDIN_FILENO.
(usage): Document the new option.
* bootstrap.conf (gnulib_modules): Add dup2.
* xargs/xargs.1 (SYNOPSIS): Replace the too-long list of options by
"[options]" - they are listed later anyway.
(OPTIONS): Document the new option.
(STANDARDS CONFORMANCE): Mention that the -o option is an extension.
* doc/find.texi (xargs options): Document the new option.
(Error Messages From xargs): Add the message when dup2() fails.
(NEWS): Mention the new option.

Fixes http://savannah.gnu.org/bugs/?51151
---
 NEWS           |  4 ++++
 bootstrap.conf |  1 +
 doc/find.texi  | 16 ++++++++++++++++
 xargs/xargs.1  | 41 +++++++++++++----------------------------
 xargs/xargs.c  | 33 ++++++++++++++++++++++++++-------
 5 files changed, 60 insertions(+), 35 deletions(-)

diff --git a/NEWS b/NEWS
index 3dd230d..91c0b95 100644
--- a/NEWS
+++ b/NEWS
@@ -35,6 +35,10 @@ of the - yet more portable - '( -type l -o -type d )'.
 find now diagnoses failures returned by readdir().  This bug was inherent
 in the use of FTS.
 
+xargs now supports the -o, --open-tty option to reopen stdin as /dev/tty
+in the child process before executing the command; useful to run an
+interactive application.  Added for compatibility with BSD.
+
 ** Documentation Changes
 
 Some minor documentation improvements are listed in "Bug Fixes" below.
diff --git a/bootstrap.conf b/bootstrap.conf
index bf44f80..07831aa 100644
--- a/bootstrap.conf
+++ b/bootstrap.conf
@@ -87,6 +87,7 @@ gnulib_modules="
     d-type
     dirent-safer
     dirname
+    dup2
     errno
     error
     faccessat
diff --git a/doc/find.texi b/doc/find.texi
index 24d8ef9..f13f0a2 100644
--- a/doc/find.texi
+++ b/doc/find.texi
@@ -3746,6 +3746,17 @@ Use at most @var{max-args} arguments per command line.  Fewer than
 option) is exceeded, unless the @samp{-x} option is given, in which
 case @code{xargs} will exit.
 
+@item --open-tty
+@itemx -o
+Reopen stdin as @file{/dev/tty} in the child process before executing
+the command, thus allowing that command to be associated to the terminal
+while @code{xargs} reads from a different stream, e.g. from a pipe.
+This is useful if you want @code{xargs} to run an interactive application.
+@example
+grep -lz PATTERN * | xargs -0o vi
+@end example
+
+
 @item --interactive
 @itemx -p
 Prompt the user about whether to run each command line and read a line
@@ -5564,6 +5575,11 @@ signals to increase or decrease the parallelism of its processing.
 If you don't plan to send it those signals, this warning can be ignored
 (though if you're a programmer, you may want to help us figure out
 why @code{xargs} is confused by your operating system).
+
+@item failed to redirect standard input of the child process
+@code{xargs} redirects the stdin stream of the command to be run to either
+@file{/dev/null} or to @file{/dev/tty} for the @samp{-o} option.
+See the manual of the system call @code{dup2(2)}.
 @end table
 
 @node Error Messages From locate
diff --git a/xargs/xargs.1 b/xargs/xargs.1
index 6837786..f66dbc0 100644
--- a/xargs/xargs.1
+++ b/xargs/xargs.1
@@ -4,34 +4,7 @@ xargs \- build and execute command lines from standard input
 .SH SYNOPSIS
 .B xargs
 .nh
-[\fB\-0prtx\fR]
-[\fB\-E \fIeof-str\fR]
-[\fB\-e\fR[\fIeof-str\fR]]
-[\fB\-\-eof\fR[=\fIeof-str\fR]]
-[\fB\-\-null\fR]
-[\fB\-d \fIdelimiter\fR]
-[\fB\-\-delimiter \fIdelimiter\fR]
-[\fB\-I \fIreplace-str\fR]
-[\fB\-i\fR[\fIreplace-str\fR]]
-[\fB\-\-replace\fR[=\fIreplace-str\fR]]
-[\fB\-l\fR[\fImax-lines\fR]]
-[\fB\-L \fImax-lines\fR]
-[\fB\-\-max\-lines\fR[=\fImax-lines\fR]]
-[\fB\-n \fImax-args\fR]
-[\fB\-\-max\-args\fR=\fImax-args\fR]
-[\fB\-s \fImax-chars\fR]
-[\fB\-\-max\-chars\fR=\fImax-chars\fR]
-[\fB\-P \fImax-procs\fR]
-[\fB\-\-max\-procs\fR=\fImax-procs\fR]
-[\fB\-\-process\-slot\-var\fR=\fIname\fR]
-[\fB\-\-interactive\fR]
-[\fB\-\-verbose\fR]
-[\fB\-\-exit\fR]
-[\fB\-\-no\-run\-if\-empty\fR]
-[\fB\-\-arg\-file\fR=\fIfile\fR]
-[\fB\-\-show\-limits\fR]
-[\fB\-\-version\fR]
-[\fB\-\-help\fR]
+[\fIoptions\fR]
 [\fIcommand\fR [\fIinitial-arguments\fR]]
 .hy
 .SH DESCRIPTION
@@ -252,6 +225,15 @@ arrange for each process to produce a separate output file (or
 otherwise use separate resources).
 .TP
 .PD
+.B \-o, \-\-open\-tty
+Reopen stdin as
+.I /dev/tty
+in the child process before executing the command.  This is useful if
+you want
+.B xargs
+to run an interactive application.
+.TP
+.PD
 .B \-p, \-\-interactive
 Prompt the user about whether to run each command line and read a line
 from the terminal.  Only run the command line if the response starts
@@ -404,6 +386,9 @@ The \-l and \-i options appear in the 1997 version of the POSIX
 standard, but do not appear in the 2004 version of the standard.
 Therefore you should use \-L and \-I instead, respectively.
 .P
+The \-o option is an extension to the POSIX standard for better
+compatibility with BSD.
+.P
 The POSIX standard allows implementations to have a limit on the size
 of arguments to the
 .B exec
diff --git a/xargs/xargs.c b/xargs/xargs.c
index 5cf8c13..a2917e4 100644
--- a/xargs/xargs.c
+++ b/xargs/xargs.c
@@ -151,6 +151,9 @@ static volatile int child_error = EXIT_SUCCESS;
 
 static volatile int original_exit_value;
 
+/* If true, open /dev/tty in the child process before executing the command.  */
+static bool open_tty = false; /* option -o */
+
 /* If true, print each command on stderr before executing it.  */
 static bool print_command = false; /* Option -t */
 
@@ -185,6 +188,7 @@ static struct option const longopts[] =
   {"replace", optional_argument, NULL, 'I'},
   {"max-lines", optional_argument, NULL, 'l'},
   {"max-args", required_argument, NULL, 'n'},
+  {"open-tty", no_argument, NULL, 'o'},
   {"interactive", no_argument, NULL, 'p'},
   {"no-run-if-empty", no_argument, NULL, 'r'},
   {"max-chars", required_argument, NULL, 's'},
@@ -509,7 +513,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:prs:txP:d:",
+  while ((optc = getopt_long (argc, argv, "+0a:E:e::i::I:l::L:n:oprs:txP:d:",
 			      longopts, &option_index)) != -1)
     {
       switch (optc)
@@ -608,6 +612,10 @@ main (int argc, char **argv)
 	  bc_ctl.exit_if_size_exceeded = true;
 	  break;
 
+	case 'o':
+	  open_tty = true;
+	  break;
+
 	case 'p':
 	  query_before_executing = true;
 	  print_command = true;
@@ -1185,22 +1193,30 @@ prep_child_for_exec (void)
   unsigned int slot = add_proc (0);
   set_slot_var (slot);
 
-  if (!keep_stdin)
+  if (!keep_stdin || open_tty)
     {
-      const char inputfile[] = "/dev/null";
-      /* fprintf (stderr, "attaching stdin to /dev/null\n"); */
+      int fd;
+      const char *inputfile = open_tty ? "/dev/tty" : "/dev/null";
 
       close (0);
-      if (open (inputfile, O_RDONLY) < 0)
+      if ((fd = open (inputfile, O_RDONLY)) < 0)
 	{
-	  /* This is not entirely fatal, since
+	  /* Treat a failure to open /dev/tty as fatal.
+	   * The other case is not entirely fatal, since
 	   * executing the child with a closed
 	   * stdin is almost as good as executing it
 	   * with its stdin attached to /dev/null.
 	   */
-	  error (0, errno, "%s",
+	  error (open_tty ? EXIT_FAILURE : 0, errno, "%s",
 		 quotearg_n_style (0, locale_quoting_style, inputfile));
 	}
+      if (STDIN_FILENO < fd)
+	{
+	  if (dup2(fd, STDIN_FILENO) != 0)
+	    error (EXIT_FAILURE, errno,
+	           _("failed to redirect standard input of the child process"));
+	  close(fd);
+	}
     }
 }
 
@@ -1676,6 +1692,9 @@ usage (int status)
   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, --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"
+         "                                 interactive application.\n"));
   HTL (_("  -P, --max-procs=MAX-PROCS    run at most MAX-PROCS processes at a time\n"));
   HTL (_("  -p, --interactive            prompt before running commands\n"));
   HTL (_("      --process-slot-var=VAR   set environment variable VAR in child processes\n"));
-- 
2.1.4

