Subject: Re: bug#54290: Reply for zdiff-fix-arg-handling-bug.patch

From: Paul Eggert <[email-masked]>
Date: Tue, 8 Mar 2022 13:09:26 -0800

On 3/7/22 18:22, Lv Ying via GNU gzip discussion and bug reports. wrote:

> However this manual separation job can not handle filename before options.
> ...
> getopt command from util-linux can handle such situation

We try to keep gzip portable to a wide variety of platforms, even those 
with ancient shells that lack getopt. I reworked zdiff.in to handle 
filenames before options (without using getopt) and installed the 
attached additional patch.

Please let us know of further problems by replying to this email and 
cc'ing to [email-masked] (there should be no need to open a new 
bug report).

From bb184b98c27fa46826eaa2f6272cb4d6eacc75a8 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Tue, 8 Mar 2022 13:02:37 -0800
Subject: [PATCH] zdiff: fix another arg-handling bug

Also allow args after file names.
Problem reported by Lv Ying <https://bugs.gnu.org/54290#12>.
---
 zdiff.in | 127 ++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 75 insertions(+), 52 deletions(-)

diff --git a/zdiff.in b/zdiff.in
index d982d9f..593cb6e 100644
--- a/zdiff.in
+++ b/zdiff.in
@@ -48,60 +48,85 @@ escape='
   $s/X$/'\''/
 '
 
-while :; do
-  argdone=:
-  case $1 in
+filesonly=
+file1=
+file2=
+needop=
+
+for arg
+do
+  case $filesonly$needop$arg in
   --h*) printf '%s\n' "$usage"   || exit 2; exit;;
   --v*) printf '%s\n' "$version" || exit 2; exit;;
-  --) shift; break;;
-  -*\'*) argdone=false arg=$1;;
-  -[CDFISUWXx])
-    cmp="$cmp $1"
-    case ${2?} in
-    -*\'*) argdone=false arg=$2;;
-    *) cmp="$cmp '$2'";;
-    esac
-    shift;;
-  -?*) cmp="$cmp '$1'";;
-  *) break;;
+  --) filesonly=t;;
+  -*\'*) cmp="$cmp '"`printf '%sX\n' "$arg" | sed "$escape"`;;
+  -[CDFISUWXx]) needop="'$arg'";;
+  -?*) cmp="$cmp '$arg'";;
+  *) case $needop in
+     '') case $arg in
+         '') printf >&2 '%s\n' "$0: empty file name"; exit 2;;
+         esac
+         case $file1 in
+         '') file1=$arg;;
+         *) case $file2 in
+            '') file2=$arg;;
+            *) printf >&2 '%s\n' "$0: extra operand '$arg'"; exit 2;;
+            esac;;
+         esac;;
+     *) cmp="$cmp $needop '$arg'"
+        needop=;;
+     esac;;
   esac
-  $argdone || cmp="$cmp '"`printf '%sX\n' "$arg" | sed "$escape"`
-  shift
 done
+case $needop in
+'') ;;
+*) printf >&2 '%s\n' "$0: $prevarg: option requires an argument -- $needop"
+   exit 2;;
+esac
+
 cmp="$cmp --"
 
-for file
-do
-  test "X$file" = X- || <"$file" || exit 2
-done
+case $file1 in
+'') printf >&2 '%s\n' "$0: missing operand"; exit 2;;
+-) ;;
+*) <"$file1" || exit 2;;
+esac
+case $file2 in
+''|-) ;;
+*) <"$file2" || exit 2;;
+esac
 
 gzip_status=0
 exec 3>&1
 
-if test $# -eq 1; then
-  case $1 in
+case $file2 in
+'')
+  case $file1 in
   *[-.]gz* | *[-.][zZ] | *.t[ga]z)
-    FILE=`expr "X$1" : 'X\(.*\)[-.][zZtga]*$'`
+    FILE=`expr "X$file1" : 'X\(.*\)[-.][zZtga]*$'`
     gzip_status=$(
       exec 4>&1
-      (gzip -cd -- "$1" 4>&-; echo $? >&4) 3>&- | eval "$cmp" - '"$FILE"' >&3
+      (gzip -cd -- "$file1" 4>&-; echo $? >&4) 3>&- |
+        eval "$cmp" - '"$FILE"' >&3
     );;
   *)
