diff --git a/AK/StringUtils.cpp b/AK/StringUtils.cpp index d660b95f0f5..bfa840bf813 100644 --- a/AK/StringUtils.cpp +++ b/AK/StringUtils.cpp @@ -62,8 +62,11 @@ bool matches(StringView str, StringView mask, CaseSensitivity case_sensitivity, record_span(string_ptr - string_start, 1); break; case '\\': - ++mask_ptr; - break; + // if backslash is last character in mask, just treat it as an exact match + // otherwise use it as escape for next character + if (mask_ptr + 1 < mask_end) + ++mask_ptr; + [[fallthrough]]; default: auto p = *mask_ptr; auto ch = *string_ptr; diff --git a/Tests/AK/TestStringUtils.cpp b/Tests/AK/TestStringUtils.cpp index d0155c049e2..93b4c451573 100644 --- a/Tests/AK/TestStringUtils.cpp +++ b/Tests/AK/TestStringUtils.cpp @@ -76,6 +76,21 @@ TEST_CASE(matches_trailing) EXPECT(AK::StringUtils::matches("ab"sv, "*ab****"sv)); } +TEST_CASE(match_backslash_escape) +{ + EXPECT(AK::StringUtils::matches("ab*"sv, "ab\\*"sv)); + EXPECT(!AK::StringUtils::matches("abc"sv, "ab\\*"sv)); + EXPECT(!AK::StringUtils::matches("abcd"sv, "ab\\*"sv)); + EXPECT(AK::StringUtils::matches("ab?"sv, "ab\\?"sv)); + EXPECT(!AK::StringUtils::matches("abc"sv, "ab\\?"sv)); +} + +TEST_CASE(match_trailing_backslash) +{ + EXPECT(AK::StringUtils::matches("x\\"sv, "x\\"sv)); + EXPECT(AK::StringUtils::matches("x\\"sv, "x\\\\"sv)); +} + TEST_CASE(convert_to_int) { auto value = AK::StringUtils::convert_to_int(StringView());