>From 7dc95d5dd3965df188533ac1ffef903cf41a11e5 Mon Sep 17 00:00:00 2001 From: Paul Eggert
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 address@hidden, using wildcard matching; a name suffix is either the whole +name, or any suffix starting after a @samp{/} and before a address@hidden/}. 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 @address@hidden as wildcards, @@ -679,9 +681,10 @@ under @option{--exclude}). @item address@hidden @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 address@hidden 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