-    printf >&2 '%s\n' "$0: $1: unknown compressed file extension"
+    printf >&2 '%s\n' "$0: $file1: unknown compressed file extension"
     exit 2;;
-  esac
-elif test $# -eq 2; then
-        case "$1" in
+  esac;;
+*)
+  case $file1,$file2 in
+  -,-)
+        gzip_status=$(
+          exec 4>&1
+          (gzip -cdfq - 4>&-; echo $? >&4) 3>&- |
+            eval "$cmp" - - >&3
+        );;
+  *)
+        case $file1 in
         *[-.]gz* | *[-.][zZ] | *.t[ga]z | -)
-                case "$2" in
+                case $file2 in
                 *[-.]gz* | *[-.][zZ] | *.t[ga]z | -)
-                    if test "$1$2" = --; then
-                        gzip_status=$(
-                          exec 4>&1
-                          (gzip -cdfq - 4>&-; echo $? >&4) 3>&- |
-                            eval "$cmp" - - >&3
-                        )
-                    elif
+                    if
                         # Reject Solaris 8's buggy /bin/bash 2.03.
                         echo X |
                          (echo X | eval "$cmp" /dev/fd/5 - >/dev/null 2>&1) \
@@ -109,8 +134,9 @@ elif test $# -eq 2; then
                     then
                         gzip_status=$(
                           exec 4>&1
-                          (gzip -cdfq -- "$1" 4>&-; echo $? >&4) 3>&- |
-                            ( (gzip -cdfq -- "$2" 4>&-; echo $? >&4) 3>&- 5<&- </dev/null |
+                          (gzip -cdfq -- "$file1" 4>&-; echo $? >&4) 3>&- |
+                              ((gzip -cdfq -- "$file2" 4>&-
+                                echo $? >&4) 3>&- 5<&- </dev/null |
                                eval "$cmp" /dev/fd/5 - >&3) 5<&0
                         )
                         cmp_status=$?
@@ -137,10 +163,10 @@ elif test $# -eq 2; then
                           set -C
                           tmp=${TMPDIR}zdiff$$
                         fi
-                        gzip -cdfq -- "$2" > "$tmp" || exit 2
+                        gzip -cdfq -- "$file2" > "$tmp" || exit 2
                         gzip_status=$(
                           exec 4>&1
-                          (gzip -cdfq -- "$1" 4>&-; echo $? >&4) 3>&- |
+                          (gzip -cdfq -- "$file1" 4>&-; echo $? >&4) 3>&- |
                             eval "$cmp" - '"$tmp"' >&3
                         )
                         cmp_status=$?
@@ -151,25 +177,22 @@ elif test $# -eq 2; then
                 *)
                     gzip_status=$(
                       exec 4>&1
-                      (gzip -cdfq -- "$1" 4>&-; echo $? >&4) 3>&- |
-                        eval "$cmp" - '"$2"' >&3
+                      (gzip -cdfq -- "$file1" 4>&-; echo $? >&4) 3>&- |
+                        eval "$cmp" - '"$file2"' >&3
                     );;
                 esac;;
-        *)      case "$2" in
+        *)      case $file2 in
                 *[-.]gz* | *[-.][zZ] | *.t[ga]z | -)
                         gzip_status=$(
                           exec 4>&1
-                          (gzip -cdfq -- "$2" 4>&-; echo $? >&4) 3>&- |
-                            eval "$cmp" '"$1"' - >&3
+                          (gzip -cdfq -- "$file2" 4>&-; echo $? >&4) 3>&- |
+                            eval "$cmp" '"$file1"' - >&3
                         );;
-                *)	eval "$cmp" '"$1"' '"$2"';;
+                *)	eval "$cmp" '"$file1"' '"$file2"';;
                 esac;;
-        esac
-else
-        printf >&2 '%s\n' \
-          "$0: invalid number of operands; try \`$0 --help' for help"
-        exit 2
-fi
+        esac;;
+  esac;;
+esac
 
 cmp_status=$?
 test "$gzip_status" -eq 0 || exit 2
-- 
2.35.1