Blob Blame History Raw
diff -up patch-2.7.6/lib/execute.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/execute.c
--- patch-2.7.6/lib/execute.c.switch-from-fork-execlp-to-execute	2019-07-29 14:40:53.264464824 +0200
+++ patch-2.7.6/lib/execute.c	2019-07-29 14:40:53.264464824 +0200
@@ -0,0 +1,273 @@
+/* Creation of autonomous subprocesses.
+   Copyright (C) 2001-2004, 2006-2018 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   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 <https://www.gnu.org/licenses/>.  */
+
+
+#include <config.h>
+
+/* Specification.  */
+#include "execute.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "error.h"
+#include "fatal-signal.h"
+#include "wait-process.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+/* Native Windows API.  */
+# include <process.h>
+# include "w32spawn.h"
+
+#else
+
+/* Unix API.  */
+# include <spawn.h>
+
+#endif
+
+
+#if defined EINTR && ((defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__)
+
+/* EINTR handling for close(), open().
+   These functions can return -1/EINTR even though we don't have any
+   signal handlers set up, namely when we get interrupted via SIGSTOP.  */
+
+static int
+nonintr_close (int fd)
+{
+  int retval;
+
+  do
+    retval = close (fd);
+  while (retval < 0 && errno == EINTR);
+
+  return retval;
+}
+#define close nonintr_close
+
+static int
+nonintr_open (const char *pathname, int oflag, mode_t mode)
+{
+  int retval;
+
+  do
+    retval = open (pathname, oflag, mode);
+  while (retval < 0 && errno == EINTR);
+
+  return retval;
+}
+#undef open /* avoid warning on VMS */
+#define open nonintr_open
+
+#endif
+
+
+/* Execute a command, optionally redirecting any of the three standard file
+   descriptors to /dev/null.  Return its exit code.
+   If it didn't terminate correctly, exit if exit_on_error is true, otherwise
+   return 127.
+   If slave_process is true, the child process will be terminated when its
+   creator receives a catchable fatal signal.  */
+int
+execute (const char *progname,
+         const char *prog_path, char **prog_argv,
+         bool ignore_sigpipe,
+         bool null_stdin, bool null_stdout, bool null_stderr,
+         bool slave_process, bool exit_on_error,
+         int *termsigp)
+{
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+  /* Native Windows API.  */
+  int orig_stdin;
+  int orig_stdout;
+  int orig_stderr;
+  int exitcode;
+  int nullinfd;
+  int nulloutfd;
+
+  /* FIXME: Need to free memory allocated by prepare_spawn.  */
+  prog_argv = prepare_spawn (prog_argv);
+
+  /* Save standard file handles of parent process.  */
+  if (null_stdin)
+    orig_stdin = dup_safer_noinherit (STDIN_FILENO);
+  if (null_stdout)
+    orig_stdout = dup_safer_noinherit (STDOUT_FILENO);
+  if (null_stderr)
+    orig_stderr = dup_safer_noinherit (STDERR_FILENO);
+  exitcode = -1;
+
+  /* Create standard file handles of child process.  */
+  nullinfd = -1;
+  nulloutfd = -1;
+  if ((!null_stdin
+       || ((nullinfd = open ("NUL", O_RDONLY, 0)) >= 0
+           && (nullinfd == STDIN_FILENO
+               || (dup2 (nullinfd, STDIN_FILENO) >= 0
+                   && close (nullinfd) >= 0))))
+      && (!(null_stdout || null_stderr)
+          || ((nulloutfd = open ("NUL", O_RDWR, 0)) >= 0
+              && (!null_stdout
+                  || nulloutfd == STDOUT_FILENO
+                  || dup2 (nulloutfd, STDOUT_FILENO) >= 0)
+              && (!null_stderr
+                  || nulloutfd == STDERR_FILENO
+                  || dup2 (nulloutfd, STDERR_FILENO) >= 0)
+              && ((null_stdout && nulloutfd == STDOUT_FILENO)
+                  || (null_stderr && nulloutfd == STDERR_FILENO)
+                  || close (nulloutfd) >= 0))))
+    /* Use spawnvpe and pass the environment explicitly.  This is needed if
+       the program has modified the environment using putenv() or [un]setenv().
+       On Windows, programs have two environments, one in the "environment
+       block" of the process and managed through SetEnvironmentVariable(), and
+       one inside the process, in the location retrieved by the 'environ'
+       macro.  When using spawnvp() without 'e', the child process inherits a
+       copy of the environment block - ignoring the effects of putenv() and
+       [un]setenv().  */
+    {
+      exitcode = spawnvpe (P_WAIT, prog_path, (const char **) prog_argv,
+                           (const char **) environ);
+      if (exitcode < 0 && errno == ENOEXEC)
+        {
+          /* prog is not a native executable.  Try to execute it as a
+             shell script.  Note that prepare_spawn() has already prepended
+             a hidden element "sh.exe" to prog_argv.  */
+          --prog_argv;
+          exitcode = spawnvpe (P_WAIT, prog_argv[0], (const char **) prog_argv,
+                               (const char **) environ);
+        }
+    }
+  if (nulloutfd >= 0)
+    close (nulloutfd);
+  if (nullinfd >= 0)
+    close (nullinfd);
+
+  /* Restore standard file handles of parent process.  */
+  if (null_stderr)
+    undup_safer_noinherit (orig_stderr, STDERR_FILENO);
+  if (null_stdout)
+    undup_safer_noinherit (orig_stdout, STDOUT_FILENO);
+  if (null_stdin)
+    undup_safer_noinherit (orig_stdin, STDIN_FILENO);
+
+  if (termsigp != NULL)
+    *termsigp = 0;
+
+  if (exitcode == -1)
+    {
+      if (exit_on_error || !null_stderr)
+        error (exit_on_error ? EXIT_FAILURE : 0, errno,
+               _("%s subprocess failed"), progname);
+      return 127;
+    }
+
+  return exitcode;
+
+#else
+
+  /* Unix API.  */
+  /* Note about 127: Some errors during posix_spawnp() cause the function
+     posix_spawnp() to return an error code; some other errors cause the
+     subprocess to exit with return code 127.  It is implementation
+     dependent which error is reported which way.  We treat both cases as
+     equivalent.  */
+  sigset_t blocked_signals;
+  posix_spawn_file_actions_t actions;
+  bool actions_allocated;
+  posix_spawnattr_t attrs;
+  bool attrs_allocated;
+  int err;
+  pid_t child;
+
+  if (slave_process)
+    {
+      sigprocmask (SIG_SETMASK, NULL, &blocked_signals);
+      block_fatal_signals ();
+    }
+  actions_allocated = false;
+  attrs_allocated = false;
+  if ((err = posix_spawn_file_actions_init (&actions)) != 0
+      || (actions_allocated = true,
+          (null_stdin
+            && (err = posix_spawn_file_actions_addopen (&actions,
+                                                        STDIN_FILENO,
+                                                        "/dev/null", O_RDONLY,
+                                                        0))
+               != 0)
+          || (null_stdout
+              && (err = posix_spawn_file_actions_addopen (&actions,
+                                                          STDOUT_FILENO,
+                                                          "/dev/null", O_RDWR,
+                                                          0))
+                 != 0)
+          || (null_stderr
+              && (err = posix_spawn_file_actions_addopen (&actions,
+                                                          STDERR_FILENO,
+                                                          "/dev/null", O_RDWR,
+                                                          0))
+                 != 0)
+          || (slave_process
+              && ((err = posix_spawnattr_init (&attrs)) != 0
+                  || (attrs_allocated = true,
+                      (err = posix_spawnattr_setsigmask (&attrs,
+                                                         &blocked_signals))
+                      != 0
+                      || (err = posix_spawnattr_setflags (&attrs,
+                                                        POSIX_SPAWN_SETSIGMASK))
+                         != 0)))
+          || (err = posix_spawnp (&child, prog_path, &actions,
+                                  attrs_allocated ? &attrs : NULL, prog_argv,
+                                  environ))
+             != 0))
+    {
+      if (actions_allocated)
+        posix_spawn_file_actions_destroy (&actions);
+      if (attrs_allocated)
+        posix_spawnattr_destroy (&attrs);
+      if (slave_process)
+        unblock_fatal_signals ();
+      if (termsigp != NULL)
+        *termsigp = 0;
+      if (exit_on_error || !null_stderr)
+        error (exit_on_error ? EXIT_FAILURE : 0, err,
+               _("%s subprocess failed"), progname);
+      return 127;
+    }
+  posix_spawn_file_actions_destroy (&actions);
+  if (attrs_allocated)
+    posix_spawnattr_destroy (&attrs);
+  if (slave_process)
+    {
+      register_slave_subprocess (child);
+      unblock_fatal_signals ();
+    }
+
+  return wait_subprocess (child, progname, ignore_sigpipe, null_stderr,
+                          slave_process, exit_on_error, termsigp);
+
+#endif
+}
diff -up patch-2.7.6/lib/execute.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/execute.h
--- patch-2.7.6/lib/execute.h.switch-from-fork-execlp-to-execute	2019-07-29 14:40:53.264464824 +0200
+++ patch-2.7.6/lib/execute.h	2019-07-29 14:40:53.264464824 +0200
@@ -0,0 +1,44 @@
+/* Creation of autonomous subprocesses.
+   Copyright (C) 2001-2003, 2008-2018 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   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 <https://www.gnu.org/licenses/>.  */
+
+#ifndef _EXECUTE_H
+#define _EXECUTE_H
+
+#include <stdbool.h>
+
+/* Execute a command, optionally redirecting any of the three standard file
+   descriptors to /dev/null.  Return its exit code.
+   If it didn't terminate correctly, exit if exit_on_error is true, otherwise
+   return 127.
+   If ignore_sigpipe is true, consider a subprocess termination due to SIGPIPE
+   as equivalent to a success.  This is suitable for processes whose only
+   purpose is to write to standard output.
+   If slave_process is true, the child process will be terminated when its
+   creator receives a catchable fatal signal.
+   If termsigp is not NULL, *termsig will be set to the signal that terminated
+   the subprocess (if supported by the platform: not on native Windows
+   platforms), otherwise 0.
+   It is recommended that no signal is blocked or ignored while execute()
+   is called.  See pipe.h for the reason.  */
+extern int execute (const char *progname,
+                    const char *prog_path, char **prog_argv,
+                    bool ignore_sigpipe,
+                    bool null_stdin, bool null_stdout, bool null_stderr,
+                    bool slave_process, bool exit_on_error,
+                    int *termsigp);
+
+#endif /* _EXECUTE_H */
diff -up patch-2.7.6/lib/fatal-signal.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/fatal-signal.c
--- patch-2.7.6/lib/fatal-signal.c.switch-from-fork-execlp-to-execute	2019-07-29 14:51:00.441882754 +0200
+++ patch-2.7.6/lib/fatal-signal.c	2019-07-29 14:51:00.441882754 +0200
@@ -0,0 +1,286 @@
+/* Emergency actions in case of a fatal signal.
+   Copyright (C) 2003-2004, 2006-2018 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   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 <https://www.gnu.org/licenses/>.  */
+
+
+#include <config.h>
+
+/* Specification.  */
+#include "fatal-signal.h"
+
+#include <stdbool.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "sig-handler.h"
+#include "xalloc.h"
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+/* ========================================================================= */
+
+
+/* The list of fatal signals.
+   These are those signals whose default action is to terminate the process
+   without a core dump, except
+     SIGKILL - because it cannot be caught,
+     SIGALRM SIGUSR1 SIGUSR2 SIGPOLL SIGIO SIGLOST - because applications
+       often use them for their own purpose,
+     SIGPROF SIGVTALRM - because they are used for profiling,
+     SIGSTKFLT - because it is more similar to SIGFPE, SIGSEGV, SIGBUS,
+     SIGSYS - because it is more similar to SIGABRT, SIGSEGV,
+     SIGPWR - because it of too special use,
+     SIGRTMIN...SIGRTMAX - because they are reserved for application use.
+   plus
+     SIGXCPU, SIGXFSZ - because they are quite similar to SIGTERM.  */
+
+static int fatal_signals[] =
+  {
+    /* ISO C 99 signals.  */
+#ifdef SIGINT
+    SIGINT,
+#endif
+#ifdef SIGTERM
+    SIGTERM,
+#endif
+    /* POSIX:2001 signals.  */
+#ifdef SIGHUP
+    SIGHUP,
+#endif
+#ifdef SIGPIPE
+    SIGPIPE,
+#endif
+    /* BSD signals.  */
+#ifdef SIGXCPU
+    SIGXCPU,
+#endif
+#ifdef SIGXFSZ
+    SIGXFSZ,
+#endif
+    /* Native Windows signals.  */
+#ifdef SIGBREAK
+    SIGBREAK,
+#endif
+    0
+  };
+
+#define num_fatal_signals (SIZEOF (fatal_signals) - 1)
+
+/* Eliminate signals whose signal handler is SIG_IGN.  */
+
+static void
+init_fatal_signals (void)
+{
+  static bool fatal_signals_initialized = false;
+  if (!fatal_signals_initialized)
+    {
+      size_t i;
+
+      for (i = 0; i < num_fatal_signals; i++)
+        {
+          struct sigaction action;
+
+          if (sigaction (fatal_signals[i], NULL, &action) >= 0
+              && get_handler (&action) == SIG_IGN)
+            fatal_signals[i] = -1;
+        }
+
+      fatal_signals_initialized = true;
+    }
+}
+
+
+/* ========================================================================= */
+
+
+typedef void (*action_t) (void);
+
+/* Type of an entry in the actions array.
+   The 'action' field is accessed from within the fatal_signal_handler(),
+   therefore we mark it as 'volatile'.  */
+typedef struct
+{
+  volatile action_t action;
+}
+actions_entry_t;
+
+/* The registered cleanup actions.  */
+static actions_entry_t static_actions[32];
+static actions_entry_t * volatile actions = static_actions;
+static sig_atomic_t volatile actions_count = 0;
+static size_t actions_allocated = SIZEOF (static_actions);
+
+
+/* The saved signal handlers.
+   Size 32 would not be sufficient: On HP-UX, SIGXCPU = 33, SIGXFSZ = 34.  */
+static struct sigaction saved_sigactions[64];
+
+
+/* Uninstall the handlers.  */
+static void
+uninstall_handlers (void)
+{
+  size_t i;
+
+  for (i = 0; i < num_fatal_signals; i++)
+    if (fatal_signals[i] >= 0)
+      {
+        int sig = fatal_signals[i];
+        if (saved_sigactions[sig].sa_handler == SIG_IGN)
+          saved_sigactions[sig].sa_handler = SIG_DFL;
+        sigaction (sig, &saved_sigactions[sig], NULL);
+      }
+}
+
+
+/* The signal handler.  It gets called asynchronously.  */
+static void
+fatal_signal_handler (int sig)
+{
+  for (;;)
+    {
+      /* Get the last registered cleanup action, in a reentrant way.  */
+      action_t action;
+      size_t n = actions_count;
+      if (n == 0)
+        break;
+      n--;
+      actions_count = n;
+      action = actions[n].action;
+      /* Execute the action.  */
+      action ();
+    }
+
+  /* Now execute the signal's default action.
+     If the signal being delivered was blocked, the re-raised signal would be
+     delivered when this handler returns.  But the way we install this handler,
+     no signal is blocked, and the re-raised signal is delivered already
+     during raise().  */
+  uninstall_handlers ();
+  raise (sig);
+}
+
+
+/* Install the handlers.  */
+static void
+install_handlers (void)
+{
+  size_t i;
+  struct sigaction action;
+
+  action.sa_handler = &fatal_signal_handler;
+  /* If we get a fatal signal while executing fatal_signal_handler, enter
+     fatal_signal_handler recursively, since it is reentrant.  Hence no
+     SA_RESETHAND.  */
+  action.sa_flags = SA_NODEFER;
+  sigemptyset (&action.sa_mask);
+  for (i = 0; i < num_fatal_signals; i++)
+    if (fatal_signals[i] >= 0)
+      {
+        int sig = fatal_signals[i];
+
+        if (!(sig < sizeof (saved_sigactions) / sizeof (saved_sigactions[0])))
+          abort ();
+        sigaction (sig, &action, &saved_sigactions[sig]);
+      }
+}
+
+
+/* Register a cleanup function to be executed when a catchable fatal signal
+   occurs.  */
+void
+at_fatal_signal (action_t action)
+{
+  static bool cleanup_initialized = false;
+  if (!cleanup_initialized)
+    {
+      init_fatal_signals ();
+      install_handlers ();
+      cleanup_initialized = true;
+    }
+
+  if (actions_count == actions_allocated)
+    {
+      /* Extend the actions array.  Note that we cannot use xrealloc(),
+         because then the cleanup() function could access an already
+         deallocated array.  */
+      actions_entry_t *old_actions = actions;
+      size_t old_actions_allocated = actions_allocated;
+      size_t new_actions_allocated = 2 * actions_allocated;
+      actions_entry_t *new_actions =
+        XNMALLOC (new_actions_allocated, actions_entry_t);
+      size_t k;
+
+      /* Don't use memcpy() here, because memcpy takes non-volatile arguments
+         and is therefore not guaranteed to complete all memory stores before
+         the next statement.  */
+      for (k = 0; k < old_actions_allocated; k++)
+        new_actions[k] = old_actions[k];
+      actions = new_actions;
+      actions_allocated = new_actions_allocated;
+      /* Now we can free the old actions array.  */
+      if (old_actions != static_actions)
+        free (old_actions);
+    }
+  /* The two uses of 'volatile' in the types above (and ISO C 99 section
+     5.1.2.3.(5)) ensure that we increment the actions_count only after
+     the new action has been written to the memory location
+     actions[actions_count].  */
+  actions[actions_count].action = action;
+  actions_count++;
+}
+
+
+/* ========================================================================= */
+
+
+static sigset_t fatal_signal_set;
+
+static void
+init_fatal_signal_set (void)
+{
+  static bool fatal_signal_set_initialized = false;
+  if (!fatal_signal_set_initialized)
+    {
+      size_t i;
+
+      init_fatal_signals ();
+
+      sigemptyset (&fatal_signal_set);
+      for (i = 0; i < num_fatal_signals; i++)
+        if (fatal_signals[i] >= 0)
+          sigaddset (&fatal_signal_set, fatal_signals[i]);
+
+      fatal_signal_set_initialized = true;
+    }
+}
+
+/* Temporarily delay the catchable fatal signals.  */
+void
+block_fatal_signals (void)
+{
+  init_fatal_signal_set ();
+  sigprocmask (SIG_BLOCK, &fatal_signal_set, NULL);
+}
+
+/* Stop delaying the catchable fatal signals.  */
+void
+unblock_fatal_signals (void)
+{
+  init_fatal_signal_set ();
+  sigprocmask (SIG_UNBLOCK, &fatal_signal_set, NULL);
+}
diff -up patch-2.7.6/lib/fatal-signal.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/fatal-signal.h
--- patch-2.7.6/lib/fatal-signal.h.switch-from-fork-execlp-to-execute	2019-07-29 14:51:09.977920729 +0200
+++ patch-2.7.6/lib/fatal-signal.h	2019-07-29 14:51:09.977920729 +0200
@@ -0,0 +1,76 @@
+/* Emergency actions in case of a fatal signal.
+   Copyright (C) 2003-2004, 2009-2018 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   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 <https://www.gnu.org/licenses/>.  */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* It is often useful to do some cleanup action when a usually fatal signal
+   terminates the process, like removing a temporary file or killing a
+   subprocess that may be stuck waiting for a device, pipe or network input.
+   Such signals are SIGHUP, SIGINT, SIGPIPE, SIGTERM, and possibly others.
+   The limitation of this facility is that it cannot work for SIGKILL.
+
+   Signals with a SIG_IGN handler are considered to be non-fatal.  The
+   functions in this file assume that when a SIG_IGN handler is installed
+   for a signal, it was installed before any functions in this file were
+   called and it stays so for the whole lifetime of the process.  */
+
+/* Register a cleanup function to be executed when a catchable fatal signal
+   occurs.
+
+   Restrictions for the cleanup function:
+     - The cleanup function can do all kinds of system calls.
+     - It can also access application dependent memory locations and data
+       structures provided they are in a consistent state. One way to ensure
+       this is through block_fatal_signals()/unblock_fatal_signals(), see
+       below.  Another - more tricky - way to ensure this is the careful use
+       of 'volatile'.
+   However,
+     - malloc() and similarly complex facilities are not safe to be called
+       because they are not guaranteed to be in a consistent state.
+     - Also, the cleanup function must not block the catchable fatal signals
+       and leave them blocked upon return.
+
+   The cleanup function is executed asynchronously.  It is unspecified
+   whether during its execution the catchable fatal signals are blocked
+   or not.  */
+extern void at_fatal_signal (void (*function) (void));
+
+
+/* Sometimes it is necessary to block the usually fatal signals while the
+   data structures being accessed by the cleanup action are being built or
+   reorganized.  This is the case, for example, when a temporary file or
+   directory is created through mkstemp() or mkdtemp(), because these
+   functions create the temporary file or directory _before_ returning its
+   name to the application.  */
+
+/* Temporarily delay the catchable fatal signals.
+   The signals will be blocked (= delayed) until the next call to
+   unblock_fatal_signals().  If the signals are already blocked, a further
+   call to block_fatal_signals() has no effect.  */
+extern void block_fatal_signals (void);
+
+/* Stop delaying the catchable fatal signals.  */
+extern void unblock_fatal_signals (void);
+
+
+#ifdef __cplusplus
+}
+#endif
diff -up patch-2.7.6/lib/gnulib.mk.switch-from-fork-execlp-to-execute patch-2.7.6/lib/gnulib.mk
--- patch-2.7.6/lib/gnulib.mk.switch-from-fork-execlp-to-execute	2018-02-03 14:31:15.000000000 +0100
+++ patch-2.7.6/lib/gnulib.mk	2019-07-29 14:49:33.437536285 +0200
@@ -21,7 +21,7 @@
 # the same distribution terms as the rest of that program.
 #
 # Generated by gnulib-tool.
