Vincent Lefevre wrote:
The documentation is still ambiguous. For the "main case", is this
the canonical name as returned by realpath?

>As you say, the 2.22 behavior does not seem ideal.
By doing a difference for subfiles of a recursive search, this is
even worse!

Please try the attached patch, which I've installed into the savannah repository. It attempts to fix the behavior, and to clarify the documentation. It is a tricky area. Hope this helps.
>From 7dc95d5dd3965df188533ac1ffef903cf41a11e5 Mon Sep 17 00:00:00 2001
From: Paul Eggert <egg...@cs.ucla.edu>
Date: Mon, 28 Dec 2015 01:04:18 -0800
Subject: [PATCH] grep: --exclude matches trailing parts of args

Problem reported by Vincent Lefevre in:
http://bugs.gnu.org/22144
* NEWS:
* doc/grep.texi (File and Directory Selection): Document this.
* src/grep.c (excluded_patterns, excluded_directory_patterns):
Now 2-element arrays, with one element for subfiles and another
for command-line args.  All uses changed.  This implements the change.
(exclude_options): New function.
* tests/include-exclude: Test the change.
---
 NEWS                  |  5 ++++
 doc/grep.texi         | 13 +++++++----
 src/grep.c            | 65 +++++++++++++++++++++++++++++++++------------------
 tests/include-exclude |  4 +++-
 4 files changed, 58 insertions(+), 29 deletions(-)

diff --git a/NEWS b/NEWS
index fc5fd3a..4e54b49 100644
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,11 @@ GNU grep NEWS                                    -*- outline -*-
   invalid UTF8 just before a match.
   [bug introduced in grep-2.22]
 
+  --exclude and related options are now matched against trailing
+  parts of command-line arguments, not against the entire arguments.
+  This partly reverts the --exclude-related change in 2.22.
+  [bug introduced in grep-2.22]
+
 
 * Noteworthy changes in release 2.22 (2015-11-01) [stable]
 
diff --git a/doc/grep.texi b/doc/grep.texi
index e3495bb..76c7f46 100644
--- a/doc/grep.texi
+++ b/doc/grep.texi
@@ -661,8 +661,10 @@ this is equivalent to the @option{-r} option.
 @opindex --exclude
 @cindex exclude files
 @cindex searching directory trees
-Skip files whose name matches the pattern @var{glob}, using wildcard
-matching.  When searching recursively, skip any subfile whose base
+Skip any command-line file with a name suffix that matches the pattern
+@var{glob}, using wildcard matching; a name suffix is either the whole
+name, or any suffix starting after a @samp{/} and before a
+non-@samp{/}.  When searching recursively, skip any subfile whose base
 name matches @var{glob}; the base name is the part after the last
 @samp{/}.  A pattern can use
 @samp{*}, @samp{?}, and @samp{[}...@samp{]} as wildcards,
@@ -679,9 +681,10 @@ under @option{--exclude}).
 @item --exclude-dir=@var{glob}
 @opindex --exclude-dir
 @cindex exclude directories
-Skip any directory whose name matches the pattern @var{glob}.  When
-searching recursively, skip any subdirectory whose base name matches
-@var{glob}.  Ignore any redundant trailing slashes in @var{glob}.
+Skip any command-line directory with a name suffix that matches the
+pattern @var{glob}.  When searching recursively, skip any subdirectory
+whose base name matches @var{glob}.  Ignore any redundant trailing
+slashes in @var{glob}.
 
 @item -I
 Process a binary file as if it did not contain matching data;
diff --git a/src/grep.c b/src/grep.c
index 2c5e09a..19ba208 100644
--- a/src/grep.c
+++ b/src/grep.c
@@ -296,8 +296,8 @@ static const struct color_cap color_dict[] =
     { NULL, NULL,                  NULL }
   };
 
-static struct exclude *excluded_patterns;
-static struct exclude *excluded_directory_patterns;
+static struct exclude *excluded_patterns[2];
+static struct exclude *excluded_directory_patterns[2];
 /* Short options.  */
 static char const short_options[] =
 "0123456789A:B:C:D:EFGHIPTUVX:abcd:e:f:hiLlm:noqRrsuvwxyZz";
@@ -640,19 +640,30 @@ context_length_arg (char const *str, intmax_t *out)
     }
 }
 
