Skip to content

Commit c50044b

Browse files
committed
utils: Add glob helper function
Signed-off-by: Philipp Jungkamp <philipp.jungkamp@rwth-aachen.de>
1 parent e29e5c2 commit c50044b

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

common/include/villas/utils.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <cstdint>
1313
#include <cstdlib>
1414
#include <list>
15+
#include <span>
1516
#include <string>
1617
#include <vector>
1718

@@ -212,6 +213,10 @@ template <class... Ts> struct overloaded : Ts... {
212213
// Explicit deduction guide (not needed as of C++20)
213214
template <class... Ts> overloaded(Ts...) -> overloaded<Ts...>;
214215

216+
// glob-style filesystem pattern matching
217+
std::vector<fs::path> glob(fs::path const &pattern,
218+
std::span<const fs::path> searchDirectories);
219+
215220
void write_to_file(std::string data, const fs::path file);
216221
std::vector<std::string> read_names_in_directory(const fs::path &directory);
217222

common/lib/utils.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
#include <cstdio>
1313
#include <cstdlib>
1414
#include <cstring>
15+
#include <filesystem>
1516
#include <fstream>
1617
#include <string>
1718
#include <vector>
1819

1920
#include <dirent.h>
2021
#include <fcntl.h>
22+
#include <fnmatch.h>
2123
#include <jansson.h>
2224
#include <openssl/bio.h>
2325
#include <openssl/buffer.h>
@@ -352,6 +354,70 @@ bool isPrivileged() {
352354
return true;
353355
}
354356

357+
// internal glob implementation details
358+
namespace {
359+
bool isGlobPattern(fs::path const &path) {
360+
static const auto specialCharacters = fs::path("?*[").native();
361+
auto const &string = path.native();
362+
return std::ranges::find_first_of(string, specialCharacters) != string.end();
363+
}
364+
365+
bool isGlobMatch(fs::path const &pattern, fs::path const &path) {
366+
return ::fnmatch(pattern.c_str(), path.c_str(), FNM_PATHNAME) == 0;
367+
}
368+
369+
void globImpl(std::vector<fs::path> &result, fs::path &&path,
370+
std::ranges::subrange<fs::path::iterator> pattern) {
371+
[[maybe_unused]] auto discardErrorCode = std::error_code{};
372+
373+
if (pattern.empty()) {
374+
// we've reached the end of our pattern
375+
if (fs::exists(path, discardErrorCode))
376+
result.push_back(path);
377+
return;
378+
}
379+
380+
if (not fs::is_directory(path, discardErrorCode))
381+
return;
382+
383+
if (not isGlobPattern(pattern.front())) {
384+
return globImpl(result, std::move(path /= pattern.front()),
385+
std::move(pattern).next());
386+
} else {
387+
auto nextPattern = std::move(pattern).next();
388+
for (auto entry : fs::directory_iterator(path)) {
389+
if (not isGlobMatch(pattern.front(), entry.path().filename()))
390+
continue;
391+
392+
globImpl(result, fs::path(entry.path()), nextPattern);
393+
}
394+
}
395+
}
396+
} // namespace
397+
398+
std::vector<fs::path> glob(fs::path const &pattern,
399+
std::span<const fs::path> searchDirectories) {
400+
auto logger = Log::get("glob");
401+
std::vector<fs::path> result;
402+
if (pattern.is_absolute()) {
403+
logger->debug("matching absolute pattern {:?}", pattern.string());
404+
globImpl(result, pattern.root_path(), pattern);
405+
} else {
406+
for (auto path : searchDirectories) {
407+
logger->debug("matching relative pattern {:?} in {:?}", pattern.string(),
408+
path.string());
409+
globImpl(result, std::move(path), pattern);
410+
}
411+
}
412+
413+
if (result.empty()) {
414+
throw std::runtime_error(
415+
fmt::format("could not find any file matching {:?}", pattern.string()));
416+
}
417+
418+
return result;
419+
}
420+
355421
void write_to_file(std::string data, const fs::path file) {
356422
villas::Log::get("Filewriter")->debug("{} > {}", data, file.string());
357423
std::ofstream outputFile(file.string());

0 commit comments

Comments
 (0)