|
12 | 12 | #include <cstdio> |
13 | 13 | #include <cstdlib> |
14 | 14 | #include <cstring> |
| 15 | +#include <filesystem> |
15 | 16 | #include <fstream> |
16 | 17 | #include <string> |
17 | 18 | #include <vector> |
18 | 19 |
|
19 | 20 | #include <dirent.h> |
20 | 21 | #include <fcntl.h> |
| 22 | +#include <fnmatch.h> |
21 | 23 | #include <jansson.h> |
22 | 24 | #include <openssl/bio.h> |
23 | 25 | #include <openssl/buffer.h> |
@@ -352,6 +354,70 @@ bool isPrivileged() { |
352 | 354 | return true; |
353 | 355 | } |
354 | 356 |
|
| 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 | + |
355 | 421 | void write_to_file(std::string data, const fs::path file) { |
356 | 422 | villas::Log::get("Filewriter")->debug("{} > {}", data, file.string()); |
357 | 423 | std::ofstream outputFile(file.string()); |
|
0 commit comments