-# Reproduce by: gnulib-tool --import --local-dir=gl --lib=libpatch --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --makefile-name=gnulib.mk --no-conditional-dependencies --no-libtool --macro-prefix=gl argmatch backupfile clock-time diffseq dirname dup2 errno exitfail extensions faccessat fchmodat fchownat fcntl-h fstatat full-write getdate getopt-gnu gettime git-version-gen gitlog-to-changelog gnupload hash ignore-value intprops largefile linked-list maintainer-makefile malloc manywarnings memchr minmax mkdirat nstrftime openat progname quotearg readlinkat realloc renameat setenv signal size_max ssize_t stat-time stdbool stdlib symlinkat sys_stat tempname time unistd unlinkat update-copyright utimensat verror xalloc xlist xmemdup0
+# Reproduce by: gnulib-tool --import --local-dir=gl --lib=libpatch --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --makefile-name=gnulib.mk --no-conditional-dependencies --no-libtool --macro-prefix=gl argmatch backupfile clock-time diffseq dirname dup2 errno execute exitfail extensions faccessat fchmodat fchownat fcntl-h fstatat full-write getdate getopt-gnu gettime git-version-gen gitlog-to-changelog gnupload hash ignore-value intprops largefile linked-list maintainer-makefile malloc manywarnings memchr minmax mkdirat nstrftime openat progname quotearg readlinkat realloc renameat setenv signal size_max ssize_t stat-time stdbool stdlib symlinkat sys_stat tempname time unistd unlinkat update-copyright utimensat verror xalloc xlist xmemdup0 wait-process fatal-signal
 
 
 MOSTLYCLEANFILES += core *.stackdump
