Skip to content

Commit cb3b8cc

Browse files
committed
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.
1 parent 23a0c59 commit cb3b8cc

File tree

1 file changed

+33
-19
lines changed

1 file changed

+33
-19
lines changed

engine/system/win/sys_main.cpp

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -145,29 +145,43 @@ bool GlobMatch(const std::filesystem::path& glob, const std::filesystem::path& f
145145
return true;
146146
}
147147

148-
// If no wildcards are present, test file path as-is.
148+
fmt::memory_buffer buf;
149+
150+
// If no wildcards are present, test file path verbatim.
151+
// We use a regex rather than string comparisons to make it case-insensitive.
149152
if (globStr.find_first_of("?*") == std::string::npos) {
150-
return glob == file;
153+
buf.resize(globStr.size());
154+
for (char ch : globStr) {
155+
fmt::format_to(fmt::appender(buf), "[{}]", ch);
156+
}
151157
}
152-
153-
// Otherwise build a regular expression from the glob and use that to match files.
154-
fmt::memory_buffer buf;
155-
auto it = fmt::appender(buf);
156-
for (char ch : glob.generic_string()) {
157-
if (ch == '*') {
158-
it = fmt::format_to(it, ".*");
159-
} else if (ch == '?') {
160-
*it++ = '.';
161-
} else if ("+[]{}+()|"sv.find(ch) != std::string_view::npos) {
162-
// Escape metacharacters
163-
it = fmt::format_to(it, "\\{}", ch);
164-
} else if (std::isalnum((unsigned char)ch)) {
165-
*it++ = ch;
166-
} else {
167-
it = fmt::format_to(it, "[{}]", ch);
158+
else {
159+
// Otherwise build a regular expression from the glob and use that to match files.
160+
auto it = fmt::appender(buf);
161+
for (char ch : glob.generic_string()) {
162+
if (ch == '*') {
163+
it = fmt::format_to(it, ".*");
164+
}
165+
else if (ch == '?') {
166+
*it++ = '.';
167+
}
168+
else if ("+[]{}+()|"sv.find(ch) != std::string_view::npos) {
169+
// Escape metacharacters
170+
it = fmt::format_to(it, "\\{}", ch);
171+
}
172+
else if (std::isalnum((unsigned char)ch)) {
173+
*it++ = ch;
174+
}
175+
else {
176+
it = fmt::format_to(it, "[{}]", ch);
177+
}
168178
}
169179
}
170-
RE2 reGlob{to_string(buf)};
180+
181+
// Assume case-insensitive comparisons are desired.
182+
RE2::Options reOpts;
183+
reOpts.set_case_sensitive(false);
184+
RE2 reGlob{to_string(buf), reOpts};
171185

172186
return RE2::FullMatch(file.generic_string(), reGlob);
173187
}

0 commit comments

Comments
 (0)