190322.1.2 · 1c2a64d58bd89cb803b6ad2f304f1a1ce7effb79
Fix -L again and decouple from -v
reading declarations
@@ -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;
@@ -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;
@@ -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
@@ -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