Skip to content

Commit

Permalink
Fixes for -L command line switch
Browse files Browse the repository at this point in the history
1) Bug fix for issue ggreer#238 where a file (call it test.txt) of contents
  "foo\n\n" will be invert matched by this command:

      ag -L foo test.txt

2) If user specifies both -L and -v on command line, that's a conflict.
   Resolve by suppressing -v .

3) Added test cases for 1 and 2.
  • Loading branch information
decaff committed Jun 19, 2015
1 parent e3c7ecc commit dbdca3c
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 18 deletions.
14 changes: 12 additions & 2 deletions src/options.c
Expand Up @@ -410,7 +410,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
opts.casing = CASE_INSENSITIVE;
break;
case 'L':
opts.invert_match = 1;
opts.invert_match_filename = 1;
/* fall through */
case 'l':
needs_query = 0;
Expand Down Expand Up @@ -454,7 +454,7 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
opts.skip_vcs_ignores = 1;
break;
case 'v':
opts.invert_match = 1;
opts.invert_match_listing = 1;
/* Color highlighting doesn't make sense when inverting matches */
opts.color = 0;
break;
Expand Down Expand Up @@ -540,6 +540,16 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {
}
}

if (opts.invert_match_listing && opts.invert_match_filename) {
/*
* User specified -L and -v at same time on the command line.
* These two options are inherently at conflict with each other.
* Opt to make -L the higher precedence option, which seems to give
* the least surprising results.
*/
opts.invert_match_listing = FALSE;
}
opts.invert_match = (opts.invert_match_listing || opts.invert_match_filename);

if (ext_index[0]) {
num_exts = combine_file_extensions(ext_index, lang_num, &extensions);
Expand Down
4 changes: 3 additions & 1 deletion src/options.h
Expand Up @@ -45,7 +45,9 @@ typedef struct {
int column;
int context;
int follow_symlinks;
int invert_match;
int invert_match_listing; /* list inverted matches */
int invert_match_filename; /* list only filename */
int invert_match; /* semi-logical OR of previous 2 fields */
int literal;
int literal_starts_wordchar;
int literal_ends_wordchar;
Expand Down
49 changes: 34 additions & 15 deletions src/search.c
Expand Up @@ -118,8 +118,18 @@ void search_buf(const char *buf, const size_t buf_len,
}
}

size_t inverted_matches = 0;
int invert_L = FALSE; /* -L conditions met */
int invert_v = FALSE; /* -v conditions met */
if (opts.invert_match) {
matches_len = invert_matches(buf, buf_len, matches, matches_len);
int all_inverted; /* T => nothing in buffer matches PATTERN */

all_inverted = (matches_len == 0);
matches_len = inverted_matches = invert_matches(
buf, buf_len, matches, matches_len
);
invert_v = (inverted_matches != 0 && opts.invert_match_listing);
invert_L = (all_inverted && opts.invert_match_filename);
}

if (opts.stats) {
Expand All @@ -133,29 +143,38 @@ void search_buf(const char *buf, const size_t buf_len,
pthread_mutex_unlock(&stats_mtx);
}

if (matches_len > 0) {
if (opts.invert_match) {
/*
* now that stats are recorded, whack "matches_len" so that it's
* not used for listing decisions in the next "if"
*/

matches_len = 0;
}

if (matches_len > 0 || invert_L || invert_v) {
if (binary == -1 && !opts.print_filename_only) {
binary = is_binary((const void *)buf, buf_len);
}
pthread_mutex_lock(&print_mtx);
if (opts.print_filename_only) {
/* If the --files-without-matches or -L option is passed we should
* not print a matching line. This option currently sets
* opts.print_filename_only and opts.invert_match. Unfortunately
* setting the latter has the side effect of making matches.len = 1
* on a file-without-matches which is not desired behaviour. See
* GitHub issue 206 for the consequences if this behaviour is not
* checked. */
if (!opts.invert_match || matches_len < 2) {
if (opts.print_count) {
print_path_count(dir_full_path, opts.path_sep, (size_t)matches_len);
} else {
print_path(dir_full_path, opts.path_sep);
}
if (opts.print_count) {
print_path_count(dir_full_path, opts.path_sep, (size_t)matches_len);
} else {
print_path(dir_full_path, opts.path_sep);
}
} else if (binary) {
print_binary_file_matches(dir_full_path);
} else {
if (opts.invert_match) {
/*
* Restore the correct value of "matches_len" (which was
* whacked up above) so that print_file_matches() works
* correctly.
*/

matches_len = inverted_matches;
}
print_file_matches(dir_full_path, buf, buf_len, matches, matches_len);
}
pthread_mutex_unlock(&print_mtx);
Expand Down
29 changes: 29 additions & 0 deletions tests/files_without_matches.t
@@ -0,0 +1,29 @@
Setup:

$ . $TESTDIR/setup.sh
$ echo 'foo' > ./blah.txt
$ echo >> ./blah.txt

This file structure caused older versions of ag to falsely report an
inverted match for "foo"

$ ag -L 'foo' ./blah.txt
[1]

Run a few more tests....

$ echo 'stuff for a test' > ./blah2.txt
$ echo 'There you go' >> ./blah2.txt
$ echo 'Down Goes Frazier' >> ./blah2.txt
$ echo 'There it is' > ./blah3.txt
$ echo '.........' >> ./blah3.txt
$ echo 'Was it there' >> ./blah3.txt

$ ag -L 'there' ./blah.txt ./blah2.txt ./blah3.txt
blah.txt

If -v and -L are both specified, -L wins ...

$ ag -v -L 'stuff' ./blah.txt ./blah2.txt ./blah3.txt
blah.txt
blah3.txt

0 comments on commit dbdca3c

Please sign in to comment.