Bruno Haible wrote:
! # Produce output and exit with code 1 if there is a write error. ! # Use 'exec echo', not plain 'echo', because the 'echo' built-in in ! # HP-UX /bin/sh does not check for write errors. ! # Use '|| exit 1', because the 'echo' program on HP-UX exits with ! # code 2 in case of a write error, but we want code 1. ! --help) (exec echo "$usage") || exit 1; exit 0;; ! --version) (exec echo "$version") || exit 1; exit 0;;
Thanks for the patch. I don't think we need worry about the first problem, since gzip assumes a working POSIX shell and the first problem is a failure to conform to POSIX. We can ask builders on HP-UX to work around the problem by configuring with SHELL=/bin/bash, or with some other POSIX-compatible shell.
The second problem is indeed a bug in gzip, since it shouldn't assume that echo exits with status 1 on failure (it could be some other positive status).
I notice that some gzip scripts already fix that bug, and some other scripts do not fix it. Also, while we're at it, scripts should use printf instead of echo if the strings might contain backslash (at least in theory; admittedly a backslash in a version number would be pretty weird). I looked for these problems in all the scripts and installed the attached to fix what I found.
From f55f2be13c2f1afa8d76daadabb3b24c7fe34743 Mon Sep 17 00:00:00 2001 From: Paul Eggert <egg...@cs.ucla.edu> Date: Sun, 12 Nov 2017 23:02:02 -0800 Subject: [PATCH] maint: script diagnostics status cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem reported by Bruno Haible (Bug#29266#20). * NEWS: Mention this. * gunzip.in, gzexe.in, zcat.in, zcmp.in, zdiff.in, zforce.in: * zgrep.in, zless.in, zmore.in, znew.in: Use printf instead of echo if the argument might contain ‘\’, at least in theory. Don’t assume printf exits with status 1 on failure; it might be some other positive status. * gzexe.in: Use printf consistently instead of echo, and proscribe it instead of echo. --- NEWS | 5 +++++ gunzip.in | 4 ++-- gzexe.in | 40 ++++++++++++++++++++-------------------- zcat.in | 4 ++-- zcmp.in | 5 ++--- zdiff.in | 9 +++++---- zforce.in | 10 +++++----- zgrep.in | 4 ++-- zless.in | 4 ++-- zmore.in | 4 ++-- znew.in | 30 +++++++++++++++--------------- 11 files changed, 62 insertions(+), 57 deletions(-) diff --git a/NEWS b/NEWS index 34eae0d..68af1dc 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,11 @@ GNU gzip NEWS -*- outline -*- 1970 and after 2106, and timestamps after 2038 on platforms with 32-bit signed time_t. [bug present since the beginning] + Commands implemented via shell scripts are now more consistent about + failure status. For example, 'gunzip --help >/dev/full' now + consistently exits with status 1 (error), instead of with status 2 + (warning) on some platforms. [bug present since the beginning] + Support for VMS and Amiga has been removed. It was not working anyway, and it reportedly caused file name glitches on MS-Windowsish platforms. diff --git a/gunzip.in b/gunzip.in index a5dfb89..0602724 100644 --- a/gunzip.in +++ b/gunzip.in @@ -50,8 +50,8 @@ With no FILE, or when FILE is -, read standard input. Report bugs to <bug-gzip@gnu.org>." case $1 in ---help) exec echo "$usage";; ---version) exec echo "$version";; +--help) printf '%s\n' "$usage" || exit 1; exit;; +--version) printf '%s\n' "$version" || exit 1; exit;; esac exec gzip -d "$@" diff --git a/gzexe.in b/gzexe.in index 87413a3..290fa1c 100644 --- a/gzexe.in +++ b/gzexe.in @@ -59,15 +59,15 @@ res=0 while :; do case $1 in -d) decomp=1; shift;; - --h*) exec echo "$usage";; - --v*) exec echo "$version";; + --h*) printf '%s\n' "$usage" || exit 1; exit;; + --v*) printf '%s\n' "$version" || exit 1; exit;; --) shift; break;; *) break;; esac done if test $# -eq 0; then - echo >&2 "$0: missing operand + printf >&2 '%s\n' "$0: missing operand Try \`$0 --help' for more information." exit 1 fi @@ -87,29 +87,29 @@ for i do esac if test ! -f "$file" || test ! -r "$file"; then res=$? - echo >&2 "$0: $i is not a readable regular file" + printf >&2 '%s\n' "$0: $i is not a readable regular file" continue fi if test $decomp -eq 0; then if sed -e 1d -e 2q "$file" | grep "^skip=[0-9][0-9]*$" >/dev/null; then - echo >&2 "$0: $i is already gzexe'd" + printf >&2 '%s\n' "$0: $i is already gzexe'd" continue fi fi if test -u "$file"; then - echo >&2 "$0: $i has setuid permission, unchanged" + printf >&2 '%s\n' "$0: $i has setuid permission, unchanged" continue fi if test -g "$file"; then - echo >&2 "$0: $i has setgid permission, unchanged" + printf >&2 '%s\n' "$0: $i has setgid permission, unchanged" continue fi case /$file in */basename | */bash | */cat | */chmod | */cp | \ - */dirname | */echo | */expr | */gzip | \ - */ln | */mkdir | */mktemp | */mv | */rm | \ + */dirname | */expr | */gzip | \ + */ln | */mkdir | */mktemp | */mv | */printf | */rm | \ */sed | */sh | */sleep | */test | */tail) - echo >&2 "$0: $i might depend on itself"; continue;; + printf >&2 '%s\n' "$0: $i might depend on itself"; continue;; esac dir=`dirname "$file"` || dir=$TMPDIR @@ -125,7 +125,7 @@ for i do tmp=$dir/gzexe$$ fi && { cp -p "$file" "$tmp" 2>/dev/null || cp "$file" "$tmp"; } || { res=$? - echo >&2 "$0: cannot copy $file" + printf >&2 '%s\n' "$0: cannot copy $file" continue } if test -w "$tmp"; then @@ -134,7 +134,7 @@ for i do writable=0 chmod u+w "$tmp" || { res=$? - echo >&2 "$0: cannot chmod $tmp" + printf >&2 '%s\n' "$0: cannot chmod $tmp" continue } fi @@ -170,7 +170,7 @@ case $0 in */*) gztmp=$gztmpdir/`basename "$0"`;; esac || { (exit 127); exit 127; } -case `echo X | tail -n +1 2>/dev/null` in +case `printf 'X\n' | tail -n +1 2>/dev/null` in X) tail_n=-n;; *) tail_n=;; esac @@ -180,13 +180,13 @@ if tail $tail_n +$skip <"$0" | gzip -cd > "$gztmp"; then (sleep 5; rm -fr "$gztmpdir") 2>/dev/null & "$gztmp" ${1+"$@"}; res=$? else - echo >&2 "Cannot decompress $0" + printf >&2 '%s\n' "Cannot decompress $0" (exit 127); res=127 fi; exit $res EOF gzip -cv9 "$file") > "$tmp" || { res=$? - echo >&2 "$0: compression not possible for $i, file unchanged." + printf >&2 '%s\n' "$0: compression not possible for $i, file unchanged." continue } @@ -198,29 +198,29 @@ EOF skip=[0-9] | skip=[0-9][0-9] | skip=[0-9][0-9][0-9]) eval "$skip_line";; esac - case `echo X | tail -n +1 2>/dev/null` in + case `printf 'X\n' | tail -n +1 2>/dev/null` in X) tail_n=-n;; *) tail_n=;; esac tail $tail_n +$skip "$file" | gzip -cd > "$tmp" || { res=$? - echo >&2 "$0: $i probably not in gzexe format, file unchanged." + printf >&2 '%s\n' "$0: $i probably not in gzexe format, file unchanged." continue } fi test $writable -eq 1 || chmod u-w "$tmp" || { res=$? - echo >&2 "$0: $tmp: cannot chmod" + printf >&2 '%s\n' "$0: $tmp: cannot chmod" continue } ln -f "$file" "$file~" || { res=$? - echo >&2 "$0: cannot backup $i as $i~" + printf >&2 '%s\n' "$0: cannot backup $i as $i~" continue } mv -f "$tmp" "$file" || { res=$? - echo >&2 "$0: cannot rename $tmp to $i" + printf >&2 '%s\n' "$0: cannot rename $tmp to $i" continue } tmp= diff --git a/zcat.in b/zcat.in index 524ac51..9cf4c2d 100644 --- a/zcat.in +++ b/zcat.in @@ -44,8 +44,8 @@ With no FILE, or when FILE is -, read standard input. Report bugs to <bug-gzip@gnu.org>." case $1 in ---help) exec echo "$usage";; ---version) exec echo "$version";; +--help) printf '%s\n' "$usage" || exit 1; exit;; +--version) printf '%s\n' "$version" || exit 1; exit;; esac exec gzip -cd "$@" diff --git a/zcmp.in b/zcmp.in index 7847350..b0a4ca2 100644 --- a/zcmp.in +++ b/zcmp.in @@ -36,10 +36,9 @@ If a FILE is '-' or missing, read standard input. Report bugs to <bug-gzip@gnu.org>." -st=0 case $1 in ---help) echo "$usage" || st=2; exit $st;; ---version) echo "$version" || st=2; exit $st;; +--help) printf '%s\n' "$usage" || exit 2; exit;; +--version) printf '%s\n' "$version" || exit 2; exit;; esac exec zdiff --__cmp "$@" diff --git a/zdiff.in b/zdiff.in index e1efc1e..0d1e749 100644 --- a/zdiff.in +++ b/zdiff.in @@ -50,8 +50,8 @@ escape=' while :; do case $1 in - --h*) printf '%s\n' "$usage" || exit 2; exit;; - --v*) echo "$version" || exit 2; exit;; + --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 '$1'";; @@ -78,7 +78,7 @@ if test $# -eq 1; then (gzip -cd -- "$1" 4>&-; echo $? >&4) 3>&- | eval "$cmp" - '"$FILE"' >&3 );; *) - echo >&2 "$0: $1: unknown compressed file extension" + printf >&2 '%s\n' "$0: $1: unknown compressed file extension" exit 2;; esac elif test $# -eq 2; then @@ -152,7 +152,8 @@ elif test $# -eq 2; then esac;; esac else - echo >&2 "$0: invalid number of operands; try \`$0 --help' for help" + printf >&2 '%s\n' \ + "$0: invalid number of operands; try \`$0 --help' for help" exit 2 fi diff --git a/zforce.in b/zforce.in index 1770af3..f0ff71c 100644 --- a/zforce.in +++ b/zforce.in @@ -38,20 +38,20 @@ not compress them twice. Report bugs to <bug-gzip@gnu.org>." if test $# = 0; then - echo >&2 "$0: invalid number of operands; try \`$0 --help' for help" + printf >&2 '%s\n' "$0: invalid number of operands; try \`$0 --help' for help" exit 1 fi res=0 for i do case "$i" in - --h*) exec echo "$usage";; - --v*) exec echo "$version";; + --h*) printf '%s\n' "$usage" ; exit;; + --v*) printf '%s\n' "$version"; exit;; *[-.]z | *[-.]gz | *.t[ag]z) continue;; esac if test ! -f "$i" ; then - echo zforce: $i not a file + printf '%s\n' "zforce: $i not a file" res=1 continue fi @@ -60,7 +60,7 @@ for i do new="$i.gz" if mv "$i" "$new"; then - echo $i -- replaced with $new + printf '%s\n' "$i -- replaced with $new" else res=$? fi diff --git a/zgrep.in b/zgrep.in index 2ed007b..0660634 100644 --- a/zgrep.in +++ b/zgrep.in @@ -144,7 +144,7 @@ while test $# -ne 0; do fi have_pat=1;; (--h | --he | --hel | --help) - echo "$usage" || exit 2 + printf '%s\n' "$usage" || exit 2 exit;; (-H | --wi | --wit | --with | --with- | --with-f | --with-fi \ | --with-fil | --with-file | --with-filen | --with-filena | --with-filenam \ @@ -158,7 +158,7 @@ while test $# -ne 0; do (-h | --no-f*) no_filename=1;; (-V | --v | --ve | --ver | --vers | --versi | --versio | --version) - echo "$version" || exit 2 + printf '%s\n' "$version" || exit 2 exit;; esac diff --git a/zless.in b/zless.in index 9a3f399..5c20d82 100644 --- a/zless.in +++ b/zless.in @@ -32,8 +32,8 @@ Options are the same as for 'less'. Report bugs to <bug-gzip@gnu.org>." case $1 in ---help) exec echo "$usage";; ---version) exec echo "$version";; +--help) printf '%s\n' "$usage" ; exit;; +--version) printf '%s\n' "$version"; exit;; esac if test "${LESSMETACHARS+set}" != set; then diff --git a/zmore.in b/zmore.in index 1c9cebb..1bd3bdb 100644 --- a/zmore.in +++ b/zmore.in @@ -31,8 +31,8 @@ Like 'more', but operate on the uncompressed contents of any compressed FILEs. Report bugs to <bug-gzip@gnu.org>." case $1 in - --h*) exec printf '%s\n' "$usage";; - --v*) exec printf '%s\n' "$version";; + --h*) printf '%s\n' "$usage" ; exit;; + --v*) printf '%s\n' "$version"; exit;; --) shift;; -?*) printf >&2 '%s\n' "$0: $1: unknown option; try '$0 --help' for help" exit 1;; diff --git a/znew.in b/znew.in index 29038df..5c98256 100644 --- a/znew.in +++ b/znew.in @@ -60,8 +60,8 @@ ext=.gz for arg do case "$arg" in - --help) exec echo "$usage";; - --version) exec echo "$version";; + --help) printf '%s\n' "$usage" ; exit;; + --version) printf '%s\n' "$version"; exit;; -*) opt="$opt $arg"; shift;; *) break;; esac @@ -72,24 +72,24 @@ if test $# -eq 0; then exit 1 fi -opt=`echo "$opt" | sed -e 's/ //g' -e 's/-//g'` +opt=`printf '%s\n' "$opt" | sed -e 's/ //g' -e 's/-//g'` case "$opt" in - *t*) check=1; opt=`echo "$opt" | sed 's/t//g'` + *t*) check=1; opt=`printf '%s\n' "$opt" | sed 's/t//g'` esac case "$opt" in - *K*) keep=1; check=1; opt=`echo "$opt" | sed 's/K//g'` + *K*) keep=1; check=1; opt=`printf '%s\n' "$opt" | sed 's/K//g'` esac case "$opt" in - *P*) pipe=1; opt=`echo "$opt" | sed 's/P//g'` + *P*) pipe=1; opt=`printf '%s\n' "$opt" | sed 's/P//g'` esac if test -n "$opt"; then opt="-$opt" fi for i do - n=`echo "$i" | sed 's/.Z$//'` + n=`printf '%s\n' "$i" | sed 's/.Z$//'` if test ! -f "$n.Z" ; then - echo "$n.Z not found" + printf '%s\n' "$n.Z not found" res=1; continue fi test $keep -eq 1 && old=`wc -c < "$n.Z"` @@ -99,7 +99,7 @@ for i do touch -r"$n.Z" -- "$n$ext" 2>/dev/null chmod --reference="$n.Z" -- "$n$ext" 2>/dev/null else - echo "error while recompressing $n.Z" + printf '%s\n' "error while recompressing $n.Z" res=1; continue fi else @@ -107,7 +107,7 @@ for i do if cp -p "$n.Z" "$n.$$"; then : else - echo "cannot backup $n.Z" + printf '%s\n' "cannot backup $n.Z" res=1; continue fi fi @@ -115,7 +115,7 @@ for i do : else test $check -eq 1 && mv "$n.$$" "$n.Z" - echo "error while uncompressing $n.Z" + printf '%s\n' "error while uncompressing $n.Z" res=1; continue fi if gzip $opt "$n"; then @@ -123,10 +123,10 @@ for i do else if test $check -eq 1; then mv "$n.$$" "$n.Z" && rm -f "$n" - echo "error while recompressing $n" + printf '%s\n' "error while recompressing $n" else # compress $n (might be dangerous if disk full) - echo "error while recompressing $n, left uncompressed" + printf '%s\n' "error while recompressing $n, left uncompressed" fi res=1; continue fi @@ -139,7 +139,7 @@ for i do else mv "$n.$$" "$n.Z" && rm -f "$n$ext" fi - echo "$n.Z smaller than $n$ext -- unchanged" + printf '%s\n' "$n.Z smaller than $n$ext -- unchanged" elif test $check -eq 1; then if gzip -t "$n$ext" ; then @@ -147,7 +147,7 @@ for i do else test $pipe -eq 0 && mv "$n.$$" "$n.Z" rm -f "$n$ext" - echo "error while testing $n$ext, $n.Z unchanged" + printf '%s\n' "error while testing $n$ext, $n.Z unchanged" res=1; continue fi elif test $pipe -eq 1; then -- 2.7.4