move glob matching impl into a source file
parent
25c97d9c8f
commit
3034918496
1
build.cc
1
build.cc
|
@ -22,6 +22,7 @@ namespace fs = ostd::filesystem;
|
|||
/* ugly, but do not explicitly compile */
|
||||
#include "src/io.cc"
|
||||
#include "src/process.cc"
|
||||
#include "src/filesystem.cc"
|
||||
|
||||
using strvec = std::vector<std::string>;
|
||||
using pathvec = std::vector<fs::path>;
|
||||
|
|
|
@ -26,8 +26,6 @@
|
|||
#ifndef OSTD_FILESYSTEM_HH
|
||||
#define OSTD_FILESYSTEM_HH
|
||||
|
||||
#include <utility>
|
||||
|
||||
#if __has_include(<filesystem>)
|
||||
# include <filesystem>
|
||||
namespace ostd {
|
||||
|
@ -42,6 +40,7 @@ namespace ostd {
|
|||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
#include "ostd/platform.hh"
|
||||
#include "ostd/range.hh"
|
||||
#include "ostd/format.hh"
|
||||
|
||||
|
@ -109,117 +108,17 @@ struct format_traits<filesystem::path> {
|
|||
};
|
||||
|
||||
namespace detail {
|
||||
inline typename filesystem::path::value_type const *glob_match_brackets(
|
||||
typename filesystem::path::value_type match,
|
||||
typename filesystem::path::value_type const *wp
|
||||
) noexcept {
|
||||
bool neg = (*wp == '!');
|
||||
if (neg) {
|
||||
++wp;
|
||||
}
|
||||
|
||||
/* grab the first character as it can be ] */
|
||||
auto c = *wp++;
|
||||
if (!c) {
|
||||
/* unterminated */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* make sure it's terminated somewhere */
|
||||
auto *eb = wp;
|
||||
for (; *eb != ']'; ++eb) {
|
||||
if (!*eb) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
++eb;
|
||||
|
||||
/* no need to worry about \0 from now on */
|
||||
do {
|
||||
/* character range */
|
||||
if ((*wp == '-') && (*(wp + 1) != ']')) {
|
||||
auto lc = *(wp + 1);
|
||||
wp += 2;
|
||||
bool within = ((match >= c) && (match <= lc));
|
||||
if (within) {
|
||||
return neg ? nullptr : eb;
|
||||
}
|
||||
c = *wp++;
|
||||
continue;
|
||||
}
|
||||
/* single-char match */
|
||||
if (match == c) {
|
||||
return neg ? nullptr : eb;
|
||||
}
|
||||
c = *wp++;
|
||||
} while (c != ']');
|
||||
|
||||
/* loop ended, so no match */
|
||||
return neg ? eb : nullptr;
|
||||
}
|
||||
|
||||
inline bool glob_match_filename_impl(
|
||||
OSTD_EXPORT bool glob_match_filename_impl(
|
||||
typename filesystem::path::value_type const *fname,
|
||||
typename filesystem::path::value_type const *wname
|
||||
) noexcept {
|
||||
/* skip any matching prefix if present */
|
||||
while (*wname && (*wname != '*')) {
|
||||
if (!*wname || (*wname == '*')) {
|
||||
break;
|
||||
}
|
||||
if (*fname) {
|
||||
/* ? wildcard matches any character */
|
||||
if (*wname == '?') {
|
||||
++wname;
|
||||
++fname;
|
||||
continue;
|
||||
}
|
||||
/* [...] wildcard */
|
||||
if (*wname == '[') {
|
||||
wname = glob_match_brackets(*fname, wname + 1);
|
||||
if (!wname) {
|
||||
return false;
|
||||
}
|
||||
++fname;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((*wname == '?') && *fname) {
|
||||
++wname;
|
||||
++fname;
|
||||
continue;
|
||||
}
|
||||
if (*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;
|
||||
}
|
||||
/* empty pattern and non-empty filename */
|
||||
if (!*wname) {
|
||||
return false;
|
||||
}
|
||||
/* keep incrementing filename until it matches */
|
||||
while (*fname) {
|
||||
if (glob_match_filename_impl(fname, wname)) {
|
||||
return true;
|
||||
}
|
||||
++fname;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
) noexcept;
|
||||
|
||||
OSTD_EXPORT void glob_match_impl(
|
||||
void (*out)(filesystem::path const &, void *),
|
||||
typename filesystem::path::iterator beg,
|
||||
typename filesystem::path::iterator &end,
|
||||
filesystem::path pre, void *data
|
||||
);
|
||||
} /* namespace detail */
|
||||
|
||||
/** @brief Checks if the given path matches the given glob pattern.
|
||||
|
@ -259,83 +158,6 @@ inline bool glob_match_filename(
|
|||
return detail::glob_match_filename_impl(filename.c_str(), pattern.c_str());
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
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) {
|
||||
/* ** as a name does recursive expansion */
|
||||
if ((c == '*') && (*(cs + 1) == '*') && !*(cs + 2)) {
|
||||
++beg;
|
||||
auto ip = pre.empty() ? "." : pre;
|
||||
if (!filesystem::is_directory(ip)) {
|
||||
return;
|
||||
}
|
||||
filesystem::recursive_directory_iterator it{ip};
|
||||
/* include "current" dir in the match */
|
||||
if (beg != end) {
|
||||
glob_match_impl(out, beg, end, pre);
|
||||
}
|
||||
for (auto &de: it) {
|
||||
/* followed by more path, only consider dirs */
|
||||
auto dp = de.path();
|
||||
if ((beg != end) && !filesystem::is_directory(dp)) {
|
||||
continue;
|
||||
}
|
||||
/* otherwise also match files */
|
||||
if (pre.empty()) {
|
||||
/* avoid ugly formatting, sadly we have to do
|
||||
* with experimental fs api so no better way...
|
||||
*/
|
||||
auto dpb = dp.begin(), dpe = dp.end();
|
||||
if (*dpb == ".") {
|
||||
++dpb;
|
||||
}
|
||||
filesystem::path ap;
|
||||
while (dpb != dpe) {
|
||||
ap /= *dpb;
|
||||
++dpb;
|
||||
}
|
||||
glob_match_impl(out, beg, end, ap);
|
||||
} else {
|
||||
glob_match_impl(out, beg, end, dp);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* wildcards *, ?, [...] */
|
||||
if ((c == '*') || (c == '?') || (c == '[')) {
|
||||
++beg;
|
||||
auto ip = pre.empty() ? "." : pre;
|
||||
if (!filesystem::is_directory(ip)) {
|
||||
return;
|
||||
}
|
||||
filesystem::directory_iterator it{ip};
|
||||
for (auto &de: it) {
|
||||
auto p = de.path().filename();
|
||||
if (!glob_match_filename(p, cur)) {
|
||||
continue;
|
||||
}
|
||||
glob_match_impl(out, beg, end, pre / p);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
pre /= cur;
|
||||
++beg;
|
||||
}
|
||||
out.put(pre);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Expands a path with glob patterns.
|
||||
*
|
||||
* Individual expanded paths are put in `out` and are of the standard
|
||||
|
@ -367,7 +189,9 @@ inline OutputRange &&glob_match(
|
|||
OutputRange &&out, filesystem::path const &path
|
||||
) {
|
||||
auto pend = path.end();
|
||||
detail::glob_match_impl(out, path.begin(), pend, filesystem::path{});
|
||||
detail::glob_match_impl([](filesystem::path const &p, void *outp) {
|
||||
static_cast<std::remove_reference_t<OutputRange> *>(outp)->put(p);
|
||||
}, path.begin(), pend, filesystem::path{}, &out);
|
||||
return std::forward<OutputRange>(out);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
/* Filesystem implementation bits.
|
||||
*
|
||||
* This file is part of libostd. See COPYING.md for futher information.
|
||||
*/
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "ostd/filesystem.hh"
|
||||
|
||||
namespace ostd {
|
||||
namespace detail {
|
||||
|
||||
inline typename filesystem::path::value_type const *glob_match_brackets(
|
||||
typename filesystem::path::value_type match,
|
||||
typename filesystem::path::value_type const *wp
|
||||
) noexcept {
|
||||
bool neg = (*wp == '!');
|
||||
if (neg) {
|
||||
++wp;
|
||||
}
|
||||
|
||||
/* grab the first character as it can be ] */
|
||||
auto c = *wp++;
|
||||
if (!c) {
|
||||
/* unterminated */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* make sure it's terminated somewhere */
|
||||
auto *eb = wp;
|
||||
for (; *eb != ']'; ++eb) {
|
||||
if (!*eb) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
++eb;
|
||||
|
||||
/* no need to worry about \0 from now on */
|
||||
do {
|
||||
/* character range */
|
||||
if ((*wp == '-') && (*(wp + 1) != ']')) {
|
||||
auto lc = *(wp + 1);
|
||||
wp += 2;
|
||||
if ((match >= c) && (match <= lc)) {
|
||||
return neg ? nullptr : eb;
|
||||
}
|
||||
c = *wp++;
|
||||
continue;
|
||||
}
|
||||
/* single-char match */
|
||||
if (match == c) {
|
||||
return neg ? nullptr : eb;
|
||||
}
|
||||
c = *wp++;
|
||||
} while (c != ']');
|
||||
|
||||
/* loop ended, so no match */
|
||||
return neg ? eb : nullptr;
|
||||
}
|
||||
|
||||
OSTD_EXPORT bool glob_match_filename_impl(
|
||||
typename filesystem::path::value_type const *fname,
|
||||
typename filesystem::path::value_type const *wname
|
||||
) noexcept {
|
||||
/* skip any matching prefix if present */
|
||||
while (*wname && (*wname != '*')) {
|
||||
if (!*wname || (*wname == '*')) {
|
||||
break;
|
||||
}
|
||||
if (*fname) {
|
||||
/* ? wildcard matches any character */
|
||||
if (*wname == '?') {
|
||||
++wname;
|
||||
++fname;
|
||||
continue;
|
||||
}
|
||||
/* [...] wildcard */
|
||||
if (*wname == '[') {
|
||||
wname = glob_match_brackets(*fname, wname + 1);
|
||||
if (!wname) {
|
||||
return false;
|
||||
}
|
||||
++fname;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if ((*wname == '?') && *fname) {
|
||||
++wname;
|
||||
++fname;
|
||||
continue;
|
||||
}
|
||||
if (*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;
|
||||
}
|
||||
/* empty pattern and non-empty filename */
|
||||
if (!*wname) {
|
||||
return false;
|
||||
}
|
||||
/* keep incrementing filename until it matches */
|
||||
while (*fname) {
|
||||
if (glob_match_filename_impl(fname, wname)) {
|
||||
return true;
|
||||
}
|
||||
++fname;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
OSTD_EXPORT void glob_match_impl(
|
||||
void (*out)(filesystem::path const &, void *),
|
||||
typename filesystem::path::iterator beg,
|
||||
typename filesystem::path::iterator &end,
|
||||
filesystem::path pre, void *data
|
||||
) {
|
||||
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) {
|
||||
/* ** as a name does recursive expansion */
|
||||
if ((c == '*') && (*(cs + 1) == '*') && !*(cs + 2)) {
|
||||
++beg;
|
||||
auto ip = pre.empty() ? "." : pre;
|
||||
if (!filesystem::is_directory(ip)) {
|
||||
return;
|
||||
}
|
||||
filesystem::recursive_directory_iterator it{ip};
|
||||
/* include "current" dir in the match */
|
||||
if (beg != end) {
|
||||
glob_match_impl(out, beg, end, pre, data);
|
||||
}
|
||||
for (auto &de: it) {
|
||||
/* followed by more path, only consider dirs */
|
||||
auto dp = de.path();
|
||||
if ((beg != end) && !filesystem::is_directory(dp)) {
|
||||
continue;
|
||||
}
|
||||
/* otherwise also match files */
|
||||
if (pre.empty()) {
|
||||
/* avoid ugly formatting, sadly we have to do
|
||||
* with experimental fs api so no better way...
|
||||
*/
|
||||
auto dpb = dp.begin(), dpe = dp.end();
|
||||
if (*dpb == ".") {
|
||||
++dpb;
|
||||
}
|
||||
filesystem::path ap;
|
||||
while (dpb != dpe) {
|
||||
ap /= *dpb;
|
||||
++dpb;
|
||||
}
|
||||
glob_match_impl(out, beg, end, ap, data);
|
||||
} else {
|
||||
glob_match_impl(out, beg, end, dp, data);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* wildcards *, ?, [...] */
|
||||
if ((c == '*') || (c == '?') || (c == '[')) {
|
||||
++beg;
|
||||
auto ip = pre.empty() ? "." : pre;
|
||||
if (!filesystem::is_directory(ip)) {
|
||||
return;
|
||||
}
|
||||
filesystem::directory_iterator it{ip};
|
||||
for (auto &de: it) {
|
||||
auto p = de.path().filename();
|
||||
if (!glob_match_filename(p, cur)) {
|
||||
continue;
|
||||
}
|
||||
glob_match_impl(out, beg, end, pre / p, data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
pre /= cur;
|
||||
++beg;
|
||||
}
|
||||
out(pre, data);
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
} /* namespace ostd */
|
Loading…
Reference in New Issue