From 215dc5aa42e9813d1726500e0f7b9c4ab9c36c64 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 29 Apr 2012 19:21:09 -0700 Subject: [PATCH] exclude: process exclude and include directives in order Also, change exclude and include directives so that they apply to command-line arguments too. This restores the pre-2009 behavior, and fixes a bug reported by Quentin Arce in . * src/main.c (included_patterns): Remove. All uses removed. (skipped_file): New function. (grepdirent): New arg command_line; all callers changed. This is needed because non-command-line files can invoke fts_open, and their directory entries need to be distinguished from top-level directory entries. Move code into the new skipped_file function. (grepdesc): Check whether a command-line argument should be skipped. (main): --include and --exclude options now share excluded_patterns rather than having separate variables included_patterns and excluded_patterns. * tests/include-exclude: Add a test to detect the fixed bug. --- src/main.c | 52 +++++++++++++++++++++++++++--------------------- tests/include-exclude | 5 ++++ 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/main.c b/src/main.c index c5a8489..6d2975b 100644 --- a/src/main.c +++ b/src/main.c @@ -272,7 +272,6 @@ static const struct color_cap color_dict[] = }; static struct exclude *excluded_patterns; -static struct exclude *included_patterns; static struct exclude *excluded_directory_patterns; /* Short options. */ static char const short_options[] = @@ -449,6 +448,20 @@ context_length_arg (char const *str, intmax_t *out) } } +/* Return nonzero if the file with NAME should be skipped. + If COMMAND_LINE is nonzero, it is a command-line argument. + If IS_DIR is nonzero, it is a directory. */ +static int +skipped_file (char const *name, int command_line, int is_dir) +{ + return (is_dir + ? (directories == SKIP_DIRECTORIES + || (! (command_line && filename_prefix_len != 0) + && excluded_directory_patterns + && excluded_file_name (excluded_directory_patterns, name))) + : (excluded_patterns + && excluded_file_name (excluded_patterns, name))); +} /* Hairy buffering mechanism for grep. The intent is to keep all reads aligned on a page boundary and multiples of the @@ -1207,11 +1220,11 @@ grep (int fd, struct stat const *st) } static int -grepdirent (FTS *fts, FTSENT *ent) +grepdirent (FTS *fts, FTSENT *ent, int command_line) { int follow, dirdesc; - int command_line = ent->fts_level == FTS_ROOTLEVEL; struct stat *st = ent->fts_statp; + command_line &= ent->fts_level == FTS_ROOTLEVEL; if (ent->fts_info == FTS_DP) { @@ -1220,17 +1233,9 @@ grepdirent (FTS *fts, FTSENT *ent) return 1; } - if ((ent->fts_info == FTS_D || ent->fts_info == FTS_DC - || ent->fts_info == FTS_DNR) - ? (directories == SKIP_DIRECTORIES - || (! (command_line && filename_prefix_len != 0) - && excluded_directory_patterns - && excluded_file_name (excluded_directory_patterns, - ent->fts_name))) - : ((included_patterns - && excluded_file_name (included_patterns, ent->fts_name)) - || (excluded_patterns - && excluded_file_name (excluded_patterns, ent->fts_name)))) + if (skipped_file (ent->fts_name, command_line, + (ent->fts_info == FTS_D || ent->fts_info == FTS_DC + || ent->fts_info == FTS_DNR))) { fts_set (fts, ent, FTS_SKIP); return 1; @@ -1336,6 +1341,11 @@ grepdesc (int desc, int command_line) suppressible_error (filename, errno); goto closeout; } + + if (desc != STDIN_FILENO && command_line + && skipped_file (filename, 1, S_ISDIR (st.st_mode))) + goto closeout; + if (desc != STDIN_FILENO && directories == RECURSE_DIRECTORIES && S_ISDIR (st.st_mode)) { @@ -1360,7 +1370,7 @@ grepdesc (int desc, int command_line) if (!fts) xalloc_die (); while ((ent = fts_read (fts))) - status &= grepdirent (fts, ent); + status &= grepdirent (fts, ent, command_line); if (errno) suppressible_error (filename, errno); if (fts_close (fts) != 0) @@ -2076,9 +2086,12 @@ main (int argc, char **argv) break; case EXCLUDE_OPTION: + case INCLUDE_OPTION: if (!excluded_patterns) excluded_patterns = new_exclude (); - add_exclude (excluded_patterns, optarg, EXCLUDE_WILDCARDS); + add_exclude (excluded_patterns, optarg, + (EXCLUDE_WILDCARDS + | (opt == INCLUDE_OPTION ? EXCLUDE_INCLUDE : 0))); break; case EXCLUDE_FROM_OPTION: if (!excluded_patterns) @@ -2096,13 +2109,6 @@ main (int argc, char **argv) add_exclude (excluded_directory_patterns, optarg, EXCLUDE_WILDCARDS); break; - case INCLUDE_OPTION: - if (!included_patterns) - included_patterns = new_exclude (); - add_exclude (included_patterns, optarg, - EXCLUDE_WILDCARDS | EXCLUDE_INCLUDE); - break; - case GROUP_SEPARATOR_OPTION: group_separator = optarg; break; diff --git a/tests/include-exclude b/tests/include-exclude index 406e0d4..28465cb 100755 --- a/tests/include-exclude +++ b/tests/include-exclude @@ -19,6 +19,11 @@ grep -r --exclude='a*' . x > out || fail=1 sort out > k && mv k out compare exp-not-a out || fail=1 +grep -r --exclude='Rumpelstiltskin' --include='a*' --exclude='a*' . x > out || + fail=1 +sort out > k && mv k out +compare exp-not-a out || fail=1 + grep -r --exclude='[ab]' . x > out || fail=1 sort out > k && mv k out compare exp-not-ab out || fail=1 -- 1.7.6.5