Description: fix arbitrary-file-write vulnerability (CVE-2022-1271)
 Backported from gzip version 1.12:
 .
 * zgrep.in: The issue with the old code is that with multiple
 newlines, the N-command will read the second line of input,
 then the s-commands will be skipped because it's not the end
 of the file yet, then a new sed cycle starts and the pattern
 space is printed and emptied. So only the last line or two get
 escaped. This patch makes sed read all lines into the pattern
 space and then do the escaping.
 .
 This vulnerability was discovered by:
 cleemy desu wayo working with Trend Micro Zero Day Initiative
 .
 * zgrep.in (res): When escaping the file name do not rely on GNU
 sed’s extension to POSIX with respect to s/.../\n/.  Instead, use
 features that should also work with AIX and/or Solaris sed.  This is
 simpler anyway, and would have prevented the recently-fixed bug.
 .
 * gzexe.in: Avoid an unnecessary invocation of ‘grep’,
 by using sed instead.  Also, look only for at-most-3-digit numbers,
 for consistency with the rest of the script.
 .
 * gzexe.in, zdiff.in, zgrep.in:
 Run expr and sed in the C locale when it might help to avoid
 undefined behavior on non-GNU platforms.
 .
 * sample/zfile, znew.in: Run in the C locale, for simplicity and
 to avoid undefined behavior on non-GNU platforms.
 .
 Problem reported by Jim Avera (Bug#31280).
 This became more of an issue when GNU grep 3.5 (2020) started sending
 "binary file matches" diagnostics to stderr instead of to stdout.
 .
 * tests/Makefile.am (TESTS): Add zgrep-binary.
 .
 * tests/zgrep-binary: New test.
 .
 * zgrep.in (args): New var, to accumulate args separately
 from grep command, so we can prepend args if need be.
 Most uses of 'grep' changed to use 'args' instead, or also.
 (with_filename): Set to 1 if more than one file and -h not given;
 this simplifies later code.
 (gnuish_grep): New var; evaluates to true if grep supports
 -H and --label options, as is true for GNU and FreeBSD grep.
 Append -H to 'grep' if outputting file names with GNUish grep,
 and use --label with GNUish grep unless reading from stdin,
 as that’s safer and more efficient than relabeling with 'sed'.

Origin: backport,
 https://git.savannah.gnu.org/cgit/gzip.git/commit/?id=dc9740df61e575e8c3148b7bd3c147a81ea00c7c
 https://git.savannah.gnu.org/cgit/gzip.git/commit/?id=d74a30d45c6834c8e9f87115197370fe86656d81
 https://git.savannah.gnu.org/cgit/gzip.git/commit/?id=c99f320d5c0fd98fe88d9cea5407eb7ad9d50e8a
 https://git.savannah.gnu.org/cgit/gzip.git/commit/?id=6543c09c6ecfb1630085d440b76511953bc5a2cb
 https://git.savannah.gnu.org/cgit/gzip.git/commit/?id=0e2d07fc2c4393cfb9dbab580d0bee4525b9c9b3
 https://git.savannah.gnu.org/cgit/gzip.git/commit/?id=5e1fc8b92c1af9382365aef0f9130341ee1d2c76
 https://git.savannah.gnu.org/cgit/gzip.git/commit/?id=9d3248751178939713a39115cf68ec8a11506cc9

---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
diff --git a/gzexe.in b/gzexe.in
index 3076b68..ee8a9f5 100644
--- a/gzexe.in
+++ b/gzexe.in
@@ -91,10 +91,11 @@ for i do
     continue
   fi
   if test $decomp -eq 0; then
-    if sed -e 1d -e 2q "$file" | grep "^skip=[0-9][0-9]*$" >/dev/null; then
+    case `LC_ALL=C sed -n -e 1d -e '/^skip=[0-9][0-9]*$/p' -e 2q "$file"` in
+    skip=[0-9] | skip=[0-9][0-9] | skip=[0-9][0-9][0-9])
       printf >&2 '%s\n' "$0: $i is already gzexe'd"
-      continue
-    fi
+      continue;;
+    esac
   fi
   if test -u "$file"; then
     printf >&2 '%s\n' "$0: $i has setuid permission, unchanged"
@@ -202,7 +203,7 @@ EOF
   else
     # decompression
     skip=44
-    skip_line=`sed -e 1d -e 2q "$file"`
+    skip_line=`LC_ALL=C sed -e 1d -e 2q "$file"`
     case $skip_line in
     skip=[0-9] | skip=[0-9][0-9] | skip=[0-9][0-9][0-9])
       eval "$skip_line";;
diff --git a/sample/zfile b/sample/zfile
index 6b4514c..d6e7a59 100755
--- a/sample/zfile
+++ b/sample/zfile
@@ -1,5 +1,8 @@
 #!/bin/sh
 
+LC_ALL=C
+export LC_ALL
+
 if test $# = 0; then
   echo 'zfile: file(1) for programs which may be compressed with gzexe'
   echo usage: `basename $0`  files...
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 691bbf8..1e6b05b 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -34,6 +34,8 @@ TESTS =					\
   z-suffix				\
   zdiff					\
   zgrep-f				\
