grep: emit GNU's 'Invalid range end' for reversed bracket ranges

A reversed range like [b-a] makes oniguruma fail with 'empty range in
char class', which uu_grep wrapped as 'invalid pattern "[b-a]": ...'.
GNU grep instead prints the bare POSIX diagnostic 'Invalid range end'
and exits 2. Translate the oniguruma error code
(ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS) to GNU's wording, leaving other
compile errors to fall back to oniguruma's text. Fixes the GNU testsuite
'reversed-range-endpoints' test.
This commit is contained in:
Sylvestre Ledru
2026-05-30 17:50:02 +02:00
parent 1a3e8a391d
commit e767a30c1a
2 changed files with 35 additions and 2 deletions
+22 -2
View File
@@ -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> {
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
+13
View File
@@ -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.