@@ -378,6 +378,12 @@ EXTRA_libpatch_a_SOURCES += euidaccess.c
 
 ## end   gnulib module euidaccess
 
+## begin gnulib module execute
+
+libpatch_a_SOURCES += execute.h execute.c w32spawn.h
+
+## end   gnulib module execute
+
 ## begin gnulib module exitfail
 
 libpatch_a_SOURCES += exitfail.c
@@ -2481,6 +2487,28 @@ libpatch_a_SOURCES += verror.h verror.c
 
 ## end   gnulib module verror
 
+## begin gnulib module wait-process
+
+libpatch_a_SOURCES += wait-process.h wait-process.c
+
+## end   gnulib module wait-process
+
+## begin gnulib module fatal-signal
+
+libpatch_a_SOURCES += fatal-signal.h fatal-signal.c
+
+## end   gnulib module fatal-signal
+
+## begin gnulib module sigaction
+
+libpatch_a_SOURCES += sig-handler.c
+
+EXTRA_DIST += sig-handler.h sigaction.c
+
+EXTRA_libpatch_a_SOURCES += sigaction.c
+
+## end   gnulib module sigaction
+
 ## begin gnulib module wchar
 
 BUILT_SOURCES += wchar.h
@@ -2694,6 +2722,7 @@ EXTRA_libpatch_a_SOURCES += xmemdup0.c
 
 ## end   gnulib module xmemdup0
 
