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
[email protected]
http://lists.gnu.org/mailman/listinfo/bug-coreutils