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()
134135find_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
148175bool 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 ();
0 commit comments