diff --git a/findutils/grep.c b/findutils/grep.c index 7bade87cb..e543ee07f 100644 --- a/findutils/grep.c +++ b/findutils/grep.c @@ -24,7 +24,7 @@ /* options */ #define OPTSTR_GREP \ - "lnqvscFiHhe:f:Lor" \ + "lnqvscFiHhe:f:Lorm:" \ USE_FEATURE_GREP_CONTEXT("A:B:C:") \ USE_FEATURE_GREP_EGREP_ALIAS("E") \ USE_DESKTOP("w") \ @@ -33,26 +33,27 @@ /* ignored: -I "assume binary files have no matches" */ enum { - OPTBIT_l, - OPTBIT_n, - OPTBIT_q, - OPTBIT_v, - OPTBIT_s, - OPTBIT_c, - OPTBIT_F, - OPTBIT_i, - OPTBIT_H, - OPTBIT_h, - OPTBIT_e, - OPTBIT_f, - OPTBIT_L, - OPTBIT_o, - OPTBIT_r, - USE_FEATURE_GREP_CONTEXT( OPTBIT_A ,) - USE_FEATURE_GREP_CONTEXT( OPTBIT_B ,) - USE_FEATURE_GREP_CONTEXT( OPTBIT_C ,) - USE_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) - USE_DESKTOP( OPTBIT_w ,) + OPTBIT_l, /* list matched file names only */ + OPTBIT_n, /* print line# */ + OPTBIT_q, /* quiet - exit(0) of first match */ + OPTBIT_v, /* invert the match, to select non-matching lines */ + OPTBIT_s, /* suppress errors about file open errors */ + OPTBIT_c, /* count matches per file (suppresses normal output) */ + OPTBIT_F, /* literal match */ + OPTBIT_i, /* case-insensitive */ + OPTBIT_H, /* force filename display */ + OPTBIT_h, /* inhibit filename display */ + OPTBIT_e, /* -e PATTERN */ + OPTBIT_f, /* -f FILE_WITH_PATTERNS */ + OPTBIT_L, /* list unmatched file names only */ + OPTBIT_o, /* show only matching parts of lines */ + OPTBIT_r, /* recurse dirs */ + OPTBIT_m, /* -m MAX_MATCHES */ + USE_FEATURE_GREP_CONTEXT( OPTBIT_A ,) /* -A NUM: after-match context */ + USE_FEATURE_GREP_CONTEXT( OPTBIT_B ,) /* -B NUM: before-match context */ + USE_FEATURE_GREP_CONTEXT( OPTBIT_C ,) /* -C NUM: -A and -B combined */ + USE_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */ + USE_DESKTOP( OPTBIT_w ,) /* whole word match */ OPT_l = 1 << OPTBIT_l, OPT_n = 1 << OPTBIT_n, OPT_q = 1 << OPTBIT_q, @@ -68,6 +69,7 @@ enum { OPT_L = 1 << OPTBIT_L, OPT_o = 1 << OPTBIT_o, OPT_r = 1 << OPTBIT_r, + OPT_m = 1 << OPTBIT_m, OPT_A = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0, OPT_B = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0, OPT_C = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0, @@ -85,6 +87,7 @@ enum { typedef unsigned char byte_t; +static int max_matches; static int reflags; static byte_t invert_search; static byte_t print_filename; @@ -97,7 +100,6 @@ static int lines_after; static char **before_buf; static int last_line_printed; #endif /* ENABLE_FEATURE_GREP_CONTEXT */ - /* globals used internally */ static llist_t *pattern_head; /* growable list of patterns to match */ static const char *cur_file; /* the current file we are reading */ @@ -146,6 +148,8 @@ static int grep_file(FILE *file) int print_n_lines_after = 0; int curpos = 0; /* track where we are in the circular 'before' buffer */ int idx = 0; /* used for iteration through the circular buffer */ +#else + enum { print_n_lines_after = 0 }; #endif /* ENABLE_FEATURE_GREP_CONTEXT */ while ((line = xmalloc_getline(file)) != NULL) { @@ -212,6 +216,12 @@ static int grep_file(FILE *file) return 1; /* one match */ } +#if ENABLE_FEATURE_GREP_CONTEXT + /* Were we printing context and saw next (unwanted) match? */ + if ((option_mask32 & OPT_m) && nmatches > max_matches) + break; +#endif + /* print the matched line */ if (PRINT_MATCH_COUNTS == 0) { #if ENABLE_FEATURE_GREP_CONTEXT @@ -255,7 +265,7 @@ static int grep_file(FILE *file) #if ENABLE_FEATURE_GREP_CONTEXT else { /* no match */ /* if we need to print some context lines after the last match, do so */ - if (print_n_lines_after /* && (last_line_printed != linenum) */ ) { + if (print_n_lines_after) { print_line(line, linenum, '-'); print_n_lines_after--; } else if (lines_before) { @@ -264,12 +274,17 @@ static int grep_file(FILE *file) before_buf[curpos] = line; curpos = (curpos + 1) % lines_before; /* avoid free(line) - we took line */ - continue; + line = NULL; } } #endif /* ENABLE_FEATURE_GREP_CONTEXT */ free(line); + + /* Did we print all context after last requested match? */ + if ((option_mask32 & OPT_m) + && !print_n_lines_after && nmatches == max_matches) + break; } /* special-case file post-processing for options where we don't print line @@ -311,7 +326,6 @@ static char * add_grep_list_data(char *pattern) return (char *)gl; } - static void load_regexes_from_file(llist_t *fopt) { char *line; @@ -365,6 +379,7 @@ int grep_main(int argc, char **argv) { FILE *file; int matched; + char *mopt; llist_t *fopt = NULL; /* do normal option parsing */ @@ -376,7 +391,7 @@ int grep_main(int argc, char **argv) opt_complementary = "H-h:e::f::C-AB"; getopt32(argc, argv, OPTSTR_GREP, - &pattern_head, &fopt, + &pattern_head, &fopt, &mopt, &slines_after, &slines_before, &Copt); if (option_mask32 & OPT_C) { @@ -405,8 +420,11 @@ int grep_main(int argc, char **argv) /* with auto sanity checks */ opt_complementary = "H-h:e::f::c-n:q-n:l-n"; getopt32(argc, argv, OPTSTR_GREP, - &pattern_head, &fopt); + &pattern_head, &fopt, &mopt); #endif + if (option_mask32 & OPT_m) { + max_matches = xatoi_u(mopt); + } invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */ if (pattern_head != NULL) { @@ -487,12 +505,7 @@ int grep_main(int argc, char **argv) } matched += grep_file(file); fclose_if_not_stdin(file); - grep_done: - if (matched < 0) { - /* we found a match but were told to be quiet, stop here and - * return success */ - break; - } + grep_done: ; } /* destroy all the elments in the pattern list */ @@ -512,10 +525,6 @@ int grep_main(int argc, char **argv) } } /* 0 = success, 1 = failed, 2 = error */ - /* If the -q option is specified, the exit status shall be zero - * if an input line is selected, even if an error was detected. */ - if (BE_QUIET && matched) - return 0; if (open_errors) return 2; return !matched; /* invert return value 0 = success, 1 = failed */ diff --git a/include/usage.h b/include/usage.h index 68325046f..bd9cb9908 100644 --- a/include/usage.h +++ b/include/usage.h @@ -1164,14 +1164,15 @@ "\n -v Select non-matching lines" \ "\n -s Suppress file open/read error messages" \ "\n -c Only print count of matching lines" \ - "\n -f Read PATTERN from file" \ "\n -o Show only the part of a line that matches PATTERN" \ + "\n -m MAX Match up to MAX times per file" \ USE_DESKTOP( \ "\n -w Match whole words only") \ - "\n -e PATTERN is a regular expression" \ "\n -F PATTERN is a set of newline-separated strings" \ USE_FEATURE_GREP_EGREP_ALIAS( \ "\n -E PATTERN is an extended regular expression") \ + "\n -e PTRN Pattern to match" \ + "\n -f FILE Read pattern from file" \ USE_FEATURE_GREP_CONTEXT( \ "\n -A Print NUM lines of trailing context" \ "\n -B Print NUM lines of leading context" \