aboutsummaryrefslogtreecommitdiff
path: root/src/scripts/xzgrep.in
diff options
context:
space:
mode:
Diffstat (limited to 'src/scripts/xzgrep.in')
-rw-r--r--src/scripts/xzgrep.in72
1 files changed, 53 insertions, 19 deletions
diff --git a/src/scripts/xzgrep.in b/src/scripts/xzgrep.in
index 78f5bd31..c5b89430 100644
--- a/src/scripts/xzgrep.in
+++ b/src/scripts/xzgrep.in
@@ -182,7 +182,9 @@ for i; do
*[-.]zst | *[-.]tzst) uncompress="zstd -cdfq";; # zstd needs -q.
*) uncompress="$xz -cdf";;
esac
- # Fail if xz or grep (or sed) fails.
+ # xz_status will hold the decompressor's exit status.
+ # Exit status of grep (and in rare cases, printf or sed) is
+ # available as the exit status of this assignment command.
xz_status=$(
exec 5>&1
($uncompress -- "$i" 5>&-; echo $? >&5) 3>&- |
@@ -232,33 +234,65 @@ for i; do
# $i already ends with a colon so don't add it here.
sed_script="s|^|$i|"
- # Fail if grep or sed fails.
+ # If grep or sed fails, pick the larger value of the two exit statuses.
+ # If sed fails, use at least 2 since we use >= 2 to indicate errors.
r=$(
exec 4>&1
(eval "$grep" 4>&-; echo $? >&4) 3>&- |
LC_ALL=C sed "$sed_script" >&3 4>&-
- ) || r=2
+ ) || {
+ sed_status=$?
+ test "$sed_status" -lt 2 && sed_status=2
+ test "$r" -lt "$sed_status" && r=$sed_status
+ }
exit $r
fi >&3 5>&-
)
r=$?
- # fail occurred previously, nothing worse can happen
- test $res -gt 1 && continue
-
- if test "$xz_status" -eq 0; then
- :
- elif test "$xz_status" -ge 128 \
- && test "$(kill -l "$xz_status" 2> /dev/null)" = "PIPE"; then
- :
- else
- r=2
+ # If grep or sed or other non-decompression command failed with a signal,
+ # exit immediately and ignore the possible remaining files.
+ #
+ # NOTE: Instead of 128 + signal_number, some shells use
+ # 256 + signal_number (ksh) or 384 + signal_number (yash).
+ # This is fine for us since their "exit" and "kill -l" commands take
+ # this into account. (At least the versions I tried do but there is
+ # a report of an old ksh variant whose "exit" truncates the exit status
+ # to 8 bits without any special handling for values indicating a signal.)
+ test "$r" -ge 128 && exit "$r"
+
+ if test -z "$xz_status"; then
+ # Something unusual happened, for example, we got a signal and
+ # the exit status of the decompressor was never echoed and thus
+ # $xz_status is empty. Exit immediately and ignore the possible
+ # remaining files.
+ exit 2
+ elif test "$xz_status" -ge 128; then
+ # The decompressor died due to a signal. SIGPIPE is ignored since it can
+ # occur if grep exits before the whole file has been decompressed (grep -q
+ # can do that). If the decompressor died with some other signal, exit
+ # immediately and ignore the possible remaining files.
+ test "$(kill -l "$xz_status" 2> /dev/null)" != "PIPE" && exit "$xz_status"
+ elif test "$xz_status" -gt 0; then
+ # Decompression failed but we will continue with the remaining
+ # files anwyway. Set exit status to at least 2 to indicate an error.
+ test "$r" -lt 2 && r=2
fi
- # still no match
- test $r -eq 1 && continue
-
- # 0 == match, >=2 == fail
- res=$r
+ # Since res=1 is the initial value, we only need to care about
+ # matches (r == 0) and errors (r >= 2) here; r == 1 can be ignored.
+ if test "$r" -ge 2; then
+ # An error occurred in decompressor, grep, or some other command. Update
+ # res unless a larger error code has been seen with an earlier file.
+ test "$res" -lt "$r" && res=$r
+ elif test "$r" -eq 0; then
+ # grep found a match and no errors occurred. Update res if no errors have
+ # occurred with earlier files.
+ test "$res" -eq 1 && res=0
+ fi
done
-exit $res
+
+# 0: At least one file matched and no errors occurred.
+# 1: No matches were found and no errors occurred.
+# >=2: Error. It's unknown if matches were found.
+exit "$res"