[packages/patch] - rel 3; fixes from FC

arekm arekm at pld-linux.org
Tue Feb 25 22:39:36 CET 2020


commit 93925d0d9d75587c27ed526835c8572c1b38a780
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Tue Feb 25 22:39:27 2020 +0100

    - rel 3; fixes from FC

 CVE-2018-1000156.patch                             |  211 +++
 patch-2.7.6-CVE-2018-17942.patch                   |   14 +
 ...-6952-fix-swapping-fake-lines-in-pch_swap.patch |   25 +
 patch-2.7.6-CVE-2019-13636-symlinks.patch          |  102 ++
 ...ed-ed-directly-instead-of-using-the-shell.patch |   33 +
 patch-2.7.6-abort_when_cleaning_up_fails.patch     |   46 +
 ...-files-to-be-missing-for-ed-style-patches.patch |   28 +
 ...lid-memory-access-in-context-format-diffs.patch |   21 +
 ..._file_attributes-sign-conversion-warnings.patch |   24 +
 patch-2.7.6-avoid-warnings-gcc8.patch              |   85 +
 patch-2.7.6-avoid_invalid_memory_access.patch      |   21 +
 patch-2.7.6-check-of-return-value-of-fwrite.patch  |   75 +
 patch-2.7.6-cleanups-in-do_ed_script.patch         |   91 +
 patch-2.7.6-crash-RLIMIT_NOFILE.patch              |   84 +
 ...k-temporary-file-on-failed-ed-style-patch.patch |   95 ++
 ...-file-on-failed-multi-file-ed-style-patch.patch |   71 +
 patch-2.7.6-failed_assertion.patch                 |   29 +
 patch-2.7.6-fix-ed-style-test-failure.patch        |   22 +
 patch-2.7.6-fix-korn-shell-incompatibility.patch   |   21 +
 ....6-fix-segfault-with-mangled-rename-patch.patch |   24 +
 ...improve_support_for_memory_leak_detection.patch |   48 +
 patch-2.7.6-make-debug-output-more-useful.patch    |   40 +
 ...test-when-the-ed-utility-is-not-installed.patch |   20 +
 ...-2.7.6-switch-from-fork-execlp-to-execute.patch | 1774 ++++++++++++++++++++
 patch-2.7.6-test-suite-compatibility-fixes.patch   |  124 ++
 patch-CVE-2018-1000156.patch                       |  179 +-
 patch-selinux.patch                                |  326 ++++
 patch.spec                                         |   52 +-
 28 files changed, 3589 insertions(+), 96 deletions(-)
---
diff --git a/patch.spec b/patch.spec
index dad52cb..5af8c8e 100644
--- a/patch.spec
+++ b/patch.spec
@@ -11,13 +11,36 @@ Summary(tr.UTF-8):	GNU yama yardımcı programları
 Summary(uk.UTF-8):	Утиліта GNU patch, для модифікації/апгрейду файлів
 Name:		patch
 Version:	2.7.6
-Release:	2
+Release:	3
 License:	GPL v3+
 Group:		Applications/Text
 Source0:	http://ftp.gnu.org/gnu/patch/%{name}-%{version}.tar.xz
 # Source0-md5:	78ad9937e4caadcba1526ef1853730d5
 Source1:	%{name}.1.pl
-Patch0:		patch-CVE-2018-1000156.patch
+Patch0: patch-2.7.6-avoid-set_file_attributes-sign-conversion-warnings.patch
+Patch1: patch-2.7.6-test-suite-compatibility-fixes.patch
+Patch2: patch-2.7.6-fix-korn-shell-incompatibility.patch
+Patch3: patch-2.7.6-fix-segfault-with-mangled-rename-patch.patch
+Patch4: patch-2.7.6-allow-input-files-to-be-missing-for-ed-style-patches.patch
+Patch5: patch-CVE-2018-1000156.patch
+Patch6: patch-2.7.6-CVE-2019-13638-invoked-ed-directly-instead-of-using-the-shell.patch
+Patch7: patch-2.7.6-switch-from-fork-execlp-to-execute.patch
+Patch8: patch-2.7.6-cleanups-in-do_ed_script.patch
+Patch9: patch-2.7.6-avoid-warnings-gcc8.patch
+Patch10: patch-2.7.6-check-of-return-value-of-fwrite.patch
+Patch11: patch-2.7.6-fix-ed-style-test-failure.patch
+Patch12: patch-2.7.6-dont-leak-temporary-file-on-failed-ed-style-patch.patch
+Patch13: patch-2.7.6-dont-leak-temporary-file-on-failed-multi-file-ed-style-patch.patch
+Patch14: patch-2.7.6-make-debug-output-more-useful.patch
+Patch15: patch-2.7.6-CVE-2018-6952-fix-swapping-fake-lines-in-pch_swap.patch
+Patch16: patch-2.7.6-improve_support_for_memory_leak_detection.patch
+Patch17: patch-2.7.6-skip-ed-test-when-the-ed-utility-is-not-installed.patch
+Patch18: patch-2.7.6-abort_when_cleaning_up_fails.patch
+Patch19: patch-2.7.6-crash-RLIMIT_NOFILE.patch
+Patch20: patch-2.7.6-CVE-2019-13636-symlinks.patch
+Patch21: patch-2.7.6-avoid-invalid-memory-access-in-context-format-diffs.patch
+Patch22: patch-2.7.6-CVE-2018-17942.patch
+Patch23: patch-2.7.6-failed_assertion.patch
 URL:		http://www.gnu.org/software/patch/
 BuildRequires:	autoconf >= 2.65
 BuildRequires:	automake >= 1:1.11.2
@@ -77,7 +100,30 @@ Patch - це програма, яка допомогає в модифікаці
 
 %prep
 %setup -q
-%patch0 -p1
+%patch0 -p1 -b .avoid-set_file_attributes-sign-conversion-warnings
+%patch1 -p1 -b .test-suite-compatibility-fixes
+%patch2 -p1 -b .fix-korn-shell-incompatibility
+%patch3 -p1 -b .fix-segfault-with-mangled-rename-patch
+%patch4 -p1 -b .allow-input-files-to-be-missing-for-ed-style-patches
+%patch5 -p1 -b .CVE-2018-1000156
+%patch6 -p1 -b .CVE-2019-13638-invoked-ed-directly-instead-of-using-the-shell
+%patch7 -p1 -b .switch-from-fork-execlp-to-execute
+%patch8 -p1 -b .cleanups-in-do_ed_script
+%patch9 -p1 -b .avoid-warnings-gcc8
+%patch10 -p1 -b .check-of-return-value-of-fwrite
+%patch11 -p1 -b .fix-ed-style-test-failure
+%patch12 -p1 -b .dont-leak-temporary-file-on-failed-ed-style-patch
+%patch13 -p1 -b .dont-leak-temporary-file-on-failed-multi-file-ed-style-patch
+%patch14 -p1 -b .make-debug-output-more-useful
+%patch15 -p1 -b .CVE-2018-6952-fix-swapping-fake-lines-in-pch_swap
+%patch16 -p1 -b .improve_support_for_memory_leak_detection
+%patch17 -p1 -b .skip-ed-test-when-the-ed-utility-is-not-installed
+%patch18 -p1 -b .abort_when_cleaning_up_fails
+%patch19 -p1 -b .crash-RLIMIT_NOFILE
+%patch20 -p1 -b .CVE-2019-13636-symlinks
+%patch21 -p1 -b .avoid-invalid-memory-access-in-context-format-diffs
+%patch22 -p1 -b .CVE-2018-17942-gnulib_buffer_overflow
+%patch23 -p1 -b .failed_assertion
 
 %build
 %{__aclocal} -I m4
