initial basic glob matching support in filesystem.hh
parent
a414ec7544
commit
75b70e7227
|
@ -14,6 +14,9 @@
|
|||
* It also provides range integration for directory iterators and
|
||||
* ostd::format_traits specialization for std::filesystem::path.
|
||||
*
|
||||
* Additionally, it implements glob matching following POSIX with its
|
||||
* own extensions (mainly recursive glob matching via `**`).
|
||||
*
|
||||
* @include listdir.cc
|
||||
*
|
||||
* @copyright See COPYING.md in the project tree for further information.
|
||||
|
@ -22,6 +25,8 @@
|
|||
#ifndef OSTD_FILESYSTEM_HH
|
||||
#define OSTD_FILESYSTEM_HH
|
||||
|
||||
#include <utility>
|
||||
|
||||
#if __has_include(<filesystem>)
|
||||
# include <filesystem>
|
||||
namespace ostd {
|
||||
|
@ -102,6 +107,111 @@ struct format_traits<filesystem::path> {
|
|||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
inline bool glob_matches_filename(
|
||||
typename filesystem::path::value_type const *fname,
|
||||
typename filesystem::path::value_type const *wname
|
||||
) {
|
||||
/* skip any matching prefix if present */
|
||||
while (*wname && (*wname != '*')) {
|
||||
/* wildcard/other escapes */
|
||||
if ((*wname == '\\') && !*++wname) {
|
||||
break;
|
||||
}
|
||||
if (!*fname || (*fname++ != *wname++)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/* skip wildcards; a wildcard matches 0 or more */
|
||||
if (*wname == '*') {
|
||||
while (*wname == '*') {
|
||||
++wname;
|
||||
}
|
||||
/* was trailing so everything matches */
|
||||
if (!*wname) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
/* prefix skipping matched entire filename */
|
||||
if (!*fname) {
|
||||
return true;
|
||||
}
|
||||
/* keep incrementing filename until it matches */
|
||||
while (*fname) {
|
||||
if (glob_matches_filename(fname, wname)) {
|
||||
return true;
|
||||
}
|
||||
++fname;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename OR>
|
||||
inline void glob_match_impl(
|
||||
OR &out,
|
||||
typename filesystem::path::iterator beg,
|
||||
typename filesystem::path::iterator &end,
|
||||
filesystem::path pre
|
||||
) {
|
||||
while (beg != end) {
|
||||
auto cur = *beg;
|
||||
auto *cs = cur.c_str();
|
||||
/* this part of the path might contain wildcards */
|
||||
for (auto c = *cs; c; c = *++cs) {
|
||||
/* escape sequences for wildcards */
|
||||
if (c == '\\') {
|
||||
if (!*++cs) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* either * or ** */
|
||||
if (c == '*') {
|
||||
++beg;
|
||||
auto ip = pre;
|
||||
if (ip.empty()) {
|
||||
ip = ".";
|
||||
}
|
||||
if (*(cs + 1) == '*') {
|
||||
filesystem::recursive_directory_iterator it{ip};
|
||||
for (auto &de: it) {
|
||||
auto p = de.path();
|
||||
if (
|
||||
!glob_matches_filename(p.c_str(), cur.c_str())
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
glob_match_impl(out, beg, end, p);
|
||||
}
|
||||
} else {
|
||||
filesystem::directory_iterator it{ip};
|
||||
for (auto &de: it) {
|
||||
auto p = de.path();
|
||||
if (
|
||||
!glob_matches_filename(p.c_str(), cur.c_str())
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
glob_match_impl(out, beg, end, p);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
pre /= cur;
|
||||
++beg;
|
||||
}
|
||||
out.put(pre);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename OR>
|
||||
inline OR &&glob_match(OR &&out, filesystem::path const &path) {
|
||||
auto pend = path.end();
|
||||
detail::glob_match_impl(out, path.begin(), pend, filesystem::path{});
|
||||
return std::forward<OR>(out);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
||||
} /* namespace ostd */
|
||||
|
|
Loading…
Reference in New Issue