Skip to content

Commit c30ac24

Browse files
committed
Support more wildcard patterns in file searches
The naive implementation only supported `*`, `*.*` and `*.ext` type patterns. This improves the matching mechanism by mechanically rewriting the file part of the pattern into a regular expression. Do not pass untrusted paths into this as it doesn't guard against expressions of high complexity.
1 parent 73b0adc commit c30ac24

4 files changed

Lines changed: 50 additions & 17 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
/Debug/
1212
/Release/
1313
/SimpleGraphic.vcxproj.user
14+
vcpkg_installed/

engine/render/r_main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include <fmt/chrono.h>
1010
#include <map>
11+
#include <sstream>
1112

1213
// =======
1314
// Classes

engine/system/win/sys_main.cpp

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
#include <fmt/chrono.h>
9+
#include <re2/re2.h>
910

1011
#include "sys_local.h"
1112

@@ -134,15 +135,41 @@ find_c::find_c()
134135
find_c::~find_c()
135136
{}
136137

137-
bool GlobMatch(const std::filesystem::path& glob, const std::filesystem::path& file) {
138-
// TODO(LV): Future-proof to handle more than /* and /*.ext
138+
bool GlobMatch(const std::filesystem::path& glob, const std::filesystem::path& file)
139+
{
140+
using namespace std::literals::string_view_literals;
141+
auto globStr = glob.generic_string();
142+
143+
// Deal with traditional "everything" wildcards.
139144
if (glob == "*" || glob == "*.*") {
140145
return true;
141146
}
142-
if (glob.stem() == "*" && glob.has_extension() && file.has_extension()) {
143-
return glob.extension() == file.extension();
147+
148+
// If no wildcards are present, test file path as-is.
149+
if (globStr.find_first_of("?*") == std::string::npos) {
150+
return glob == file;
151+
}
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);
168+
}
144169
}
145-
return glob == file;
170+
RE2 reGlob{to_string(buf)};
171+
172+
return RE2::FullMatch(file.generic_string(), reGlob);
146173
}
147174

148175
bool find_c::FindFirst(const char* fileSpec)
@@ -152,8 +179,9 @@ bool find_c::FindFirst(const char* fileSpec)
152179

153180
std::error_code ec;
154181
for (iter = std::filesystem::directory_iterator(p.parent_path(), ec); iter != std::filesystem::directory_iterator{}; ++iter) {
155-
if (GlobMatch(glob, *iter)) {
156-
fileName = iter->path().filename().string();
182+
auto candFilename = iter->path().filename();
183+
if (GlobMatch(glob, candFilename)) {
184+
fileName = candFilename.string();
157185
isDirectory = iter->is_directory();
158186
fileSize = iter->file_size();
159187
auto mod = iter->last_write_time();
@@ -171,8 +199,9 @@ bool find_c::FindNext()
171199
}
172200

173201
for (++iter; iter != std::filesystem::directory_iterator{}; ++iter) {
174-
if (GlobMatch(glob, *iter)) {
175-
fileName = iter->path().filename().string();
202+
auto candFilename = iter->path().filename();
203+
if (GlobMatch(glob, candFilename)) {
204+
fileName = candFilename.string();
176205
isDirectory = iter->is_directory();
177206
fileSize = iter->file_size();
178207
auto mod = iter->last_write_time();

vcpkg.txt

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
fmt:x86-windows-static
2-
giflib:x86-windows-static
3-
glfw3:x86-windows-static
4-
libjpeg-turbo:x86-windows-static
5-
liblzma:x86-windows-static
6-
libpng:x86-windows-static
7-
luajit:x86-windows-static
8-
zlib:x86-windows-static
1+
angle:x86-windows
2+
fmt:x86-windows
3+
giflib:x86-windows
4+
glfw3:x86-windows
5+
libjpeg-turbo:x86-windows
6+
liblzma:x86-windows
7+
libpng:x86-windows
8+
luajit:x86-windows
9+
re2:x86-windows
10+
zlib:x86-windows

0 commit comments

Comments
 (0)