diff --git a/CVE-2018-1000156.patch b/CVE-2018-1000156.patch
new file mode 100644
index 0000000..36f33de
--- /dev/null
+++ b/CVE-2018-1000156.patch
@@ -0,0 +1,211 @@
+From 123eaff0d5d1aebe128295959435b9ca5909c26d Mon Sep 17 00:00:00 2001
+From: Andreas Gruenbacher <agruen at gnu.org>
+Date: Fri, 6 Apr 2018 12:14:49 +0200
+Subject: Fix arbitrary command execution in ed-style patches
+ (CVE-2018-1000156)
+
+* src/pch.c (do_ed_script): Write ed script to a temporary file instead
+of piping it to ed: this will cause ed to abort on invalid commands
+instead of rejecting them and carrying on.
+* tests/ed-style: New test case.
+* tests/Makefile.am (TESTS): Add test case.
+---
+ src/pch.c         | 91 ++++++++++++++++++++++++++++++++++++++++---------------
+ tests/Makefile.am |  1 +
+ tests/ed-style    | 41 +++++++++++++++++++++++++
+ 3 files changed, 108 insertions(+), 25 deletions(-)
+ create mode 100644 tests/ed-style
+
+diff --git a/src/pch.c b/src/pch.c
+index 0c5cc26..4fd5a05 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -33,6 +33,7 @@
+ # include <io.h>
+ #endif
+ #include <safe.h>
++#include <sys/wait.h>
+ 
+ #define INITHUNKMAX 125			/* initial dynamic allocation size */
+ 
+@@ -2389,24 +2390,28 @@ do_ed_script (char const *inname, char const *outname,
+     static char const editor_program[] = EDITOR_PROGRAM;
+ 
+     file_offset beginning_of_this_line;
+-    FILE *pipefp = 0;
+     size_t chars_read;
++    FILE *tmpfp = 0;
++    char const *tmpname;
++    int tmpfd;
++    pid_t pid;
++
++    if (! dry_run && ! skip_rest_of_patch)
++      {
++	/* Write ed script to a temporary file.  This causes ed to abort on
++	   invalid commands such as when line numbers or ranges exceed the
++	   number of available lines.  When ed reads from a pipe, it rejects
++	   invalid commands and treats the next line as a new command, which
++	   can lead to arbitrary command execution.  */
++
++	tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0);
++	if (tmpfd == -1)
++	  pfatal ("Can't create temporary file %s", quotearg (tmpname));
++	tmpfp = fdopen (tmpfd, "w+b");
++	if (! tmpfp)
++	  pfatal ("Can't open stream for file %s", quotearg (tmpname));
++      }
+ 
+-    if (! dry_run && ! skip_rest_of_patch) {
+-	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
+-	if (inerrno != ENOENT)
+-	  {
+-	    *outname_needs_removal = true;
+-	    copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
+-	  }
+-	sprintf (buf, "%s %s%s", editor_program,
+-		 verbosity == VERBOSE ? "" : "- ",
+-		 outname);
+-	fflush (stdout);
+-	pipefp = popen(buf, binary_transput ? "wb" : "w");
+-	if (!pipefp)
+-	  pfatal ("Can't open pipe to %s", quotearg (buf));
+-    }
+     for (;;) {
+ 	char ed_command_letter;
+ 	beginning_of_this_line = file_tell (pfp);
+@@ -2417,14 +2422,14 @@ do_ed_script (char const *inname, char const *outname,
+ 	}
+ 	ed_command_letter = get_ed_command_letter (buf);
+ 	if (ed_command_letter) {
+-	    if (pipefp)
+-		if (! fwrite (buf, sizeof *buf, chars_read, pipefp))
++	    if (tmpfp)
++		if (! fwrite (buf, sizeof *buf, chars_read, tmpfp))
+ 		    write_fatal ();
+ 	    if (ed_command_letter != 'd' && ed_command_letter != 's') {
+ 	        p_pass_comments_through = true;
+ 		while ((chars_read = get_line ()) != 0) {
+-		    if (pipefp)
+-			if (! fwrite (buf, sizeof *buf, chars_read, pipefp))
++		    if (tmpfp)
++			if (! fwrite (buf, sizeof *buf, chars_read, tmpfp))
+ 			    write_fatal ();
+ 		    if (chars_read == 2  &&  strEQ (buf, ".\n"))
+ 			break;
+@@ -2437,13 +2442,49 @@ do_ed_script (char const *inname, char const *outname,
+ 	    break;
+ 	}
+     }
+-    if (!pipefp)
++    if (!tmpfp)
+       return;
+-    if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, pipefp) == 0
+-	|| fflush (pipefp) != 0)
++    if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0
++	|| fflush (tmpfp) != 0)
+       write_fatal ();
+-    if (pclose (pipefp) != 0)
+-      fatal ("%s FAILED", editor_program);
++
++    if (lseek (tmpfd, 0, SEEK_SET) == -1)
++      pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname));
++
++    if (! dry_run && ! skip_rest_of_patch) {
++	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
++	*outname_needs_removal = true;
++	if (inerrno != ENOENT)
++	  {
++	    *outname_needs_removal = true;
++	    copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
++	  }
++	sprintf (buf, "%s %s%s", editor_program,
++		 verbosity == VERBOSE ? "" : "- ",
++		 outname);
++	fflush (stdout);
++
++	pid = fork();
++	if (pid == -1)
++	  pfatal ("Can't fork");
++	else if (pid == 0)
++	  {
++	    dup2 (tmpfd, 0);
++	    execl ("/bin/sh", "sh", "-c", buf, (char *) 0);
++	    _exit (2);
++	  }
++	else
++	  {
++	    int wstatus;
++	    if (waitpid (pid, &wstatus, 0) == -1
++	        || ! WIFEXITED (wstatus)
++		|| WEXITSTATUS (wstatus) != 0)
++	      fatal ("%s FAILED", editor_program);
++	  }
++    }
++
++    fclose (tmpfp);
++    safe_unlink (tmpname);
+ 
+     if (ofp)
+       {
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 6b6df63..16f8693 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -32,6 +32,7 @@ TESTS = \
+ 	crlf-handling \
+ 	dash-o-append \
+ 	deep-directories \
++	ed-style \
+ 	empty-files \
+ 	false-match \
+ 	fifo \
+diff --git a/tests/ed-style b/tests/ed-style
+new file mode 100644
+index 0000000..d8c0689
+--- /dev/null
++++ b/tests/ed-style
+@@ -0,0 +1,41 @@
++# Copyright (C) 2018 Free Software Foundation, Inc.
++#
++# Copying and distribution of this file, with or without modification,
++# in any medium, are permitted without royalty provided the copyright
++# notice and this notice are preserved.
++
++. $srcdir/test-lib.sh
++
++require cat
++use_local_patch
++use_tmpdir
++
++# ==============================================================
++
++cat > ed1.diff <<EOF
++0a
++foo
++.
++EOF
++
++check 'patch -e foo -i ed1.diff' <<EOF
++EOF
++
++check 'cat foo' <<EOF
++foo
++EOF
++
++cat > ed2.diff <<EOF
++1337a
++r !echo bar
++,p
++EOF
++
++check 'patch -e foo -i ed2.diff 2> /dev/null || echo "Status: $?"' <<EOF
++?
++Status: 2
++EOF
++
++check 'cat foo' <<EOF
++foo
++EOF
+-- 
+cgit v1.0-41-gc330
+
diff --git a/patch-2.7.6-CVE-2018-17942.patch b/patch-2.7.6-CVE-2018-17942.patch
new file mode 100644
index 0000000..5eb224e
--- /dev/null
+++ b/patch-2.7.6-CVE-2018-17942.patch
@@ -0,0 +1,14 @@
+diff -up patch-2.7.6/lib/vasnprintf.c.me patch-2.7.6/lib/vasnprintf.c
+--- patch-2.7.6/lib/vasnprintf.c.me	2018-11-26 14:02:03.401718842 +0100
++++ patch-2.7.6/lib/vasnprintf.c	2018-11-26 14:03:02.923913446 +0100
+@@ -860,7 +860,9 @@ convert_to_decimal (mpn_t a, size_t extr
+   size_t a_len = a.nlimbs;
+   /* 0.03345 is slightly larger than log(2)/(9*log(10)).  */
+   size_t c_len = 9 * ((size_t)(a_len * (GMP_LIMB_BITS * 0.03345f)) + 1);
+-  char *c_ptr = (char *) malloc (xsum (c_len, extra_zeroes));
++  /* We need extra_zeroes bytes for zeroes, followed by c_len bytes for the
++     digits of a, followed by 1 byte for the terminating NUL.  */
++  char *c_ptr = (char *) malloc (xsum (xsum (extra_zeroes, c_len), 1));
+   if (c_ptr != NULL)
+     {
+       char *d_ptr = c_ptr;
diff --git a/patch-2.7.6-CVE-2018-6952-fix-swapping-fake-lines-in-pch_swap.patch b/patch-2.7.6-CVE-2018-6952-fix-swapping-fake-lines-in-pch_swap.patch
new file mode 100644
index 0000000..257a300
--- /dev/null
+++ b/patch-2.7.6-CVE-2018-6952-fix-swapping-fake-lines-in-pch_swap.patch
@@ -0,0 +1,25 @@
+commit 9c986353e420ead6e706262bf204d6e03322c300
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Fri Aug 17 13:35:40 2018 +0200
+
+    Fix swapping fake lines in pch_swap
+    
+    * src/pch.c (pch_swap): Fix swapping p_bfake and p_efake when there is a
+    blank line in the middle of a context-diff hunk: that empty line stays
+    in the middle of the hunk and isn't swapped.
+    
+    Fixes: https://savannah.gnu.org/bugs/index.php?53133
+
+diff --git a/src/pch.c b/src/pch.c
+index e92bc64..a500ad9 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -2122,7 +2122,7 @@ pch_swap (void)
+     }
+     if (p_efake >= 0) {			/* fix non-freeable ptr range */
+ 	if (p_efake <= i)
+-	    n = p_end - i + 1;
++	    n = p_end - p_ptrn_lines;
+ 	else
+ 	    n = -i;
+ 	p_efake += n;
diff --git a/patch-2.7.6-CVE-2019-13636-symlinks.patch b/patch-2.7.6-CVE-2019-13636-symlinks.patch
new file mode 100644
index 0000000..bf3e603
--- /dev/null
+++ b/patch-2.7.6-CVE-2019-13636-symlinks.patch
@@ -0,0 +1,102 @@
+commit dce4683cbbe107a95f1f0d45fabc304acfb5d71a
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Mon Jul 15 16:21:48 2019 +0200
+
+    Don't follow symlinks unless --follow-symlinks is given
+    
+    * src/inp.c (plan_a, plan_b), src/util.c (copy_to_fd, copy_file,
+    append_to_file): Unless the --follow-symlinks option is given, open files with
+    the O_NOFOLLOW flag to avoid following symlinks.  So far, we were only doing
+    that consistently for input files.
+    * src/util.c (create_backup): When creating empty backup files, (re)create them
+    with O_CREAT | O_EXCL to avoid following symlinks in that case as well.
+
+diff --git a/src/inp.c b/src/inp.c
+index 32d0919..22d7473 100644
+--- a/src/inp.c
++++ b/src/inp.c
+@@ -238,8 +238,13 @@ plan_a (char const *filename)
+     {
+       if (S_ISREG (instat.st_mode))
+         {
+-	  int ifd = safe_open (filename, O_RDONLY|binary_transput, 0);
++	  int flags = O_RDONLY | binary_transput;
+ 	  size_t buffered = 0, n;
++	  int ifd;
++
++	  if (! follow_symlinks)
++	    flags |= O_NOFOLLOW;
++	  ifd = safe_open (filename, flags, 0);
+ 	  if (ifd < 0)
+ 	    pfatal ("can't open file %s", quotearg (filename));
+ 
+@@ -340,6 +345,7 @@ plan_a (char const *filename)
+ static void
+ plan_b (char const *filename)
+ {
++  int flags = O_RDONLY | binary_transput;
+   int ifd;
+   FILE *ifp;
+   int c;
+@@ -353,7 +359,9 @@ plan_b (char const *filename)
+ 
+   if (instat.st_size == 0)
+     filename = NULL_DEVICE;
+-  if ((ifd = safe_open (filename, O_RDONLY | binary_transput, 0)) < 0
++  if (! follow_symlinks)
++    flags |= O_NOFOLLOW;
++  if ((ifd = safe_open (filename, flags, 0)) < 0
+       || ! (ifp = fdopen (ifd, binary_transput ? "rb" : "r")))
+     pfatal ("Can't open file %s", quotearg (filename));
+   if (TMPINNAME_needs_removal)
+diff --git a/src/util.c b/src/util.c
+index 1cc08ba..fb38307 100644
+--- a/src/util.c
++++ b/src/util.c
+@@ -388,7 +388,7 @@ create_backup (char const *to, const struct stat *to_st, bool leave_original)
+ 
+ 	  try_makedirs_errno = ENOENT;
+ 	  safe_unlink (bakname);
+-	  while ((fd = safe_open (bakname, O_CREAT | O_WRONLY | O_TRUNC, 0666)) < 0)
++	  while ((fd = safe_open (bakname, O_CREAT | O_EXCL | O_WRONLY | O_TRUNC, 0666)) < 0)
+ 	    {
+ 	      if (errno != try_makedirs_errno)
+ 		pfatal ("Can't create file %s", quotearg (bakname));
+@@ -579,10 +579,13 @@ create_file (char const *file, int open_flags, mode_t mode,
+ static void
+ copy_to_fd (const char *from, int tofd)
+ {
++  int from_flags = O_RDONLY | O_BINARY;
+   int fromfd;
+   ssize_t i;
+ 
+-  if ((fromfd = safe_open (from, O_RDONLY | O_BINARY, 0)) < 0)
++  if (! follow_symlinks)
++    from_flags |= O_NOFOLLOW;
++  if ((fromfd = safe_open (from, from_flags, 0)) < 0)
+     pfatal ("Can't reopen file %s", quotearg (from));
+   while ((i = read (fromfd, buf, bufsize)) != 0)
+     {
+@@ -625,6 +628,8 @@ copy_file (char const *from, char const *to, struct stat *tost,
+   else
+     {
+       assert (S_ISREG (mode));
++      if (! follow_symlinks)
++	to_flags |= O_NOFOLLOW;
+       tofd = create_file (to, O_WRONLY | O_BINARY | to_flags, mode,
+ 			  to_dir_known_to_exist);
+       copy_to_fd (from, tofd);
+@@ -640,9 +645,12 @@ copy_file (char const *from, char const *to, struct stat *tost,
+ void
+ append_to_file (char const *from, char const *to)
+ {
++  int to_flags = O_WRONLY | O_APPEND | O_BINARY;
+   int tofd;
+ 
+-  if ((tofd = safe_open (to, O_WRONLY | O_BINARY | O_APPEND, 0)) < 0)
++  if (! follow_symlinks)
++    to_flags |= O_NOFOLLOW;
++  if ((tofd = safe_open (to, to_flags, 0)) < 0)
+     pfatal ("Can't reopen file %s", quotearg (to));
+   copy_to_fd (from, tofd);
+   if (close (tofd) != 0)
diff --git a/patch-2.7.6-CVE-2019-13638-invoked-ed-directly-instead-of-using-the-shell.patch b/patch-2.7.6-CVE-2019-13638-invoked-ed-directly-instead-of-using-the-shell.patch
new file mode 100644
index 0000000..4a9f2d6
--- /dev/null
+++ b/patch-2.7.6-CVE-2019-13638-invoked-ed-directly-instead-of-using-the-shell.patch
@@ -0,0 +1,33 @@
+commit 3fcd042d26d70856e826a42b5f93dc4854d80bf0
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Fri Apr 6 19:36:15 2018 +0200
+
+    Invoke ed directly instead of using the shell
+    
+    * src/pch.c (do_ed_script): Invoke ed directly instead of using a shell
+    command to avoid quoting vulnerabilities.
+
+diff --git a/src/pch.c b/src/pch.c
+index 4fd5a05..16e001a 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -2459,9 +2459,6 @@ do_ed_script (char const *inname, char const *outname,
+ 	    *outname_needs_removal = true;
+ 	    copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
+ 	  }
+-	sprintf (buf, "%s %s%s", editor_program,
+-		 verbosity == VERBOSE ? "" : "- ",
+-		 outname);
+ 	fflush (stdout);
+ 
+ 	pid = fork();
+@@ -2470,7 +2467,8 @@ do_ed_script (char const *inname, char const *outname,
+ 	else if (pid == 0)
+ 	  {
+ 	    dup2 (tmpfd, 0);
+-	    execl ("/bin/sh", "sh", "-c", buf, (char *) 0);
++	    assert (outname[0] != '!' && outname[0] != '-');
++	    execlp (editor_program, editor_program, "-", outname, (char  *) NULL);
+ 	    _exit (2);
+ 	  }
+ 	else
diff --git a/patch-2.7.6-abort_when_cleaning_up_fails.patch b/patch-2.7.6-abort_when_cleaning_up_fails.patch
new file mode 100644
index 0000000..56dbda8
--- /dev/null
+++ b/patch-2.7.6-abort_when_cleaning_up_fails.patch
@@ -0,0 +1,46 @@
+commit b7b028a77bd855f6f56b17c8837fc1cca77b469d
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Fri Jun 28 00:30:25 2019 +0200
+
+    Abort when cleaning up fails
+    
+    When a fatal error triggers during cleanup, another attempt will be made to
+    clean up, which will likely lead to the same fatal error.  So instead, bail out
+    when that happens.
+    src/patch.c (cleanup): Bail out when called recursively.
+    (main): There is no need to call output_files() before cleanup() as cleanup()
+    already does that.
+
+diff --git a/src/patch.c b/src/patch.c
+index 4616a48..02fd982 100644
+--- a/src/patch.c
++++ b/src/patch.c
+@@ -685,7 +685,6 @@ main (int argc, char **argv)
+     }
+     if (outstate.ofp && (ferror (outstate.ofp) || fclose (outstate.ofp) != 0))
+       write_fatal ();
+-    output_files (NULL);
+     cleanup ();
+     delete_files ();
+     if (somefailed)
+@@ -1991,7 +1990,6 @@ void
+ fatal_exit (int sig)
+ {
+   cleanup ();
+-
+   if (sig)
+     exit_with_signal (sig);
+ 
+@@ -2011,6 +2009,12 @@ remove_if_needed (char const *name, bool *needs_removal)
+ static void
+ cleanup (void)
+ {
++  static bool already_cleaning_up;
++
++  if (already_cleaning_up)
++    return;
++  already_cleaning_up = true;
++
+   remove_if_needed (TMPINNAME, &TMPINNAME_needs_removal);
+   remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal);
+   remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal);
diff --git a/patch-2.7.6-allow-input-files-to-be-missing-for-ed-style-patches.patch b/patch-2.7.6-allow-input-files-to-be-missing-for-ed-style-patches.patch
new file mode 100644
index 0000000..34b8fb4
--- /dev/null
+++ b/patch-2.7.6-allow-input-files-to-be-missing-for-ed-style-patches.patch
@@ -0,0 +1,28 @@
+commit b5a91a01e5d0897facdd0f49d64b76b0f02b43e1
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Fri Apr 6 11:34:51 2018 +0200
+
+    Allow input files to be missing for ed-style patches
+    
+    * src/pch.c (do_ed_script): Allow input files to be missing so that new
+    files will be created as with non-ed-style patches.
+
+diff --git a/src/pch.c b/src/pch.c
+index bc6278c..0c5cc26 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -2394,9 +2394,11 @@ do_ed_script (char const *inname, char const *outname,
+ 
+     if (! dry_run && ! skip_rest_of_patch) {
+ 	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
+-	assert (! inerrno);
+-	*outname_needs_removal = true;
+-	copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
++	if (inerrno != ENOENT)
++	  {
++	    *outname_needs_removal = true;
++	    copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
++	  }
+ 	sprintf (buf, "%s %s%s", editor_program,
+ 		 verbosity == VERBOSE ? "" : "- ",
+ 		 outname);
diff --git a/patch-2.7.6-avoid-invalid-memory-access-in-context-format-diffs.patch b/patch-2.7.6-avoid-invalid-memory-access-in-context-format-diffs.patch
new file mode 100644
index 0000000..39b59d1
--- /dev/null
+++ b/patch-2.7.6-avoid-invalid-memory-access-in-context-format-diffs.patch
@@ -0,0 +1,21 @@
+commit 15b158db3ae11cb835f2eb8d2eb48e09d1a4af48
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Mon Jul 15 19:10:02 2019 +0200
+
+    Avoid invalid memory access in context format diffs
+    
+    * src/pch.c (another_hunk): Avoid invalid memory access in context format
+    diffs.
+
+diff --git a/src/pch.c b/src/pch.c
+index a500ad9..cb54e03 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -1328,6 +1328,7 @@ another_hunk (enum diff difftype, bool rev)
+ 		  ptrn_prefix_context = context;
+ 		ptrn_suffix_context = context;
+ 		if (repl_beginning
++		    || p_end <= 0
+ 		    || (p_end
+ 			!= p_ptrn_lines + 1 + (p_Char[p_end - 1] == '\n')))
+ 		  {
diff --git a/patch-2.7.6-avoid-set_file_attributes-sign-conversion-warnings.patch b/patch-2.7.6-avoid-set_file_attributes-sign-conversion-warnings.patch
new file mode 100644
index 0000000..dd57034
--- /dev/null
+++ b/patch-2.7.6-avoid-set_file_attributes-sign-conversion-warnings.patch
@@ -0,0 +1,24 @@
+commit 3bbebbb29f6fbbf2988b9f2e75695b7c0b1f1c9b
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Wed Feb 7 12:01:22 2018 +0100
+
+    Avoid set_file_attributes sign conversion warnings
+    
+    * src/util.c (set_file_attributes): Avoid sign conversion warnings when
+    assigning -1 to uid_t / gid_t.
+
+diff --git a/src/util.c b/src/util.c
+index b1c7266..1cc08ba 100644
+--- a/src/util.c
++++ b/src/util.c
+@@ -256,8 +256,8 @@ set_file_attributes (char const *to, enum file_attributes attr,
+     }
+   if (attr & FA_IDS)
+     {
+-      static uid_t euid = -1;
+-      static gid_t egid = -1;
++      static uid_t euid = (uid_t)-1;
++      static gid_t egid = (gid_t)-1;
+       uid_t uid;
+       uid_t gid;
+ 
diff --git a/patch-2.7.6-avoid-warnings-gcc8.patch b/patch-2.7.6-avoid-warnings-gcc8.patch
new file mode 100644
index 0000000..5cc3366
--- /dev/null
+++ b/patch-2.7.6-avoid-warnings-gcc8.patch
@@ -0,0 +1,85 @@
+commit ae81be0024ea4eaf139b7ba57e9a8ce9e4a163ec
+Author: Jim Meyering <jim at meyering.net>
+Date:   Fri Apr 6 17:17:11 2018 -0700
+
+    maint: avoid warnings from GCC8
+    
+    Hi Andreas,
+    
+    I configured with --enable-gcc-warnings and bleeding-edge gcc
+    (version 8.0.1 20180406) and hit some warning-escalated-to-errors.
+    This fixes them:
+    
+    >From a71ddb200dbe7ac0f9258796b5a51979b2740e88 Mon Sep 17 00:00:00 2001
+    From: Jim Meyering <meyering at fb.com>
+    Date: Fri, 6 Apr 2018 16:47:00 -0700
+    Subject: [PATCH] maint: avoid warnings from GCC8
+    
+    * src/common.h (FALLTHROUGH): Define.
+    * src/patch.c (abort_hunk_context): Use FALLTHROUGH macro in place of
+    a comment.  This avoids a warning from -Wimplicit-fallthrough=.
+    * src/pch.c (do_ed_script): Add otherwise unnecessary initialization
+    to avoid warning from -Wmaybe-uninitialized.
+    (another_hunk): Use FALLTHROUGH macro here, too, twice.
+
+diff --git a/src/common.h b/src/common.h
+index ec50b40..904a3f8 100644
+--- a/src/common.h
++++ b/src/common.h
+@@ -218,3 +218,11 @@ bool merge_hunk (int hunk, struct outstate *, lin where, bool *);
+ #else
+ # define merge_hunk(hunk, outstate, where, somefailed) false
+ #endif
++
++#ifndef FALLTHROUGH
++# if __GNUC__ < 7
++#  define FALLTHROUGH ((void) 0)
++# else
++#  define FALLTHROUGH __attribute__ ((__fallthrough__))
++# endif
++#endif
+diff --git a/src/patch.c b/src/patch.c
+index 0fe6d72..1ae91d9 100644
+--- a/src/patch.c
++++ b/src/patch.c
+@@ -1381,7 +1381,7 @@ abort_hunk_context (bool header, bool reverse)
+ 	    break;
+ 	case ' ': case '-': case '+': case '!':
+ 	    fprintf (rejfp, "%c ", pch_char (i));
+-	    /* fall into */
++	    FALLTHROUGH;
+ 	case '\n':
+ 	    pch_write_line (i, rejfp);
+ 	    break;
+diff --git a/src/pch.c b/src/pch.c
+index 1055542..cda3dfa 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -1735,7 +1735,7 @@ another_hunk (enum diff difftype, bool rev)
+ 		break;
+ 	    case '=':
+ 		ch = ' ';
+-		/* FALL THROUGH */
++		FALLTHROUGH;
+ 	    case ' ':
+ 		if (fillsrc > p_ptrn_lines) {
+ 		    free(s);
+@@ -1756,7 +1756,7 @@ another_hunk (enum diff difftype, bool rev)
+ 		    p_end = fillsrc-1;
+ 		    return -1;
+ 		}
+-		/* FALL THROUGH */
++		FALLTHROUGH;
+ 	    case '+':
+ 		if (filldst > p_end) {
+ 		    free(s);
+@@ -2394,8 +2394,7 @@ do_ed_script (char const *inname, char const *outname,
+     size_t chars_read;
+     FILE *tmpfp = 0;
+     char const *tmpname;
+-    int tmpfd;
+-    pid_t pid;
++    int tmpfd = -1; /* placate gcc's -Wmaybe-uninitialized */
+     int exclusive = *outname_needs_removal ? 0 : O_EXCL;
+     char const **ed_argv;
+     int stdin_dup, status;
diff --git a/patch-2.7.6-avoid_invalid_memory_access.patch b/patch-2.7.6-avoid_invalid_memory_access.patch
new file mode 100644
index 0000000..39b59d1
--- /dev/null
+++ b/patch-2.7.6-avoid_invalid_memory_access.patch
@@ -0,0 +1,21 @@
+commit 15b158db3ae11cb835f2eb8d2eb48e09d1a4af48
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Mon Jul 15 19:10:02 2019 +0200
+
+    Avoid invalid memory access in context format diffs
+    
+    * src/pch.c (another_hunk): Avoid invalid memory access in context format
+    diffs.
+
+diff --git a/src/pch.c b/src/pch.c
+index a500ad9..cb54e03 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -1328,6 +1328,7 @@ another_hunk (enum diff difftype, bool rev)
+ 		  ptrn_prefix_context = context;
+ 		ptrn_suffix_context = context;
+ 		if (repl_beginning
++		    || p_end <= 0
+ 		    || (p_end
+ 			!= p_ptrn_lines + 1 + (p_Char[p_end - 1] == '\n')))
+ 		  {
diff --git a/patch-2.7.6-check-of-return-value-of-fwrite.patch b/patch-2.7.6-check-of-return-value-of-fwrite.patch
new file mode 100644
index 0000000..01242c0
--- /dev/null
+++ b/patch-2.7.6-check-of-return-value-of-fwrite.patch
@@ -0,0 +1,75 @@
+commit 1e9104c18019e7dc6b5590aea4b1d4f9d8ecfd56
+Author: Bruno Haible <bruno at clisp.org>
+Date:   Sat Apr 7 12:21:04 2018 +0200
+
+    Fix check of return value of fwrite().
+    
+    * src/patch.c (copy_till): Consider incomplete fwrite() write as an error.
+    * src/pch.c (pch_write_line, do_ed_script): Likewise.
+
+diff --git a/src/patch.c b/src/patch.c
+index 1ae91d9..3fcaec5 100644
+--- a/src/patch.c
++++ b/src/patch.c
+@@ -2,7 +2,7 @@
+ 
+ /* Copyright (C) 1984, 1985, 1986, 1987, 1988 Larry Wall
+ 
+-   Copyright (C) 1989-1993, 1997-1999, 2002-2003, 2006, 2009-2012 Free Software
++   Copyright (C) 1989-1993, 1997-1999, 2002-2003, 2006, 2009-2018 Free Software
+    Foundation, Inc.
+ 
+    This program is free software: you can redistribute it and/or modify
+@@ -1641,7 +1641,7 @@ copy_till (struct outstate *outstate, lin lastline)
+ 	if (size)
+ 	  {
+ 	    if ((! outstate->after_newline  &&  putc ('\n', fp) == EOF)
+-		|| ! fwrite (s, sizeof *s, size, fp))
++		|| fwrite (s, sizeof *s, size, fp) < size)
+ 	      write_fatal ();
+ 	    outstate->after_newline = s[size - 1] == '\n';
+ 	    outstate->zero_output = false;
+diff --git a/src/pch.c b/src/pch.c
+index cda3dfa..79a3c99 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -2279,8 +2279,11 @@ pfetch (lin line)
+ bool
+ pch_write_line (lin line, FILE *file)
+ {
+-  bool after_newline = (p_len[line] > 0) && (p_line[line][p_len[line] - 1] == '\n');
+-  if (! fwrite (p_line[line], sizeof (*p_line[line]), p_len[line], file))
++  bool after_newline =
++    (p_len[line] > 0) && (p_line[line][p_len[line] - 1] == '\n');
++
++  if (fwrite (p_line[line], sizeof (*p_line[line]), p_len[line], file)
++      < p_len[line])
+     write_fatal ();
+   return after_newline;
+ }
+@@ -2427,13 +2430,14 @@ do_ed_script (char const *inname, char const *outname,
+ 	ed_command_letter = get_ed_command_letter (buf);
+ 	if (ed_command_letter) {
+ 	    if (tmpfp)
+-		if (! fwrite (buf, sizeof *buf, chars_read, tmpfp))
++		if (fwrite (buf, sizeof *buf, chars_read, tmpfp) < chars_read)
+ 		    write_fatal ();
+ 	    if (ed_command_letter != 'd' && ed_command_letter != 's') {
+ 	        p_pass_comments_through = true;
+ 		while ((chars_read = get_line ()) != 0) {
+ 		    if (tmpfp)
+-			if (! fwrite (buf, sizeof *buf, chars_read, tmpfp))
++			if (fwrite (buf, sizeof *buf, chars_read, tmpfp)
++			    < chars_read)
+ 			    write_fatal ();
+ 		    if (chars_read == 2  &&  strEQ (buf, ".\n"))
+ 			break;
+@@ -2448,7 +2452,7 @@ do_ed_script (char const *inname, char const *outname,
+     }
+     if (dry_run || skip_rest_of_patch)
+       return;
+-    if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0
++    if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) < (size_t) 4
+ 	|| fflush (tmpfp) != 0)
+       write_fatal ();
+ 
diff --git a/patch-2.7.6-cleanups-in-do_ed_script.patch b/patch-2.7.6-cleanups-in-do_ed_script.patch
new file mode 100644
index 0000000..1fab381
--- /dev/null
+++ b/patch-2.7.6-cleanups-in-do_ed_script.patch
@@ -0,0 +1,91 @@
+commit 2a32bf09f5e9572da4be183bb0dbde8164351474
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Fri Apr 6 20:32:46 2018 +0200
+
+    Minor cleanups in do_ed_script
+    
+    * src/pch.c (do_ed_script): Minor cleanups.
+
+diff --git a/src/pch.c b/src/pch.c
+index 1f14624..1055542 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -2396,6 +2396,10 @@ do_ed_script (char const *inname, char const *outname,
+     char const *tmpname;
+     int tmpfd;
+     pid_t pid;
++    int exclusive = *outname_needs_removal ? 0 : O_EXCL;
++    char const **ed_argv;
++    int stdin_dup, status;
++
+ 
+     if (! dry_run && ! skip_rest_of_patch)
+       {
+@@ -2443,7 +2447,7 @@ do_ed_script (char const *inname, char const *outname,
+ 	    break;
+ 	}
+     }
+-    if (!tmpfp)
++    if (dry_run || skip_rest_of_patch)
+       return;
+     if (fwrite ("w\nq\n", sizeof (char), (size_t) 4, tmpfp) == 0
+ 	|| fflush (tmpfp) != 0)
+@@ -2452,36 +2456,29 @@ do_ed_script (char const *inname, char const *outname,
+     if (lseek (tmpfd, 0, SEEK_SET) == -1)
+       pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname));
+ 
+-    if (! dry_run && ! skip_rest_of_patch) {
+-	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
+-	char const **ed_argv;
+-	int stdin_dup, status;
+-
++    if (inerrno != ENOENT)
++      {
+ 	*outname_needs_removal = true;
+-	if (inerrno != ENOENT)
+-	  {
+-	    *outname_needs_removal = true;
+-	    copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
+-	  }
+-	fflush (stdout);
+-
+-	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");
+-    }
++	copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
++      }
++    fflush (stdout);
++
++    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);
+     safe_unlink (tmpname);
diff --git a/patch-2.7.6-crash-RLIMIT_NOFILE.patch b/patch-2.7.6-crash-RLIMIT_NOFILE.patch
new file mode 100644
index 0000000..c0f4fe1
--- /dev/null
+++ b/patch-2.7.6-crash-RLIMIT_NOFILE.patch
@@ -0,0 +1,84 @@
+commit 61d7788b83b302207a67b82786f4fd79e3538f30
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Thu Jun 27 11:10:43 2019 +0200
+
+    Don't crash when RLIMIT_NOFILE is set to RLIM_INFINITY
+    
+    * src/safe.c (min_cached_fds): Define minimum number of cached dir file
+    descriptors.
+    (max_cached_fds): Change type to rlim_t to allow storing RLIM_INFINITY.
+    (init_dirfd_cache): Set max_cached_fds to RLIM_INFINITY when RLIMIT_NOFILE is
+    RLIM_INFINITY.  Set the initial hash table size to min_cached_fds, independent
+    of RLIMIT_NOFILE: patches commonly only affect one or a few files, so a small
+    hash table will usually suffice; if needed, the hash table will grow.
+    (insert_cached_dirfd): Don't shrink the cache when max_cached_fds is
+    RLIM_INFINITY.
+
+diff --git a/src/safe.c b/src/safe.c
+index 5a7202f..f147b0e 100644
+--- a/src/safe.c
++++ b/src/safe.c
+@@ -67,7 +67,8 @@ struct cached_dirfd {
+ };
+ 
+ static Hash_table *cached_dirfds = NULL;
+-static size_t max_cached_fds;
++static rlim_t min_cached_fds = 8;
++static rlim_t max_cached_fds;
+ LIST_HEAD (lru_list);
+ 
+ static size_t hash_cached_dirfd (const void *entry, size_t table_size)
+@@ -98,11 +99,17 @@ static void init_dirfd_cache (void)
+ {
+   struct rlimit nofile;
+ 
+-  max_cached_fds = 8;
+   if (getrlimit (RLIMIT_NOFILE, &nofile) == 0)
+-    max_cached_fds = MAX (nofile.rlim_cur / 4, max_cached_fds);
++    {
++      if (nofile.rlim_cur == RLIM_INFINITY)
++        max_cached_fds = RLIM_INFINITY;
++      else
++	max_cached_fds = MAX (nofile.rlim_cur / 4, min_cached_fds);
++    }
++  else
++    max_cached_fds = min_cached_fds;
+ 
+-  cached_dirfds = hash_initialize (max_cached_fds,
++  cached_dirfds = hash_initialize (min_cached_fds,
+ 				   NULL,
+ 				   hash_cached_dirfd,
+ 				   compare_cached_dirfds,
+@@ -148,20 +155,23 @@ static void insert_cached_dirfd (struct cached_dirfd *entry, int keepfd)
+   if (cached_dirfds == NULL)
+     init_dirfd_cache ();
+ 
+-  /* Trim off the least recently used entries */
+-  while (hash_get_n_entries (cached_dirfds) >= max_cached_fds)
++  if (max_cached_fds != RLIM_INFINITY)
+     {
+-      struct cached_dirfd *last =
+-	list_entry (lru_list.prev, struct cached_dirfd, lru_link);
+-      if (&last->lru_link == &lru_list)
+-	break;
+-      if (last->fd == keepfd)
++      /* Trim off the least recently used entries */
++      while (hash_get_n_entries (cached_dirfds) >= max_cached_fds)
+ 	{
+-	  last = list_entry (last->lru_link.prev, struct cached_dirfd, lru_link);
++	  struct cached_dirfd *last =
++	    list_entry (lru_list.prev, struct cached_dirfd, lru_link);
+ 	  if (&last->lru_link == &lru_list)
+ 	    break;
++	  if (last->fd == keepfd)
++	    {
++	      last = list_entry (last->lru_link.prev, struct cached_dirfd, lru_link);
++	      if (&last->lru_link == &lru_list)
++		break;
++	    }
++	  remove_cached_dirfd (last);
+ 	}
+-      remove_cached_dirfd (last);
+     }
+ 
+   /* Only insert if the parent still exists. */
diff --git a/patch-2.7.6-dont-leak-temporary-file-on-failed-ed-style-patch.patch b/patch-2.7.6-dont-leak-temporary-file-on-failed-ed-style-patch.patch
new file mode 100644
index 0000000..d0c7869
--- /dev/null
+++ b/patch-2.7.6-dont-leak-temporary-file-on-failed-ed-style-patch.patch
@@ -0,0 +1,95 @@
+commit 19599883ffb6a450d2884f081f8ecf68edbed7ee
+Author: Jean Delvare <jdelvare at suse.de>
+Date:   Thu May 3 14:31:55 2018 +0200
+
+    Don't leak temporary file on failed ed-style patch
+    
+    Now that we write ed-style patches to a temporary file before we
+    apply them, we need to ensure that the temporary file is removed
+    before we leave, even on fatal error.
+    
+    * src/pch.c (do_ed_script): Use global TMPEDNAME instead of local
+      tmpname. Don't unlink the file directly, instead tag it for removal
+      at exit time.
+    * src/patch.c (cleanup): Unlink TMPEDNAME at exit.
+    
+    This closes bug #53820:
+    https://savannah.gnu.org/bugs/index.php?53820
+    
+    Fixes: 123eaff0d5d1 ("Fix arbitrary command execution in ed-style patches (CVE-2018-1000156)")
+
+diff --git a/src/common.h b/src/common.h
+index 904a3f8..53c5e32 100644
+--- a/src/common.h
++++ b/src/common.h
+@@ -94,10 +94,12 @@ XTERN char const *origsuff;
+ XTERN char const * TMPINNAME;
+ XTERN char const * TMPOUTNAME;
+ XTERN char const * TMPPATNAME;
++XTERN char const * TMPEDNAME;
+ 
+ XTERN bool TMPINNAME_needs_removal;
+ XTERN bool TMPOUTNAME_needs_removal;
+ XTERN bool TMPPATNAME_needs_removal;
++XTERN bool TMPEDNAME_needs_removal;
+ 
+ #ifdef DEBUGGING
+ XTERN int debug;
+diff --git a/src/patch.c b/src/patch.c
+index 3fcaec5..9146597 100644
+--- a/src/patch.c
++++ b/src/patch.c
+@@ -1999,6 +1999,7 @@ cleanup (void)
+   remove_if_needed (TMPINNAME, &TMPINNAME_needs_removal);
+   remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal);
+   remove_if_needed (TMPPATNAME, &TMPPATNAME_needs_removal);
++  remove_if_needed (TMPEDNAME, &TMPEDNAME_needs_removal);
+   remove_if_needed (TMPREJNAME, &TMPREJNAME_needs_removal);
+   output_files (NULL);
+ }
+diff --git a/src/pch.c b/src/pch.c
+index 79a3c99..1bb3153 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -2396,7 +2396,6 @@ do_ed_script (char const *inname, char const *outname,
+     file_offset beginning_of_this_line;
+     size_t chars_read;
+     FILE *tmpfp = 0;
+-    char const *tmpname;
+     int tmpfd = -1; /* placate gcc's -Wmaybe-uninitialized */
+     int exclusive = *outname_needs_removal ? 0 : O_EXCL;
+     char const **ed_argv;
+@@ -2411,12 +2410,13 @@ do_ed_script (char const *inname, char const *outname,
+ 	   invalid commands and treats the next line as a new command, which
+ 	   can lead to arbitrary command execution.  */
+ 
+-	tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0);
++	tmpfd = make_tempfile (&TMPEDNAME, 'e', NULL, O_RDWR | O_BINARY, 0);
+ 	if (tmpfd == -1)
+-	  pfatal ("Can't create temporary file %s", quotearg (tmpname));
++	  pfatal ("Can't create temporary file %s", quotearg (TMPEDNAME));
++	TMPEDNAME_needs_removal = true;
+ 	tmpfp = fdopen (tmpfd, "w+b");
+ 	if (! tmpfp)
+-	  pfatal ("Can't open stream for file %s", quotearg (tmpname));
++	  pfatal ("Can't open stream for file %s", quotearg (TMPEDNAME));
+       }
+ 
+     for (;;) {
+@@ -2457,7 +2457,7 @@ do_ed_script (char const *inname, char const *outname,
+       write_fatal ();
+ 
+     if (lseek (tmpfd, 0, SEEK_SET) == -1)
+-      pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname));
++      pfatal ("Can't rewind to the beginning of file %s", quotearg (TMPEDNAME));
+ 
+     if (inerrno != ENOENT)
+       {
+@@ -2484,7 +2484,6 @@ do_ed_script (char const *inname, char const *outname,
+       pfatal ("Failed to duplicate standard input");
+ 
+     fclose (tmpfp);
+-    safe_unlink (tmpname);
+ 
+     if (ofp)
+       {
diff --git a/patch-2.7.6-dont-leak-temporary-file-on-failed-multi-file-ed-style-patch.patch b/patch-2.7.6-dont-leak-temporary-file-on-failed-multi-file-ed-style-patch.patch
new file mode 100644
index 0000000..959a59c
--- /dev/null
+++ b/patch-2.7.6-dont-leak-temporary-file-on-failed-multi-file-ed-style-patch.patch
@@ -0,0 +1,71 @@
+commit 369dcccdfa6336e5a873d6d63705cfbe04c55727
+Author: Jean Delvare <jdelvare at suse.de>
+Date:   Mon May 7 15:14:45 2018 +0200
+
+    Don't leak temporary file on failed multi-file ed-style patch
+    
+    The previous fix worked fine with single-file ed-style patches, but
+    would still leak temporary files in the case of multi-file ed-style
+    patch. Fix that case as well, and extend the test case to check for
+    it.
+    
+    * src/patch.c (main): Unlink TMPEDNAME if needed before moving to
+      the next file in a patch.
+    
+    This closes bug #53820:
+    https://savannah.gnu.org/bugs/index.php?53820
+    
+    Fixes: 123eaff0d5d1 ("Fix arbitrary command execution in ed-style patches (CVE-2018-1000156)")
+    Fixes: 19599883ffb6 ("Don't leak temporary file on failed ed-style patch")
+
+diff --git a/src/patch.c b/src/patch.c
+index 9146597..81c7a02 100644
+--- a/src/patch.c
++++ b/src/patch.c
+@@ -236,6 +236,7 @@ main (int argc, char **argv)
+ 	    }
+ 	  remove_if_needed (TMPOUTNAME, &TMPOUTNAME_needs_removal);
+ 	}
++      remove_if_needed (TMPEDNAME, &TMPEDNAME_needs_removal);
+ 
+       if (! skip_rest_of_patch && ! file_type)
+ 	{
+diff --git a/tests/ed-style b/tests/ed-style
+index 6b6ef9d..504e6e5 100644
+--- a/tests/ed-style
++++ b/tests/ed-style
+@@ -38,3 +38,34 @@ EOF
+ check 'cat foo' <<EOF
+ foo
+ EOF
++
++# Test the case where one ed-style patch modifies several files
++
++cat > ed3.diff <<EOF
++--- foo
+++++ foo
++1c
++bar
++.
++--- baz
+++++ baz
++0a
++baz
++.
++EOF
++
++# Apparently we can't create a file with such a patch, while it works fine
++# when the file name is provided on the command line
++cat > baz <<EOF
++EOF
++
++check 'patch -e -i ed3.diff' <<EOF
++EOF
++
++check 'cat foo' <<EOF
++bar
++EOF
++
++check 'cat baz' <<EOF
++baz
++EOF
diff --git a/patch-2.7.6-failed_assertion.patch b/patch-2.7.6-failed_assertion.patch
new file mode 100644
index 0000000..f3d9022
--- /dev/null
+++ b/patch-2.7.6-failed_assertion.patch
@@ -0,0 +1,29 @@
+commit 76e775847f4954b63dc72afe34d9d921c6688b31
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Tue Jul 16 01:16:28 2019 +0200
+
+    Fix failed assertion 'outstate->after_newline'
+    
+    The assertion triggers when the -o FILE option is used, more than one output
+    file is written into FILE, and one of those files (except the last one) ends in
+    the middle of a line.
+    * src/patch.c (main): Fix the case described above.
+
+diff --git a/src/patch.c b/src/patch.c
+index 02fd982..3794319 100644
+--- a/src/patch.c
++++ b/src/patch.c
+@@ -369,6 +369,13 @@ main (int argc, char **argv)
+ 	    /* outstate.ofp now owns the file descriptor */
+ 	    outfd = -1;
+ 	  }
++	else
++	  {
++	    /* When writing to a single output file (-o FILE), always pretend
++	       that the output file ends in a newline.  Otherwise, when another
++	       file is written to the same output file, apply_hunk will fail.  */
++	    outstate.after_newline = true;
++	  }
+ 
+ 	/* find out where all the lines are */
+ 	if (!skip_rest_of_patch) {
diff --git a/patch-2.7.6-fix-ed-style-test-failure.patch b/patch-2.7.6-fix-ed-style-test-failure.patch
new file mode 100644
index 0000000..609516d
--- /dev/null
+++ b/patch-2.7.6-fix-ed-style-test-failure.patch
@@ -0,0 +1,22 @@
+commit 458ac51a05426c1af9aa6bf1342ecf60728c19b4
+Author: Bruno Haible <bruno at clisp.org>
+Date:   Sat Apr 7 12:34:03 2018 +0200
+
+    Fix 'ed-style' test failure.
+    
+    * tests/ed-style: Remove '?' line from expected output.
+
+diff --git a/tests/ed-style b/tests/ed-style
+index d8c0689..6b6ef9d 100644
+--- a/tests/ed-style
++++ b/tests/ed-style
+@@ -31,8 +31,7 @@ r !echo bar
+ ,p
+ EOF
+ 
+-check 'patch -e foo -i ed2.diff 2> /dev/null || echo "Status: $?"' <<EOF
+-?
++check 'patch -e foo -i ed2.diff > /dev/null 2> /dev/null || echo "Status: $?"' <<EOF
+ Status: 2
+ EOF
+ 
diff --git a/patch-2.7.6-fix-korn-shell-incompatibility.patch b/patch-2.7.6-fix-korn-shell-incompatibility.patch
new file mode 100644
index 0000000..ec2c23f
--- /dev/null
+++ b/patch-2.7.6-fix-korn-shell-incompatibility.patch
@@ -0,0 +1,21 @@
+commit 074e2395f81d0ecaa66b71a6c228c70b49db72e5
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Wed Feb 7 17:05:00 2018 +0100
+
+    Test suite: fix Korn shell incompatibility
+    
+    tests/merge: In a Korn shell, shift apparently fails when $# is 0.
+
+diff --git a/tests/merge b/tests/merge
+index b628891..e950b92 100644
+--- a/tests/merge
++++ b/tests/merge
+@@ -32,7 +32,7 @@ x2() {
+ 	shift
+     done > b.sed
+     echo "$body" | sed -f b.sed > b
+-    shift
++    test $# -eq 0 || shift
+     while test $# -gt 0 ; do
+ 	echo "$1"
+ 	shift
diff --git a/patch-2.7.6-fix-segfault-with-mangled-rename-patch.patch b/patch-2.7.6-fix-segfault-with-mangled-rename-patch.patch
new file mode 100644
index 0000000..c5bcba5
--- /dev/null
+++ b/patch-2.7.6-fix-segfault-with-mangled-rename-patch.patch
@@ -0,0 +1,24 @@
+commit f290f48a621867084884bfff87f8093c15195e6a
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Mon Feb 12 16:48:24 2018 +0100
+
+    Fix segfault with mangled rename patch
+    
+    http://savannah.gnu.org/bugs/?53132
+    * src/pch.c (intuit_diff_type): Ensure that two filenames are specified
+    for renames and copies (fix the existing check).
+
+diff --git a/src/pch.c b/src/pch.c
+index ff9ed2c..bc6278c 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -974,7 +974,8 @@ intuit_diff_type (bool need_header, mode_t *p_file_type)
+     if ((pch_rename () || pch_copy ())
+ 	&& ! inname
+ 	&& ! ((i == OLD || i == NEW) &&
+-	      p_name[! reverse] &&
++	      p_name[reverse] && p_name[! reverse] &&
++	      name_is_valid (p_name[reverse]) &&
+ 	      name_is_valid (p_name[! reverse])))
+       {
+ 	say ("Cannot %s file without two valid file names\n", pch_rename () ? "rename" : "copy");
diff --git a/patch-2.7.6-improve_support_for_memory_leak_detection.patch b/patch-2.7.6-improve_support_for_memory_leak_detection.patch
new file mode 100644
index 0000000..fc92ff0
--- /dev/null
+++ b/patch-2.7.6-improve_support_for_memory_leak_detection.patch
@@ -0,0 +1,48 @@
+commit 2b584aec9e5f2806b1eccadcabe7e901fcfa0b0a
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Thu Jun 27 11:02:02 2019 +0200
+
+    Improve support for memory leak detection
+    
+    When building with the address sanitizer on, free some more resources before
+    exiting.  (This is unnecessary when not looking for memory leaks.)
+    * src/patch.c (init_files_to_delete): Add dispose function for freeing
+    filenames.
+
+diff --git a/src/patch.c b/src/patch.c
+index 81c7a02..4616a48 100644
+--- a/src/patch.c
++++ b/src/patch.c
+@@ -36,6 +36,10 @@
+ #include <minmax.h>
+ #include <safe.h>
+ 
++#ifdef __SANITIZE_ADDRESS__
++# define FREE_BEFORE_EXIT
++#endif
++
+ /* procedures */
+ 
+ static FILE *create_output_file (char const *, int);
+@@ -1777,10 +1781,20 @@ struct file_to_delete {
+ 
+ static gl_list_t files_to_delete;
+ 
++#ifdef FREE_BEFORE_EXIT
++void dispose_file_to_delete (const void *elt)
++{
++	free ((void *) elt);
++}
++#else
++#define dispose_file_to_delete NULL
++#endif
++
+ static void
+ init_files_to_delete (void)
+ {
+-  files_to_delete = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL, true);
++  files_to_delete = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL,
++					  dispose_file_to_delete, true);
+ }
+ 
+ static void
diff --git a/patch-2.7.6-make-debug-output-more-useful.patch b/patch-2.7.6-make-debug-output-more-useful.patch
new file mode 100644
index 0000000..69e79c2
--- /dev/null
+++ b/patch-2.7.6-make-debug-output-more-useful.patch
@@ -0,0 +1,40 @@
+commit ff81775f4eb6ab9a91b75e4031e8216654c0c76a
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Fri Aug 17 10:31:22 2018 +0200
+
+    Make the (debug & 2) output more useful
+    
+    * src/pch.c (another_hunk): In the (debug & 2) output, fix how empty
+    lines that are not part of the patch context are printed.  Also, add
+    newlines to lines that are missing them to keep the output readable.
+
+diff --git a/src/pch.c b/src/pch.c
+index 1bb3153..e92bc64 100644
+--- a/src/pch.c
++++ b/src/pch.c
+@@ -1916,8 +1916,13 @@ another_hunk (enum diff difftype, bool rev)
+ 	lin i;
+ 
+ 	for (i = 0; i <= p_end + 1; i++) {
+-	    fprintf (stderr, "%s %c",
+-		     format_linenum (numbuf0, i),
++	    fputs (format_linenum (numbuf0, i), stderr);
++	    if (p_Char[i] == '\n')
++	      {
++	        fputc('\n', stderr);
++		continue;
++	      }
++	    fprintf (stderr, " %c",
+ 		     p_Char[i]);
+ 	    if (p_Char[i] == '*')
+ 	      fprintf (stderr, " %s,%s\n",
+@@ -1930,7 +1935,8 @@ another_hunk (enum diff difftype, bool rev)
+ 	    else if (p_Char[i] != '^')
+ 	      {
+ 		fputs(" |", stderr);
+-		pch_write_line (i, stderr);
++		if (! pch_write_line (i, stderr))
++		  fputc('\n', stderr);
+ 	      }
+ 	    else
+ 	      fputc('\n', stderr);
diff --git a/patch-2.7.6-skip-ed-test-when-the-ed-utility-is-not-installed.patch b/patch-2.7.6-skip-ed-test-when-the-ed-utility-is-not-installed.patch
new file mode 100644
index 0000000..e9738ad
--- /dev/null
+++ b/patch-2.7.6-skip-ed-test-when-the-ed-utility-is-not-installed.patch
@@ -0,0 +1,20 @@
+commit a5b442ce01b80a758606ede316f739426a12bc33
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Thu Jun 27 11:09:31 2019 +0200
+
+    Skip "ed" test when the ed utility is not installed
+    
+    * tests/ed-style: Require ed.
+
+diff --git a/tests/ed-style b/tests/ed-style
+index 504e6e5..9907cb6 100644
+--- a/tests/ed-style
++++ b/tests/ed-style
+@@ -7,6 +7,7 @@
+ . $srcdir/test-lib.sh
+ 
+ require cat
++require ed
+ use_local_patch
+ use_tmpdir
+ 
diff --git a/patch-2.7.6-switch-from-fork-execlp-to-execute.patch b/patch-2.7.6-switch-from-fork-execlp-to-execute.patch
new file mode 100644
index 0000000..4212552
--- /dev/null
+++ b/patch-2.7.6-switch-from-fork-execlp-to-execute.patch
@@ -0,0 +1,1774 @@
+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 at 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 at 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 at 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 at 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 at 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 at 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 at 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 at 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);
diff --git a/patch-2.7.6-test-suite-compatibility-fixes.patch b/patch-2.7.6-test-suite-compatibility-fixes.patch
new file mode 100644
index 0000000..ce3e36f
--- /dev/null
+++ b/patch-2.7.6-test-suite-compatibility-fixes.patch
@@ -0,0 +1,124 @@
+commit f6bc5b14bd193859851d15a049bafb1007acd288
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Wed Feb 7 12:10:41 2018 +0100
+
+    Test suite compatibility fixes
+    
+    * tests/crlf-handling, tests/git-cleanup, tests/test-lib.sh: Use printf
+    instead of echo -e / echo -n for compatibility with systems that don't
+    support these echo options.
+    * tests/merge: Minor other cleanups.
+
+diff --git a/tests/crlf-handling b/tests/crlf-handling
+index 239149c..c192cac 100644
+--- a/tests/crlf-handling
++++ b/tests/crlf-handling
+@@ -14,7 +14,7 @@ use_local_patch
+ use_tmpdir
+ 
+ lf2crlf() {
+-    while read l; do echo -e "$l\r"; done
++    while read l; do printf "%s\r\n" "$l"; done
+ }
+ 
+ echo 1 > a
+diff --git a/tests/git-cleanup b/tests/git-cleanup
+index 2e3e4c6..ca527a1 100644
+--- a/tests/git-cleanup
++++ b/tests/git-cleanup
+@@ -36,8 +36,8 @@ BAD PATCH
+ EOF
+ 
+ echo 1 > f
+-echo -n '' > g
+-echo -n '' > h
++printf '' > g
++printf '' > h
+ 
+ check 'patch -f -i 1.diff || echo status: $?' <<EOF
+ patching file f
+diff --git a/tests/merge b/tests/merge
+index 22d787b..b628891 100644
+--- a/tests/merge
++++ b/tests/merge
+@@ -30,30 +30,28 @@ x2() {
+     while test $# -gt 0 && test "$1" != -- ; do
+ 	echo "$1"
+ 	shift
+-    done > a.sed
+-    echo "$body" | sed -f a.sed > b
++    done > b.sed
++    echo "$body" | sed -f b.sed > b
+     shift
+     while test $# -gt 0 ; do
+ 	echo "$1"
+ 	shift
+-    done > b.sed
+-    echo "$body" | sed -f b.sed > c
+-    rm -f a.sed b.sed
++    done > c.sed
++    echo "$body" | sed -f c.sed > c
++    rm -f b.sed c.sed
+     output=`diff -u a b | patch $ARGS -f c`
+     status=$?
+     echo "$output" | sed -e '/^$/d' -e '/^patching file c$/d'
+     cat c
+-    test $status == 0 || echo "Status: $status"
++    test $status = 0 || echo "Status: $status"
+ }
+ 
+ x() {
+-    ARGS="$ARGS --merge" x2 "$@"
++    ARGS="--merge" x2 "$@"
+     echo
+-    ARGS="$ARGS --merge=diff3" x2 "$@"
++    ARGS="--merge=diff3" x2 "$@"
+ }
+ 
+-unset ARGS
+-
+ # ==============================================================
+ 
+ check 'x 3' <<EOF
+diff --git a/tests/test-lib.sh b/tests/test-lib.sh
+index be0d7e3..661da52 100644
+--- a/tests/test-lib.sh
++++ b/tests/test-lib.sh
+@@ -41,7 +41,7 @@ use_local_patch() {
+ 
+     eval 'patch() {
+ 	if test -n "$GDB" ; then
+-	  echo -e "\n" >&3
++	  printf "\n\n" >&3
+ 	  gdbserver localhost:53153 $PATCH "$@" 2>&3
+ 	else
+           $PATCH "$@"
+@@ -113,22 +113,15 @@ cleanup() {
+     exit $status
+ }
+ 
+-if test -z "`echo -n`"; then
+-    if eval 'test -n "${BASH_LINENO[0]}" 2>/dev/null'; then
+-	eval '
+-	    _start_test() {
+-		echo -n "[${BASH_LINENO[2]}] $* -- "
+-	    }'
+-    else
+-	eval '
+-	    _start_test() {
+-		echo -n "* $* -- "
+-	    }'
+-    fi
++if eval 'test -n "${BASH_LINENO[0]}" 2>/dev/null'; then
++    eval '
++	_start_test() {
++	    printf "[${BASH_LINENO[2]}] %s -- " "$*"
++	}'
+ else
+     eval '
+ 	_start_test() {
+-	    echo "* $*"
++	    printf "* %s -- " "$*"
+ 	}'
+ fi
+ 
diff --git a/patch-CVE-2018-1000156.patch b/patch-CVE-2018-1000156.patch
index fc3b0c2..988964f 100644
--- a/patch-CVE-2018-1000156.patch
+++ b/patch-CVE-2018-1000156.patch
@@ -1,6 +1,19 @@
-diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c
---- patch-2.7.6/src/pch.c.CVE-2018-1000156	2018-02-03 12:41:49.000000000 +0000
-+++ patch-2.7.6/src/pch.c	2018-05-03 12:50:43.374036905 +0100
+commit 123eaff0d5d1aebe128295959435b9ca5909c26d
+Author: Andreas Gruenbacher <agruen at gnu.org>
+Date:   Fri Apr 6 12:14:49 2018 +0200
+
+    Fix arbitrary command execution in ed-style patches (CVE-2018-1000156)
+    
+    * src/pch.c (do_ed_script): Write ed script to a temporary file instead
+    of piping it to ed: this will cause ed to abort on invalid commands
+    instead of rejecting them and carrying on.
+    * tests/ed-style: New test case.
+    * tests/Makefile.am (TESTS): Add test case.
+
+diff --git a/src/pch.c b/src/pch.c
+index 0c5cc26..4fd5a05 100644
+--- a/src/pch.c
++++ b/src/pch.c
 @@ -33,6 +33,7 @@
  # include <io.h>
  #endif
@@ -9,7 +22,7 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c
  
  #define INITHUNKMAX 125			/* initial dynamic allocation size */
  
-@@ -2388,22 +2389,28 @@ do_ed_script (char const *inname, char c
+@@ -2389,24 +2390,28 @@ do_ed_script (char const *inname, char const *outname,
      static char const editor_program[] = EDITOR_PROGRAM;
  
      file_offset beginning_of_this_line;
@@ -17,30 +30,32 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c
      size_t chars_read;
 +    FILE *tmpfp = 0;
 +    char const *tmpname;
-+    int tmpfd = -1;
++    int tmpfd;
 +    pid_t pid;
 +
 +    if (! dry_run && ! skip_rest_of_patch)
 +      {
-+       /* Write ed script to a temporary file.  This causes ed to abort on
-+          invalid commands such as when line numbers or ranges exceed the
-+          number of available lines.  When ed reads from a pipe, it rejects
-+          invalid commands and treats the next line as a new command, which
-+          can lead to arbitrary command execution.  */
++	/* Write ed script to a temporary file.  This causes ed to abort on
++	   invalid commands such as when line numbers or ranges exceed the
++	   number of available lines.  When ed reads from a pipe, it rejects
++	   invalid commands and treats the next line as a new command, which
++	   can lead to arbitrary command execution.  */
 +
-+       tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0);
-+       if (tmpfd == -1)
-+         pfatal ("Can't create temporary file %s", quotearg (tmpname));
-+       tmpfp = fdopen (tmpfd, "w+b");
-+       if (! tmpfp)
-+         pfatal ("Can't open stream for file %s", quotearg (tmpname));
++	tmpfd = make_tempfile (&tmpname, 'e', NULL, O_RDWR | O_BINARY, 0);
++	if (tmpfd == -1)
++	  pfatal ("Can't create temporary file %s", quotearg (tmpname));
++	tmpfp = fdopen (tmpfd, "w+b");
++	if (! tmpfp)
++	  pfatal ("Can't open stream for file %s", quotearg (tmpname));
 +      }
  
 -    if (! dry_run && ! skip_rest_of_patch) {
 -	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
--	assert (! inerrno);
--	*outname_needs_removal = true;
--	copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
+-	if (inerrno != ENOENT)
+-	  {
+-	    *outname_needs_removal = true;
+-	    copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
+-	  }
 -	sprintf (buf, "%s %s%s", editor_program,
 -		 verbosity == VERBOSE ? "" : "- ",
 -		 outname);
@@ -52,7 +67,7 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c
      for (;;) {
  	char ed_command_letter;
  	beginning_of_this_line = file_tell (pfp);
-@@ -2414,14 +2421,14 @@ do_ed_script (char const *inname, char c
+@@ -2417,14 +2422,14 @@ do_ed_script (char const *inname, char const *outname,
  	}
  	ed_command_letter = get_ed_command_letter (buf);
  	if (ed_command_letter) {
@@ -71,7 +86,7 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c
  			    write_fatal ();
  		    if (chars_read == 2  &&  strEQ (buf, ".\n"))
  			break;
-@@ -2434,13 +2441,50 @@ do_ed_script (char const *inname, char c
+@@ -2437,13 +2442,49 @@ do_ed_script (char const *inname, char const *outname,
  	    break;
  	}
      }
@@ -90,47 +105,60 @@ diff -up patch-2.7.6/src/pch.c.CVE-2018-1000156 patch-2.7.6/src/pch.c
 +      pfatal ("Can't rewind to the beginning of file %s", quotearg (tmpname));
 +
 +    if (! dry_run && ! skip_rest_of_patch) {
-+       int exclusive = *outname_needs_removal ? 0 : O_EXCL;
-+       *outname_needs_removal = true;
-+       if (inerrno != ENOENT)
-+         {
-+           *outname_needs_removal = true;
-+           copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
-+         }
-+       sprintf (buf, "%s %s%s", editor_program,
-+                verbosity == VERBOSE ? "" : "- ",
-+                outname);
-+       fflush (stdout);
++	int exclusive = *outname_needs_removal ? 0 : O_EXCL;
++	*outname_needs_removal = true;
++	if (inerrno != ENOENT)
++	  {
++	    *outname_needs_removal = true;
++	    copy_file (inname, outname, 0, exclusive, instat.st_mode, true);
++	  }
++	sprintf (buf, "%s %s%s", editor_program,
++		 verbosity == VERBOSE ? "" : "- ",
++		 outname);
++	fflush (stdout);
 +
-+       pid = fork();
-+       if (pid == -1)
-+         pfatal ("Can't fork");
-+       else if (pid == 0)
-+         {
-+           dup2 (tmpfd, 0);
-+           execl ("/bin/sh", "sh", "-c", buf, (char *) 0);
-+           _exit (2);
-+         }
-+       else
-+         {
-+           int wstatus;
-+           if (waitpid (pid, &wstatus, 0) == -1
-+               || ! WIFEXITED (wstatus)
-+               || WEXITSTATUS (wstatus) != 0)
-+             fatal ("%s FAILED", editor_program);
-+         }
++	pid = fork();
++	if (pid == -1)
++	  pfatal ("Can't fork");
++	else if (pid == 0)
++	  {
++	    dup2 (tmpfd, 0);
++	    execl ("/bin/sh", "sh", "-c", buf, (char *) 0);
++	    _exit (2);
++	  }
++	else
++	  {
++	    int wstatus;
++	    if (waitpid (pid, &wstatus, 0) == -1
++	        || ! WIFEXITED (wstatus)
++		|| WEXITSTATUS (wstatus) != 0)
++	      fatal ("%s FAILED", editor_program);
++	  }
 +    }
 +
 +    fclose (tmpfp);
-+    unlink (tmpname);
-+    free((char*) tmpname);
++    safe_unlink (tmpname);
  
      if (ofp)
        {
-diff -up patch-2.7.6/tests/ed-style.CVE-2018-1000156 patch-2.7.6/tests/ed-style
---- patch-2.7.6/tests/ed-style.CVE-2018-1000156	2018-05-03 12:50:28.988937938 +0100
-+++ patch-2.7.6/tests/ed-style	2018-05-03 12:51:35.841397873 +0100
-@@ -0,0 +1,40 @@
+diff --git a/tests/Makefile.am b/tests/Makefile.am
+index 6b6df63..16f8693 100644
+--- a/tests/Makefile.am
++++ b/tests/Makefile.am
+@@ -32,6 +32,7 @@ TESTS = \
+ 	crlf-handling \
+ 	dash-o-append \
+ 	deep-directories \
++	ed-style \
+ 	empty-files \
+ 	false-match \
+ 	fifo \
+diff --git a/tests/ed-style b/tests/ed-style
+new file mode 100644
+index 0000000..d8c0689
+--- /dev/null
++++ b/tests/ed-style
+@@ -0,0 +1,41 @@
 +# Copyright (C) 2018 Free Software Foundation, Inc.
 +#
 +# Copying and distribution of this file, with or without modification,
@@ -139,7 +167,7 @@ diff -up patch-2.7.6/tests/ed-style.CVE-2018-1000156 patch-2.7.6/tests/ed-style
 +
 +. $srcdir/test-lib.sh
 +
-+require_cat
++require cat
 +use_local_patch
 +use_tmpdir
 +
@@ -164,46 +192,11 @@ diff -up patch-2.7.6/tests/ed-style.CVE-2018-1000156 patch-2.7.6/tests/ed-style
 +,p
 +EOF
 +
-+check 'patch -e foo -i ed2.diff > /dev/null 2> /dev/null || echo "Status: $?"' <<EOF
++check 'patch -e foo -i ed2.diff 2> /dev/null || echo "Status: $?"' <<EOF
++?
 +Status: 2
 +EOF
 +
 +check 'cat foo' <<EOF
 +foo
 +EOF
-diff -up patch-2.7.6/tests/Makefile.am.CVE-2018-1000156 patch-2.7.6/tests/Makefile.am
---- patch-2.7.6/tests/Makefile.am.CVE-2018-1000156	2018-05-03 12:50:28.988937938 +0100
-+++ patch-2.7.6/tests/Makefile.am	2018-05-03 12:50:57.340132989 +0100
-@@ -32,6 +32,7 @@ TESTS = \
- 	crlf-handling \
- 	dash-o-append \
- 	deep-directories \
-+	ed-style \
- 	empty-files \
- 	false-match \
- 	fifo \
-diff -up patch-2.7.6/tests/Makefile.in.CVE-2018-1000156 patch-2.7.6/tests/Makefile.in
---- patch-2.7.6/tests/Makefile.in.CVE-2018-1000156	2018-05-03 12:50:28.989937944 +0100
-+++ patch-2.7.6/tests/Makefile.in	2018-05-03 12:51:25.974329992 +0100
-@@ -1308,6 +1308,7 @@ TESTS = \
- 	crlf-handling \
- 	dash-o-append \
- 	deep-directories \
-+	ed-style \
- 	empty-files \
- 	false-match \
- 	fifo \
-@@ -1645,6 +1646,13 @@ empty-files.log: empty-files
- 	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
- 	--log-file $$b.log --trs-file $$b.trs \
- 	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
-+	"$$tst" $(AM_TESTS_FD_REDIRECT)
-+ed-style.log: empty-files
-+	@p='ed-style'; \
-+	b='ed-style'; \
-+	$(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
-+	--log-file $$b.log --trs-file $$b.trs \
-+	$(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
- 	"$$tst" $(AM_TESTS_FD_REDIRECT)
- false-match.log: false-match
- 	@p='false-match'; \
diff --git a/patch-selinux.patch b/patch-selinux.patch
new file mode 100644
index 0000000..7c16a1a
--- /dev/null
+++ b/patch-selinux.patch
@@ -0,0 +1,326 @@
+diff -up patch-2.7.6/src/common.h.selinux patch-2.7.6/src/common.h
+--- patch-2.7.6/src/common.h.selinux	2018-02-03 12:41:49.000000000 +0000
++++ patch-2.7.6/src/common.h	2018-02-12 12:29:44.415225377 +0000
+@@ -30,6 +30,8 @@
+ #include <sys/types.h>
+ #include <time.h>
+ 
++#include <selinux/selinux.h>
++
+ #include <sys/stat.h>
+ 
+ #include <limits.h>
+@@ -84,6 +86,7 @@ XTERN char *outfile;
+ XTERN int inerrno;
+ XTERN int invc;
+ XTERN struct stat instat;
++XTERN security_context_t incontext;
+ XTERN bool dry_run;
+ XTERN bool posixly_correct;
+ 
+diff -up patch-2.7.6/src/inp.c.selinux patch-2.7.6/src/inp.c
+--- patch-2.7.6/src/inp.c.selinux	2017-09-04 12:34:16.000000000 +0100
++++ patch-2.7.6/src/inp.c	2018-02-12 12:29:44.415225377 +0000
+@@ -145,7 +145,7 @@ get_input_file (char const *filename, ch
+     char *getbuf;
+ 
+     if (inerrno == -1)
+-      inerrno = stat_file (filename, &instat);
++	inerrno = stat_file (filename, &instat, &incontext);
+ 
+     /* Perhaps look for RCS or SCCS versions.  */
+     if (S_ISREG (file_type)
+@@ -190,7 +190,7 @@ get_input_file (char const *filename, ch
+ 	    }
+ 
+ 	    if (cs && version_get (filename, cs, ! inerrno, elsewhere, getbuf,
+-				   &instat))
++				   &instat, &incontext))
+ 	      inerrno = 0;
+ 
+ 	    free (getbuf);
+@@ -201,6 +201,7 @@ get_input_file (char const *filename, ch
+       {
+ 	instat.st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
+ 	instat.st_size = 0;
++	incontext = NULL;
+       }
+     else if (! ((S_ISREG (file_type) || S_ISLNK (file_type))
+ 	        && (file_type & S_IFMT) == (instat.st_mode & S_IFMT)))
+diff -up patch-2.7.6/src/Makefile.am.selinux patch-2.7.6/src/Makefile.am
+--- patch-2.7.6/src/Makefile.am.selinux	2017-09-04 12:34:16.000000000 +0100
++++ patch-2.7.6/src/Makefile.am	2018-02-12 12:29:44.415225377 +0000
+@@ -37,7 +37,7 @@ patch_SOURCES = \
+ 
+ AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
+ patch_LDADD = $(LDADD) $(top_builddir)/lib/libpatch.a $(LIB_CLOCK_GETTIME) \
+-	      $(LIB_XATTR) $(LIB_EACCESS)
++	      $(LIB_XATTR) $(LIB_EACCESS) -lselinux
+ 
+ if ENABLE_MERGE
+   patch_SOURCES += merge.c
+diff -up patch-2.7.6/src/Makefile.in.selinux patch-2.7.6/src/Makefile.in
+--- patch-2.7.6/src/Makefile.in.selinux	2018-02-03 13:33:56.000000000 +0000
++++ patch-2.7.6/src/Makefile.in	2018-02-12 12:29:44.415225377 +0000
+@@ -1147,7 +1147,7 @@ patch_SOURCES = bestmatch.h common.h inp
+ AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib \
+ 	$(am__append_2)
+ patch_LDADD = $(LDADD) $(top_builddir)/lib/libpatch.a $(LIB_CLOCK_GETTIME) \
+-	      $(LIB_XATTR) $(LIB_EACCESS)
++	      $(LIB_XATTR) $(LIB_EACCESS) -lselinux
+ 
+ all: all-am
+ 
+diff -up patch-2.7.6/src/patch.c.selinux patch-2.7.6/src/patch.c
+--- patch-2.7.6/src/patch.c.selinux	2018-02-03 12:41:49.000000000 +0000
++++ patch-2.7.6/src/patch.c	2018-02-12 12:30:27.315164138 +0000
+@@ -269,19 +269,19 @@ main (int argc, char **argv)
+ 	  if (! strcmp (inname, outname))
+ 	    {
+ 	      if (inerrno == -1)
+-		inerrno = stat_file (inname, &instat);
++		inerrno = stat_file (inname, &instat, NULL);
+ 	      outstat = instat;
+ 	      outerrno = inerrno;
+ 	    }
+ 	  else
+-	    outerrno = stat_file (outname, &outstat);
++	    outerrno = stat_file (outname, &outstat, NULL);
+ 
+ 	  if (! outerrno)
+ 	    {
+ 	      if (has_queued_output (&outstat))
+ 		{
+ 		  output_files (&outstat);
+-		  outerrno = stat_file (outname, &outstat);
++		  outerrno = stat_file (outname, &outstat, NULL);
+ 		  inerrno = -1;
+ 		}
+ 	      if (! outerrno)
+@@ -598,7 +598,7 @@ main (int argc, char **argv)
+ 			}
+ 		      else
+ 			{
+-			  attr |= FA_IDS | FA_MODE | FA_XATTRS;
++			  attr |= FA_IDS | FA_MODE | FA_XATTRS | FA_SECCONTEXT;
+ 			  set_file_attributes (TMPOUTNAME, attr, inname, &instat,
+ 					       mode, &new_time);
+ 			}
+@@ -658,7 +658,7 @@ main (int argc, char **argv)
+ 			struct stat oldst;
+ 			int olderrno;
+ 
+-			olderrno = stat_file (rej, &oldst);
++			olderrno = stat_file (rej, &oldst, NULL);
+ 			if (olderrno && olderrno != ENOENT)
+ 			  write_fatal ();
+ 		        if (! olderrno && lookup_file_id (&oldst) == CREATED)
+@@ -1790,7 +1790,7 @@ delete_file_later (const char *name, con
+ 
+   if (! st)
+     {
+-      if (stat_file (name, &st_tmp) != 0)
++      if (stat_file (name, &st_tmp, NULL) != 0)
+ 	pfatal ("Can't get file attributes of %s %s", "file", name);
+       st = &st_tmp;
+     }
+diff -up patch-2.7.6/src/pch.c.selinux patch-2.7.6/src/pch.c
+--- patch-2.7.6/src/pch.c.selinux	2018-02-03 12:41:49.000000000 +0000
++++ patch-2.7.6/src/pch.c	2018-02-12 12:29:44.416225375 +0000
+@@ -1,6 +1,6 @@
+ /* reading patches */
+ 
+-/* Copyright (C) 1986, 1987, 1988 Larry Wall
++/* Copyright (C) 1986, 1987, 1988, 2012 Larry Wall
+ 
+    Copyright (C) 1990-1993, 1997-2003, 2006, 2009-2012 Free Software
+    Foundation, Inc.
+@@ -296,7 +296,7 @@ there_is_another_patch (bool need_header
+ 	if (t > buf + 1 && *(t - 1) == '\n')
+ 	  {
+ 	    inname = xmemdup0 (buf, t - buf - 1);
+-	    inerrno = stat_file (inname, &instat);
++	    inerrno = stat_file (inname, &instat, &incontext);
+ 	    if (inerrno)
+ 	      {
+ 		perror (inname);
+@@ -433,6 +433,7 @@ intuit_diff_type (bool need_header, mode
+     bool extended_headers = false;
+     enum nametype i;
+     struct stat st[3];
++    security_context_t con[3];
+     int stat_errno[3];
+     int version_controlled[3];
+     enum diff retval;
+@@ -473,6 +474,7 @@ intuit_diff_type (bool need_header, mode
+     version_controlled[OLD] = -1;
+     version_controlled[NEW] = -1;
+     version_controlled[INDEX] = -1;
++    con[OLD] = con[NEW] = con[INDEX] = NULL;
+     p_rfc934_nesting = 0;
+     p_timestamp[OLD].tv_sec = p_timestamp[NEW].tv_sec = -1;
+     p_says_nonexistent[OLD] = p_says_nonexistent[NEW] = 0;
+@@ -883,7 +885,7 @@ intuit_diff_type (bool need_header, mode
+ 		}
+ 	      else
+ 		{
+-		  stat_errno[i] = stat_file (p_name[i], &st[i]);
++		  stat_errno[i] = stat_file (p_name[i], &st[i], &con[i]);
+ 		  if (! stat_errno[i])
+ 		    {
+ 		      if (lookup_file_id (&st[i]) == DELETE_LATER)
+@@ -922,7 +924,7 @@ intuit_diff_type (bool need_header, mode
+ 			  if (cs)
+ 			    {
+ 			      if (version_get (p_name[i], cs, false, readonly,
+-					       getbuf, &st[i]))
++					       getbuf, &st[i], &con[i]))
+ 				stat_errno[i] = 0;
+ 			      else
+ 				version_controlled[i] = 0;
+@@ -985,7 +987,7 @@ intuit_diff_type (bool need_header, mode
+       {
+ 	if (inname)
+ 	  {
+-	    inerrno = stat_file (inname, &instat);
++	    inerrno = stat_file (inname, &instat, &incontext);
+ 	    if (inerrno || (instat.st_mode & S_IFMT) == file_type)
+ 	      maybe_reverse (inname, inerrno, inerrno || instat.st_size == 0);
+ 	  }
+@@ -998,8 +1000,14 @@ intuit_diff_type (bool need_header, mode
+ 	inerrno = stat_errno[i];
+ 	invc = version_controlled[i];
+ 	instat = st[i];
++	incontext = con[i];
++	con[i] = NULL;
+       }
+ 
++    for (i = OLD; i <= INDEX; i++)
++      if (con[i])
++	freecon (con[i]);
++
+     return retval;
+ }
+ 
+diff -up patch-2.7.6/src/util.c.selinux patch-2.7.6/src/util.c
+--- patch-2.7.6/src/util.c.selinux	2018-02-03 12:41:49.000000000 +0000
++++ patch-2.7.6/src/util.c	2018-02-12 12:29:44.417225374 +0000
+@@ -300,6 +300,23 @@ set_file_attributes (char const *to, enu
+ 		S_ISLNK (mode) ? "symbolic link" : "file",
+ 		quotearg (to));
+     }
++  if (attr & FA_SECCONTEXT)
++    {
++      security_context_t outcontext;
++      if (incontext && getfilecon (to, &outcontext) != -1 && outcontext)
++	{
++	  if (strcmp (outcontext, incontext) &&
++	      setfilecon (to, incontext) != 0)
++	    {
++	      freecon (outcontext);
++	      if (errno != ENOTSUP && errno != EPERM)
++		pfatal ("Can't set security context on file %s",
++			quotearg (to));
++	    }
++	  else
++	    freecon (outcontext);
++	}
++    }
+ }
+ 
+ static void
+@@ -446,7 +463,7 @@ move_file (char const *from, bool *from_
+   struct stat to_st;
+   int to_errno;
+ 
+-  to_errno = stat_file (to, &to_st);
++  to_errno = stat_file (to, &to_st, NULL);
+   if (backup)
+     create_backup (to, to_errno ? NULL : &to_st, false);
+   if (! to_errno)
+@@ -818,7 +835,8 @@ version_controller (char const *filename
+    Return true if successful.  */
+ bool
+ version_get (char const *filename, char const *cs, bool exists, bool readonly,
+-	     char const *getbuf, struct stat *filestat)
++	     char const *getbuf, struct stat *filestat,
++	     security_context_t *filecontext)
+ {
+   if (patch_get < 0)
+     {
+@@ -843,6 +861,13 @@ version_get (char const *filename, char
+ 	fatal ("Can't get file %s from %s", quotearg (filename), cs);
+       if (safe_stat (filename, filestat) != 0)
+ 	pfatal ("%s", quotearg (filename));
++      if (filecontext && getfilecon (filename, filecontext) == -1)
++	{
++	  if (errno == ENODATA || errno == ENOTSUP)
++	    *filecontext = NULL;
++	  else
++	    pfatal ("%s", quotearg (filename));
++	}
+     }
+ 
+   return 1;
+@@ -1670,12 +1695,28 @@ make_tempfile (char const **name, char l
+   return fd;
+ }
+ 
+-int stat_file (char const *filename, struct stat *st)
++int stat_file (char const *filename, struct stat *st, security_context_t *con)
+ {
+   int (*xstat)(char const *, struct stat *) =
+     follow_symlinks ? safe_stat : safe_lstat;
++  int (*xgetfilecon)(char const *, security_context_t *) =
++    follow_symlinks ? getfilecon : lgetfilecon;
++
++  if (xstat (filename, st) == 0)
++    {
++      if (con)
++	{
++	  if (xgetfilecon (filename, con) != -1 ||
++	      errno == ENODATA || errno == ENOTSUP)
++	    return 0;
+ 
+-  return xstat (filename, st) == 0 ? 0 : errno;
++	  *con = NULL;
++	}
++      else
++	return 0;
++    }
++ 
++  return errno;
+ }
+ 
+ /* Check if a filename is relative and free of ".." components.
+diff -up patch-2.7.6/src/util.h.selinux patch-2.7.6/src/util.h
+--- patch-2.7.6/src/util.h.selinux	2018-02-03 12:41:49.000000000 +0000
++++ patch-2.7.6/src/util.h	2018-02-12 12:30:08.533190949 +0000
+@@ -44,7 +44,7 @@ char *parse_name (char const *, int, cha
+ char *savebuf (char const *, size_t);
+ char *savestr (char const *);
+ char const *version_controller (char const *, bool, struct stat const *, char **, char **);
+-bool version_get (char const *, char const *, bool, bool, char const *, struct stat *);
++bool version_get (char const *, char const *, bool, bool, char const *, struct stat *, security_context_t *);
+ int create_file (char const *, int, mode_t, bool);
+ int systemic (char const *);
+ char *format_linenum (char[LINENUM_LENGTH_BOUND + 1], lin);
+@@ -67,7 +67,7 @@ void insert_file_id (struct stat const *
+ enum file_id_type lookup_file_id (struct stat const *);
+ void set_queued_output (struct stat const *, bool);
+ bool has_queued_output (struct stat const *);
+-int stat_file (char const *, struct stat *);
++int stat_file (char const *, struct stat *, security_context_t *);
+ bool filename_is_safe (char const *) _GL_ATTRIBUTE_PURE;
+ bool cwd_is_root (char const *);
+ 
+@@ -75,7 +75,8 @@ enum file_attributes {
+   FA_TIMES = 1,
+   FA_IDS = 2,
+   FA_MODE = 4,
+-  FA_XATTRS = 8
++  FA_XATTRS = 8,
++  FA_SECCONTEXT = 16
+ };
+ 
+ void set_file_attributes (char const *, enum file_attributes, char const *,
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/patch.git/commitdiff/93925d0d9d75587c27ed526835c8572c1b38a780




More information about the pld-cvs-commit mailing list