Index: contrib/netbsd-tests/usr.bin/grep/t_grep.sh =================================================================== --- contrib/netbsd-tests/usr.bin/grep/t_grep.sh +++ contrib/netbsd-tests/usr.bin/grep/t_grep.sh @@ -494,6 +494,39 @@ atf_check -s exit:1 grep -v -w "x" test1 atf_check -s exit:1 grep -v -w "x" test2 } + +atf_test_case ocolor_metadata +ocolor_metadata_head() +{ + atf_set "descr" "Check for -n/-b producing per-line metadata output" +} +ocolor_metadata_body() +{ + grep_type + if [ $? -eq $GREP_TYPE_GNU_FREEBSD ]; then + atf_expect_fail "this test does not pass with GNU grep in base" + fi + + printf "xxx\nyyyy\nzzz\nfoobarbaz\n" > test1 + check_expr="^[0-9]:[^:]+$" + + atf_check -o inline:"1:1:xx\n" grep -bon "xx$" test1 + + atf_check -o inline:"2:4:yyyy\n" grep -bn "yy" test1 + + atf_check -o inline:"2:6:yy\n" grep -bon "yy$" test1 + + # These checks ensure that grep isn't producing bogus line numbering + # in the middle of a line. + atf_check -s exit:1 -x \ + "grep -Eon 'x|y|z|f' test1 | grep -Ev '${check_expr}'" + + atf_check -s exit:1 -x \ + "grep -En 'x|y|z|f' --color=always test1 | grep -Ev '${check_expr}'" + + atf_check -s exit:1 -x \ + "grep -Eon 'x|y|z|f' --color=always test1 | grep -Ev '${check_expr}'" +} # End FreeBSD atf_init_test_cases() @@ -527,5 +560,6 @@ atf_add_test_case fgrep_sanity atf_add_test_case egrep_sanity atf_add_test_case grep_sanity + atf_add_test_case ocolor_metadata # End FreeBSD } Index: usr.bin/grep/util.c =================================================================== --- usr.bin/grep/util.c +++ usr.bin/grep/util.c @@ -61,10 +61,11 @@ * other useful bits */ struct parsec { - regmatch_t matches[MAX_LINE_MATCHES]; /* Matches made */ - struct str ln; /* Current line */ - size_t matchidx; /* Latest used match index */ - bool binary; /* Binary file? */ + regmatch_t matches[MAX_LINE_MATCHES]; /* Matches made */ + struct str ln; /* Current line */ + size_t matchidx; /* Latest match index */ + int printed; /* Metadata printed? */ + bool binary; /* Binary file? */ }; @@ -239,12 +240,14 @@ pc.ln.len = 0; pc.ln.off = -1; pc.binary = f->binary; + pc.printed = 0; tail = 0; last_outed = 0; same_file = false; for (c = 0; c == 0 || !(lflag || qflag); ) { /* Reset match count for every line processed */ + pc.printed = 0; pc.matchidx = 0; pc.ln.off += pc.ln.len + 1; if ((pc.ln.dat = grep_fgetln(f, &pc.ln.len)) == NULL || @@ -262,7 +265,6 @@ if (pc.ln.len > 0 && pc.ln.dat[pc.ln.len - 1] == fileeol) --pc.ln.len; pc.ln.line_no++; - /* Return if we need to skip a binary file */ if (pc.binary && binbehave == BINFILE_SKIP) { grep_close(f); @@ -292,7 +294,7 @@ if (t != 0 && doctx) { /* Deal with any -A context */ if (tail > 0) { - printline(&pc, '-'); + grep_printline(&pc.ln, '-'); tail--; if (Bflag > 0) clearqueue(); @@ -601,6 +603,7 @@ static void printline(struct parsec *pc, int sep) { + off_t lnoff; size_t a = 0; size_t i, matchidx; regmatch_t match; @@ -613,13 +616,27 @@ /* --color and -o */ if ((oflag || color) && matchidx > 0) { - printline_metadata(&pc->ln, sep); + /* + * Save the offset; we're going to fudge it when printing + * partial lines, then restore it later. + */ + lnoff = pc->ln.off; + /* Only print metadata once per line if --color */ + 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 */ if (match.rm_so == match.rm_eo) continue; - if (!oflag) + /* + * Metadata is printed on a per-line basis, so every + * match gets file metadata with the -o flag. + */ + if (oflag) { + pc->ln.off = lnoff + match.rm_so; + printline_metadata(&pc->ln, sep); + } else fwrite(pc->ln.dat + a, match.rm_so - a, 1, stdout); if (color) @@ -638,6 +655,8 @@ stdout); putchar('\n'); } + pc->ln.off = lnoff; } else grep_printline(&pc->ln, sep); + ++pc->printed; }