+/* Return the add_exclude options suitable for excluding a file name.
+   If COMMAND_LINE, it is a command-line file name.  */
+static int
+exclude_options (bool command_line)
+{
+  return EXCLUDE_WILDCARDS | (command_line ? 0 : EXCLUDE_ANCHORED);
+}
+
 /* Return true if the file with NAME should be skipped.
    If COMMAND_LINE, it is a command-line argument.
    If IS_DIR, it is a directory.  */
 static bool
 skipped_file (char const *name, bool command_line, bool is_dir)
 {
-  return (is_dir
-          ? (directories == SKIP_DIRECTORIES
-             || (! (command_line && omit_dot_slash)
-                 && excluded_directory_patterns
-                 && excluded_file_name (excluded_directory_patterns, name)))
-          : (excluded_patterns
-             && excluded_file_name (excluded_patterns, name)));
+  struct exclude **pats;
+  if (! is_dir)
+    pats = excluded_patterns;
+  else if (directories == SKIP_DIRECTORIES)
+    return true;
+  else if (command_line && omit_dot_slash)
+    return false;
+  else
+    pats = excluded_directory_patterns;
+  return pats[command_line] && excluded_file_name (pats[command_line], name);
 }
 
 /* Hairy buffering mechanism for grep.  The intent is to keep
@@ -2446,28 +2457,36 @@ main (int argc, char **argv)
 
       case EXCLUDE_OPTION:
       case INCLUDE_OPTION:
-        if (!excluded_patterns)
-          excluded_patterns = new_exclude ();
-        add_exclude (excluded_patterns, optarg,
-                     (EXCLUDE_ANCHORED | EXCLUDE_WILDCARDS
-                      | (opt == INCLUDE_OPTION ? EXCLUDE_INCLUDE : 0)));
+        for (int cmd = 0; cmd < 2; cmd++)
+          {
+            if (!excluded_patterns[cmd])
+              excluded_patterns[cmd] = new_exclude ();
+            add_exclude (excluded_patterns[cmd], optarg,
+                         ((opt == INCLUDE_OPTION ? EXCLUDE_INCLUDE : 0)
+                          | exclude_options (cmd)));
+          }
         break;
       case EXCLUDE_FROM_OPTION:
-        if (!excluded_patterns)
-          excluded_patterns = new_exclude ();
-        if (add_exclude_file (add_exclude, excluded_patterns, optarg,
-                              EXCLUDE_ANCHORED | EXCLUDE_WILDCARDS, '\n') != 0)
+        for (int cmd = 0; cmd < 2; cmd++)
           {
-            error (EXIT_TROUBLE, errno, "%s", optarg);
+            if (!excluded_patterns[cmd])
+              excluded_patterns[cmd] = new_exclude ();
+            if (add_exclude_file (add_exclude, excluded_patterns[cmd],
+                                  optarg, exclude_options (cmd), '\n')
+                != 0)
+              error (EXIT_TROUBLE, errno, "%s", optarg);
           }
         break;
 
       case EXCLUDE_DIRECTORY_OPTION:
-        if (!excluded_directory_patterns)
-          excluded_directory_patterns = new_exclude ();
         strip_trailing_slashes (optarg);
-        add_exclude (excluded_directory_patterns, optarg,
-                     EXCLUDE_ANCHORED | EXCLUDE_WILDCARDS);
+        for (int cmd = 0; cmd < 2; cmd++)
+          {
+            if (!excluded_directory_patterns[cmd])
+              excluded_directory_patterns[cmd] = new_exclude ();
+            add_exclude (excluded_directory_patterns[cmd], optarg,
+                         exclude_options (cmd));
+          }
         break;
 
       case GROUP_SEPARATOR_OPTION:
diff --git a/tests/include-exclude b/tests/include-exclude
index 6a9d269..c3d22a1 100755
--- a/tests/include-exclude
+++ b/tests/include-exclude
@@ -66,9 +66,11 @@ for exclude in 'x' 'x*'; do
     grep -rl --exclude-dir="$exclude" . x > out
     test $? -eq 1 || fail=1
     compare /dev/null out || fail=1
+done
 
+for exclude in 'x' 'x*' './x' './x*'; do
     grep -rl --exclude-dir="$exclude" . ./x | sort > out || fail=1
-    compare exp-dotnames out || fail=1
+    compare /dev/null out || fail=1
 done
 
 Exit $fail
-- 
2.5.0

Reply via email to