Hi,

This program uses the temporary fifo management system that I built
for zargs to provide generic process substitution for arguments to a
sub-command.

This program has some advantages over the process substitution built
into some shells (bash, zsh, ksh, ???):

1. It doesn't rely on having a shell that supports built-in process
substitution.
2. By using descriptively named temporary fifos it allows programs
that include filenames in output or diagnostic messages to provide
more useful information than with '/dev/fd/*' inputs.
3. It supports `--files0-from=F' style argument passing, as well.

Examples:

Where in bash you might do:

$ uniq bigfile | tee >(wc -l > bigfile-uniq.count) | gzip -c > bigfile-uniq.gz

With psub you would do:

$ uniq bigfile | psub tee ">wc -l > bigfile-uniq.count" | gzip -c >
bigfile-uniq.gz


And where in bash you might do:

$ wc -l <(ls a) <(ls b)
      2 /dev/fd/63
      1 /dev/fd/62
      3 total

With psub you could do:

$ psub wc -l "<ls a" "<ls b"
      2 /tmp/psub25j3Al/ls a
      1 /tmp/psub25j3Al/ls b
      3 total

Or even:

$ find * -maxdepth 0 -type d | sed "s/^/<ls /" | tr '\n' '\0' | psub
wc -l --files0-from=-
      2 /tmp/psubSZT8xC/ls a
      5 /tmp/psubSZT8xC/ls autom4te.cache
      1 /tmp/psubSZT8xC/ls b
     20 /tmp/psubSZT8xC/ls build-aux
     13 /tmp/psubSZT8xC/ls doc
      3 /tmp/psubSZT8xC/ls gl
     19 /tmp/psubSZT8xC/ls gnulib
    269 /tmp/psubSZT8xC/ls gnulib-tests
    648 /tmp/psubSZT8xC/ls lib
    299 /tmp/psubSZT8xC/ls m4
    203 /tmp/psubSZT8xC/ls man
      3 /tmp/psubSZT8xC/ls old
    121 /tmp/psubSZT8xC/ls po
    345 /tmp/psubSZT8xC/ls src
     49 /tmp/psubSZT8xC/ls tests
   2000 total

The attached version doesn't include any `doc/' or `tests/' yet, but
should be functional enough to give a sense of usage.  If this looks
like something that might be a useful addition to coreutils I'll be
happy to write some documentation and tests and resubmit.

Also available for fetch at:

$ git fetch git://repo.or.cz/coreutils/bo.git psub:psub

Thanks,

Bo
From d61aff113e88ed72efc6d566b45fc487ab91481c Mon Sep 17 00:00:00 2001
From: Bo Borgerson <[EMAIL PROTECTED]>
Date: Sun, 27 Apr 2008 20:11:39 -0400
Subject: [PATCH] Add new program: psub

* src/psub.c: New program to manage temporary fifos for pipelines.
* man/psub.x: Manfile template for new program.
* src/Makefile.am: List new program.

Signed-off-by: Bo Borgerson <[EMAIL PROTECTED]>
---
 man/psub.x      |    4 +
 src/Makefile.am |    2 +-
 src/psub.c      |  864 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 869 insertions(+), 1 deletions(-)
 create mode 100644 man/psub.x
 create mode 100644 src/psub.c

diff --git a/man/psub.x b/man/psub.x
new file mode 100644
index 0000000..aaa7313
--- /dev/null
+++ b/man/psub.x
@@ -0,0 +1,4 @@
+[NAME]
+psub \- manage pipelines with temporary fifos
+[DESCRIPTION]
+.\" Add any additional description here
diff --git a/src/Makefile.am b/src/Makefile.am
index 668e178..225194b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -37,7 +37,7 @@ EXTRA_PROGRAMS = \
   nl od paste pr ptx sha1sum sha224sum sha256sum sha384sum sha512sum \
   shuf sort split sum tac tail tr tsort unexpand uniq wc \
   basename date dirname echo env expr factor false \
-  id kill logname pathchk printenv printf pwd \
+  id kill logname pathchk printenv printf psub pwd \
   runcon seq sleep tee \
   test true tty whoami yes \
   base64
