Subject: Re: bug#28105: Inconsistent exit code with --files-without-match

From: Paul Eggert <[email-masked]>
Date: Sat, 22 Aug 2020 14:11:47 -0700

On 8/7/20 4:14 AM, Antonio Diaz Diaz wrote:
> I propose to revert this change to remain consistent with POSIX.

It's not a POSIX issue, since POSIX doesn't specify -L, which means grep can do 
whatever it wants if you specify -L.

> But in GNU grep 3.2 to 3.4 -q inverts the exit status when -L is also used. Now 'grep -L > /dev/null' and 'grep -Lq' return opposite values.

That is indeed a bug.

I looked into this a bit further, and it turns out that when the Git folks were 
told that git-grep behaved differently from GNU grep 3.1 and earlier, they 
changed git-grep to match. This was around the same time we changed GNU grep to 
behave more like the git-grep at the time. What a comedy of errors, huh?

Anyway, given all these issues it does seem like it's wise to revert the change. 
I installed the attached patch into GNU grep master, with the goal of something 
like this appearing in the next grep release. Thanks for reporting the problem.
>From 0435ebca64fbafcd62008c991dd9377d8a792dfc Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email-masked]>
Date: Sat, 22 Aug 2020 14:06:26 -0700
Subject: [PATCH] Revert -L exit status change introduced in grep 3.2

Problems reported by Antonio Diaz Diaz in:
https://bugs.gnu.org/28105#29
* NEWS, doc/grep.texi (Exit Status), src/grep.c (usage):
Adjust documentation accordingly.
* src/grep.c (grepdesc, main): Go back to old behavior.
* tests/skip-read: Adjust tests accordingly.
---
 NEWS            |  7 +++++++
 doc/grep.texi   |  3 ---
 src/grep.c      | 10 +++++-----
 tests/skip-read |  4 ++--
 4 files changed, 14 insertions(+), 10 deletions(-)

diff --git a/NEWS b/NEWS
index 6abb7c2..5f4c0b4 100644
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,13 @@ GNU grep NEWS                                    -*- outline -*-
 
 * Noteworthy changes in release ?.? (????-??-??) [?]
 
+** Changes in behavior
+
+  The --files-without-match (-L) option has reverted to its behavior
+  in grep 3.1 and earlier.  That is, grep -L again succeeds when a
+  line is selected, not when a file is listed.  The behavior in grep
+  3.2 through 3.4 was causing compatibility problems.
+
 
 * Noteworthy changes in release 3.4 (2020-01-02) [stable]
 
diff --git a/doc/grep.texi b/doc/grep.texi
index 4b26f9d..02b1968 100644
--- a/doc/grep.texi
+++ b/doc/grep.texi
@@ -1112,9 +1112,6 @@ and only when @env{POSIXLY_CORRECT} is not set.
 
 Normally the exit status is 0 if a line is selected, 1 if no lines
 were selected, and 2 if an error occurred.  However, if the
-@option{-L} or @option{--files-without-match} is used, the exit status
-is 0 if a file is listed, 1 if no files were listed, and 2 if an error
-occurred.  Also, if the
 @option{-q} or @option{--quiet} or @option{--silent} option is used
 and a line is selected, the exit status is 0 even if an error
 occurred.  Other @command{grep} implementations may exit with status
diff --git a/src/grep.c b/src/grep.c
index 7ba602d..5764b2a 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -1864,11 +1864,11 @@ grepdesc (int desc, bool command_line)
         fflush_errno ();
     }
 
-  status = !count == !(list_files == LISTFILES_NONMATCHING);
+  status = !count;
 
-  if (list_files == LISTFILES_NONE || dev_null_output)
+  if (list_files == LISTFILES_NONE)
     finalize_input (desc, &st, ineof);
-  else if (status == 0)
+  else if (list_files == (status ? LISTFILES_NONMATCHING : LISTFILES_MATCHING))
     {
       print_filename ();
       putchar_errno ('\n' & filename_mask);
@@ -1997,7 +1997,7 @@ Context control:\n\
       printf (_("\
 When FILE is '-', read standard input.  With no FILE, read '.' if\n\
 recursive, '-' otherwise.  With fewer than two FILEs, assume -h.\n\
-Exit status is 0 if any line (or file if -L) is selected, 1 otherwise;\n\
+Exit status is 0 if any line is selected, 1 otherwise;\n\
 if any error occurs and -q is not given, the exit status is 2.\n"));
       emit_bug_reporting_address ();
     }
@@ -2835,7 +2835,7 @@ main (int argc, char **argv)
 
   /* POSIX says -c, -l and -q are mutually exclusive.  In this
      implementation, -q overrides -l and -L, which in turn override -c.  */
-  if (exit_on_match)
+  if (exit_on_match | dev_null_output)
     list_files = LISTFILES_NONE;
   if ((exit_on_match | dev_null_output) || list_files != LISTFILES_NONE)
     {
diff --git a/tests/skip-read b/tests/skip-read
index 2a17334..1e9e718 100755
--- a/tests/skip-read
+++ b/tests/skip-read
@@ -15,9 +15,9 @@ for opts in '-m0 y' '-f /dev/null' '-v ""'; do
       eval returns_ 1 grep -l $opts $matcher /dev/null > out || fail=1
       compare /dev/null out || fail=1
     done
-    eval grep -L $opts $matcher /dev/null > out || fail=1
+    eval returns_ 1 grep -L $opts $matcher /dev/null > out || fail=1
     compare exp out || fail=1
-    eval grep -L $opts $matcher /dev/null > /dev/null || fail=1
+    eval returns_ 1 grep -L $opts $matcher /dev/null > /dev/null || fail=1
   done
 done
 
-- 
2.17.1