grep: fix -A handling in conjunction with -m match limitation

The basic issue here is that grep, when given -m 1, would stop all
line processing once it hit the match count and exit immediately.  The
problem with exiting immediately is that -A processing only happens when
subsequent lines are processed and do not match.

The fix here is relatively easy; when bsdgrep matches a line, it resets
the 'tail' of the matching context to the value supplied to -A and
dumps anything that's been queued up for -B. After the current line has
been printed and tail is reset, we check our mcount and do what's
needed. Therefore, at the time that we decide we're doing nothing, we
know that 'tail' of the context is correct and we can simply continue
on if there's still more to pick up.

With this change, we still bail out immediately if there's been no -A
flag. If -A was supplied, we signal that we should continue on. However,
subsequent lines will not even bothere to try and process the line.  We
have reached the match count, so even if the next line would match then
we must process it if it hadn't. Thus, the loop in procfile() can
short-circuit and just process the line as a non-match until
procmatches() indicates that it's safe to stop.

A test has been added to reflect both that we should be picking up the
next line and that the next line should be considered a non-match even
if it should have been.

PR:		253350
MFC-after:	3 days
This commit is contained in:
Kyle Evans 2021-02-08 12:31:17 -06:00
parent 32bf05ad89
commit 3e2d96ac97
2 changed files with 37 additions and 1 deletions

View file

@ -907,6 +907,22 @@ mflag_body()
atf_check -o inline:"test1:2\n" grep -m 2 -EHc "a|b|e|f" test1
}
atf_test_case mflag_trail_ctx
mflag_trail_ctx_head()
{
atf_set "descr" "Check proper handling of -m with trailing context (PR 253350)"
}
mflag_trail_ctx_body()
{
printf "foo\nfoo\nbar\nfoo\nbar\nfoo\nbar\n" > test1
# Should pick up the next line after matching the first.
atf_check -o inline:"foo\nfoo\n" grep -A1 -m1 foo test1
# Make sure the trailer is picked up as a non-match!
atf_check -o inline:"1:foo\n2-foo\n" grep -A1 -nm1 foo test1
}
atf_test_case zgrep_multiple_files
zgrep_multiple_files_head()
{
@ -978,6 +994,7 @@ atf_init_test_cases()
atf_add_test_case fgrep_oflag
atf_add_test_case cflag
atf_add_test_case mflag
atf_add_test_case mflag_trail_ctx
atf_add_test_case zgrep_multiple_files
# End FreeBSD
}

View file

@ -252,6 +252,16 @@ static bool
procmatches(struct mprintc *mc, struct parsec *pc, bool matched)
{
if (mflag && mcount <= 0) {
/*
* We already hit our match count, but we need to keep dumping
* lines until we've lost our tail.
*/
grep_printline(&pc->ln, '-');
mc->tail--;
return (mc->tail != 0);
}
/*
* XXX TODO: This should loop over pc->matches and handle things on a
* line-by-line basis, setting up a `struct str` as needed.
@ -265,7 +275,7 @@ procmatches(struct mprintc *mc, struct parsec *pc, bool matched)
/* XXX TODO: Decrement by number of matched lines */
mcount -= 1;
if (mcount <= 0)
return (false);
return (mc->tail != 0);
}
} else if (mc->doctx)
procmatch_nomatch(mc, pc);
@ -357,6 +367,15 @@ procfile(const char *fn)
return (0);
}
if (mflag && mcount <= 0) {
/*
* Short-circuit, already hit match count and now we're
* just picking up any remaining pieces.
*/
if (!procmatches(&mc, &pc, false))
break;
continue;
}
line_matched = procline(&pc) == !vflag;
if (line_matched)
++lines;