add recursive directory range

master
Daniel Kolesa 2018-04-16 00:19:48 +02:00
parent 4ef512effa
commit ad635c8a23
2 changed files with 159 additions and 22 deletions

View File

@ -23,6 +23,8 @@
#include <string.h> #include <string.h>
#include <stack>
#include <list>
#include <utility> #include <utility>
#include <initializer_list> #include <initializer_list>
#include <type_traits> #include <type_traits>
@ -721,6 +723,9 @@ private:
ostd::path p_path{}; ostd::path p_path{};
}; };
namespace detail {
}
struct directory_range: input_range<directory_range> { struct directory_range: input_range<directory_range> {
using range_category = input_range_tag; using range_category = input_range_tag;
using value_type = directory_entry; using value_type = directory_entry;
@ -732,7 +737,7 @@ struct directory_range: input_range<directory_range> {
open(p); open(p);
} }
directory_range(directory_range const &r) noexcept: directory_range(directory_range const &r):
p_current(r.p_current), p_dir(r.p_dir), p_current(r.p_current), p_dir(r.p_dir),
p_handle(r.p_handle), p_owned(false) p_handle(r.p_handle), p_owned(false)
{} {}
@ -761,9 +766,9 @@ struct directory_range: input_range<directory_range> {
} }
private: private:
void open(path const &p); OSTD_EXPORT void open(path const &p);
void close() noexcept; OSTD_EXPORT void close() noexcept;
void read_next(); OSTD_EXPORT void read_next();
directory_entry p_current{}; directory_entry p_current{};
path p_dir{}; path p_dir{};
@ -771,6 +776,61 @@ private:
bool p_owned = false; bool p_owned = false;
}; };
struct recursive_directory_range: input_range<recursive_directory_range> {
using range_category = input_range_tag;
using value_type = directory_entry;
using reference = directory_entry const &;
using size_type = std::size_t;
recursive_directory_range() = delete;
recursive_directory_range(path const &p) {
open(p);
}
recursive_directory_range(recursive_directory_range const &r):
p_current(r.p_current), p_dir(r.p_dir),
p_stack(r.p_stack), p_owned(false)
{}
~recursive_directory_range() {
close();
}
recursive_directory_range &operator=(
recursive_directory_range const &r
) noexcept {
close();
p_stack = r.p_stack;
p_owned = false;
return *this;
}
bool empty() const noexcept {
return p_current.path().empty();
}
void pop_front() {
read_next();
}
reference front() const noexcept {
return p_current;
}
private:
using hstack = std::stack<void *, std::list<void *>>;
OSTD_EXPORT void open(path const &p);
OSTD_EXPORT void close() noexcept;
OSTD_EXPORT void read_next();
directory_entry p_current{};
path p_dir{};
hstack p_handles{};
hstack *p_stack = nullptr;
bool p_owned = false;
};
OSTD_EXPORT path cwd(); OSTD_EXPORT path cwd();
OSTD_EXPORT path home(); OSTD_EXPORT path home();

View File

@ -5,13 +5,39 @@
#include <cstdlib> #include <cstdlib>
#include <dirent.h> #include <dirent.h>
#include <sys/stat.h>
#include "ostd/path.hh" #include "ostd/path.hh"
namespace ostd { namespace ostd {
namespace fs { namespace fs {
void directory_range::open(path const &p) { static void dir_read_next(DIR *dh, directory_entry &cur, path const &base) {
struct dirent d;
struct dirent *o;
for (;;) {
if (readdir_r(dh, &d, &o)) {
/* FIXME: throw */
abort();
}
if (!o) {
cur.clear();
return;
}
string_range nm{static_cast<char const *>(o->d_name)};
if ((nm == ".") || (nm == "..")) {
continue;
}
path p{base};
p.append(nm);
cur.assign(std::move(p));
break;
}
}
/* dir range */
OSTD_EXPORT void directory_range::open(path const &p) {
DIR *d = opendir(p.string().data()); DIR *d = opendir(p.string().data());
if (!d) { if (!d) {
/* FIXME: throw */ /* FIXME: throw */
@ -23,7 +49,7 @@ void directory_range::open(path const &p) {
read_next(); read_next();
} }
void directory_range::close() noexcept { OSTD_EXPORT void directory_range::close() noexcept {
if (!p_owned) { if (!p_owned) {
return; return;
} }
@ -31,27 +57,78 @@ void directory_range::close() noexcept {
p_owned = false; p_owned = false;
} }
void directory_range::read_next() { OSTD_EXPORT void directory_range::read_next() {
struct dirent d; dir_read_next(static_cast<DIR *>(p_handle), p_current, p_dir);
struct dirent *o; }
DIR *dh = static_cast<DIR *>(p_handle);
for (;;) { /* recursive dir range */
if (readdir_r(dh, &d, &o)) {
OSTD_EXPORT void recursive_directory_range::open(path const &p) {
DIR *d = opendir(p.string().data());
if (!d) {
/* FIXME: throw */
abort();
}
p_dir = p;
p_stack = &p_handles;
p_stack->push(d);
p_owned = true;
read_next();
}
OSTD_EXPORT void recursive_directory_range::close() noexcept {
if (!p_owned) {
return;
}
while (!p_handles.empty()) {
closedir(static_cast<DIR *>(p_handles.top()));
p_handles.pop();
}
p_owned = false;
}
OSTD_EXPORT void recursive_directory_range::read_next() {
directory_entry curd;
if (p_stack->empty()) {
return;
}
dir_read_next(static_cast<DIR *>(p_stack->top()), curd, p_dir);
struct stat sb;
/* check if dir and recurse maybe */
while (!curd.path().empty()) {
if (stat(curd.path().string().data(), &sb) < 0) {
/* FIXME: throw */ /* FIXME: throw */
abort(); abort();
} }
if (!o) { if (S_ISDIR(sb.st_mode)) {
p_current.clear(); /* directory, recurse into it */
DIR *nd = opendir(curd.path().string().data());
if (!nd) {
abort();
}
p_dir = curd;
p_stack->push(nd);
dir_read_next(nd, curd, p_dir);
p_current = curd;
if (curd.path().empty()) {
break;
} else {
return;
}
} else {
/* not a dir, stick with it */
p_current = curd;
return; return;
} }
string_range nm{static_cast<char const *>(o->d_name)}; }
if ((nm == ".") || (nm == "..")) { /* empty result - done with a dir, pop */
continue; closedir(static_cast<DIR *>(p_stack->top()));
} p_stack->pop();
path p{p_dir}; if (!p_stack->empty()) {
p.append(nm); p_current.assign(p_dir);
p_current.assign(std::move(p)); p_dir.remove_name();
break; } else {
p_current.clear();
} }
} }