diff --git a/src/matcher.rs b/src/matcher.rs index dac786b..592d944 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -277,6 +277,11 @@ impl CompiledPattern { // GNU grep supports \` and \' as buffer anchors in BRE and ERE. syntax.enable_operators(SyntaxOperator::SYNTAX_OPERATOR_ESC_GNU_BUF_ANCHOR); } + if config.regex_mode == RegexMode::Basic { + // GNU grep supports \s and \S as extensions in BRE, like \w and \W. + // `Syntax::grep()` leaves them off, so they parse as literal s / S. + syntax.enable_operators(SyntaxOperator::SYNTAX_OPERATOR_ESC_S_WHITE_SPACE); + } if matches!(config.regex_mode, RegexMode::Basic | RegexMode::Extended) && has_confusing_bracket(pattern.as_bytes()) diff --git a/tests/test_grep.rs b/tests/test_grep.rs index b477329..65fc379 100644 --- a/tests/test_grep.rs +++ b/tests/test_grep.rs @@ -100,6 +100,30 @@ fn gnu_buffer_anchors() { .stdout_only("cat\ntar\n"); } +#[test] +fn whitespace_escapes_in_basic_regexp() { + // GNU grep honors \s and \S in BRE as well as ERE, like \w and \W. + let input = "a b\nxy\n"; + + for args in [ + vec![r"\s"], + vec!["-G", r"\s"], + vec!["-E", r"\s"], + vec!["-e", r"\s"], + ] { + let (_s, mut c) = ucmd(); + c.args(&args).pipe_in(input).succeeds().stdout_only("a b\n"); + } + + for args in [vec![r"\S"], vec!["-G", r"\S"], vec!["-E", r"\S"]] { + let (_s, mut c) = ucmd(); + c.args(&args) + .pipe_in(input) + .succeeds() + .stdout_only("a b\nxy\n"); + } +} + #[test] fn ere_metacharacters() { let cases: &[(&[&str], &str, &str)] = &[