@ 201216.1

190322.1.2 · 1c2a64d58bd89cb803b6ad2f304f1a1ce7effb79

Fix -L again and decouple from -v

reading declarations

src/options.c

@@ -446,8 +446,9 @@ void parse_options(int argc, char **argv, char **base_paths[], char **paths[]) {                 opts.casing = CASE_INSENSITIVE;                 break;             case 'L':-                opts.invert_match = 1;-            /* fall through */+                opts.print_nonmatching_files = 1;+                opts.print_path = PATH_PRINT_TOP;+                break;             case 'l':                 needs_query = 0;                 opts.print_filename_only = 1;

src/options.h

@@ -60,6 +60,7 @@ typedef struct {     int print_break;     int print_count;     int print_filename_only;+    int print_nonmatching_files;     int print_path;     int print_all_paths;     int print_line_numbers;

src/search.c

@@ -175,25 +175,16 @@ multiline_done:         pthread_mutex_unlock(&stats_mtx);     } -    if (matches_len > 0 || opts.print_all_paths) {+    if (!opts.print_nonmatching_files && (matches_len > 0 || opts.print_all_paths)) {         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);@@ -260,6 +251,7 @@ void search_file(const char *file_full_path) {     char *buf = NULL;     struct stat statbuf;     int rv = 0;+    int matches_count = -1;     FILE *fp = NULL;      rv = stat(file_full_path, &statbuf);@@ -309,7 +301,7 @@ void search_file(const char *file_full_path) {     if (statbuf.st_mode & S_IFIFO) {         log_debug("%s is a named pipe. stream searching", file_full_path);         fp = fdopen(fd, "r");-        search_stream(fp, file_full_path);+        matches_count = search_stream(fp, file_full_path);         fclose(fp);         goto cleanup;     }@@ -318,7 +310,7 @@ void search_file(const char *file_full_path) {      if (f_len == 0) {         if (opts.query[0] == '.' && opts.query_len == 1 && !opts.literal && opts.search_all_files) {-            search_buf(buf, f_len, file_full_path);+            matches_count = search_buf(buf, f_len, file_full_path);         } else {             log_debug("Skipping %s: file is empty.", file_full_path);         }@@ -376,7 +368,7 @@ void search_file(const char *file_full_path) { #if HAVE_FOPENCOOKIE             log_debug("%s is a compressed file. stream searching", file_full_path);             fp = decompress_open(fd, "r", zip_type);-            search_stream(fp, file_full_path);+            matches_count = search_stream(fp, file_full_path);             fclose(fp); #else             int _buf_len = (int)f_len;@@ -385,17 +377,24 @@ void search_file(const char *file_full_path) {                 log_err("Cannot decompress zipped file %s", file_full_path);                 goto cleanup;             }-            search_buf(_buf, _buf_len, file_full_path);+            matches_count = search_buf(_buf, _buf_len, file_full_path);             free(_buf); #endif             goto cleanup;         }     } -    search_buf(buf, f_len, file_full_path);+    matches_count = search_buf(buf, f_len, file_full_path);  cleanup: +    if (opts.print_nonmatching_files && matches_count == 0) {+        pthread_mutex_lock(&print_mtx);+        print_path(file_full_path, opts.path_sep);+        pthread_mutex_unlock(&print_mtx);+        opts.match_found = 1;+    }+     print_cleanup_context();     if (buf != NULL) { #ifdef _WIN32

tests/files_with_matches.t

@@ -3,6 +3,10 @@ Setup:   $ . $TESTDIR/setup.sh   $ printf 'foo\n' > ./foo.txt   $ printf 'bar\n' > ./bar.txt+  $ printf 'foo\nbar\nbaz\n' > ./baz.txt+  $ printf 'duck\nanother duck\nyet another duck\n' > ./duck.txt+  $ cp duck.txt goose.txt+  $ echo "GOOSE!!!" >> ./goose.txt  Files with matches: @@ -12,8 +16,17 @@ Files with matches:   foo.txt   $ ag --files-with-matches foo bar.txt   [1]+  $ ag --files-with-matches foo foo.txt bar.txt baz.txt+  foo.txt+  baz.txt+  $ ag --files-with-matches bar foo.txt bar.txt baz.txt+  bar.txt+  baz.txt+  $ ag --files-with-matches foo bar.txt baz.txt+  baz.txt  Files without matches:+(Prints names of files in which no line matches query)    $ ag --files-without-matches bar foo.txt   foo.txt@@ -21,3 +34,30 @@ Files without matches:   foo.txt   $ ag --files-without-matches bar bar.txt   [1]+  $ ag --files-without-matches foo foo.txt bar.txt baz.txt+  bar.txt+  $ ag --files-without-matches bar foo.txt bar.txt baz.txt+  foo.txt++Files with inverted matches:+(Prints names of files in which some line doesn't match query)++  $ ag --files-with-matches --invert-match bar bar.txt+  [1]+  $ ag --files-with-matches --invert-match foo foo.txt bar.txt baz.txt+  bar.txt+  baz.txt+  $ ag --files-with-matches --invert-match bar foo.txt bar.txt baz.txt+  foo.txt+  baz.txt++Files without inverted matches:+(Prints names of files in which no line doesn't match query,+ i.e. where every line matches query)++  $ ag --files-without-matches --invert-match duck duck.txt+  duck.txt+  $ ag --files-without-matches --invert-match duck goose.txt+  [1]+  $ ag --files-without-matches --invert-match duck duck.txt goose.txt+  duck.txt