1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 11:37:44 +00:00

expr: Fix error message for too big range quantifier index

This commit is contained in:
Teemu Pätsi 2025-05-27 14:02:24 +03:00
parent 6aeae43f3c
commit 07caa4867b
No known key found for this signature in database
3 changed files with 78 additions and 30 deletions

View file

@ -50,6 +50,8 @@ pub enum ExprError {
InvalidBracketContent, InvalidBracketContent,
#[error("Trailing backslash")] #[error("Trailing backslash")]
TrailingBackslash, TrailingBackslash,
#[error("Regular expression too big")]
TooBigRangeQuantifierIndex,
} }
impl UError for ExprError { impl UError for ExprError {

View file

@ -192,14 +192,15 @@ impl StringOp {
let _ = re_string.pop(); let _ = re_string.pop();
} }
re_string.push(curr); re_string.push(curr);
} else if is_valid_range_quantifier(&pattern_chars) { } else {
// Check if the following section is a valid range quantifier
verify_range_quantifier(&pattern_chars)?;
re_string.push(curr); re_string.push(curr);
// Set the lower bound of range quantifier to 0 if it is missing // Set the lower bound of range quantifier to 0 if it is missing
if pattern_chars.peek() == Some(&',') { if pattern_chars.peek() == Some(&',') {
re_string.push('0'); re_string.push('0');
} }
} else {
return Err(ExprError::InvalidBracketContent);
} }
} }
_ => re_string.push(curr), _ => re_string.push(curr),
@ -222,7 +223,7 @@ impl StringOp {
// "invalid repeat range {lower,upper}" // "invalid repeat range {lower,upper}"
-123 => ExprError::InvalidBracketContent, -123 => ExprError::InvalidBracketContent,
// "too big number for repeat range" // "too big number for repeat range"
-201 => ExprError::InvalidBracketContent, -201 => ExprError::TooBigRangeQuantifierIndex,
_ => ExprError::InvalidRegexExpression, _ => ExprError::InvalidRegexExpression,
})?; })?;
@ -277,7 +278,7 @@ where
/// - `r"\{,6\}"` /// - `r"\{,6\}"`
/// - `r"\{3,6\}"` /// - `r"\{3,6\}"`
/// - `r"\{,\}"` /// - `r"\{,\}"`
fn is_valid_range_quantifier<I>(pattern_chars: &I) -> bool fn verify_range_quantifier<I>(pattern_chars: &I) -> Result<(), ExprError>
where where
I: Iterator<Item = char> + Clone, I: Iterator<Item = char> + Clone,
{ {
@ -285,15 +286,19 @@ where
let mut quantifier = String::new(); let mut quantifier = String::new();
let mut pattern_chars_clone = pattern_chars.clone().peekable(); let mut pattern_chars_clone = pattern_chars.clone().peekable();
let Some(mut prev) = pattern_chars_clone.next() else { let Some(mut prev) = pattern_chars_clone.next() else {
return false; return Err(ExprError::UnmatchedOpeningBrace);
}; };
if pattern_chars_clone.peek().is_none() {
return Err(ExprError::UnmatchedOpeningBrace);
}
let mut prev_is_escaped = false; let mut prev_is_escaped = false;
while let Some(curr) = pattern_chars_clone.next() { while let Some(curr) = pattern_chars_clone.next() {
if prev == '\\' && curr == '}' && !prev_is_escaped { if prev == '\\' && curr == '}' && !prev_is_escaped {
break; break;
} }
if pattern_chars_clone.peek().is_none() { if pattern_chars_clone.peek().is_none() {
return false; return Err(ExprError::UnmatchedOpeningBrace);
} }
quantifier.push(prev); quantifier.push(prev);
@ -302,19 +307,32 @@ where
} }
// Check if parsed quantifier is valid // Check if parsed quantifier is valid
let is_valid_range_index = |s: &str| s.parse::<i32>().map_or(true, |x| x <= i16::MAX as i32);
let re = Regex::new(r"^(\d*,\d*|\d+)").expect("valid regular expression"); let re = Regex::new(r"^(\d*,\d*|\d+)").expect("valid regular expression");
if let Some(captures) = re.captures(&quantifier) { if let Some(captures) = re.captures(&quantifier) {
let matched = captures.at(0).unwrap_or_default(); let matched = captures.at(0).unwrap_or_default();
match matched.split_once(',') { match matched.split_once(',') {
None => is_valid_range_index(matched), Some(("", "")) => Ok(()),
Some(("", "")) => true, Some((x, "")) | Some(("", x)) => match x.parse::<i32>() {
Some((x, "")) => is_valid_range_index(x), Ok(x) if x <= i16::MAX.into() => Ok(()),
Some(("", x)) => is_valid_range_index(x), Ok(_) => Err(ExprError::TooBigRangeQuantifierIndex),
Some((f, l)) => f <= l && is_valid_range_index(f) && is_valid_range_index(l), Err(_) => Err(ExprError::InvalidBracketContent),
},
Some((f, l)) => match (f.parse::<i32>(), l.parse::<i32>()) {
(Ok(f), Ok(l)) if f > l => Err(ExprError::InvalidBracketContent),
(Ok(f), Ok(l)) if f > i16::MAX.into() || l > i16::MAX.into() => {
Err(ExprError::TooBigRangeQuantifierIndex)
}
(Ok(_), Ok(_)) => Ok(()),
_ => Err(ExprError::InvalidBracketContent),
},
None => match matched.parse::<i32>() {
Ok(x) if x <= i16::MAX.into() => Ok(()),
Ok(_) => Err(ExprError::TooBigRangeQuantifierIndex),
Err(_) => Err(ExprError::InvalidBracketContent),
},
} }
} else { } else {
false Err(ExprError::InvalidBracketContent)
} }
} }
@ -789,7 +807,7 @@ pub fn is_truthy(s: &NumOrStr) -> bool {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::ExprError; use crate::ExprError;
use crate::syntax_tree::is_valid_range_quantifier; use crate::syntax_tree::verify_range_quantifier;
use super::{ use super::{
AstNode, AstNodeInner, BinOp, NumericOp, RelationOp, StringOp, check_posix_regex_errors, AstNode, AstNodeInner, BinOp, NumericOp, RelationOp, StringOp, check_posix_regex_errors,
@ -999,19 +1017,47 @@ mod test {
#[test] #[test]
fn test_is_valid_range_quantifier() { fn test_is_valid_range_quantifier() {
assert!(is_valid_range_quantifier(&"3\\}".chars())); assert!(verify_range_quantifier(&"3\\}".chars()).is_ok());
assert!(is_valid_range_quantifier(&"3,\\}".chars())); assert!(verify_range_quantifier(&"3,\\}".chars()).is_ok());
assert!(is_valid_range_quantifier(&",6\\}".chars())); assert!(verify_range_quantifier(&",6\\}".chars()).is_ok());
assert!(is_valid_range_quantifier(&"3,6\\}".chars())); assert!(verify_range_quantifier(&"3,6\\}".chars()).is_ok());
assert!(is_valid_range_quantifier(&",\\}".chars())); assert!(verify_range_quantifier(&",\\}".chars()).is_ok());
assert!(is_valid_range_quantifier(&"3,6\\}anything".chars())); assert!(verify_range_quantifier(&"32767\\}anything".chars()).is_ok());
assert!(!is_valid_range_quantifier(&"\\{3,6\\}".chars())); assert_eq!(
assert!(!is_valid_range_quantifier(&"\\}".chars())); verify_range_quantifier(&"\\{3,6\\}".chars()),
assert!(!is_valid_range_quantifier(&"".chars())); Err(ExprError::InvalidBracketContent)
assert!(!is_valid_range_quantifier(&"3".chars())); );
assert!(!is_valid_range_quantifier(&"3,".chars())); assert_eq!(
assert!(!is_valid_range_quantifier(&",6".chars())); verify_range_quantifier(&"\\}".chars()),
assert!(!is_valid_range_quantifier(&"3,6".chars())); Err(ExprError::InvalidBracketContent)
assert!(!is_valid_range_quantifier(&",".chars())); );
assert_eq!(
verify_range_quantifier(&"".chars()),
Err(ExprError::UnmatchedOpeningBrace)
);
assert_eq!(
verify_range_quantifier(&"3".chars()),
Err(ExprError::UnmatchedOpeningBrace)
);
assert_eq!(
verify_range_quantifier(&"3,".chars()),
Err(ExprError::UnmatchedOpeningBrace)
);
assert_eq!(
verify_range_quantifier(&",6".chars()),
Err(ExprError::UnmatchedOpeningBrace)
);
assert_eq!(
verify_range_quantifier(&"3,6".chars()),
Err(ExprError::UnmatchedOpeningBrace)
);
assert_eq!(
verify_range_quantifier(&",".chars()),
Err(ExprError::UnmatchedOpeningBrace)
);
assert_eq!(
verify_range_quantifier(&"32768\\}".chars()),
Err(ExprError::TooBigRangeQuantifierIndex)
);
} }
} }

View file

@ -1210,7 +1210,7 @@ mod gnu_expr {
.args(&["_", ":", "a\\{32768\\}"]) .args(&["_", ":", "a\\{32768\\}"])
.fails_with_code(2) .fails_with_code(2)
.no_stdout() .no_stdout()
.stderr_contains("Invalid content of \\{\\}"); .stderr_contains("Regular expression too big\n");
} }
#[test] #[test]