diff options
Diffstat (limited to 'usr.bin/grep/util.c')
-rw-r--r-- | usr.bin/grep/util.c | 72 |
1 files changed, 59 insertions, 13 deletions
diff --git a/usr.bin/grep/util.c b/usr.bin/grep/util.c index 4e1c44b442f2..ed87e56956f6 100644 --- a/usr.bin/grep/util.c +++ b/usr.bin/grep/util.c @@ -72,7 +72,7 @@ static int litexec(const struct pat *pat, const char *string, size_t nmatch, regmatch_t pmatch[]); #endif static bool procline(struct parsec *pc); -static void printline(struct parsec *pc, int sep); +static bool printline(struct parsec *pc, int sep, size_t *last_out); static void printline_metadata(struct str *line, int sep); bool @@ -214,15 +214,29 @@ procmatch_match(struct mprintc *mc, struct parsec *pc) /* Print the matching line, but only if not quiet/binary */ if (mc->printmatch) { - printline(pc, ':'); + size_t last_out; + bool terminated; + + last_out = 0; + terminated = printline(pc, ':', &last_out); while (pc->matchidx >= MAX_MATCHES) { /* Reset matchidx and try again */ pc->matchidx = 0; if (procline(pc) == !vflag) - printline(pc, ':'); + terminated = printline(pc, ':', &last_out); else break; } + + /* + * The above loop processes the entire line as long as we keep + * hitting the maximum match count. At this point, we know + * that there's nothing left to be printed and can terminate the + * line. + */ + if (!terminated) + printline(pc, ':', &last_out); + first_match = false; mc->same_file = true; mc->last_outed = 0; @@ -748,26 +762,39 @@ printline_metadata(struct str *line, int sep) } /* - * Prints a matching line according to the command line options. + * Prints a matching line according to the command line options. We need + * *last_out to be populated on entry in case this is just a continuation of + * matches within the same line. + * + * Returns true if the line was terminated, false if it was not. */ -static void -printline(struct parsec *pc, int sep) +static bool +printline(struct parsec *pc, int sep, size_t *last_out) { - size_t a = 0; + size_t a = *last_out; size_t i, matchidx; regmatch_t match; + bool terminated; + + /* + * Nearly all paths below will terminate the line by default, but it is + * avoided in some circumstances in case we don't have the full context + * available here. + */ + terminated = true; /* If matchall, everything matches but don't actually print for -o */ if (oflag && matchall) - return; + return (terminated); matchidx = pc->matchidx; /* --color and -o */ - if ((oflag || color) && matchidx > 0) { + if ((oflag || color) && (pc->printed > 0 || matchidx > 0)) { /* Only print metadata once per line if --color */ - if (!oflag && pc->printed == 0) + if (!oflag && pc->printed == 0) { printline_metadata(&pc->ln, sep); + } for (i = 0; i < matchidx; i++) { match = pc->matches[i]; /* Don't output zero length matches */ @@ -780,9 +807,10 @@ printline(struct parsec *pc, int sep) if (oflag) { pc->ln.boff = match.rm_so; printline_metadata(&pc->ln, sep); - } else + } else { fwrite(pc->ln.dat + a, match.rm_so - a, 1, stdout); + } if (color) fprintf(stdout, "\33[%sm\33[K", color); fwrite(pc->ln.dat + match.rm_so, @@ -793,13 +821,31 @@ printline(struct parsec *pc, int sep) if (oflag) putchar('\n'); } - if (!oflag) { - if (pc->ln.len - a > 0) + + /* + * Don't terminate if we reached the match limit; we may have + * other matches on this line to process. + */ + *last_out = a; + if (!oflag && matchidx != MAX_MATCHES) { + if (pc->ln.len - a > 0) { fwrite(pc->ln.dat + a, pc->ln.len - a, 1, stdout); + *last_out = pc->ln.len; + } putchar('\n'); + } else if (!oflag) { + /* + * -o is terminated on every match output, so this + * branch is only designed to capture MAX_MATCHES in a + * line which may be a signal to us for a lack of + * context. The caller will know more and call us again + * to terminate if it needs to. + */ + terminated = false; } } else grep_printline(&pc->ln, sep); pc->printed++; + return (terminated); } |