+  zgrep-abuse				\
+  zgrep-binary				\
   zgrep-context				\
   zgrep-signal				\
   znew-k
diff --git a/tests/zgrep-abuse b/tests/zgrep-abuse
new file mode 100755
index 0000000..3e8a8f9
--- /dev/null
+++ b/tests/zgrep-abuse
@@ -0,0 +1,41 @@
+#!/bin/sh
+# Show how zgrep applied to a crafted file name may overwrite
+# a selected file with chosen content.  Fixed in gzip-1.12.
+
+# Copyright (C) 2022 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/>.
+# limit so don't run it by default.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ..
+
+: > z || framework_failure_
+echo test |gzip > 'z|
+p
+1s|.*|chosen-content|
+1w hacked
+etouch .\x2fhacked2
+d
+#
+#' || framework_failure_
+
+fail=0
+
+zgrep test z* > /dev/null
+
+# Before the fix, each of these would be created.
+test -f hacked && fail=1
+test -f hacked2 && fail=1
+
+Exit $fail
diff --git a/tests/zgrep-binary b/tests/zgrep-binary
new file mode 100755
index 0000000..6c00cef
--- /dev/null
+++ b/tests/zgrep-binary
@@ -0,0 +1,30 @@
+#!/bin/sh
+# 'zgrep PATTERN FILE' would output "(standard input): binary file matches"
+# without mentioning FILE.  Fixed in gzip-1.12.
+
+# Copyright (C) 2022 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/>.
+# limit so don't run it by default.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ..
+
+fail=0
+
+unset GREP_OPTIONS
+
+printf 'foo\0\n' >f || framework_failure_
+LC_ALL=C zgrep foo f >out 2>err && grep '(standard input)' out err && fail=1
+
+Exit $fail
diff --git a/zdiff.in b/zdiff.in
index 4cf7395..e8df1ec 100644
--- a/zdiff.in
+++ b/zdiff.in
@@ -53,7 +53,7 @@ while :; do
   --h*) printf '%s\n' "$usage"   || exit 2; exit;;
   --v*) printf '%s\n' "$version" || exit 2; exit;;
   --) shift; break;;
-  -*\'*) cmp="$cmp '"`printf '%sX\n' "$1" | sed "$escape"`;;
+  -*\'*) cmp="$cmp '"`printf '%sX\n' "$1" | LC_ALL=C sed "$escape"`;;
   -?*) cmp="$cmp '$1'";;
   *) break;;
   esac
@@ -72,7 +72,7 @@ exec 3>&1
 if test $# -eq 1; then
   case $1 in
   *[-.]gz* | *[-.][zZ] | *.t[ga]z)
-    FILE=`expr "X$1" : 'X\(.*\)[-.][zZtga]*$'`
+    FILE=`LC_ALL=C expr "X$1" : 'X\(.*\)[-.][zZtga]*$'`
     gzip_status=$(
       exec 4>&1
       (gzip -cd -- "$1" 4>&-; echo $? >&4) 3>&- | eval "$cmp" - '"$FILE"' >&3
diff --git a/zgrep.in b/zgrep.in
index 3efdb52..d1bd56b 100644
--- a/zgrep.in
+++ b/zgrep.in
@@ -23,6 +23,7 @@
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 grep='${GREP-'\''@GREP@'\''}'
+args=
 
 version='zgrep (gzip) @VERSION@
 Copyright (C) 2010-2018 Free Software Foundation, Inc.
@@ -64,30 +65,30 @@ while test $# -ne 0; do
 
   case $option in
   (-[0123456789EFGHIKLPRTUVZabchilnoqrsuvwxyz]*[!0123456789]*)
-    arg2=-\'$(expr "X$option" : 'X-.[0-9]*\(.*\)' | sed "$escape")
+    arg2=-\'$(LC_ALL=C expr "X$option" : 'X-.[0-9]*\(.*\)' | LC_ALL=C sed "$escape")
     eval "set -- $arg2 "'${1+"$@"}'
