initial basic glob matching support in filesystem.hh

master
Daniel Kolesa 2017-06-10 17:47:26 +02:00
parent a414ec7544
commit 75b70e7227
1 changed files with 110 additions and 0 deletions

View File

@ -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 */