Index: contrib/netbsd-tests/usr.bin/grep/d_color_a.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_color_a.in @@ -0,0 +1 @@ +abcd* Index: contrib/netbsd-tests/usr.bin/grep/d_color_a.out =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_color_a.out @@ -0,0 +1 @@ +abcd* Index: contrib/netbsd-tests/usr.bin/grep/d_color_b.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_color_b.in @@ -0,0 +1,5 @@ +fojeiwuroiuwet +ljfajsljkfabcdddjlfkajlkj +abcaaa +zzzabc + Index: contrib/netbsd-tests/usr.bin/grep/d_color_b.out =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_color_b.out @@ -0,0 +1,3 @@ +ljfajsljkfabcdddjlfkajlkj +abcaaa +zzzabc Index: contrib/netbsd-tests/usr.bin/grep/d_color_c.out =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_color_c.out @@ -0,0 +1,3 @@ +ljfajsljkfabcdddjlfkajlkj +abcaaa +zzzabc Index: contrib/netbsd-tests/usr.bin/grep/d_escmap.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_escmap.in @@ -0,0 +1 @@ +f.oo Index: contrib/netbsd-tests/usr.bin/grep/d_f_file_empty.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_f_file_empty.in @@ -0,0 +1,2 @@ +Fish zebra monkey suits + Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_a.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_a.in @@ -0,0 +1 @@ +01:1:01 Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_a.out =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_a.out @@ -0,0 +1,3 @@ +0 +: +:0 Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_b.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_b.in @@ -0,0 +1 @@ +1:1:01 Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_b.out =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_b.out @@ -0,0 +1,2 @@ +: +:0 Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_c.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_c.in @@ -0,0 +1 @@ +bla bla Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_c.out =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_c.out @@ -0,0 +1,2 @@ +bla +bla Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_d.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_d.in @@ -0,0 +1,3 @@ +bla +bla + Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_e.in =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_e.in @@ -0,0 +1 @@ +abcdef Index: contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_e.out =================================================================== --- /dev/null +++ contrib/netbsd-tests/usr.bin/grep/d_oflag_zerolen_e.out @@ -0,0 +1 @@ +ab 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 @@ -227,9 +227,85 @@ grep -z -C1 cod test1 test2 } +atf_test_case oflag_zerolen +oflag_zerolen_head() +{ + atf_set "descr" "Checks behavior of zero-length matches with -o flag" +} +oflag_zerolen_body() +{ + atf_check -o file:"$(atf_get_srcdir)/d_oflag_zerolen_a.out" \ + grep -Eo '(^|:)0*' "$(atf_get_srcdir)/d_oflag_zerolen_a.in" + + atf_check -o file:"$(atf_get_srcdir)/d_oflag_zerolen_b.out" \ + grep -Eo '(^|:)0*' "$(atf_get_srcdir)/d_oflag_zerolen_b.in" + + atf_check -o file:"$(atf_get_srcdir)/d_oflag_zerolen_c.out" \ + grep -Eo '[[:alnum:]]*' "$(atf_get_srcdir)/d_oflag_zerolen_c.in" + + atf_check -o empty grep -Eo '' "$(atf_get_srcdir)/d_oflag_zerolen_d.in" + + atf_check -o file:"$(atf_get_srcdir)/d_oflag_zerolen_e.out" \ + grep -o -e 'ab' -e 'bc' "$(atf_get_srcdir)/d_oflag_zerolen_e.in" + + atf_check -o file:"$(atf_get_srcdir)/d_oflag_zerolen_e.out" \ + grep -o -e 'bc' -e 'ab' "$(atf_get_srcdir)/d_oflag_zerolen_e.in" +} + +atf_test_case xflag +xflag_head() +{ + atf_set "descr" "Check that we actually get a match with -x flag" +} +xflag_body() +{ + echo 128 > match_file + seq 1 128 > pattern_file + grep -xf pattern_file match_file +} + +atf_test_case color +color_head() +{ + atf_set "descr" "Checks behavior using --color" +} +color_body() +{ + echo 'abcd*' > grepfile + echo 'abc$' >> grepfile + echo '^abc' >> grepfile + + atf_check -o file:"$(atf_get_srcdir)/d_color_a.out" \ + grep --color=auto -e '.*' -e 'a' "$(atf_get_srcdir)/d_color_a.in" + + atf_check -o file:"$(atf_get_srcdir)/d_color_b.out" \ + grep --color=auto -f grepfile "$(atf_get_srcdir)/d_color_b.in" + + atf_check -o file:"$(atf_get_srcdir)/d_color_c.out" \ + grep --color=always -f grepfile "$(atf_get_srcdir)/d_color_b.in" +} + +atf_test_case f_file_empty +f_file_empty_body() +{ + printf "\0\n" > nulpat + + atf_check -s exit:1 grep -f nulpat "$(atf_get_srcdir)/d_f_file_empty.in" +} + +atf_test_case escmap +escmap_body() +{ + atf_check -s exit:1 env -i MALLOC_CONF=junk:true grep -o 'f.o\.' "$(atf_get_srcdir)/d_escmap.in" + atf_check -s exit:1 env -i MALLOC_CONF=junk:false grep -o 'f.o\.' "$(atf_get_srcdir)/d_escmap.in" + + atf_check -o not-empty env -i MALLOC_CONF=junk:true grep -o 'f.o.' "$(atf_get_srcdir)/d_escmap.in" + atf_check -o not-empty env -i MALLOC_CONF=junk:false grep -o 'f.o.' "$(atf_get_srcdir)/d_escmap.in" +} + atf_init_test_cases() { - atf_add_test_case basic + atf_add_test_case basic atf_add_test_case binary atf_add_test_case recurse atf_add_test_case recurse_symlink @@ -245,4 +321,9 @@ atf_add_test_case zgrep atf_add_test_case nonexistent atf_add_test_case context2 + atf_add_test_case oflag_zerolen + atf_add_test_case xflag + atf_add_test_case color + atf_add_test_case f_file_empty + atf_add_test_case escmap } Index: share/mk/src.opts.mk =================================================================== --- share/mk/src.opts.mk +++ share/mk/src.opts.mk @@ -100,7 +100,6 @@ GNU \ GNU_DIFF \ GNU_GREP \ - GNU_GREP_COMPAT \ GPIO \ GPL_DTC \ GROFF \ @@ -182,6 +181,7 @@ BSD_GREP \ CLANG_EXTRAS \ DTRACE_TESTS \ + GNU_GREP_COMPAT \ HESIOD \ LIBSOFT \ NAND \ Index: tools/build/options/WITHOUT_GNU_GREP_COMPAT =================================================================== --- tools/build/options/WITHOUT_GNU_GREP_COMPAT +++ /dev/null @@ -1,3 +0,0 @@ -.\" $FreeBSD$ -Set this option to omit the gnu extensions to grep from being included in -BSD grep. Index: tools/build/options/WITH_GNU_GREP_COMPAT =================================================================== --- /dev/null +++ tools/build/options/WITH_GNU_GREP_COMPAT @@ -0,0 +1,2 @@ +.\" $FreeBSD$ +Set this option to include the gnu extensions in BSD grep. Index: usr.bin/grep/Makefile =================================================================== --- usr.bin/grep/Makefile +++ usr.bin/grep/Makefile @@ -25,6 +25,7 @@ .if ${MK_BSD_GREP} == "yes" LINKS= ${BINDIR}/grep ${BINDIR}/egrep \ ${BINDIR}/grep ${BINDIR}/fgrep \ + ${BINDIR}/grep ${BINDIR}/rgrep \ ${BINDIR}/grep ${BINDIR}/zgrep \ ${BINDIR}/grep ${BINDIR}/zegrep \ ${BINDIR}/grep ${BINDIR}/zfgrep Index: usr.bin/grep/file.c =================================================================== --- usr.bin/grep/file.c +++ usr.bin/grep/file.c @@ -197,7 +197,7 @@ } /* Look for a newline in the remaining part of the buffer */ - if ((p = memchr(bufpos, '\n', bufrem)) != NULL) { + if ((p = memchr(bufpos, fileeol, bufrem)) != NULL) { ++p; /* advance over newline */ ret = bufpos; len = p - bufpos; @@ -219,7 +219,7 @@ if (bufrem == 0) /* EOF: return partial line */ break; - if ((p = memchr(bufpos, '\n', bufrem)) == NULL && + if ((p = memchr(bufpos, fileeol, bufrem)) == NULL && filebehave != FILE_MMAP) continue; if (p == NULL) { @@ -322,7 +322,7 @@ goto error2; /* Check for binary stuff, if necessary */ - if (binbehave != BINFILE_TEXT && memchr(bufpos, '\0', bufrem) != NULL) + if (binbehave != BINFILE_TEXT && fileeol != '\0' && memchr(bufpos, '\0', bufrem) != NULL) f->binary = true; return (f); Index: usr.bin/grep/grep.h =================================================================== --- usr.bin/grep/grep.h +++ usr.bin/grep/grep.h @@ -116,6 +116,7 @@ extern unsigned long long Aflag, Bflag; extern long long mcount; extern long long mlimit; +extern char fileeol; extern char *label; extern const char *color; extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave; Index: usr.bin/grep/grep.1 =================================================================== --- usr.bin/grep/grep.1 +++ usr.bin/grep/grep.1 @@ -40,7 +40,7 @@ .Sh SYNOPSIS .Nm grep .Bk -words -.Op Fl abcdDEFGHhIiJLlmnOopqRSsUVvwxZ +.Op Fl abcdDEFGHhIiJLlmnOopqRSsUVvwxZz .Op Fl A Ar num .Op Fl B Ar num .Op Fl C Ns Op Ar num @@ -374,7 +374,10 @@ Equivalent to .Fl i . Obsoleted. -.It Fl Z , Fl z , Fl Fl decompress +.It Fl z , Fl Fl null-data +Treat input and output data as sequences of lines terminated by at +zero-byte instead of a newline. +.It Fl Z , Fl Fl decompress Force .Nm grep to behave as Index: usr.bin/grep/grep.c =================================================================== --- usr.bin/grep/grep.c +++ usr.bin/grep/grep.c @@ -66,7 +66,7 @@ /* 1*/ "(standard input)", /* 2*/ "cannot read bzip2 compressed file", /* 3*/ "unknown %s option", -/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n", +/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n", /* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n", /* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n", /* 7*/ "\t[--null] [pattern] [file ...]\n", @@ -109,6 +109,7 @@ bool mflag; /* -m x: stop reading the files after x matches */ long long mcount; /* count for -m */ long long mlimit; /* requested value for -m */ +char fileeol; /* indicator for eol */ bool nflag; /* -n: show line numbers in front of matching lines */ bool oflag; /* -o: print only matching part */ bool qflag; /* -q: quiet mode (don't output anything) */ @@ -165,7 +166,7 @@ exit(2); } -static const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy"; +static const char *optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXyz"; static const struct option long_options[] = { @@ -215,6 +216,7 @@ {"word-regexp", no_argument, NULL, 'w'}, {"line-regexp", no_argument, NULL, 'x'}, {"xz", no_argument, NULL, 'X'}, + {"null-data", no_argument, NULL, 'z'}, {"decompress", no_argument, NULL, 'Z'}, {NULL, no_argument, NULL, 0} }; @@ -314,8 +316,12 @@ } len = 0; line = NULL; - while ((rlen = getline(&line, &len, f)) != -1) + while ((rlen = getline(&line, &len, f)) != -1) { + if(line[0] == 0) + continue; add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen); + } + free(line); if (ferror(f)) err(2, "%s", fn); @@ -360,6 +366,9 @@ } else if (pn[0] == 'l' && pn[1] == 'z') { filebehave = FILE_LZMA; pn += 2; + } else if (pn[0] == 'r') { + dirbehave = DIR_RECURSE; + Hflag = true; } else if (pn[0] == 'z') { filebehave = FILE_GZIP; pn += 1; @@ -377,6 +386,7 @@ newarg = 1; prevoptind = 1; needpattern = 1; + fileeol = '\n'; eopts = getenv("GREP_OPTIONS"); @@ -598,6 +608,9 @@ case 'X': filebehave = FILE_XZ; break; + case 'z': + fileeol = '\0'; + break; case 'Z': filebehave = FILE_GZIP; break; @@ -725,7 +738,7 @@ if ((aargc == 0 || aargc == 1) && !Hflag) hflag = true; - if (aargc == 0) + if (aargc == 0 && (dirbehave != DIR_RECURSE || patterns == 0)) exit(!procfile("-")); if (dirbehave == DIR_RECURSE) Index: usr.bin/grep/regex/tre-fastmatch.c =================================================================== --- usr.bin/grep/regex/tre-fastmatch.c +++ usr.bin/grep/regex/tre-fastmatch.c @@ -99,6 +99,18 @@ fg->pattern[siz] = '\0'; \ } \ +#define CONV_MBS_PAT(src, dest, destsz) \ + { \ + destsz = wcstombs(NULL, src, 0); \ + if (destsz == (size_t)-1) \ + return REG_BADPAT; \ + dest = xmalloc(destsz + 1); \ + if (dest == NULL) \ + return REG_ESPACE; \ + wcstombs(dest, src, destsz); \ + dest[destsz] = '\0'; \ + } \ + #define IS_OUT_OF_BOUNDS \ ((!fg->reversed \ ? ((type == STR_WIDE) ? ((j + fg->wlen) > len) \ @@ -338,7 +350,7 @@ * Fills in the good suffix table for SB/MB strings. */ #define FILL_BMGS \ - if (!fg->hasdot) \ + if (fg->len > 0 && !fg->hasdot) \ { \ fg->sbmGs = xmalloc(fg->len * sizeof(int)); \ if (!fg->sbmGs) \ @@ -348,13 +360,17 @@ else \ _FILL_BMGS(fg->sbmGs, fg->pattern, fg->len, false); \ DPRINT_BMGS(fg->len, "GS shift for pos %d is %d\n", fg->sbmGs); \ + } \ + else \ + { \ + fg->sbmGs = NULL; \ } /* * Fills in the good suffix table for wide strings. */ #define FILL_BMGS_WIDE \ - if (!fg->hasdot) \ + if (fg->wlen > 0 && !fg->hasdot) \ { \ fg->bmGs = xmalloc(fg->wlen * sizeof(int)); \ if (!fg->bmGs) \ @@ -365,6 +381,10 @@ _FILL_BMGS(fg->bmGs, fg->wpattern, fg->wlen, true); \ DPRINT_BMGS(fg->wlen, "GS shift (wide) for pos %d is %d\n", \ fg->bmGs); \ + } \ + else \ + { \ + fg->bmGs = NULL; \ } #define _FILL_BMGS(arr, pat, plen, wide) \ @@ -631,7 +651,7 @@ if (escaped) { if (!_escmap) - _escmap = xmalloc(n * sizeof(bool)); + _escmap = xcalloc(n, sizeof(bool)); if (!_escmap) { xfree(tmp); @@ -711,36 +731,38 @@ * than in to the wide string so traverse the converted string, as well, * to store these positions. */ - if (fg->hasdot || (fg->wescmap != NULL)) - { - if (fg->wescmap != NULL) - { - fg->escmap = xmalloc(fg->len * sizeof(bool)); - if (!fg->escmap) - { - tre_free_fast(fg); - return REG_ESPACE; - } + if (fg->hasdot || (fg->wescmap != NULL)) { + if (fg->wescmap != NULL) { + fg->escmap = xcalloc(fg->len, sizeof(bool)); + if (!fg->escmap) { + tre_free_fast(fg); + return REG_ESPACE; + } } - escaped = false; - for (unsigned int i = 0; i < fg->len; i++) - if (fg->pattern[i] == '\\') - escaped = !escaped; - else if (fg->pattern[i] == '.' && fg->escmap && escaped) - { - fg->escmap[i] = true; - escaped = false; - } - else if (fg->pattern[i] == '.' && !escaped) - { - hasdot = i; - if (firstdot == -1) - firstdot = i; - } - else - escaped = false; - } + escaped = false; + char *_checkpat = NULL; + size_t _checklen = 0; + unsigned int escofs = 0; + /* Make a copy of the original pattern, because fg->pattern has already been stripped */ + CONV_MBS_PAT(pat, _checkpat, _checklen); + for (unsigned int i = 0; i < n; i++) { + if (_checkpat[i] == '\\') { + escaped = !escaped; + if(escaped) + ++ escofs; + } else if (_checkpat[i] == '.' && fg->escmap && escaped) { + fg->escmap[i - escofs] = true; + escaped = false; + } else if (_checkpat[i] == '.' && !escaped) { + hasdot = i; + if (firstdot == -1) + firstdot = i; + } else + escaped = false; + } + xfree(_checkpat); + } #else SAVE_PATTERN(tmp, pos, fg->pattern, fg->len); fg->escmap = _escmap; Index: usr.bin/grep/tests/Makefile =================================================================== --- usr.bin/grep/tests/Makefile +++ usr.bin/grep/tests/Makefile @@ -8,6 +8,11 @@ ${PACKAGE}FILES+= d_begin_end_a.out ${PACKAGE}FILES+= d_begin_end_b.out ${PACKAGE}FILES+= d_binary.out +${PACKAGE}FILES+= d_color_a.in +${PACKAGE}FILES+= d_color_a.out +${PACKAGE}FILES+= d_color_b.in +${PACKAGE}FILES+= d_color_b.out +${PACKAGE}FILES+= d_color_c.out ${PACKAGE}FILES+= d_context2_a.out ${PACKAGE}FILES+= d_context2_b.out ${PACKAGE}FILES+= d_context2_c.out @@ -18,12 +23,23 @@ ${PACKAGE}FILES+= d_context_c.out ${PACKAGE}FILES+= d_context_d.out ${PACKAGE}FILES+= d_egrep.out +${PACKAGE}FILES+= d_escmap.in +${PACKAGE}FILES+= d_f_file_empty.in ${PACKAGE}FILES+= d_file_exp.in ${PACKAGE}FILES+= d_file_exp.out ${PACKAGE}FILES+= d_ignore_case.out ${PACKAGE}FILES+= d_input ${PACKAGE}FILES+= d_invert.in ${PACKAGE}FILES+= d_invert.out +${PACKAGE}FILES+= d_oflag_zerolen_a.in +${PACKAGE}FILES+= d_oflag_zerolen_a.out +${PACKAGE}FILES+= d_oflag_zerolen_b.in +${PACKAGE}FILES+= d_oflag_zerolen_b.out +${PACKAGE}FILES+= d_oflag_zerolen_c.in +${PACKAGE}FILES+= d_oflag_zerolen_c.out +${PACKAGE}FILES+= d_oflag_zerolen_d.in +${PACKAGE}FILES+= d_oflag_zerolen_e.in +${PACKAGE}FILES+= d_oflag_zerolen_e.out ${PACKAGE}FILES+= d_recurse.out ${PACKAGE}FILES+= d_recurse_symlink.err ${PACKAGE}FILES+= d_recurse_symlink.out Index: usr.bin/grep/util.c =================================================================== --- usr.bin/grep/util.c +++ usr.bin/grep/util.c @@ -55,6 +55,9 @@ static int linesqueued; static int procline(struct str *l, int); +static int lasta; +static bool ctxover; + bool file_matching(const char *fname) { @@ -111,6 +114,7 @@ FTSENT *p; int c, fts_flags; bool ok; + char **tdir = NULL; c = fts_flags = 0; @@ -128,7 +132,14 @@ fts_flags |= FTS_NOSTAT | FTS_NOCHDIR; - if (!(fts = fts_open(argv, fts_flags, NULL))) + if(argv[0] == NULL) { + tdir = grep_malloc(2 * sizeof(char *)); + if((tdir[0] = getcwd(NULL, 0)) == NULL) + err(2, "getcwd"); + tdir[1] = NULL; + } + + if (!(fts = fts_open((tdir != NULL ? tdir : argv), fts_flags, NULL))) err(2, "fts_open"); while ((p = fts_read(fts)) != NULL) { switch (p->fts_info) { @@ -165,6 +176,12 @@ } fts_close(fts); + + if(tdir != NULL) { + free(tdir[0]); + free(tdir); + } + return (c); } @@ -209,8 +226,10 @@ strcpy(ln.file, fn); ln.line_no = 0; ln.len = 0; + ctxover = false; linesqueued = 0; tail = 0; + lasta = 0; ln.off = -1; for (c = 0; c == 0 || !(lflag || qflag); ) { @@ -221,7 +240,7 @@ else break; } - if (ln.len > 0 && ln.dat[ln.len - 1] == '\n') + if (ln.len > 0 && ln.dat[ln.len - 1] == fileeol) --ln.len; ln.line_no++; @@ -232,10 +251,21 @@ free(f); return (0); } - /* Process the file line-by-line */ + + /* Process the file line-by-line, enqueue non-matching lines */ if ((t = procline(&ln, f->binary)) == 0 && Bflag > 0) { - enqueue(&ln); - linesqueued++; + /* Except don't enqueue lines that appear in -A ctx */ + if (ln.line_no == 0 || lasta != ln.line_no) { + /* queue is maxed to Bflag number of lines */ + enqueue(&ln); + linesqueued++; + ctxover = false; + } else { + /* Indicate to procline() that we have ctx overlap, make sure queue is empty at this point */ + if (!ctxover) + clearqueue(); + ctxover = true; + } } c += t; if (mflag && mcount <= 0) @@ -276,28 +306,29 @@ procline(struct str *l, int nottext) { regmatch_t matches[MAX_LINE_MATCHES]; - regmatch_t pmatch; - size_t st = 0; + regmatch_t pmatch, lastmatch; + size_t st = 0, nst = 0; unsigned int i; - int c = 0, m = 0, r = 0; + int c = 0, m = 0, r = 0, lastmatches = 0, leflags = eflags; + int startm = 0; /* Loop to process the whole line */ while (st <= l->len) { - pmatch.rm_so = st; - pmatch.rm_eo = l->len; - + lastmatches = 0; + startm = m; + if (st > 0) + leflags |= REG_NOTBOL; /* Loop to compare with all the patterns */ for (i = 0; i < patterns; i++) { + pmatch.rm_so = st; + pmatch.rm_eo = l->len; if (fg_pattern[i].pattern) r = fastexec(&fg_pattern[i], - l->dat, 1, &pmatch, eflags); + l->dat, 1, &pmatch, leflags); else r = regexec(&r_pattern[i], l->dat, 1, - &pmatch, eflags); + &pmatch, leflags); r = (r == 0) ? 0 : REG_NOMATCH; - st = (cflags & REG_NOSUB) - ? (size_t)l->len - : (size_t)pmatch.rm_eo; if (r == REG_NOMATCH) continue; /* Check for full match */ @@ -324,10 +355,29 @@ r = REG_NOMATCH; } if (r == 0) { + lastmatches++; + lastmatch = pmatch; + /* Skip over zero-length matches */ + if (pmatch.rm_so == pmatch.rm_eo) + continue; if (m == 0) c++; - if (m < MAX_LINE_MATCHES) - matches[m++] = pmatch; + + if (m < MAX_LINE_MATCHES) { + /* Replace previous match if the new one is earlier and/or longer */ + if (m > startm) { + if (pmatch.rm_so < matches[m-1].rm_so || + (pmatch.rm_so == matches[m-1].rm_so && (pmatch.rm_eo - pmatch.rm_so) > (matches[m-1].rm_eo - matches[m-1].rm_so))) { + matches[m-1] = pmatch; + nst = pmatch.rm_eo; + } + } else { + /* Advance as normal if not */ + matches[m++] = pmatch; + nst = pmatch.rm_eo; + } + } + /* matches - skip further patterns */ if ((color == NULL && !oflag) || qflag || lflag) @@ -344,8 +394,19 @@ if (!wflag && ((color == NULL && !oflag) || qflag || lflag || Lflag)) break; - if (st == (size_t)pmatch.rm_so) - break; /* No matches */ + /* If we didn't have any matches or REG_NOSUB set */ + if (lastmatches == 0 || (cflags & REG_NOSUB)) + nst = l->len; + + if (lastmatches == 0) + /* No matches */ + break; + else if (st == nst && lastmatch.rm_so == lastmatch.rm_eo) + /* Zero-length match -- advance one more so we don't get stuck */ + nst++; + + /* Advance st based on previous matches */ + st = nst; } @@ -359,17 +420,18 @@ /* Dealing with the context */ if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) { if (c) { - if (!first && !prev && !tail && Aflag) + if (!first && !prev && !tail && (Bflag || Aflag) && !ctxover) printf("--\n"); tail = Aflag; if (Bflag > 0) { - if (!first && !prev) - printf("--\n"); printqueue(); + ctxover = false; } linesqueued = 0; printline(l, ':', matches, m); } else { + /* Print -A lines following matches */ + lasta = l->line_no; printline(l, '-', matches, m); tail--; } @@ -444,6 +506,10 @@ size_t a = 0; int i, n = 0; + /* If matchall, everything matches but don't actually print for -o */ + if (oflag && matchall) + return; + if (!hflag) { if (!nullflag) { fputs(line->file, stdout); @@ -492,6 +558,6 @@ } } else { fwrite(line->dat, line->len, 1, stdout); - putchar('\n'); + putchar(fileeol); } }