Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 68 additions & 6 deletions src/uu/expr/src/syntax_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,6 @@
let first = pattern_chars.next();
match first {
Some('^') => {} // Start of string anchor is already added
Some('*') => re_string.push_str(r"\*"),
Some('$') if !is_end_of_expression(&pattern_chars) => re_string.push_str(r"\$"),
Some('\\') if right.len() == 1 => return Err(ExprError::TrailingBackslash),
Some(char) => re_string.push(char),
Expand Down Expand Up @@ -191,10 +190,17 @@
'\\' if !curr_is_escaped && pattern_chars.peek().is_none() => {
return Err(ExprError::TrailingBackslash);
}
'{' if curr_is_escaped && is_valid_range_quantifier(&pattern_chars) => {
re_string.push(curr);
// Set the lower bound of range quantifier to 0 if it is missing
if pattern_chars.peek() == Some(&',') {
re_string.push('0');
}
}
_ => re_string.push(curr),
}

prev_is_escaped = prev == '\\' && !prev_is_escaped;
prev_is_escaped = curr_is_escaped;
prev = curr;
}

Expand Down Expand Up @@ -244,6 +250,46 @@
}
}

/// Check if regex pattern character iterator is at the start of a valid range quantifier.
/// The iterator's start position is expected to be after the opening brace.
/// Range quantifier ends to closing brace.
///
/// # Examples of valid range quantifiers
///
/// - `r"\{3\}"`
/// - `r"\{3,\}"`
/// - `r"\{,6\}"`
/// - `r"\{3,6\}"`
/// - `r"\{,\}"`
fn is_valid_range_quantifier<I>(pattern_chars: &I) -> bool
where
I: Iterator<Item = char> + Clone,
{
// Parse the string between braces
let mut quantifier = String::new();
let mut pattern_chars_clone = pattern_chars.clone().peekable();
let Some(mut prev) = pattern_chars_clone.next() else {
return false;

Check warning on line 272 in src/uu/expr/src/syntax_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/expr/src/syntax_tree.rs#L272

Added line #L272 was not covered by tests
};
let mut prev_is_escaped = false;
while let Some(curr) = pattern_chars_clone.next() {
if prev == '\\' && curr == '}' && !prev_is_escaped {
break;
}
if pattern_chars_clone.peek().is_none() {
return false;

Check warning on line 280 in src/uu/expr/src/syntax_tree.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/expr/src/syntax_tree.rs#L280

Added line #L280 was not covered by tests
}

quantifier.push(prev);
prev_is_escaped = prev == '\\' && !prev_is_escaped;
prev = curr;
}

// Check if parsed quantifier is valid
let re = Regex::new(r"(\d+|\d*,\d*)").expect("valid regular expression");
re.is_match(&quantifier)
}

/// Check for errors in a supplied regular expression
///
/// GNU coreutils shows messages for invalid regular expressions
Expand Down Expand Up @@ -287,10 +333,7 @@
.expect("splitn always returns at least one string"),
repetition.next(),
) {
("", None) => {
// Empty repeating pattern
invalid_content_error = true;
}
("", Some("")) => {}
(x, None | Some("")) => {
if x.parse::<i16>().is_err() {
invalid_content_error = true;
Expand Down Expand Up @@ -750,6 +793,7 @@
mod test {
use crate::ExprError;
use crate::ExprError::InvalidBracketContent;
use crate::syntax_tree::is_valid_range_quantifier;

use super::{
AstNode, AstNodeInner, BinOp, NumericOp, RelationOp, StringOp, check_posix_regex_errors,
Expand Down Expand Up @@ -998,4 +1042,22 @@
Err(InvalidBracketContent)
);
}

#[test]
fn test_is_valid_range_quantifier() {
assert!(is_valid_range_quantifier(&"3\\}".chars()));
assert!(is_valid_range_quantifier(&"3,\\}".chars()));
assert!(is_valid_range_quantifier(&",6\\}".chars()));
assert!(is_valid_range_quantifier(&"3,6\\}".chars()));
assert!(is_valid_range_quantifier(&",\\}".chars()));
assert!(is_valid_range_quantifier(&"3,6\\}anything".chars()));
assert!(!is_valid_range_quantifier(&"\\{3,6\\}".chars()));
assert!(!is_valid_range_quantifier(&"\\}".chars()));
assert!(!is_valid_range_quantifier(&"".chars()));
assert!(!is_valid_range_quantifier(&"3".chars()));
assert!(!is_valid_range_quantifier(&"3,".chars()));
assert!(!is_valid_range_quantifier(&",6".chars()));
assert!(!is_valid_range_quantifier(&"3,6".chars()));
assert!(!is_valid_range_quantifier(&",".chars()));
}
}
5 changes: 0 additions & 5 deletions tests/by-util/test_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,6 @@ mod gnu_expr {
.stdout_only("\n");
}

#[ignore]
#[test]
fn test_bre17() {
new_ucmd!()
Expand All @@ -884,7 +883,6 @@ mod gnu_expr {
.stdout_only("{1}a\n");
}

#[ignore]
#[test]
fn test_bre18() {
new_ucmd!()
Expand All @@ -893,7 +891,6 @@ mod gnu_expr {
.stdout_only("1\n");
}

#[ignore]
#[test]
fn test_bre19() {
new_ucmd!()
Expand Down Expand Up @@ -1105,7 +1102,6 @@ mod gnu_expr {
.stderr_contains("Invalid content of \\{\\}");
}

#[ignore]
#[test]
fn test_bre45() {
new_ucmd!()
Expand All @@ -1114,7 +1110,6 @@ mod gnu_expr {
.stdout_only("1\n");
}

#[ignore]
#[test]
fn test_bre46() {
new_ucmd!()
Expand Down
Loading