Updated glob dependency to commit b66732b0e72

This commit is contained in:
Bartek Kryza
2023-01-21 17:28:39 +01:00
parent 9a527eef0c
commit b3701fa810

View File

@@ -1,22 +1,31 @@
#pragma once #pragma once
#include <cassert> #include <cassert>
#include <filesystem>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <map> #include <map>
#include <regex> #include <regex>
#include <string> #include <string>
#include <vector> #include <vector>
namespace fs = std::filesystem;
#ifdef GLOB_USE_GHC_FILESYSTEM
#include <ghc/filesystem.hpp>
#else
#include <filesystem>
#endif
namespace glob { namespace glob {
#ifdef GLOB_USE_GHC_FILESYSTEM
namespace fs = ghc::filesystem;
#else
namespace fs = std::filesystem;
#endif
namespace { namespace {
static inline bool string_replace( static inline
std::string &str, const std::string &from, const std::string &to) bool string_replace(std::string &str, const std::string &from, const std::string &to) {
{
std::size_t start_pos = str.find(from); std::size_t start_pos = str.find(from);
if (start_pos == std::string::npos) if (start_pos == std::string::npos)
return false; return false;
@@ -24,8 +33,8 @@ static inline bool string_replace(
return true; return true;
} }
static inline std::string translate(const std::string &pattern) static inline
{ std::string translate(const std::string &pattern) {
std::size_t i = 0, n = pattern.size(); std::size_t i = 0, n = pattern.size();
std::string result_string; std::string result_string;
@@ -34,11 +43,9 @@ static inline std::string translate(const std::string &pattern)
i += 1; i += 1;
if (c == '*') { if (c == '*') {
result_string += ".*"; result_string += ".*";
} } else if (c == '?') {
else if (c == '?') {
result_string += "."; result_string += ".";
} } else if (c == '[') {
else if (c == '[') {
auto j = i; auto j = i;
if (j < n && pattern[j] == '!') { if (j < n && pattern[j] == '!') {
j += 1; j += 1;
@@ -51,21 +58,16 @@ static inline std::string translate(const std::string &pattern)
} }
if (j >= n) { if (j >= n) {
result_string += "\\["; result_string += "\\[";
} } else {
else { auto stuff = std::string(pattern.begin() + i, pattern.begin() + j);
auto stuff =
std::string(pattern.begin() + i, pattern.begin() + j);
if (stuff.find("--") == std::string::npos) { if (stuff.find("--") == std::string::npos) {
string_replace( string_replace(stuff, std::string{"\\"}, std::string{R"(\\)"});
stuff, std::string{"\\"}, std::string{R"(\\)"}); } else {
}
else {
std::vector<std::string> chunks; std::vector<std::string> chunks;
std::size_t k = 0; std::size_t k = 0;
if (pattern[i] == '!') { if (pattern[i] == '!') {
k = i + 2; k = i + 2;
} } else {
else {
k = i + 1; k = i + 1;
} }
@@ -74,27 +76,22 @@ static inline std::string translate(const std::string &pattern)
if (k == std::string::npos) { if (k == std::string::npos) {
break; break;
} }
chunks.push_back(std::string( chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + k));
pattern.begin() + i, pattern.begin() + k));
i = k + 1; i = k + 1;
k = k + 3; k = k + 3;
} }
chunks.push_back( chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + j));
std::string(pattern.begin() + i, pattern.begin() + j));
// Escape backslashes and hyphens for set difference (--). // Escape backslashes and hyphens for set difference (--).
// Hyphens that create ranges shouldn't be escaped. // Hyphens that create ranges shouldn't be escaped.
bool first = false; bool first = false;
for (auto &s : chunks) { for (auto &s : chunks) {
string_replace( string_replace(s, std::string{"\\"}, std::string{R"(\\)"});
s, std::string{"\\"}, std::string{R"(\\)"}); string_replace(s, std::string{"-"}, std::string{R"(\-)"});
string_replace(
s, std::string{"-"}, std::string{R"(\-)"});
if (first) { if (first) {
stuff += s; stuff += s;
first = false; first = false;
} } else {
else {
stuff += "-" + s; stuff += "-" + s;
} }
} }
@@ -102,7 +99,7 @@ static inline std::string translate(const std::string &pattern)
// Escape set operations (&&, ~~ and ||). // Escape set operations (&&, ~~ and ||).
std::string result; std::string result;
std::regex_replace(std::back_inserter(result), // ressult std::regex_replace(std::back_inserter(result), // result
stuff.begin(), stuff.end(), // string stuff.begin(), stuff.end(), // string
std::regex(std::string{R"([&~|])"}), // pattern std::regex(std::string{R"([&~|])"}), // pattern
std::string{R"(\\\1)"}); // repl std::string{R"(\\\1)"}); // repl
@@ -110,34 +107,29 @@ static inline std::string translate(const std::string &pattern)
i = j + 1; i = j + 1;
if (stuff[0] == '!') { if (stuff[0] == '!') {
stuff = "^" + std::string(stuff.begin() + 1, stuff.end()); stuff = "^" + std::string(stuff.begin() + 1, stuff.end());
} } else if (stuff[0] == '^' || stuff[0] == '[') {
else if (stuff[0] == '^' || stuff[0] == '[') {
stuff = "\\\\" + stuff; stuff = "\\\\" + stuff;
} }
result_string = result_string + "[" + stuff + "]"; result_string = result_string + "[" + stuff + "]";
} }
} } else {
else {
// SPECIAL_CHARS // SPECIAL_CHARS
// closing ')', '}' and ']' // closing ')', '}' and ']'
// '-' (a range in character set) // '-' (a range in character set)
// '&', '~', (extended character set operations) // '&', '~', (extended character set operations)
// '#' (comment) and WHITESPACE (ignored) in verbose mode // '#' (comment) and WHITESPACE (ignored) in verbose mode
static std::string special_characters = static std::string special_characters = "()[]{}?*+-|^$\\.&~# \t\n\r\v\f";
"()[]{}?*+-|^$\\.&~# \t\n\r\v\f";
static std::map<int, std::string> special_characters_map; static std::map<int, std::string> special_characters_map;
if (special_characters_map.empty()) { if (special_characters_map.empty()) {
for (auto &c : special_characters) { for (auto &sc : special_characters) {
special_characters_map.insert( special_characters_map.insert(
std::make_pair(static_cast<int>(c), std::make_pair(static_cast<int>(sc), std::string{"\\"} + std::string(1, sc)));
std::string{"\\"} + std::string(1, c)));
} }
} }
if (special_characters.find(c) != std::string::npos) { if (special_characters.find(c) != std::string::npos) {
result_string += special_characters_map[static_cast<int>(c)]; result_string += special_characters_map[static_cast<int>(c)];
} } else {
else {
result_string += c; result_string += c;
} }
} }
@@ -145,77 +137,69 @@ static inline std::string translate(const std::string &pattern)
return std::string{"(("} + result_string + std::string{R"()|[\r\n])$)"}; return std::string{"(("} + result_string + std::string{R"()|[\r\n])$)"};
} }
static inline std::regex compile_pattern(const std::string &pattern) static inline
{ std::regex compile_pattern(const std::string &pattern) {
return std::regex(translate(pattern), std::regex::ECMAScript); return std::regex(translate(pattern), std::regex::ECMAScript);
} }
static inline bool fnmatch_case( static inline
const fs::path &name, const std::string &pattern) bool fnmatch(const fs::path &name, const std::string &pattern) {
{
return std::regex_match(name.string(), compile_pattern(pattern)); return std::regex_match(name.string(), compile_pattern(pattern));
} }
static inline std::vector<fs::path> filter( static inline
const std::vector<fs::path> &names, const std::string &pattern) std::vector<fs::path> filter(const std::vector<fs::path> &names,
{ const std::string &pattern) {
// std::cout << "Pattern: " << pattern << "\n"; // std::cout << "Pattern: " << pattern << "\n";
std::vector<fs::path> result; std::vector<fs::path> result;
for (auto &name : names) { for (auto &name : names) {
// std::cout << "Checking for " << name.string() << "\n"; // std::cout << "Checking for " << name.string() << "\n";
if (fnmatch_case(name, pattern)) { if (fnmatch(name, pattern)) {
result.push_back(name); result.push_back(name);
} }
} }
return result; return result;
} }
static inline fs::path expand_tilde(fs::path path) static inline
{ fs::path expand_tilde(fs::path path) {
if (path.empty()) if (path.empty()) return path;
return path;
#ifdef _WIN32 #ifdef _WIN32
char *home; const char * home_variable = "USERNAME";
size_t sz;
[[maybe_unused]] errno_t err = _dupenv_s(&home, &sz, "USERPROFILE");
#else #else
const char *home = std::getenv("HOME"); const char * home_variable = "USER";
#endif #endif
const char * home = std::getenv(home_variable);
if (home == nullptr) { if (home == nullptr) {
throw std::invalid_argument( throw std::invalid_argument("error: Unable to expand `~` - HOME environment variable not set.");
"error: Unable to expand `~` - HOME environment variable not set.");
} }
std::string s = path.string(); std::string s = path.string();
if (s[0] == '~') { if (s[0] == '~') {
s = std::string(home) + s.substr(1, s.size() - 1); s = std::string(home) + s.substr(1, s.size() - 1);
return fs::path(s); return fs::path(s);
} } else {
else {
return path; return path;
} }
} }
static inline bool has_magic(const std::string &pathname) static inline
{ bool has_magic(const std::string &pathname) {
static const auto magic_check = std::regex("([*?[])"); static const auto magic_check = std::regex("([*?[])");
return std::regex_search(pathname, magic_check); return std::regex_search(pathname, magic_check);
} }
static inline bool is_hidden(const std::string &pathname) static inline
{ bool is_hidden(const std::string &pathname) {
return std::regex_match( return std::regex_match(pathname, std::regex("^(.*\\/)*\\.[^\\.\\/]+\\/*$"));
pathname, std::regex("^(.*\\/)*\\.[^\\.\\/]+\\/*$"));
} }
static inline bool is_recursive(const std::string &pattern) static inline
{ bool is_recursive(const std::string &pattern) { return pattern == "**"; }
return pattern == "**";
}
static inline std::vector<fs::path> iter_directory( static inline
const fs::path &dirname, bool dironly) std::vector<fs::path> iter_directory(const fs::path &dirname, bool dironly) {
{
std::vector<fs::path> result; std::vector<fs::path> result;
auto current_directory = dirname; auto current_directory = dirname;
@@ -225,20 +209,18 @@ static inline std::vector<fs::path> iter_directory(
if (fs::exists(current_directory)) { if (fs::exists(current_directory)) {
try { try {
for (auto &entry : fs::directory_iterator(current_directory, for (auto &entry : fs::directory_iterator(
fs::directory_options::follow_directory_symlink | current_directory, fs::directory_options::follow_directory_symlink |
fs::directory_options::skip_permission_denied)) { fs::directory_options::skip_permission_denied)) {
if (!dironly || entry.is_directory()) { if (!dironly || entry.is_directory()) {
if (dirname.is_absolute()) { if (dirname.is_absolute()) {
result.push_back(entry.path()); result.push_back(entry.path());
} } else {
else {
result.push_back(fs::relative(entry.path())); result.push_back(fs::relative(entry.path()));
} }
} }
} }
} } catch (std::exception&) {
catch (std::exception &) {
// not a directory // not a directory
// do nothing // do nothing
} }
@@ -248,9 +230,8 @@ static inline std::vector<fs::path> iter_directory(
} }
// Recursively yields relative pathnames inside a literal directory. // Recursively yields relative pathnames inside a literal directory.
static inline std::vector<fs::path> rlistdir( static inline
const fs::path &dirname, bool dironly) std::vector<fs::path> rlistdir(const fs::path &dirname, bool dironly) {
{
std::vector<fs::path> result; std::vector<fs::path> result;
auto names = iter_directory(dirname, dironly); auto names = iter_directory(dirname, dironly);
for (auto &x : names) { for (auto &x : names) {
@@ -266,9 +247,9 @@ static inline std::vector<fs::path> rlistdir(
// This helper function recursively yields relative pathnames inside a literal // This helper function recursively yields relative pathnames inside a literal
// directory. // directory.
static inline std::vector<fs::path> glob2(const fs::path &dirname, static inline
[[maybe_unused]] const std::string &pattern, bool dironly) std::vector<fs::path> glob2(const fs::path &dirname, [[maybe_unused]] const std::string &pattern,
{ bool dironly) {
// std::cout << "In glob2\n"; // std::cout << "In glob2\n";
std::vector<fs::path> result; std::vector<fs::path> result;
assert(is_recursive(pattern)); assert(is_recursive(pattern));
@@ -281,9 +262,9 @@ static inline std::vector<fs::path> glob2(const fs::path &dirname,
// These 2 helper functions non-recursively glob inside a literal directory. // These 2 helper functions non-recursively glob inside a literal directory.
// They return a list of basenames. _glob1 accepts a pattern while _glob0 // They return a list of basenames. _glob1 accepts a pattern while _glob0
// takes a literal basename (so it only has to check for its existence). // takes a literal basename (so it only has to check for its existence).
static inline std::vector<fs::path> glob1( static inline
const fs::path &dirname, const std::string &pattern, bool dironly) std::vector<fs::path> glob1(const fs::path &dirname, const std::string &pattern,
{ bool dironly) {
// std::cout << "In glob1\n"; // std::cout << "In glob1\n";
auto names = iter_directory(dirname, dironly); auto names = iter_directory(dirname, dironly);
std::vector<fs::path> filtered_names; std::vector<fs::path> filtered_names;
@@ -302,9 +283,9 @@ static inline std::vector<fs::path> glob1(
return filter(filtered_names, pattern); return filter(filtered_names, pattern);
} }
static inline std::vector<fs::path> glob0( static inline
const fs::path &dirname, const fs::path &basename, bool /*dironly*/) std::vector<fs::path> glob0(const fs::path &dirname, const fs::path &basename,
{ bool /*dironly*/) {
// std::cout << "In glob0\n"; // std::cout << "In glob0\n";
std::vector<fs::path> result; std::vector<fs::path> result;
if (basename.empty()) { if (basename.empty()) {
@@ -312,8 +293,7 @@ static inline std::vector<fs::path> glob0(
if (fs::is_directory(dirname)) { if (fs::is_directory(dirname)) {
result = {basename}; result = {basename};
} }
} } else {
else {
if (fs::exists(dirname / basename)) { if (fs::exists(dirname / basename)) {
result = {basename}; result = {basename};
} }
@@ -321,10 +301,11 @@ static inline std::vector<fs::path> glob0(
return result; return result;
} }
static inline std::vector<fs::path> glob(const std::string &pathname, } // namespace end
bool recursive = false, bool dironly = false,
std::filesystem::path root_directory = std::filesystem::current_path()) static inline
{ std::vector<fs::path> glob(const std::string &pathname, bool recursive = false,
bool dironly = false) {
std::vector<fs::path> result; std::vector<fs::path> result;
auto path = fs::path(pathname); auto path = fs::path(pathname);
@@ -340,16 +321,10 @@ static inline std::vector<fs::path> glob(const std::string &pathname,
if (!has_magic(pathname)) { if (!has_magic(pathname)) {
assert(!dironly); assert(!dironly);
if (!basename.empty()) { if (!basename.empty()) {
if (!root_directory.empty() && !path.is_absolute()) { if (fs::exists(path)) {
if (fs::exists(root_directory / path)) {
result.push_back(path); result.push_back(path);
} }
} } else {
else if (fs::exists(path)) {
result.push_back(path);
}
}
else {
// Patterns ending with a slash should match only directories // Patterns ending with a slash should match only directories
if (fs::is_directory(dirname)) { if (fs::is_directory(dirname)) {
result.push_back(path); result.push_back(path);
@@ -361,8 +336,7 @@ static inline std::vector<fs::path> glob(const std::string &pathname,
if (dirname.empty()) { if (dirname.empty()) {
if (recursive && is_recursive(basename.string())) { if (recursive && is_recursive(basename.string())) {
return glob2(dirname, basename.string(), dironly); return glob2(dirname, basename.string(), dironly);
} } else {
else {
return glob1(dirname, basename.string(), dironly); return glob1(dirname, basename.string(), dironly);
} }
} }
@@ -370,28 +344,24 @@ static inline std::vector<fs::path> glob(const std::string &pathname,
std::vector<fs::path> dirs; std::vector<fs::path> dirs;
if (dirname != fs::path(pathname) && has_magic(dirname.string())) { if (dirname != fs::path(pathname) && has_magic(dirname.string())) {
dirs = glob(dirname.string(), recursive, true); dirs = glob(dirname.string(), recursive, true);
} } else {
else {
dirs = {dirname}; dirs = {dirname};
} }
std::function<std::vector<fs::path>( std::function<std::vector<fs::path>(const fs::path &, const std::string &, bool)>
const fs::path &, const std::string &, bool)>
glob_in_dir; glob_in_dir;
if (has_magic(basename.string())) { if (has_magic(basename.string())) {
if (recursive && is_recursive(basename.string())) { if (recursive && is_recursive(basename.string())) {
glob_in_dir = glob2; glob_in_dir = glob2;
} } else {
else {
glob_in_dir = glob1; glob_in_dir = glob1;
} }
} } else {
else {
glob_in_dir = glob0; glob_in_dir = glob0;
} }
for (auto &d : dirs) { for (auto &d : dirs) {
for (auto &name : glob_in_dir(root_directory / d, basename.string(), dironly)) { for (auto &name : glob_in_dir(d, basename.string(), dironly)) {
fs::path subresult = name; fs::path subresult = name;
if (name.parent_path().empty()) { if (name.parent_path().empty()) {
subresult = d / name; subresult = d / name;
@@ -403,28 +373,20 @@ static inline std::vector<fs::path> glob(const std::string &pathname,
return result; return result;
} }
} // namespace end
static inline std::vector<fs::path> glob(const std::string &pathname) static inline
{ std::vector<fs::path> glob(const std::string &pathname) {
return glob(pathname, false); return glob(pathname, false);
} }
static inline std::vector<fs::path> rglob(const std::string &pathname) static inline
{ std::vector<fs::path> rglob(const std::string &pathname) {
return glob(pathname, true); return glob(pathname, true);
} }
static inline std::vector<fs::path> glob( static inline
const std::string &pathname, const std::filesystem::path root_directory) std::vector<fs::path> glob(const std::vector<std::string> &pathnames) {
{ std::vector<fs::path> result;
return glob(pathname, true, false, root_directory);
}
static inline std::vector<std::filesystem::path> glob(
const std::vector<std::string> &pathnames)
{
std::vector<std::filesystem::path> result;
for (auto &pathname : pathnames) { for (auto &pathname : pathnames) {
for (auto &match : glob(pathname, false)) { for (auto &match : glob(pathname, false)) {
result.push_back(std::move(match)); result.push_back(std::move(match));
@@ -433,10 +395,9 @@ static inline std::vector<std::filesystem::path> glob(
return result; return result;
} }
static inline std::vector<std::filesystem::path> rglob( static inline
const std::vector<std::string> &pathnames) std::vector<fs::path> rglob(const std::vector<std::string> &pathnames) {
{ std::vector<fs::path> result;
std::vector<std::filesystem::path> result;
for (auto &pathname : pathnames) { for (auto &pathname : pathnames) {
for (auto &match : glob(pathname, true)) { for (auto &match : glob(pathname, true)) {
result.push_back(std::move(match)); result.push_back(std::move(match));
@@ -445,15 +406,15 @@ static inline std::vector<std::filesystem::path> rglob(
return result; return result;
} }
static inline std::vector<std::filesystem::path> glob( static inline
const std::initializer_list<std::string> &pathnames) std::vector<fs::path>
{ glob(const std::initializer_list<std::string> &pathnames) {
return glob(std::vector<std::string>(pathnames)); return glob(std::vector<std::string>(pathnames));
} }
static inline std::vector<std::filesystem::path> rglob( static inline
const std::initializer_list<std::string> &pathnames) std::vector<fs::path>
{ rglob(const std::initializer_list<std::string> &pathnames) {
return rglob(std::vector<std::string>(pathnames)); return rglob(std::vector<std::string>(pathnames));
} }