diff --git a/src/matcher.rs b/src/matcher.rs index eed77c9..f24638f 100644 --- a/src/matcher.rs +++ b/src/matcher.rs @@ -8,7 +8,9 @@ use onig::{ EncodedBytes, Regex, RegexOptions, Region, SearchOptions, Syntax, SyntaxBehavior, SyntaxOperator, }; -use onig_sys::{OnigEncCtype_ONIGENC_CTYPE_WORD, OnigEncodingUTF8}; +use onig_sys::{ + ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS, OnigEncCtype_ONIGENC_CTYPE_WORD, OnigEncodingUTF8, +}; use uucore::error::{UResult, USimpleError}; pub struct Matcher<'a> { @@ -240,7 +242,12 @@ impl CompiledPattern { fn compile_with(pattern: &str, syntax: &Syntax, options: RegexOptions) -> UResult { Regex::with_options_and_encoding(pattern, options, syntax).map_err(|err| { - USimpleError::new(2, format!("invalid pattern \"{pattern}\": {err}")) + // Prefer GNU grep's wording for the errors it has a dedicated + // message for; fall back to oniguruma's text otherwise. + match gnu_error_message(err.code()) { + Some(msg) => USimpleError::new(2, msg.to_string()), + None => USimpleError::new(2, format!("invalid pattern \"{pattern}\": {err}")), + } }) } @@ -296,6 +303,19 @@ impl CompiledPattern { } } +/// Map an oniguruma compile-error code to GNU grep's wording for the same +/// condition, when one exists. GNU emits a bare POSIX-style diagnostic (e.g. +/// `Invalid range end`) rather than oniguruma's phrasing, so translating keeps +/// us byte-compatible. Returns `None` for errors with no GNU equivalent, where +/// the caller falls back to oniguruma's own message. +fn gnu_error_message(code: i32) -> Option<&'static str> { + match code { + // e.g. `[b-a]`: a range whose end precedes its start. + ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS => Some("Invalid range end"), + _ => None, + } +} + /// Reject the confusing `[:name:]` bracket form the way GNU grep does. /// /// A bracket expression like `[:space:]` is almost always a misspelled diff --git a/tests/test_grep.rs b/tests/test_grep.rs index 8d92c9d..63187eb 100644 --- a/tests/test_grep.rs +++ b/tests/test_grep.rs @@ -180,6 +180,19 @@ fn lookalike_brackets_are_not_confusing() { .no_output(); } +#[test] +fn reversed_range_uses_gnu_wording() { + // A range like `[b-a]` is an error; GNU prints the bare POSIX diagnostic + // "Invalid range end" (not oniguruma's phrasing) and exits 2. + for args in [&["[b-a]"][..], &["-E", "[b-a]"][..]] { + let (_s, mut c) = ucmd(); + c.args(args) + .pipe_in("x\n") + .fails_with_code(2) + .stderr_is("grep: Invalid range end\n"); + } +} + #[test] fn fixed_string_is_literal() { // Metacharacters are not interpreted.