From cb3b8cce270aca0f1813bc71a647b282846f0ce9 Mon Sep 17 00:00:00 2001 From: Lars Viklund Date: Tue, 26 Mar 2024 22:50:45 +0100 Subject: [PATCH] fix: make file searches case-insensitive As we moved from WinAPI `FindFirstFile` to `std::filesystem` path tests and globbing was accidentally made case-sensitive as we use direct string comparison or regular expressions to match files. We now always construct a regular expression for the glob and make the regex match case-insensitive. The regex is rebuilt on every file test but as the set of files is likely to be small this is of low impact; caching the regex in the search object would spread RE2 into the header. --- engine/system/win/sys_main.cpp | 52 +++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/engine/system/win/sys_main.cpp b/engine/system/win/sys_main.cpp index d929eca..fe03efc 100644 --- a/engine/system/win/sys_main.cpp +++ b/engine/system/win/sys_main.cpp @@ -145,29 +145,43 @@ bool GlobMatch(const std::filesystem::path& glob, const std::filesystem::path& f return true; } - // If no wildcards are present, test file path as-is. + fmt::memory_buffer buf; + + // If no wildcards are present, test file path verbatim. + // We use a regex rather than string comparisons to make it case-insensitive. if (globStr.find_first_of("?*") == std::string::npos) { - return glob == file; + buf.resize(globStr.size()); + for (char ch : globStr) { + fmt::format_to(fmt::appender(buf), "[{}]", ch); + } } - - // Otherwise build a regular expression from the glob and use that to match files. - fmt::memory_buffer buf; - auto it = fmt::appender(buf); - for (char ch : glob.generic_string()) { - if (ch == '*') { - it = fmt::format_to(it, ".*"); - } else if (ch == '?') { - *it++ = '.'; - } else if ("+[]{}+()|"sv.find(ch) != std::string_view::npos) { - // Escape metacharacters - it = fmt::format_to(it, "\\{}", ch); - } else if (std::isalnum((unsigned char)ch)) { - *it++ = ch; - } else { - it = fmt::format_to(it, "[{}]", ch); + else { + // Otherwise build a regular expression from the glob and use that to match files. + auto it = fmt::appender(buf); + for (char ch : glob.generic_string()) { + if (ch == '*') { + it = fmt::format_to(it, ".*"); + } + else if (ch == '?') { + *it++ = '.'; + } + else if ("+[]{}+()|"sv.find(ch) != std::string_view::npos) { + // Escape metacharacters + it = fmt::format_to(it, "\\{}", ch); + } + else if (std::isalnum((unsigned char)ch)) { + *it++ = ch; + } + else { + it = fmt::format_to(it, "[{}]", ch); + } } } - RE2 reGlob{to_string(buf)}; + + // Assume case-insensitive comparisons are desired. + RE2::Options reOpts; + reOpts.set_case_sensitive(false); + RE2 reGlob{to_string(buf), reOpts}; return RE2::FullMatch(file.generic_string(), reGlob); }