24 Commits

Author SHA1 Message Date
Wondr e925ff4ac8 grep: honor GNU buffer anchors (#56) 2026-06-05 17:33:23 +02:00
Leonard Hecker f2f86ef572 grep: Fix slow_path_binary_handling test 2026-06-05 17:04:50 +02:00
Wondr bc61aece73 grep: cover zero-width EOF only-matching behavior (#44) 2026-06-05 16:52:30 +02:00
Wondr 55a7b0180b grep: suppress EOF output in quiet mode (#41) 2026-06-05 16:48:36 +02:00
Wondr d556159952 grep: let dot match newline in null-data mode (#45) 2026-06-05 16:25:04 +02:00
Wondr 7c79cb4e3a grep: keep invalid UTF-8 text under -I (#49) 2026-06-05 16:06:49 +02:00
Sylvestre Ledru b4980df814 Merge pull request #16 from uutils/literal-fast-path
perf: buffer-at-a-time search for literal patterns
2026-06-05 08:28:04 +02:00
Sylvestre Ledru e9825e3503 Merge pull request #12 from uutils/grep-initial-tab-empty-line
grep: don't emit the -T alignment tab on empty lines
2026-06-05 08:27:43 +02:00
Sylvestre Ledru f5d5f6c063 Merge pull request #40 from koopatroopa787/issue-34-perl-single-pattern
fix: reject multiple patterns when -P/--perl-regexp is used
2026-06-05 08:25:18 +02:00
Wondr ad7595ffe6 grep: select zero-width matches under -w and -x 2026-06-05 04:39:22 +01:00
Kanishk Sachan e3d80f59e2 fix: reject multiple patterns when -P/--perl-regexp is used
GNU grep's PCRE backend supports only a single pattern. Supplying
multiple patterns via repeated -e flags, or a pattern string that
contains a literal newline, must exit 2 with the message:

    the -P option only supports a single pattern

Add the validation immediately after patterns are collected, before
regex-mode selection. Add a test covering:
  - two separate -e flags with -P
  - a newline-embedded pattern string with -P
  - single -e with -P still works normally

Closes #34
2026-06-05 01:25:22 +01:00
Sylvestre Ledru 56d774f576 test: cover slow-path modes that literal tests no longer reach
The buffer-at-a-time fast path now serves the literal patterns that the
existing -l/-L/-q and binary tests used, leaving the line-at-a-time
engine's equivalents uncovered. Add bracket-class (non-literal) tests
for -l/-L/-q and binary handling (notice, -a text, without-match bail,
and the finalize-time notice), plus a fast-path test for a NUL that is
only discovered after a line was already printed.

No dead code was found: the remaining uncovered lines are writer I/O
error-propagation arms and pre-existing filesystem error handlers.
2026-06-04 22:24:27 +02:00
Sylvestre Ledru 28186e9ec3 perf: buffer-at-a-time search for literal patterns
Literal searches were ~50-70x slower than GNU grep because every line
paid per-line costs (terminator scan, NUL scan, dispatch) even when a
buffer held no match. Add a buffer-at-a-time driver that scans whole
chunks with a substring searcher and only locates line boundaries
around the matches it finds; a chunk with no match costs a single
vectorized sweep and no per-line work.

The driver activates only for plain ASCII literal patterns (case
sensitive, no metacharacters) in the simpler output modes: -c, -l, -L,
-q, and plain line printing with -n/-b/filename/-m. Anything needing
match positions, context, inversion, color, or special binary handling
falls back to the unchanged line-at-a-time path. Output stays
byte-identical to that path, including binary/invalid-UTF-8 behavior.

- line_buffer: read_chunk() yields the largest span of complete lines.
- matcher: expose per-pattern memmem searchers when every pattern is a
  plain literal (plain_literal()).
- searcher: eligible_for_fast_path(), fast_locate(), fast_print().

All scanning rides on the memchr crate (SIMD memchr/memrchr/memmem).
Unit tests for read_chunk and plain_literal; integration tests for
prefixes, -m, and multi-chunk line-number correctness.

Benchmarks (31 MB corpus) vs prior release:
  -F (no match):  232ms -> 15ms  (15.9x; now faster than GNU)
  -c literal:     229ms -> 15ms  (15.2x)
  plain print:    248ms -> 18ms  (13.5x)
Regex and -i paths are unchanged (still the line-at-a-time engine).
2026-06-04 22:24:27 +02:00
rifatx f4798cb6d0 fix -l and -L to be mutually exclusive so that last one wins (#30) 2026-06-04 22:14:12 +02:00
Sylvestre Ledru a7b15320af grep: don't emit the -T alignment tab on empty lines
With -T, grep pads the prefix with a tab so line content lands on a tab
stop. GNU omits that tab when the line has no content: an empty line
prints just its prefix (a whitespace-only line still gets the tab).
uu_grep always wrote the tab, so empty matched lines gained a spurious
trailing tab. Gate the tab on non-empty content. Fixes the GNU testsuite
'initial-tab' test.
2026-05-30 19:13:52 +02:00
Sylvestre Ledru da0ada8a37 test: use platform-specific expected message for nonexistent-file error 2026-05-30 11:33:47 +02:00
Sylvestre Ledru fcf46d8f56 test: cover -D skip on an explicit special-file argument 2026-05-30 11:28:04 +02:00
Sylvestre Ledru 55cb643545 test: cover strip_dot_prefix for implicit-cwd recursive search 2026-05-30 11:28:04 +02:00
Sylvestre Ledru 89aec4a45a test: cover -T line-number width on the file (not stdin) path 2026-05-30 11:28:03 +02:00
Sylvestre Ledru 6c5b7dc9e3 test: cover -L listing when the pattern matches one file 2026-05-30 11:28:03 +02:00
Sylvestre Ledru ff918e4a63 grep: strip trailing (os error N) from file error messages to match GNU (#6) 2026-05-29 22:24:20 +02:00
Sylvestre Ledru c50c0458cb fix: accept repeated options like GNU grep (#2)
GNU grep tolerates options given more than once (boolean flags are
idempotent, value options take the last occurrence), but clap rejected
them with "cannot be used multiple times" (exit 2). Set
args_override_self(true) so clap replaces rather than errors; args with
ArgAction::Append (-e/-f/--include/--exclude) still accumulate.

Found by the differential fuzzer (e.g. `grep -e .* -E -n -o -n -x`).

Add a regression test covering repeated booleans, repeated value options
(last wins), and that -e still accumulates patterns.
2026-05-29 22:21:27 +02:00
Leonard Hecker ebe62e0cf7 Fix flaky tests 2026-05-28 22:32:49 +02:00
Leonard Hecker 9bff77613e Initial import 2026-05-28 20:19:53 +02:00