move glob matching impl into a source file

master
Daniel Kolesa 2017-06-16 18:54:59 +02:00
parent 25c97d9c8f
commit 3034918496
3 changed files with 213 additions and 189 deletions

View File

@ -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>;

View File

@ -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);
}

199
src/filesystem.cc 100644
View File

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