grep: implement -m MAX_MATCHES; fix help text grep_file 771 822 +51 packed_usage 22573 22610 +37 .rodata 128058 128090 +32 grep_main
No binary (build failed)
—
(log yok)
| @@ -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 */ | |
| @@ -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" \ | |