+
 ## begin gnulib module xsize
 
 libpatch_a_SOURCES += xsize.h xsize.c
diff -up patch-2.7.6/lib/sigaction.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/sigaction.c
--- patch-2.7.6/lib/sigaction.c.switch-from-fork-execlp-to-execute	2019-07-29 14:50:31.833768833 +0200
+++ patch-2.7.6/lib/sigaction.c	2019-07-29 14:50:31.833768833 +0200
@@ -0,0 +1,204 @@
+/* POSIX compatible signal blocking.
+   Copyright (C) 2008-2018 Free Software Foundation, Inc.
+   Written by Eric Blake <ebb9@byu.net>, 2008.
+
+   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 <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <signal.h>
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+/* This implementation of sigaction is tailored to native Windows behavior:
+   signal() has SysV semantics (ie. the handler is uninstalled before
+   it is invoked).  This is an inherent data race if an asynchronous
+   signal is sent twice in a row before we can reinstall our handler,
+   but there's nothing we can do about it.  Meanwhile, sigprocmask()
+   is not present, and while we can use the gnulib replacement to
+   provide critical sections, it too suffers from potential data races
+   in the face of an ill-timed asynchronous signal.  And we compound
+   the situation by reading static storage in a signal handler, which
+   POSIX warns is not generically async-signal-safe.  Oh well.
+
+   Additionally:
+     - We don't implement SA_NOCLDSTOP or SA_NOCLDWAIT, because SIGCHLD
+       is not defined.
+     - We don't implement SA_ONSTACK, because sigaltstack() is not present.
+     - We ignore SA_RESTART, because blocking native Windows API calls are
+       not interrupted anyway when an asynchronous signal occurs, and the
+       MSVCRT runtime never sets errno to EINTR.
+     - We don't implement SA_SIGINFO because it is impossible to do so
+       portably.
+
+   POSIX states that an application should not mix signal() and
+   sigaction().  We support the use of signal() within the gnulib
+   sigprocmask() substitute, but all other application code linked
+   with this module should stick with only sigaction().  */
+
+/* Check some of our assumptions.  */
+#if defined SIGCHLD || defined HAVE_SIGALTSTACK || defined HAVE_SIGINTERRUPT
+# error "Revisit the assumptions made in the sigaction module"
+#endif
+
+/* Out-of-range substitutes make a good fallback for uncatchable
+   signals.  */
+#ifndef SIGKILL
+# define SIGKILL (-1)
+#endif
+#ifndef SIGSTOP
+# define SIGSTOP (-1)
+#endif
+
+/* On native Windows, as of 2008, the signal SIGABRT_COMPAT is an alias
+   for the signal SIGABRT.  Only one signal handler is stored for both
+   SIGABRT and SIGABRT_COMPAT.  SIGABRT_COMPAT is not a signal of its own.  */
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+# undef SIGABRT_COMPAT
+# define SIGABRT_COMPAT 6
+#endif
+
+/* A signal handler.  */
+typedef void (*handler_t) (int signal);
+
+/* Set of current actions.  If sa_handler for an entry is NULL, then
+   that signal is not currently handled by the sigaction handler.  */
+static struct sigaction volatile action_array[NSIG] /* = 0 */;
+
+/* Signal handler that is installed for signals.  */
+static void
+sigaction_handler (int sig)
+{
+  handler_t handler;
+  sigset_t mask;
+  sigset_t oldmask;
+  int saved_errno = errno;
+  if (sig < 0 || NSIG <= sig || !action_array[sig].sa_handler)
+    {
+      /* Unexpected situation; be careful to avoid recursive abort.  */
+      if (sig == SIGABRT)
+        signal (SIGABRT, SIG_DFL);
+      abort ();
+    }
+
+  /* Reinstall the signal handler when required; otherwise update the
+     bookkeeping so that the user's handler may call sigaction and get
+     accurate results.  We know the signal isn't currently blocked, or
+     we wouldn't be in its handler, therefore we know that we are not
+     interrupting a sigaction() call.  There is a race where any
+     asynchronous instance of the same signal occurring before we
+     reinstall the handler will trigger the default handler; oh
+     well.  */
+  handler = action_array[sig].sa_handler;
+  if ((action_array[sig].sa_flags & SA_RESETHAND) == 0)
+    signal (sig, sigaction_handler);
+  else
+    action_array[sig].sa_handler = NULL;
+
+  /* Block appropriate signals.  */
+  mask = action_array[sig].sa_mask;
+  if ((action_array[sig].sa_flags & SA_NODEFER) == 0)
+    sigaddset (&mask, sig);
+  sigprocmask (SIG_BLOCK, &mask, &oldmask);
+
+  /* Invoke the user's handler, then restore prior mask.  */
+  errno = saved_errno;
+  handler (sig);
+  saved_errno = errno;
+  sigprocmask (SIG_SETMASK, &oldmask, NULL);
+  errno = saved_errno;
+}
+
+/* Change and/or query the action that will be taken on delivery of
+   signal SIG.  If not NULL, ACT describes the new behavior.  If not
+   NULL, OACT is set to the prior behavior.  Return 0 on success, or
+   set errno and return -1 on failure.  */
+int
+sigaction (int sig, const struct sigaction *restrict act,
+           struct sigaction *restrict oact)
+{
+  sigset_t mask;
+  sigset_t oldmask;
+  int saved_errno;
+
+  if (sig < 0 || NSIG <= sig || sig == SIGKILL || sig == SIGSTOP
+      || (act && act->sa_handler == SIG_ERR))
+    {
+      errno = EINVAL;
+      return -1;
+    }
+
+#ifdef SIGABRT_COMPAT
+  if (sig == SIGABRT_COMPAT)
+    sig = SIGABRT;
+#endif
+
+  /* POSIX requires sigaction() to be async-signal-safe.  In other
+     words, if an asynchronous signal can occur while we are anywhere
+     inside this function, the user's handler could then call
+     sigaction() recursively and expect consistent results.  We meet
+     this rule by using sigprocmask to block all signals before
+     modifying any data structure that could be read from a signal
+     handler; this works since we know that the gnulib sigprocmask
+     replacement does not try to use sigaction() from its handler.  */
+  if (!act && !oact)
+    return 0;
+  sigfillset (&mask);
+  sigprocmask (SIG_BLOCK, &mask, &oldmask);
+  if (oact)
+    {
+      if (action_array[sig].sa_handler)
+        *oact = action_array[sig];
+      else
+        {
+          /* Safe to change the handler at will here, since all
+             signals are currently blocked.  */
+          oact->sa_handler = signal (sig, SIG_DFL);
+          if (oact->sa_handler == SIG_ERR)
+            goto failure;
+          signal (sig, oact->sa_handler);
+          oact->sa_flags = SA_RESETHAND | SA_NODEFER;
+          sigemptyset (&oact->sa_mask);
+        }
+    }
+
+  if (act)
+    {
+      /* Safe to install the handler before updating action_array,
+         since all signals are currently blocked.  */
+      if (act->sa_handler == SIG_DFL || act->sa_handler == SIG_IGN)
+        {
+          if (signal (sig, act->sa_handler) == SIG_ERR)
+            goto failure;
+          action_array[sig].sa_handler = NULL;
+        }
+      else
+        {
+          if (signal (sig, sigaction_handler) == SIG_ERR)
+            goto failure;
+          action_array[sig] = *act;
+        }
+    }
+  sigprocmask (SIG_SETMASK, &oldmask, NULL);
+  return 0;
+
+ failure:
+  saved_errno = errno;
+  sigprocmask (SIG_SETMASK, &oldmask, NULL);
+  errno = saved_errno;
+  return -1;
+}
diff -up patch-2.7.6/lib/sig-handler.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/sig-handler.c
--- patch-2.7.6/lib/sig-handler.c.switch-from-fork-execlp-to-execute	2019-07-29 14:50:17.265710820 +0200
+++ patch-2.7.6/lib/sig-handler.c	2019-07-29 14:48:19.707242671 +0200
@@ -0,0 +1,3 @@
+#include <config.h>
+#define SIG_HANDLER_INLINE _GL_EXTERN_INLINE
+#include "sig-handler.h"
diff -up patch-2.7.6/lib/sig-handler.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/sig-handler.h
--- patch-2.7.6/lib/sig-handler.h.switch-from-fork-execlp-to-execute	2019-07-29 14:50:12.249690845 +0200
+++ patch-2.7.6/lib/sig-handler.h	2019-07-29 14:48:23.099256180 +0200
@@ -0,0 +1,54 @@
+/* Convenience declarations when working with <signal.h>.
+
+   Copyright (C) 2008-2018 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 <https://www.gnu.org/licenses/>.  */
+
+#ifndef _GL_SIG_HANDLER_H
+#define _GL_SIG_HANDLER_H
+
+#include <signal.h>
+
+#ifndef _GL_INLINE_HEADER_BEGIN
+ #error "Please include config.h first."
+#endif
+_GL_INLINE_HEADER_BEGIN
+#ifndef SIG_HANDLER_INLINE
+# define SIG_HANDLER_INLINE _GL_INLINE
+#endif
+
+/* Convenience type when working with signal handlers.  */
+typedef void (*sa_handler_t) (int);
+
+/* Return the handler of a signal, as a sa_handler_t value regardless
+   of its true type.  The resulting function can be compared to
+   special values like SIG_IGN but it is not portable to call it.  */
+SIG_HANDLER_INLINE sa_handler_t _GL_ATTRIBUTE_PURE
+get_handler (struct sigaction const *a)
+{
+#ifdef SA_SIGINFO
+  /* POSIX says that special values like SIG_IGN can only occur when
+     action.sa_flags does not contain SA_SIGINFO.  But in Linux 2.4,
+     for example, sa_sigaction and sa_handler are aliases and a signal
+     is ignored if sa_sigaction (after casting) equals SIG_IGN.  So
+     use (and cast) sa_sigaction in that case.  */
+  if (a->sa_flags & SA_SIGINFO)
+    return (sa_handler_t) a->sa_sigaction;
+#endif
+  return a->sa_handler;
+}
+
+_GL_INLINE_HEADER_END
+
+#endif /* _GL_SIG_HANDLER_H */
diff -up patch-2.7.6/lib/w32spawn.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/w32spawn.h
--- patch-2.7.6/lib/w32spawn.h.switch-from-fork-execlp-to-execute	2019-07-29 14:40:53.265464828 +0200
+++ patch-2.7.6/lib/w32spawn.h	2019-07-29 14:40:53.265464828 +0200
@@ -0,0 +1,233 @@
+/* Auxiliary functions for the creation of subprocesses.  Native Windows API.
+   Copyright (C) 2001, 2003-2018 Free Software Foundation, Inc.
+   Written by Bruno Haible <bruno@clisp.org>, 2003.
+
+   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 <https://www.gnu.org/licenses/>.  */
+
+#ifndef __KLIBC__
+/* Get declarations of the native Windows API functions.  */
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+#endif
+
+/* Get _open_osfhandle().  */
+#include <io.h>
+
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+/* Get _get_osfhandle().  */
+# if GNULIB_MSVC_NOTHROW
+#  include "msvc-nothrow.h"
+# else
+#  include <io.h>
+# endif
+
+#include "cloexec.h"
+#include "xalloc.h"
+
+/* Duplicates a file handle, making the copy uninheritable.
+   Returns -1 for a file handle that is equivalent to closed.  */
+static int
+dup_noinherit (int fd)
+{
+  fd = dup_cloexec (fd);
+  if (fd < 0 && errno == EMFILE)
+    error (EXIT_FAILURE, errno, _("_open_osfhandle failed"));
+
+  return fd;
+}
+
+/* Returns a file descriptor equivalent to FD, except that the resulting file
+   descriptor is none of STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
+   FD must be open and non-inheritable.  The result will be non-inheritable as
+   well.
+   If FD < 0, FD itself is returned.  */
+static int
+fd_safer_noinherit (int fd)
+{
+  if (STDIN_FILENO <= fd && fd <= STDERR_FILENO)
+    {
+      /* The recursion depth is at most 3.  */
+      int nfd = fd_safer_noinherit (dup_noinherit (fd));
+      int saved_errno = errno;
+      close (fd);
+      errno = saved_errno;
+      return nfd;
+    }
+  return fd;
+}
+
+/* Duplicates a file handle, making the copy uninheritable and ensuring the
+   result is none of STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO.
+   Returns -1 for a file handle that is equivalent to closed.  */
+static int
+dup_safer_noinherit (int fd)
+{
+  return fd_safer_noinherit (dup_noinherit (fd));
+}
+
+/* Undoes the effect of TEMPFD = dup_safer_noinherit (ORIGFD);  */
+static void
+undup_safer_noinherit (int tempfd, int origfd)
+{
+  if (tempfd >= 0)
+    {
+      if (dup2 (tempfd, origfd) < 0)
+        error (EXIT_FAILURE, errno, _("cannot restore fd %d: dup2 failed"),
+               origfd);
+      close (tempfd);
+    }
+  else
+    {
+      /* origfd was closed or open to no handle at all.  Set it to a closed
+         state.  This is (nearly) equivalent to the original state.  */
+      close (origfd);
+    }
+}
+
+/* Prepares an argument vector before calling spawn().
+   Note that spawn() does not by itself call the command interpreter
+     (getenv ("COMSPEC") != NULL ? getenv ("COMSPEC") :
+      ({ OSVERSIONINFO v; v.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+         GetVersionEx(&v);
+         v.dwPlatformId == VER_PLATFORM_WIN32_NT;
+      }) ? "cmd.exe" : "command.com").
+   Instead it simply concatenates the arguments, separated by ' ', and calls
+   CreateProcess().  We must quote the arguments since Windows CreateProcess()
+   interprets characters like ' ', '\t', '\\', '"' (but not '<' and '>') in a
+   special way:
+   - Space and tab are interpreted as delimiters. They are not treated as
+     delimiters if they are surrounded by double quotes: "...".
+   - Unescaped double quotes are removed from the input. Their only effect is
+     that within double quotes, space and tab are treated like normal
+     characters.
+   - Backslashes not followed by double quotes are not special.
+   - But 2*n+1 backslashes followed by a double quote become
+     n backslashes followed by a double quote (n >= 0):
+       \" -> "
+       \\\" -> \"
+       \\\\\" -> \\"
+   - '*', '?' characters may get expanded through wildcard expansion in the
+     callee: By default, in the callee, the initialization code before main()
+     takes the result of GetCommandLine(), wildcard-expands it, and passes it
+     to main(). The exceptions to this rule are:
+       - programs that inspect GetCommandLine() and ignore argv,
+       - mingw programs that have a global variable 'int _CRT_glob = 0;',
+       - Cygwin programs, when invoked from a Cygwin program.
+ */
+#ifndef __KLIBC__
+# define SHELL_SPECIAL_CHARS "\"\\ \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037*?"
+# define SHELL_SPACE_CHARS " \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037"
+#else
+# define SHELL_SPECIAL_CHARS ""
+# define SHELL_SPACE_CHARS ""
+#endif
+static char **
+prepare_spawn (char **argv)
+{
+  size_t argc;
+  char **new_argv;
+  size_t i;
+
+  /* Count number of arguments.  */
+  for (argc = 0; argv[argc] != NULL; argc++)
+    ;
+
+  /* Allocate new argument vector.  */
+  new_argv = XNMALLOC (1 + argc + 1, char *);
+
+  /* Add an element upfront that can be used when argv[0] turns out to be a
+     script, not a program.
+     On Unix, this would be "/bin/sh". On native Windows, "sh" is actually
+     "sh.exe".  We have to omit the directory part and rely on the search in
+     PATH, because the mingw "mount points" are not visible inside Windows
+     CreateProcess().  */
+  *new_argv++ = "sh.exe";
+
+  /* Put quoted arguments into the new argument vector.  */
+  for (i = 0; i < argc; i++)
+    {
+      const char *string = argv[i];
+
+      if (string[0] == '\0')
+        new_argv[i] = xstrdup ("\"\"");
+      else if (strpbrk (string, SHELL_SPECIAL_CHARS) != NULL)
+        {
+          bool quote_around = (strpbrk (string, SHELL_SPACE_CHARS) != NULL);
+          size_t length;
+          unsigned int backslashes;
+          const char *s;
+          char *quoted_string;
+          char *p;
+
+          length = 0;
+          backslashes = 0;
+          if (quote_around)
+            length++;
+          for (s = string; *s != '\0'; s++)
+            {
+              char c = *s;
+              if (c == '"')
+                length += backslashes + 1;
+              length++;
+              if (c == '\\')
+                backslashes++;
+              else
+                backslashes = 0;
+            }
+          if (quote_around)
+            length += backslashes + 1;
+
+          quoted_string = (char *) xmalloc (length + 1);
+
+          p = quoted_string;
+          backslashes = 0;
+          if (quote_around)
+            *p++ = '"';
+          for (s = string; *s != '\0'; s++)
+            {
+              char c = *s;
+              if (c == '"')
+                {
+                  unsigned int j;
+                  for (j = backslashes + 1; j > 0; j--)
+                    *p++ = '\\';
+                }
+              *p++ = c;
+              if (c == '\\')
+                backslashes++;
+              else
+                backslashes = 0;
+            }
+          if (quote_around)
+            {
+              unsigned int j;
+              for (j = backslashes; j > 0; j--)
+                *p++ = '\\';
+              *p++ = '"';
+            }
+          *p = '\0';
+
+          new_argv[i] = quoted_string;
+        }
+      else
+        new_argv[i] = (char *) string;
+    }
+  new_argv[argc] = NULL;
+
+  return new_argv;
+}
diff -up patch-2.7.6/lib/wait-process.c.switch-from-fork-execlp-to-execute patch-2.7.6/lib/wait-process.c
--- patch-2.7.6/lib/wait-process.c.switch-from-fork-execlp-to-execute	2019-07-29 14:50:49.937840928 +0200
+++ patch-2.7.6/lib/wait-process.c	2019-07-29 14:45:17.196515863 +0200
@@ -0,0 +1,361 @@
+/* Waiting for a subprocess to finish.
+   Copyright (C) 2001-2003, 2005-2018 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   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 <https://www.gnu.org/licenses/>.  */
+
+
+#include <config.h>
+
+/* Specification.  */
+#include "wait-process.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "error.h"
+#include "fatal-signal.h"
+#include "xalloc.h"
+#include "gettext.h"
+
+#define _(str) gettext (str)
+
+#define SIZEOF(a) (sizeof(a) / sizeof(a[0]))
+
+
+#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
+
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+
+/* The return value of spawnvp() is really a process handle as returned
+   by CreateProcess().  Therefore we can kill it using TerminateProcess.  */
+# define kill(pid,sig) TerminateProcess ((HANDLE) (pid), sig)
+
+#endif
+
+
+/* Type of an entry in the slaves array.
+   The 'used' bit determines whether this entry is currently in use.
+   (If pid_t was an atomic type like sig_atomic_t, we could just set the
+   'child' field to 0 when unregistering a slave process, and wouldn't need
+   the 'used' field.)
+   The 'used' and 'child' fields are accessed from within the cleanup_slaves()
+   action, therefore we mark them as 'volatile'.  */
+typedef struct
+{
+  volatile sig_atomic_t used;
+  volatile pid_t child;
+}
+slaves_entry_t;
+
+/* The registered slave subprocesses.  */
+static slaves_entry_t static_slaves[32];
+static slaves_entry_t * volatile slaves = static_slaves;
+static sig_atomic_t volatile slaves_count = 0;
+static size_t slaves_allocated = SIZEOF (static_slaves);
+
+/* The termination signal for slave subprocesses.
+   2003-10-07:  Terminator becomes Governator.  */
+#ifdef SIGHUP
+# define TERMINATOR SIGHUP
+#else
+# define TERMINATOR SIGTERM
+#endif
+
+/* The cleanup action.  It gets called asynchronously.  */
+static void
+cleanup_slaves (void)
+{
+  for (;;)
+    {
+      /* Get the last registered slave.  */
+      size_t n = slaves_count;
+      if (n == 0)
+        break;
+      n--;
+      slaves_count = n;
+      /* Skip unused entries in the slaves array.  */
+      if (slaves[n].used)
+        {
+          pid_t slave = slaves[n].child;
+
+          /* Kill the slave.  */
+          kill (slave, TERMINATOR);
+        }
+    }
+}
+
+/* Register a subprocess as being a slave process.  This means that the
+   subprocess will be terminated when its creator receives a catchable fatal
+   signal or exits normally.  Registration ends when wait_subprocess()
+   notices that the subprocess has exited.  */
+void
+register_slave_subprocess (pid_t child)
+{
+  static bool cleanup_slaves_registered = false;
+  if (!cleanup_slaves_registered)
+    {
+      atexit (cleanup_slaves);
+      at_fatal_signal (cleanup_slaves);
+      cleanup_slaves_registered = true;
+    }
+
+  /* Try to store the new slave in an unused entry of the slaves array.  */
+  {
+    slaves_entry_t *s = slaves;
+    slaves_entry_t *s_end = s + slaves_count;
+
+    for (; s < s_end; s++)
+      if (!s->used)
+        {
+          /* The two uses of 'volatile' in the slaves_entry_t type above
+             (and ISO C 99 section 5.1.2.3.(5)) ensure that we mark the
+             entry as used only after the child pid has been written to the
+             memory location s->child.  */
+          s->child = child;
+          s->used = 1;
+          return;
+        }
+  }
+
+  if (slaves_count == slaves_allocated)
+    {
+      /* Extend the slaves array.  Note that we cannot use xrealloc(),
+         because then the cleanup_slaves() function could access an already
+         deallocated array.  */
+      slaves_entry_t *old_slaves = slaves;
+      size_t new_slaves_allocated = 2 * slaves_allocated;
+      slaves_entry_t *new_slaves =
+        (slaves_entry_t *)
+        malloc (new_slaves_allocated * sizeof (slaves_entry_t));
+      if (new_slaves == NULL)
+        {
+          /* xalloc_die() will call exit() which will invoke cleanup_slaves().
+             Additionally we need to kill child, because it's not yet among
+             the slaves list.  */
+          kill (child, TERMINATOR);
+          xalloc_die ();
+        }
+      memcpy (new_slaves, old_slaves,
+              slaves_allocated * sizeof (slaves_entry_t));
+      slaves = new_slaves;
+      slaves_allocated = new_slaves_allocated;
+      /* Now we can free the old slaves array.  */
+      if (old_slaves != static_slaves)
+        free (old_slaves);
+    }
+  /* The three uses of 'volatile' in the types above (and ISO C 99 section
+     5.1.2.3.(5)) ensure that we increment the slaves_count only after the
+     new slave and its 'used' bit have been written to the memory locations
+     that make up slaves[slaves_count].  */
+  slaves[slaves_count].child = child;
+  slaves[slaves_count].used = 1;
+  slaves_count++;
+}
+
+/* Unregister a child from the list of slave subprocesses.  */
+static void
+unregister_slave_subprocess (pid_t child)
+{
+  /* The easiest way to remove an entry from a list that can be used by
+     an asynchronous signal handler is just to mark it as unused.  For this,
+     we rely on sig_atomic_t.  */
+  slaves_entry_t *s = slaves;
+  slaves_entry_t *s_end = s + slaves_count;
+
+  for (; s < s_end; s++)
+    if (s->used && s->child == child)
+      s->used = 0;
+}
+
+
+/* Wait for a subprocess to finish.  Return its exit code.
+   If it didn't terminate correctly, exit if exit_on_error is true, otherwise
+   return 127.  */
+int
+wait_subprocess (pid_t child, const char *progname,
+                 bool ignore_sigpipe, bool null_stderr,
+                 bool slave_process, bool exit_on_error,
+                 int *termsigp)
+{
+#if HAVE_WAITID && defined WNOWAIT && 0
+  /* Commented out because waitid() without WEXITED and with WNOWAIT doesn't
+     work: On Solaris 7 and OSF/1 4.0, it returns -1 and sets errno = ECHILD,
+     and on HP-UX 10.20 it just hangs.  */
+  /* Use of waitid() with WNOWAIT avoids a race condition: If slave_process is
+     true, and this process sleeps a very long time between the return from
+     waitpid() and the execution of unregister_slave_subprocess(), and
+     meanwhile another process acquires the same PID as child, and then - still
+     before unregister_slave_subprocess() - this process gets a fatal signal,
+     it would kill the other totally unrelated process.  */
+  siginfo_t info;
+
+  if (termsigp != NULL)
+    *termsigp = 0;
+  for (;;)
+    {
+      if (waitid (P_PID, child, &info, WEXITED | (slave_process ? WNOWAIT : 0))
+          < 0)
+        {
+# ifdef EINTR
+          if (errno == EINTR)
+            continue;
+# endif
+          if (exit_on_error || !null_stderr)
+            error (exit_on_error ? EXIT_FAILURE : 0, errno,
+                   _("%s subprocess"), progname);
+          return 127;
+        }
+
+      /* info.si_code is set to one of CLD_EXITED, CLD_KILLED, CLD_DUMPED,
+         CLD_TRAPPED, CLD_STOPPED, CLD_CONTINUED.  Loop until the program
+         terminates.  */
+      if (info.si_code == CLD_EXITED
+          || info.si_code == CLD_KILLED || info.si_code == CLD_DUMPED)
+        break;
+    }
+
+  /* The child process has exited or was signalled.  */
+
+  if (slave_process)
+    {
+      /* Unregister the child from the list of slave subprocesses, so that
+         later, when we exit, we don't kill a totally unrelated process which
+         may have acquired the same pid.  */
+      unregister_slave_subprocess (child);
+
+      /* Now remove the zombie from the process list.  */
+      for (;;)
+        {
+          if (waitid (P_PID, child, &info, WEXITED) < 0)
+            {
+# ifdef EINTR
+              if (errno == EINTR)
+                continue;
+# endif
+              if (exit_on_error || !null_stderr)
+                error (exit_on_error ? EXIT_FAILURE : 0, errno,
+                       _("%s subprocess"), progname);
+              return 127;
+            }
+          break;
+        }
+    }
+
+  switch (info.si_code)
+    {
+    case CLD_KILLED:
+    case CLD_DUMPED:
+      if (termsigp != NULL)
+        *termsigp = info.si_status; /* TODO: or info.si_signo? */
+# ifdef SIGPIPE
+      if (info.si_status == SIGPIPE && ignore_sigpipe)
+        return 0;
+# endif
+      if (exit_on_error || (!null_stderr && termsigp == NULL))
+        error (exit_on_error ? EXIT_FAILURE : 0, 0,
+               _("%s subprocess got fatal signal %d"),
+               progname, info.si_status);
+      return 127;
+    case CLD_EXITED:
+      if (info.si_status == 127)
+        {
+          if (exit_on_error || !null_stderr)
+            error (exit_on_error ? EXIT_FAILURE : 0, 0,
+                   _("%s subprocess failed"), progname);
+          return 127;
+        }
+      return info.si_status;
+    default:
+      abort ();
+    }
+#else
+  /* waitpid() is just as portable as wait() nowadays.  */
+  int status;
+
+  if (termsigp != NULL)
+    *termsigp = 0;
+  status = 0;
+  for (;;)
+    {
+      int result = waitpid (child, &status, 0);
+
+      if (result != child)
+        {
+# ifdef EINTR
+          if (errno == EINTR)
+            continue;
+# endif
+# if 0 /* defined ECHILD */
+          if (errno == ECHILD)
+            {
+              /* Child process nonexistent?! Assume it terminated
+                 successfully.  */
+              status = 0;
+              break;
+            }
+# endif
+          if (exit_on_error || !null_stderr)
+            error (exit_on_error ? EXIT_FAILURE : 0, errno,
+                   _("%s subprocess"), progname);
+          return 127;
+        }
+
+      /* One of WIFSIGNALED (status), WIFEXITED (status), WIFSTOPPED (status)
+         must always be true, since we did not specify WCONTINUED in the
+         waitpid() call.  Loop until the program terminates.  */
+      if (!WIFSTOPPED (status))
+        break;
+    }
+
+  /* The child process has exited or was signalled.  */
+
+  if (slave_process)
+    /* Unregister the child from the list of slave subprocesses, so that
+       later, when we exit, we don't kill a totally unrelated process which
+       may have acquired the same pid.  */
+    unregister_slave_subprocess (child);
+
+  if (WIFSIGNALED (status))
+    {
+      if (termsigp != NULL)
+        *termsigp = WTERMSIG (status);
+# ifdef SIGPIPE
+      if (WTERMSIG (status) == SIGPIPE && ignore_sigpipe)
+        return 0;
+# endif
+      if (exit_on_error || (!null_stderr && termsigp == NULL))
+        error (exit_on_error ? EXIT_FAILURE : 0, 0,
+               _("%s subprocess got fatal signal %d"),
+               progname, (int) WTERMSIG (status));
+      return 127;
+    }
+  if (!WIFEXITED (status))
+    abort ();
+  if (WEXITSTATUS (status) == 127)
+    {
+      if (exit_on_error || !null_stderr)
+        error (exit_on_error ? EXIT_FAILURE : 0, 0,
+               _("%s subprocess failed"), progname);
+      return 127;
+    }
+  return WEXITSTATUS (status);
+#endif
+}
diff -up patch-2.7.6/lib/wait-process.h.switch-from-fork-execlp-to-execute patch-2.7.6/lib/wait-process.h
--- patch-2.7.6/lib/wait-process.h.switch-from-fork-execlp-to-execute	2019-07-29 14:50:46.505827261 +0200
+++ patch-2.7.6/lib/wait-process.h	2019-07-29 14:45:20.715529876 +0200
@@ -0,0 +1,74 @@
+/* Waiting for a subprocess to finish.
+   Copyright (C) 2001-2003, 2006, 2008-2018 Free Software Foundation, Inc.
+   Written by Bruno Haible <haible@clisp.cons.org>, 2001.
+
+   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 <https://www.gnu.org/licenses/>.  */
+
+#ifndef _WAIT_PROCESS_H
+#define _WAIT_PROCESS_H
+
+/* Get pid_t.  */
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <stdbool.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Wait for a subprocess to finish.  Return its exit code.
+   If it didn't terminate correctly, exit if exit_on_error is true, otherwise
+   return 127.
+   Arguments:
+   - child is the pid of the subprocess.
+   - progname is the name of the program executed by the subprocess, used for
+     error messages.
+   - If ignore_sigpipe is true, consider a subprocess termination due to
+     SIGPIPE as equivalent to a success.  This is suitable for processes whose
+     only purpose is to write to standard output.  This flag can be safely set
+     to false when the process' standard output is known to go to DEV_NULL.
+   - If null_stderr is true, the usual error message to stderr will be omitted.
+     This is suitable when the subprocess does not fulfill an important task.
+   - slave_process should be set to true if the process has been launched as a
+     slave process.
+   - If exit_on_error is true, any error will cause the main process to exit
+     with an error status.
+   - If termsigp is not NULL: *termsig will be set to the signal that
+     terminated the subprocess (if supported by the platform: not on native
+     Windows platforms), otherwise 0, and the error message about the signal
+     that terminated the subprocess will be omitted.
+   Prerequisites: The signal handler for SIGCHLD should not be set to SIG_IGN,
+   otherwise this function will not work.  */
+extern int wait_subprocess (pid_t child, const char *progname,
+                            bool ignore_sigpipe, bool null_stderr,
+                            bool slave_process, bool exit_on_error,
+                            int *termsigp);
+
+/* Register a subprocess as being a slave process.  This means that the
+   subprocess will be terminated when its creator receives a catchable fatal
+   signal or exits normally.  Registration ends when wait_subprocess()
+   notices that the subprocess has exited.  */
+extern void register_slave_subprocess (pid_t child);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _WAIT_PROCESS_H */
diff -up patch-2.7.6/src/pch.c.switch-from-fork-execlp-to-execute patch-2.7.6/src/pch.c
--- patch-2.7.6/src/pch.c.switch-from-fork-execlp-to-execute	2019-07-29 14:40:53.262464816 +0200
+++ patch-2.7.6/src/pch.c	2019-07-29 15:01:10.338312098 +0200
@@ -33,7 +33,8 @@
 # include <io.h>
 #endif
 #include <safe.h>