diff --git a/src/psub.c b/src/psub.c
new file mode 100644
index 0000000..1834d0c
--- /dev/null
+++ b/src/psub.c
@@ -0,0 +1,864 @@
+/* psub -- manage pipelines with temporary fifos
+   Copyright (C) 2008 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+
+/* psub - manage pipelines with temporary fifos
+
+   Written by Bo Borgerson.  */
+
+#include <config.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include "system.h"
+#include "error.h"
+#include "long-options.h"
+#include "readtokens0.h"
+#include "quote.h"
+#include "xstrtol.h"
+
+#define PROGRAM_NAME "psub"
+
+#define AUTHORS "Bo Borgerson"
+
+#ifndef DEFAULT_TMPDIR
+# define DEFAULT_TMPDIR "/tmp"
+#endif
+
+/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
+   present.  */
+#ifndef SA_NOCLDSTOP
+# define SA_NOCLDSTOP 0
+/* No sigprocmask.  Always 'return' zero. */
+# define sigprocmask(How, Set, Oset) (0)
+# define sigset_t int
+# if ! HAVE_SIGINTERRUPT
+#  define siginterrupt(sig, flag) /* empty */
+# endif
+#endif
+
+#define FILES_FROM_OPT "--files0-from="
+#define FILES_FROM_OPT_LEN strlen (FILES_FROM_OPT);
+
+/* By default we won't put more than this many fifos in a single directory.
+   Can be overridden on the command-line with the --max-directory-entries
+   (-M) option. */
+#define DEFAULT_MAX_DIRENTS 128
+
+static char *program_name;
+
+/* ARGs prefixed with this string will be left alone
+   (the string is stripped). */
+static char *skip_opt = "--psub-skip=";
+
+static struct option const long_options[] = {
+  {"max-directory-entries", required_argument, NULL, 'M'},
+  {"temporary-directory", required_argument, NULL, 'T'},
+  {GETOPT_HELP_OPTION_DECL},
+  {GETOPT_VERSION_OPTION_DECL},
+  {NULL, 0, NULL, 0}
+};
+
+static void
+usage (int status)
+{
+  if (status != EXIT_SUCCESS)
+    fprintf (stderr, _("Try `%s --help' for more information.\n"),
+             program_name);
+  else
+    {
+      printf (_("\
+Usage: %s [OPTION]... COMMAND ARG [ARG]...\n\
+   Or: %s [OPTION]... COMMAND [OPTION]... --files0-from=F\n\
+"), program_name, program_name);
+
+      printf (_("\
+Examine the ARGs to COMMAND.  For any ARG that begins with %s or %s\n\
+create a temporary fifo and set up the rest of ARG as a sub-pipeline\n\
+run with `sh', to either write to or read from the fifo as appropriate.\n\
+"), "<", ">");
+      fputs (_("\
+Clean up all the temporary fifos after COMMAND finishes.\n\
+\n\
+Options (must come before COMMAND):\n\
+"), stdout);
+      printf (_("\
+  -T, --temporary-directory=DIR    put temporary directories in DIR,\n\
+                                   not $TMPDIR or %s;\n\
+  -M, --max-directory-entries=MAX  put at most MAX fifos in any directory;\n\
+                                   create a new directory after MAX\n\
+"), DEFAULT_TMPDIR);
+      fputs (HELP_OPTION_DESCRIPTION, stdout);
+      fputs (VERSION_OPTION_DESCRIPTION, stdout);
+      fputs (_("\
+\n\
+Interpreted options to COMMAND:\n\
+\n\
+"), stdout);
+      fputs (_("\
+      --psub-skip=ARG   pass this ARG through unaffected\n\
+      --files0-from=F   read additional ARGs from F and pipe the modified\n\
+                        set to COMMAND through a fifo; F is replaced with\n\
+                        the name of the fifo.\n\
+"), stdout);
+      emit_bug_reporting_address ();
+    }
+  exit (status);
+}
+
+/* The set of signals we need to look out for. */
+static int const all_sig[] =
+{
+  /* The usual suspects. */
+  SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
+#ifdef SIGPOLL
+  SIGPOLL,
+#endif
+#ifdef SIGPROF
+  SIGPROF,
+#endif
+#ifdef SIGVTALRM
+  SIGVTALRM,
+#endif
+#ifdef SIGXCPU
+  SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+  SIGXFSZ,
+#endif
+};
+
+enum { nsigs = sizeof all_sig / sizeof all_sig[0] };
+
+/* The total number of forked processes that have not been reaped yet. */
+static size_t nprocs;
+
+/* The PID of our main child (the one who will be reading from our fifos). */
+static int command_pid;
+
+/* The exit status of our main child. */
+static int command_status;
+
+/* A list of the names of our fifos (so we can unlink them later). */
+static char **fifos;
+
+/* How many fifos are in our list. */
+static size_t fifo_count;
+
+/* How many fifos we've got space allocated for currently. */
+static size_t fifo_alloc;
+
+/* A list of temporary directories containing our fifos. */
+static char **fifo_dirs;
+
+/* How many temporary directories we have in our list. */
+static size_t fifo_dir_count = 0;
+
+/* How many directories we have space allocated for currently. */
+static size_t fifo_dir_alloc = 0;
+
+/* Directory where we create our temporary directories. */
+static char *fifo_dir_base;
+
+/* How many fifos we've put into the last temporary directory we created. */
+static size_t cur_fifo_dir_fifos = 0;
+
+/* How many fifos we'll put into a directory before creating a new one.
+   Can be set on the command-line with --max-directory-entries (-M). */
+static size_t max_fifo_dir_fifos = DEFAULT_MAX_DIRENTS;
+
+/* If our COMMAND uses --files0-from=F, then we need to take ARGs from that
+   file, and transform the --files0-from=F option to point to a fifo we'll
+   write to. */
+static char *files_from = NULL;
+
+/* The length first of the prefix "--files0-from=", then of our full
+   replacement */
+static size_t files_from_opt_len;
+
+/* The string containing first the prefix "--files0-from", then our
+   full replacement. */
+static char *files_from_opt;
+
+/* The index in the ARG list of the --files0-from option. */
+static int files_from_opt_i = 0;
+
+/* The set of signals that are caught.  */
+static sigset_t caught_signals;
+
+/* Critical section status.  */
+struct cs_status
+{
+  bool valid;
+  sigset_t sigs;
+};
+
+/* Enter a critical section.  */
+static struct cs_status
+cs_enter (void)
+{
+  struct cs_status status;
+  status.valid = (sigprocmask (SIG_BLOCK,
+                  &caught_signals, &status.sigs) == 0);
+  return status;
+}
+
+/* Leave a critical section.  */
+static void
+cs_leave (struct cs_status status)
+{
+  if (status.valid)
+    {
+      /* Ignore failure when restoring the signal mask. */
+      sigprocmask (SIG_SETMASK, &status.sigs, NULL);
+    }
+}
+
+/* Clean up after ourselves.  First unlink fifos, then rmdir directories.
+   Free memory, even though we currently only clean up at exit. */
+
+static void
+cleanup (void)
+{
+  int i;
+
+  for (i = 0; i < fifo_count; i++)
+    {
+      unlink (fifos[i]);
+      free (fifos[i]);
+    }
+  if (fifos)
+    {
+      free (fifos);
+      fifos = NULL;
+    }
+  for (i = 0; i < fifo_dir_count; i++)
+    {
+      if(rmdir (fifo_dirs[i]) == -1)
+        error (0, errno, _("rmdir failed: %s"), fifo_dirs[i]);
+      free (fifo_dirs[i]);
+      fifo_dirs[i] = NULL;
+    }
+  if (fifo_dirs)
+    {
+      free (fifo_dirs);
+      fifo_dirs = NULL;
+    }
+  if (fifo_dir_base)
+    {
+      free (fifo_dir_base);
+      fifo_dir_base = NULL;
+    }
+}
+
+/* Cleanup actions to take when exiting.  */
+
+static void
+exit_cleanup (void)
+{
+  /* Clean up in a critical section so that a signal handler does not
+     try to clean up too.  */
+  struct cs_status cs = cs_enter ();
+  cleanup ();
+  cs_leave (cs);
+
+  close_stdout ();
+}
+
+/* Handle interrupts and hangups. */
+
+
+static void
+sighandler (int sig)
+{
+  size_t i;
+  static int n_sig = 0;
+  if (! SA_NOCLDSTOP)
+    signal (sig, SIG_IGN);
+
+  cleanup ();
+
+  for (i = 0; i < (1<<30); i++);
+
+  signal (sig, SIG_DFL);
+  raise (sig);
+}
+
+/* Wrapper around dup2 for convenience. */
+
+static void
+dup2_or_die (int oldfd, int newfd)
+{
+  if (dup2 (oldfd, newfd) < 0)
+    error (EXIT_FAILURE, errno, _("dup2 failed"));
+}
+
+/* If 0 < PID, wait for the child process with that PID to exit.
+   If PID is -1, clean up a random child process which has finished.
+   If PID is -1 and no processes have quit yet, return 0 without waiting. */
+
+static pid_t
+reap (pid_t pid)
+{
+  int status;
+  pid_t cpid = waitpid (pid, &status, pid < 0 ? WNOHANG : 0);
+
+  if (cpid < 0)
+    error (EXIT_FAILURE, errno, _("waitpid"));
+
+  else if (0 < cpid)
+    {
+      if (! WIFEXITED (status) || WEXITSTATUS (status))
+        error (0, 0, _("%s terminated abnormally"),
+               (cpid == command_pid)?"command":"child");
+      if (cpid == command_pid)
+          command_status = status;
+      else
+        --nprocs;
+    }
+
+  return cpid;
+}
+
+/* Keep reaping finished children as long as there are more to reap.
+   This doesn't block waiting for any of them, it only reaps those
+   that are already dead.  This is the sigaction handler for CHLD. */
+
+static void
+reap_some (int sig)
+{
+  pid_t pid;
+
+  while (0 < nprocs && (pid = reap (-1)));
+
+}
+
+/* Fork a child process.  Don't let the child know where the fifos
+   and temporary directories are.  We want to clean them up ourselves.
+   Return the PID of the child or -1 if fork failed.  */
+
+static pid_t
+safe_fork (void)
+{
+#if HAVE_WORKING_FORK
+  char **saved_fifos;
+  int saved_errno;
+  int i;
+  unsigned int wait_retry = 1;
+  pid_t pid IF_LINT (= -1);
+  struct cs_status cs;
+
+
+  /* This is so the child process won't delete our temp files
+     if it receives a signal before exec-ing.  */
+  cs = cs_enter ();
+  saved_fifos = fifos;
+  fifos = NULL;
+
+  pid = fork ();
+  saved_errno = errno;
+  if (pid)
+    fifos = saved_fifos;
+
+  if (0 == pid)
+    for (i = 0; i < nsigs; i++)
+      signal (all_sig[i], SIG_DFL);
+
+  cs_leave (cs);
+  errno = saved_errno;
+
+  if (0 < pid)
+    ++nprocs;
+
+  return pid;
+
+#else  /* ! HAVE_WORKING_FORK */
+  return -1;
+#endif
+}
+
+/* Create a temporary directory into which we'll put fifos. */
+
+static void
+create_fifo_dir (void)
+{
+  static char const slashbase[] = "/psubXXXXXX";
+  struct cs_status cs;
+  int saved_errno;
+  size_t len = strlen (fifo_dir_base);
+  char *success;
+  char *fifo_dir;
+
+  if (fifo_dir_count == fifo_dir_alloc)
+    fifo_dirs = X2NREALLOC (fifo_dirs, &fifo_dir_alloc);
+
+  /* One extra for the trailing slash */
+  fifo_dirs[fifo_dir_count] = xmalloc (len + sizeof slashbase + 1);
+
+  fifo_dir = fifo_dirs[fifo_dir_count++];
+
+  memcpy (fifo_dir, fifo_dir_base, len);
+  memcpy (fifo_dir + len, slashbase, sizeof slashbase);
+
+
+  /* Create the directory in a critical section, to avoid races. */
+  cs = cs_enter ();
+  success = mkdtemp (fifo_dir);
+  saved_errno = errno;
+  cs_leave (cs);
+  errno = saved_errno;
+
+  if (!success)
+    {
+      /* Note that the last FIFO_DIR doesn't get freed, but we're exiting
+         anyway. */
+      fifo_dir_count--;
+      error (EXIT_FAILURE, errno,
+             _("cannot create temporary directory: %s"), fifo_dir);
+    }
+
+  /* Append a slash so we don't have to worry about it later. */
+  fifo_dir[len + sizeof slashbase - 1] = '/';
+  fifo_dir[len + sizeof slashbase] = '\0';
+
+  cur_fifo_dir_fifos = 0;
+}
+
+/* Use DIR as our base temp dir. */
+static void
+set_temp_dir (char const *dir)
+{
+  size_t len;
+  if (fifo_dir_base)
+    {
+      if (!STREQ (fifo_dir_base, dir))
+        error (EXIT_FAILURE, 0, _("multiple temp dirs specified"));
+    }
+  else
+    {
+      len = strlen (dir);
+      fifo_dir_base = xmalloc (len + 1);
+      memcpy (fifo_dir_base, dir, len);
+    }
+}
+
+/* Create a new fifo with a name resembling the file or command it's
+   associated with. */
+static char *
+new_fifo (char const *file_name)
+{
+  char *fifo_tag = strrchr (file_name, '/');
+  size_t len1, len2, len3 = 0;
+  size_t len3_alloc = INT_BUFSIZE_BOUND (unsigned int) + 1;
+  char i_str [INT_BUFSIZE_BOUND (unsigned int) + 1];
+  unsigned int i = 0;
+  int still_trying = 1;
+  char *fifo;
+  char *fifo_dir;
+
+  if (fifo_tag)
+    fifo_tag++;
+  else
+    fifo_tag = (char *)file_name;
+
+  if (fifo_count == fifo_alloc)
+    fifos = X2NREALLOC (fifos, &fifo_alloc);
+
+  if (!fifo_dir_count || cur_fifo_dir_fifos == max_fifo_dir_fifos)
+    create_fifo_dir ();
+
+  cur_fifo_dir_fifos++;
+
+  fifo_dir = fifo_dirs[fifo_dir_count - 1];
+
+  len1 = strlen (fifo_dir);
+  len2 = strlen (fifo_tag);
+
+  fifos[fifo_count] = xmalloc (len1 + len2 + len3_alloc);
+
+  fifo = fifos[fifo_count++];
+
+  memcpy (fifo, fifo_dir, len1);
+  memcpy (fifo + len1, fifo_tag, len2 + 1);
+
+  /* There may be files with the same name in different directories, so
+     we need to handle collisions.  This gets inefficient as we get more
+     identical filenames. */
+  while (still_trying && (i < UINT_MAX))
+    {
+
+      if (i)
+        {
+          sprintf (i_str, "-%d", i);
+          len3 = strlen (i_str);
+          memcpy (fifo + len1 + len2, i_str, len3 + 1);
+        }
+
+      if ((still_trying = mkfifo (fifo, 0600)) != 0)
+        {
+          if (errno != EEXIST)
+            error (EXIT_FAILURE, errno, _("cannot create fifo %s"),
+                   quote (fifo));
+        }
+
+      i++;
+    }
+
+  return fifo;
+}
+
+/* If this ARG looks like an option, return it.  Otherwise, return NULL.
+   Also, if it looks like a --files0-from option, set up for files_from
+   processing. */
+static char *
+handle_option (int i, char *p)
+{
+
+  static size_t skip_opt_len = -1;
+
+  if (skip_opt_len == -1)
+    skip_opt_len = strlen (skip_opt);
+
+  if (!strncmp (p, files_from_opt, files_from_opt_len))
+    {
+      files_from = p + files_from_opt_len;
+      files_from_opt_i = i;
+      return p;
+    }
+  else if (!strncmp (p, skip_opt, skip_opt_len))
+    return p + skip_opt_len;
+  else if (*p == '-')
+    return p;
+
+  return NULL;
+}
+
+/* Treat this ARG as a sub-command to be executed.  Hook its output up to
+   a fifo, and return the name of the fifo. */
+static char *
+handle_exec (int i, char *p)
+{
+
+  if (p[0] == '<' || p[0] == '>')
+    {
+      int pipefd;
+      char control_char = *p++;
+      char *fifo = new_fifo (p);
+
+      pid_t helper_pid = safe_fork ();
+
+      if (helper_pid > 0)
+	{
+
+	  return fifo;
+
+	}
+      else if (helper_pid == 0)
+	{
+
+	  close (STDIN_FILENO);
+	  close (STDOUT_FILENO);
+
+	  if (control_char == '>')
+	    {
+	      /* This will block. */
+	      if ((pipefd = open (fifo, O_RDONLY)) == -1)
+		error (EXIT_FAILURE, errno, _("open failed: %s"), fifo);
+
+	      dup2_or_die (pipefd, STDIN_FILENO);
+	    }
+	  else
+	    {
+	      /* This will block. */
+	      if ((pipefd = open (fifo, O_WRONLY)) == -1)
+		error (EXIT_FAILURE, errno, _("open failed: %s"), fifo);
+
+	      dup2_or_die (pipefd, STDOUT_FILENO);
+	    }
+
+	  execlp ("sh", "sh", "-c", p, NULL);
+
+	  error (EXIT_FAILURE, errno, _("execlp failed: sh -c %s"), quote (p));
+
+	}
+
+      error (EXIT_FAILURE, errno, _("fork failed"));
+    }
+
+}
+
+
+
+/* Cascade through ARG handlers in order of precedence.  Return the new
+   value to go in ARG's place. */
+static char *
+handle_arg (int i, char *p)
+{
+  char *retval;
+
+  if ((retval = handle_option (i, p)) != NULL)
+    return retval;
+
+  if ((retval = handle_exec (i, p)) != NULL)
+    return retval;
+
+  return p;
+}
+
+/* Specify the maximum number of fifos we'll create in a single directory
+   before creating a new temporary directory. */
+static void
+specify_max_dirent (int oi, char c, char const *s)
+{
+  uintmax_t n;
+  enum strtol_error e = xstrtoumax (s, NULL, 10, &n, "");
+
+  if (e == LONGINT_OK)
+    {
+      max_fifo_dir_fifos = n;
+      if (n == max_fifo_dir_fifos)
+        {
+          if (0 < max_fifo_dir_fifos)
+            return;
+          e = LONGINT_INVALID;
+        }
+      else
+        e = LONGINT_OVERFLOW;
+    }
+
+  xstrtol_fatal (e, oi, c, long_options, s);
+}
+
+
+int
+main (int argc, char **argv)
+{
+  int c = 0, i;
+  int oi = -1;
+  char **new_argv;
+  struct fstatus *fstatus;
+  struct Tokens tok;
+  char **files;
+  char *fifo;
+  int nfiles = 0;
+
+  files_from_opt_len = FILES_FROM_OPT_LEN;
+  files_from_opt = xmalloc (files_from_opt_len + 1);
+
+  memcpy (files_from_opt, FILES_FROM_OPT, files_from_opt_len);
+
+  initialize_main (&argc, &argv);
+  program_name = argv[0];
+  setlocale (LC_ALL, "");
+  bindtextdomain (PACKAGE, LOCALEDIR);
+  textdomain (PACKAGE);
+
+  initialize_exit_failure (EXIT_FAILURE);
+  atexit (exit_cleanup);
+
+  /* This signal handler setup block is basically lifted straight out of
+     sort.c, which also manages temporary files that need to be cleaned up
+     before exiting, if possible. */
+  {
+    size_t i;
+
+#if SA_NOCLDSTOP
+    struct sigaction act;
+
+    sigemptyset (&caught_signals);
+    for (i = 0; i < nsigs; i++)
+      {
+        sigaction (all_sig[i], NULL, &act);
+        if (act.sa_handler != SIG_IGN)
+          sigaddset (&caught_signals, all_sig[i]);
+      }
+
+    act.sa_handler = sighandler;
+    act.sa_mask = caught_signals;
+    act.sa_flags = 0;
+
+    for (i = 0; i < nsigs; i++)
+      if (sigismember (&caught_signals, all_sig[i]))
+        sigaction (all_sig[i], &act, NULL);
+#else
+    for (i = 0; i < nsigs; i++)
+      if (signal (all_sig[i], SIG_IGN) != SIG_IGN)
+        {
+          signal (all_sig[i], sighandler);
+          siginterrupt (all_sig[i], 1);
+        }
+#endif
+  }
+
+
+  parse_long_options (argc, argv, PROGRAM_NAME, PACKAGE_NAME, VERSION,
+                      usage, AUTHORS, (char const *) NULL);
+
+  while ((c = getopt_long (argc, argv, "+T:M:o", long_options, &oi)) != -1)
+    switch (c)
+      {
+
+      case 'T':
+        set_temp_dir (optarg);
+        break;
+
+      case 'M':
+        specify_max_dirent (oi, c, optarg);
+        break;
+
+      default:
+        usage (EXIT_FAILURE);
+        break;
+      }
+
+  if (argc - optind < 2)
+    usage (EXIT_FAILURE);
+
+  if (!fifo_dir_base)
+    {
+      char const *tmp_dir = getenv ("TMPDIR");
+      set_temp_dir (tmp_dir ? tmp_dir : DEFAULT_TMPDIR);
+    }
+
+  argc -= optind;
+  argv += optind;
+
+  new_argv = xnmalloc (argc + 1, sizeof *argv);
+  memcpy (new_argv, argv, (argc + 1) * sizeof *argv);
+
+  for (i = 1; i < argc; i++)
+    {
+      char *p = new_argv[i];
+
+      new_argv[i] = handle_arg (i, new_argv[i]);
+    }
+
+    if (files_from)
+      {
+        FILE *stream;
+
+        if (STREQ (files_from, "-"))
+          stream = stdin;
+        else
+          {
+            stream = fopen (files_from, "r");
+            if (stream == NULL)
+              error (EXIT_FAILURE, errno, _("cannot open %s for reading"),
+                     quote (files_from));
+          }
+
+        readtokens0_init (&tok);
+
+        if (! readtokens0 (stream, &tok))
+          error (EXIT_FAILURE, 0, _("cannot read file names from %s"),
+                 quote (files_from));
+
+        files = tok.tok;
+        nfiles = tok.n_tok;
+      }
+
+  if (files_from)
+    {
+      pid_t writer_pid;
+
+      for (i = 0; i < nfiles; i++)
+        if ((fifo = handle_arg (i, files[i])) != NULL)
+          files[i] = fifo;
+
+      fifo = new_fifo (files_from);
+
+      writer_pid = safe_fork ();
+
+      if (0 < writer_pid)
+        {
+          size_t fifo_len = strlen (fifo) + 1;
+          files_from_opt = xrealloc (files_from_opt,
+            files_from_opt_len + fifo_len);
+
+          /* Put the new --files0-from=F option in place of the old one. */
+          memcpy (files_from_opt + files_from_opt_len, fifo, fifo_len);
+          new_argv[files_from_opt_i] = files_from_opt;
+        }
+      else if (writer_pid == 0)
+        {
+          int writefd;
+          size_t written;
+          size_t arglen;
+          /* This will block. */
+          if ((writefd = open (fifo, O_WRONLY)) == -1)
+            error (EXIT_FAILURE, errno, _("open failed: %s"), fifo);
+          for (i = 0; i < nfiles; i++)
+            {
+              arglen = strlen (files[i]) + 1;
+              if ((written = write (writefd, files[i], arglen)) != arglen)
+                error (EXIT_FAILURE, errno, _("write failed: %s"), fifo);
+            }
+          return 0;
+        }
+      else
+        error (EXIT_FAILURE, errno, _("fork failed"));
+    }
+
+
+  command_pid = safe_fork ();
+
+  if (command_pid > 0)
+    {
+      struct sigaction child_handler;
+
+      /* The main child (command) is waited for explicitly below. */
+      if (--nprocs)
+        {
+          sigemptyset (&child_handler.sa_mask);
+
+          child_handler.sa_handler = reap_some;
+
+          /* Restart the blocking wait() after we clean up zombie helpers. */
+          child_handler.sa_flags = SA_RESTART;
+
+          sigaction (SIGCHLD, &child_handler, NULL);
+        }
+
+      /* This blocks.  We're stuck here (except for occasional non-blocking
+         reap sessions for helper children) until COMMAND is done. */
+      reap (command_pid);
+
+      if (WIFEXITED (command_status))
+        command_status = WEXITSTATUS (command_status);
+      else if (WIFSIGNALED (command_status))
+        command_status = WTERMSIG (command_status) + 128;
+
+      return command_status;
+    }
+  else if (command_pid == 0)
+    {
+
+      execvp (new_argv[0], new_argv);
+
+      error (EXIT_FAILURE, errno, _("exec of %s failed"),
+             quote (new_argv[0]));
+    }
+  else
+    error (EXIT_FAILURE, 0, _("fork failed"));
+}
+
+/*
+ * Local variables:
+ *  indent-tabs-mode: nil
+ * End:
+ */
-- 
1.5.4.3

_______________________________________________
Bug-coreutils mailing list
Bug-coreutils@gnu.org
http://lists.gnu.org/mailman/listinfo/bug-coreutils

Reply via email to