find: -name: don't panic on a malformed POSIX bracket class

A `-name` glob with a `[.`/`[=`/`[:` introducer whose closing delimiter is
missing — only a lone `]` (or the delimiter) before a multibyte char, e.g.
`'[[:]é'` — panicked (exit 101). `extract_bracket_expr` searched for *either*
the delimiter or `]` and then added a blind `+ 2`, which overshot into the
following character and sliced off a UTF-8 boundary.

Search for the actual two-byte closing sequence `<delim>]` instead. A valid
class (`[:alpha:]`) is unchanged; a malformed one returns None, so the caller
treats `[` literally and accepts the pattern like GNU find (exit 0). Reachable
via -name/-iname/-path/-ipath/-lname/-ilname. Adds a regression test.
This commit is contained in:
weili
2026-06-09 22:41:13 +02:00
committed by Sylvestre Ledru
parent 53980035af
commit 001966f326
+14 -1
View File
@@ -83,7 +83,10 @@ fn extract_bracket_expr(pattern: &str) -> Option<(String, &str)> {
if matches!(delim, '.' | '=' | ':') {
let rest = chars.as_str();
let end = rest.find([delim, ']'])? + 2;
// Search for the two-byte closer `<delim>]` (e.g. `:]`);
// matching either byte alone let `+ 2` overshoot a char boundary.
let closer = format!("{delim}]");
let end = rest.find(closer.as_str())? + 2;
expr.push_str(&rest[..end]);
chars = rest[end..].chars();
}
@@ -222,6 +225,16 @@ mod tests {
assert_glob_regex(r"foo[bar[!baz", r"foo\[bar\[!baz");
}
#[test]
fn malformed_posix_class_with_multibyte_char() {
for pat in ["[[:]é", "[[:a]é", "[[.]é", "[[:é]", "[[=]😀"] {
assert!(
glob_to_regex(pat).is_some(),
"panicked or rejected: {pat:?}"
);
}
}
#[test]
fn incomplete_escape() {
assert_eq!(glob_to_regex(r"foo\"), None);