-#include <sys/wait.h>
+#include <alloca.h>
+#include "execute.h"
 
 #define INITHUNKMAX 125			/* initial dynamic allocation size */
 
@@ -2453,6 +2463,9 @@ do_ed_script (char const *inname, char c
 
     if (! dry_run && ! skip_rest_of_patch) {
 	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
+	char const **ed_argv;
+	int stdin_dup, status;
+
 	*outname_needs_removal = true;
 	if (inerrno != ENOENT)
 	  {
@@ -2461,24 +2474,22 @@ do_ed_script (char const *inname, char c
 	  }
 	fflush (stdout);
 
-	pid = fork();
-	if (pid == -1)
-	  pfatal ("Can't fork");
-	else if (pid == 0)
-	  {
-	    dup2 (tmpfd, 0);
-	    assert (outname[0] != '!' && outname[0] != '-');
-	    execlp (editor_program, editor_program, "-", outname, (char  *) NULL);
-	    _exit (2);
-	  }
-	else
-	  {
-	    int wstatus;
-	    if (waitpid (pid, &wstatus, 0) == -1
-	        || ! WIFEXITED (wstatus)
-		|| WEXITSTATUS (wstatus) != 0)
-	      fatal ("%s FAILED", editor_program);
-	  }
+	if ((stdin_dup = dup (0)) == -1
+	    || dup2 (tmpfd, 0) == -1)
+	  pfatal ("Failed to duplicate standard input");
+	assert (outname[0] != '!' && outname[0] != '-');
+	ed_argv = alloca (4 * sizeof * ed_argv);
+	ed_argv[0] = editor_program;
+	ed_argv[1] = "-";
+	ed_argv[2] = outname;
+	ed_argv[3] = (char  *) NULL;
+	status = execute (editor_program, editor_program, (char **)ed_argv,
+			  false, false, false, false, true, false, NULL);
+	if (status)
+	  fatal ("%s FAILED", editor_program);
+	if (dup2 (stdin_dup, 0) == -1
+	    || close (stdin_dup) == -1)
+	  pfatal ("Failed to duplicate standard input");
     }
 
     fclose (tmpfp);