diff options
-rw-r--r-- | findutils/grep.c | 83 | ||||
-rw-r--r-- | include/usage.h | 5 |
2 files changed, 49 insertions, 39 deletions
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 @@ | |||
24 | 24 | ||
25 | /* options */ | 25 | /* options */ |
26 | #define OPTSTR_GREP \ | 26 | #define OPTSTR_GREP \ |
27 | "lnqvscFiHhe:f:Lor" \ | 27 | "lnqvscFiHhe:f:Lorm:" \ |
28 | USE_FEATURE_GREP_CONTEXT("A:B:C:") \ | 28 | USE_FEATURE_GREP_CONTEXT("A:B:C:") \ |
29 | USE_FEATURE_GREP_EGREP_ALIAS("E") \ | 29 | USE_FEATURE_GREP_EGREP_ALIAS("E") \ |
30 | USE_DESKTOP("w") \ | 30 | USE_DESKTOP("w") \ |
@@ -33,26 +33,27 @@ | |||
33 | /* ignored: -I "assume binary files have no matches" */ | 33 | /* ignored: -I "assume binary files have no matches" */ |
34 | 34 | ||
35 | enum { | 35 | enum { |
36 | OPTBIT_l, | 36 | OPTBIT_l, /* list matched file names only */ |
37 | OPTBIT_n, | 37 | OPTBIT_n, /* print line# */ |
38 | OPTBIT_q, | 38 | OPTBIT_q, /* quiet - exit(0) of first match */ |
39 | OPTBIT_v, | 39 | OPTBIT_v, /* invert the match, to select non-matching lines */ |
40 | OPTBIT_s, | 40 | OPTBIT_s, /* suppress errors about file open errors */ |
41 | OPTBIT_c, | 41 | OPTBIT_c, /* count matches per file (suppresses normal output) */ |
42 | OPTBIT_F, | 42 | OPTBIT_F, /* literal match */ |
43 | OPTBIT_i, | 43 | OPTBIT_i, /* case-insensitive */ |
44 | OPTBIT_H, | 44 | OPTBIT_H, /* force filename display */ |
45 | OPTBIT_h, | 45 | OPTBIT_h, /* inhibit filename display */ |
46 | OPTBIT_e, | 46 | OPTBIT_e, /* -e PATTERN */ |
47 | OPTBIT_f, | 47 | OPTBIT_f, /* -f FILE_WITH_PATTERNS */ |
48 | OPTBIT_L, | 48 | OPTBIT_L, /* list unmatched file names only */ |
49 | OPTBIT_o, | 49 | OPTBIT_o, /* show only matching parts of lines */ |
50 | OPTBIT_r, | 50 | OPTBIT_r, /* recurse dirs */ |
51 | USE_FEATURE_GREP_CONTEXT( OPTBIT_A ,) | 51 | OPTBIT_m, /* -m MAX_MATCHES */ |
52 | USE_FEATURE_GREP_CONTEXT( OPTBIT_B ,) | 52 | USE_FEATURE_GREP_CONTEXT( OPTBIT_A ,) /* -A NUM: after-match context */ |
53 | USE_FEATURE_GREP_CONTEXT( OPTBIT_C ,) | 53 | USE_FEATURE_GREP_CONTEXT( OPTBIT_B ,) /* -B NUM: before-match context */ |
54 | USE_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) | 54 | USE_FEATURE_GREP_CONTEXT( OPTBIT_C ,) /* -C NUM: -A and -B combined */ |
55 | USE_DESKTOP( OPTBIT_w ,) | 55 | USE_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */ |
56 | USE_DESKTOP( OPTBIT_w ,) /* whole word match */ | ||
56 | OPT_l = 1 << OPTBIT_l, | 57 | OPT_l = 1 << OPTBIT_l, |
57 | OPT_n = 1 << OPTBIT_n, | 58 | OPT_n = 1 << OPTBIT_n, |
58 | OPT_q = 1 << OPTBIT_q, | 59 | OPT_q = 1 << OPTBIT_q, |
@@ -68,6 +69,7 @@ enum { | |||
68 | OPT_L = 1 << OPTBIT_L, | 69 | OPT_L = 1 << OPTBIT_L, |
69 | OPT_o = 1 << OPTBIT_o, | 70 | OPT_o = 1 << OPTBIT_o, |
70 | OPT_r = 1 << OPTBIT_r, | 71 | OPT_r = 1 << OPTBIT_r, |
72 | OPT_m = 1 << OPTBIT_m, | ||
71 | OPT_A = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0, | 73 | OPT_A = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0, |
72 | OPT_B = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0, | 74 | OPT_B = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0, |
73 | OPT_C = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0, | 75 | OPT_C = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0, |
@@ -85,6 +87,7 @@ enum { | |||
85 | 87 | ||
86 | typedef unsigned char byte_t; | 88 | typedef unsigned char byte_t; |
87 | 89 | ||
90 | static int max_matches; | ||
88 | static int reflags; | 91 | static int reflags; |
89 | static byte_t invert_search; | 92 | static byte_t invert_search; |
90 | static byte_t print_filename; | 93 | static byte_t print_filename; |
@@ -97,7 +100,6 @@ static int lines_after; | |||
97 | static char **before_buf; | 100 | static char **before_buf; |
98 | static int last_line_printed; | 101 | static int last_line_printed; |
99 | #endif /* ENABLE_FEATURE_GREP_CONTEXT */ | 102 | #endif /* ENABLE_FEATURE_GREP_CONTEXT */ |
100 | |||
101 | /* globals used internally */ | 103 | /* globals used internally */ |
102 | static llist_t *pattern_head; /* growable list of patterns to match */ | 104 | static llist_t *pattern_head; /* growable list of patterns to match */ |
103 | static const char *cur_file; /* the current file we are reading */ | 105 | static const char *cur_file; /* the current file we are reading */ |
@@ -146,6 +148,8 @@ static int grep_file(FILE *file) | |||
146 | int print_n_lines_after = 0; | 148 | int print_n_lines_after = 0; |
147 | int curpos = 0; /* track where we are in the circular 'before' buffer */ | 149 | int curpos = 0; /* track where we are in the circular 'before' buffer */ |
148 | int idx = 0; /* used for iteration through the circular buffer */ | 150 | int idx = 0; /* used for iteration through the circular buffer */ |
151 | #else | ||
152 | enum { print_n_lines_after = 0 }; | ||
149 | #endif /* ENABLE_FEATURE_GREP_CONTEXT */ | 153 | #endif /* ENABLE_FEATURE_GREP_CONTEXT */ |
150 | 154 | ||
151 | while ((line = xmalloc_getline(file)) != NULL) { | 155 | while ((line = xmalloc_getline(file)) != NULL) { |
@@ -212,6 +216,12 @@ static int grep_file(FILE *file) | |||
212 | return 1; /* one match */ | 216 | return 1; /* one match */ |
213 | } | 217 | } |
214 | 218 | ||
219 | #if ENABLE_FEATURE_GREP_CONTEXT | ||
220 | /* Were we printing context and saw next (unwanted) match? */ | ||
221 | if ((option_mask32 & OPT_m) && nmatches > max_matches) | ||
222 | break; | ||
223 | #endif | ||
224 | |||
215 | /* print the matched line */ | 225 | /* print the matched line */ |
216 | if (PRINT_MATCH_COUNTS == 0) { | 226 | if (PRINT_MATCH_COUNTS == 0) { |
217 | #if ENABLE_FEATURE_GREP_CONTEXT | 227 | #if ENABLE_FEATURE_GREP_CONTEXT |
@@ -255,7 +265,7 @@ static int grep_file(FILE *file) | |||
255 | #if ENABLE_FEATURE_GREP_CONTEXT | 265 | #if ENABLE_FEATURE_GREP_CONTEXT |
256 | else { /* no match */ | 266 | else { /* no match */ |
257 | /* if we need to print some context lines after the last match, do so */ | 267 | /* if we need to print some context lines after the last match, do so */ |
258 | if (print_n_lines_after /* && (last_line_printed != linenum) */ ) { | 268 | if (print_n_lines_after) { |
259 | print_line(line, linenum, '-'); | 269 | print_line(line, linenum, '-'); |
260 | print_n_lines_after--; | 270 | print_n_lines_after--; |
261 | } else if (lines_before) { | 271 | } else if (lines_before) { |
@@ -264,12 +274,17 @@ static int grep_file(FILE *file) | |||
264 | before_buf[curpos] = line; | 274 | before_buf[curpos] = line; |
265 | curpos = (curpos + 1) % lines_before; | 275 | curpos = (curpos + 1) % lines_before; |
266 | /* avoid free(line) - we took line */ | 276 | /* avoid free(line) - we took line */ |
267 | continue; | 277 | line = NULL; |
268 | } | 278 | } |
269 | } | 279 | } |
270 | 280 | ||
271 | #endif /* ENABLE_FEATURE_GREP_CONTEXT */ | 281 | #endif /* ENABLE_FEATURE_GREP_CONTEXT */ |
272 | free(line); | 282 | free(line); |
283 | |||
284 | /* Did we print all context after last requested match? */ | ||
285 | if ((option_mask32 & OPT_m) | ||
286 | && !print_n_lines_after && nmatches == max_matches) | ||
287 | break; | ||
273 | } | 288 | } |
274 | 289 | ||
275 | /* special-case file post-processing for options where we don't print line | 290 | /* 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) | |||
311 | return (char *)gl; | 326 | return (char *)gl; |
312 | } | 327 | } |
313 | 328 | ||
314 | |||
315 | static void load_regexes_from_file(llist_t *fopt) | 329 | static void load_regexes_from_file(llist_t *fopt) |
316 | { | 330 | { |
317 | char *line; | 331 | char *line; |
@@ -365,6 +379,7 @@ int grep_main(int argc, char **argv) | |||
365 | { | 379 | { |
366 | FILE *file; | 380 | FILE *file; |
367 | int matched; | 381 | int matched; |
382 | char *mopt; | ||
368 | llist_t *fopt = NULL; | 383 | llist_t *fopt = NULL; |
369 | 384 | ||
370 | /* do normal option parsing */ | 385 | /* do normal option parsing */ |
@@ -376,7 +391,7 @@ int grep_main(int argc, char **argv) | |||
376 | opt_complementary = "H-h:e::f::C-AB"; | 391 | opt_complementary = "H-h:e::f::C-AB"; |
377 | getopt32(argc, argv, | 392 | getopt32(argc, argv, |
378 | OPTSTR_GREP, | 393 | OPTSTR_GREP, |
379 | &pattern_head, &fopt, | 394 | &pattern_head, &fopt, &mopt, |
380 | &slines_after, &slines_before, &Copt); | 395 | &slines_after, &slines_before, &Copt); |
381 | 396 | ||
382 | if (option_mask32 & OPT_C) { | 397 | if (option_mask32 & OPT_C) { |
@@ -405,8 +420,11 @@ int grep_main(int argc, char **argv) | |||
405 | /* with auto sanity checks */ | 420 | /* with auto sanity checks */ |
406 | opt_complementary = "H-h:e::f::c-n:q-n:l-n"; | 421 | opt_complementary = "H-h:e::f::c-n:q-n:l-n"; |
407 | getopt32(argc, argv, OPTSTR_GREP, | 422 | getopt32(argc, argv, OPTSTR_GREP, |
408 | &pattern_head, &fopt); | 423 | &pattern_head, &fopt, &mopt); |
409 | #endif | 424 | #endif |
425 | if (option_mask32 & OPT_m) { | ||
426 | max_matches = xatoi_u(mopt); | ||
427 | } | ||
410 | invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */ | 428 | invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */ |
411 | 429 | ||
412 | if (pattern_head != NULL) { | 430 | if (pattern_head != NULL) { |
@@ -487,12 +505,7 @@ int grep_main(int argc, char **argv) | |||
487 | } | 505 | } |
488 | matched += grep_file(file); | 506 | matched += grep_file(file); |
489 | fclose_if_not_stdin(file); | 507 | fclose_if_not_stdin(file); |
490 | grep_done: | 508 | grep_done: ; |
491 | if (matched < 0) { | ||
492 | /* we found a match but were told to be quiet, stop here and | ||
493 | * return success */ | ||
494 | break; | ||
495 | } | ||
496 | } | 509 | } |
497 | 510 | ||
498 | /* destroy all the elments in the pattern list */ | 511 | /* destroy all the elments in the pattern list */ |
@@ -512,10 +525,6 @@ int grep_main(int argc, char **argv) | |||
512 | } | 525 | } |
513 | } | 526 | } |
514 | /* 0 = success, 1 = failed, 2 = error */ | 527 | /* 0 = success, 1 = failed, 2 = error */ |
515 | /* If the -q option is specified, the exit status shall be zero | ||
516 | * if an input line is selected, even if an error was detected. */ | ||
517 | if (BE_QUIET && matched) | ||
518 | return 0; | ||
519 | if (open_errors) | 528 | if (open_errors) |
520 | return 2; | 529 | return 2; |
521 | return !matched; /* invert return value 0 = success, 1 = failed */ | 530 | 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 @@ | |||
1164 | "\n -v Select non-matching lines" \ | 1164 | "\n -v Select non-matching lines" \ |
1165 | "\n -s Suppress file open/read error messages" \ | 1165 | "\n -s Suppress file open/read error messages" \ |
1166 | "\n -c Only print count of matching lines" \ | 1166 | "\n -c Only print count of matching lines" \ |
1167 | "\n -f Read PATTERN from file" \ | ||
1168 | "\n -o Show only the part of a line that matches PATTERN" \ | 1167 | "\n -o Show only the part of a line that matches PATTERN" \ |
1168 | "\n -m MAX Match up to MAX times per file" \ | ||
1169 | USE_DESKTOP( \ | 1169 | USE_DESKTOP( \ |
1170 | "\n -w Match whole words only") \ | 1170 | "\n -w Match whole words only") \ |
1171 | "\n -e PATTERN is a regular expression" \ | ||
1172 | "\n -F PATTERN is a set of newline-separated strings" \ | 1171 | "\n -F PATTERN is a set of newline-separated strings" \ |
1173 | USE_FEATURE_GREP_EGREP_ALIAS( \ | 1172 | USE_FEATURE_GREP_EGREP_ALIAS( \ |
1174 | "\n -E PATTERN is an extended regular expression") \ | 1173 | "\n -E PATTERN is an extended regular expression") \ |
1174 | "\n -e PTRN Pattern to match" \ | ||
1175 | "\n -f FILE Read pattern from file" \ | ||
1175 | USE_FEATURE_GREP_CONTEXT( \ | 1176 | USE_FEATURE_GREP_CONTEXT( \ |
1176 | "\n -A Print NUM lines of trailing context" \ | 1177 | "\n -A Print NUM lines of trailing context" \ |
1177 | "\n -B Print NUM lines of leading context" \ | 1178 | "\n -B Print NUM lines of leading context" \ |