-    option=$(expr "X$option" : 'X\(-.[0-9]*\)');;
+    option=$(LC_ALL=C expr "X$option" : 'X\(-.[0-9]*\)');;
   (--binary-*=* | --[lm]a*=* | --reg*=*)
     ;;
   (-[ABCDXdefm] | --binary-* | --file | --[lm]a* | --reg*)
     case ${1?"$option option requires an argument"} in
     (*\'*)
-      optarg=" '"$(printf '%s\n' "$1" | sed "$escape");;
+      optarg=" '"$(printf '%s\n' "$1" | LC_ALL=C sed "$escape");;
     (*)
       optarg=" '$1'";;
     esac
     shift;;
   (-f?*\'*)
-    optarg=" '"$(expr "X$option" : 'X-f\(.*\)' | sed "$escape")
+    optarg=" '"$(LC_ALL=C expr "X$option" : 'X-f\(.*\)' | LC_ALL=C sed "$escape")
     option=-f;;
   (-f?*)
-    optarg=" '"$(expr "X$option" : 'X-f\(.*\)')\'
+    optarg=" '"$(LC_ALL=C expr "X$option" : 'X-f\(.*\)')\'
     option=-f;;
   (--file=*\'*)
-    optarg=" '"$(expr "X$option" : 'X--file=\(.*\)' | sed "$escape")
+    optarg=" '"$(LC_ALL=C expr "X$option" : 'X--file=\(.*\)' | LC_ALL=C sed "$escape")
     option=--file;;
   (--file=*)
-    optarg=" '"$(expr "X$option" : 'X--file=\(.*\)')\'
+    optarg=" '"$(LC_ALL=C expr "X$option" : 'X--file=\(.*\)')\'
     option=--file;;
   (--)
     break;;
@@ -96,7 +97,8 @@ while test $# -ne 0; do
   (*)
     case $option in
     (*\'*)
-      operands="$operands '"$(printf '%s\n' "$option" | sed "$escape");;
+      operands="$operands '"$(printf '%s\n' "$option" | LC_ALL=C sed "$escape")
+      ;;
     (*)
       operands="$operands '$option'";;
     esac
@@ -169,12 +171,12 @@ while test $# -ne 0; do
 
   case $option in
   (*\'?*)
-    option=\'$(printf '%s\n' "$option" | sed "$escape");;
+    option=\'$(printf '%s\n' "$option" | LC_ALL=C sed "$escape");;
   (*)
     option="'$option'";;
   esac
 
-  grep="$grep $option$optarg"
+  args="$args $option$optarg"
 done
 
 eval "set -- $operands "'${1+"$@"}'
@@ -182,15 +184,23 @@ eval "set -- $operands "'${1+"$@"}'
 if test $have_pat -eq 0; then
   case ${1?"missing pattern; try \`$0 --help' for help"} in
   (*\'*)
-    grep="$grep -- '"$(printf '%s\n' "$1" | sed "$escape");;
+    args="$args -- '"$(printf '%s\n' "$1" | LC_ALL=C sed "$escape");;
   (*)
-    grep="$grep -- '$1'";;
+    args="$args -- '$1'";;
   esac
   shift
 fi
 
 if test $# -eq 0; then
   set -- -
+elif test 1 -lt $# && test $no_filename -eq 0; then
+  with_filename=1
+fi
+
+l_e=$(eval "(echo e | $grep -H --label=l e) 2>/dev/null") && test "$l_e" = l:e
+gnuish_grep="test $? -eq 0"
+if $gnuish_grep && test $with_filename -eq 1; then
+  grep="$grep -H"
 fi
 
 exec 3>&1
@@ -203,9 +213,9 @@ do
     exec 5>&1
     (gzip -cdfq -- "$i" 5>&-; echo $? >&5) 3>&- |
     if test $files_with_matches -eq 1; then
-      eval "$grep" >/dev/null && { printf '%s\n' "$i" || exit 2; }
+      eval "$grep$args" >/dev/null && { printf '%s\n' "$i" || exit 2; }
     elif test $files_without_matches -eq 1; then
-      eval "$grep" >/dev/null || {
+      eval "$grep$args" >/dev/null || {
         r=$?
         if test $r -eq 1; then
           printf '%s\n' "$i" || r=2
@@ -213,26 +223,25 @@ do
         test 256 -le $r && r=$(expr 128 + $r % 128)
         exit $r
       }
-    elif test $with_filename -eq 0 &&
-         { test $# -eq 1 || test $no_filename -eq 1; }; then
-      eval "$grep"
+    elif $gnuish_grep && test "$i" != -; then
+      eval "$grep --label \"\$i\"$args"
+    elif $gnuish_grep || test $with_filename -eq 0; then
+      eval "$grep$args"
     else
       case $i in
       (*'
 '* | *'&'* | *'\'* | *'|'*)
-        i=$(printf '%s\n' "$i" |
-            sed '
-              $!N
-              $s/[&\|]/\\&/g
-              $s/\n/\\n/g
-            ');;
+          icolon=$(printf '%s\n' "$i:" |
+                     LC_ALL=C sed -e 's/[&\|]/\\&/g' -e '$!s/$/\\/');;
+      (*) icolon="$i:";;
       esac
-      sed_script="s|^|$i:|"
+      sed_script="s|^|$icolon|"
 
       # Fail if grep or sed fails.
       r=$(
         exec 4>&1
-        (eval "$grep" 4>&-; echo $? >&4) 3>&- | sed "$sed_script" >&3 4>&-
+        (eval "$grep$args" 4>&-; echo $? >&4) 3>&- |
+           LC_ALL=C sed "$sed_script" >&3 4>&-
       ) || { r=$?; test $r -lt 2 && r=2; }
       test 256 -le $r && r=$(expr 128 + $r % 128)
       exit $r
diff --git a/znew.in b/znew.in
index 2b703d1..0cf1909 100644
--- a/znew.in
+++ b/znew.in
@@ -18,6 +18,9 @@
 # with this program; if not, write to the Free Software Foundation, Inc.,
 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
+LC_ALL=C
+export LC_ALL
+
 version="znew (gzip) @VERSION@
 Copyright (C) 2010-2018 Free Software Foundation, Inc.
 This is free software.  You may redistribute copies of it under the terms of
