diff --git a/ostd/path.hh b/ostd/path.hh index 18ee659..5f93485 100644 --- a/ostd/path.hh +++ b/ostd/path.hh @@ -23,6 +23,8 @@ #include +#include +#include #include #include #include @@ -721,6 +723,9 @@ private: ostd::path p_path{}; }; +namespace detail { +} + struct directory_range: input_range { using range_category = input_range_tag; using value_type = directory_entry; @@ -732,7 +737,7 @@ struct directory_range: input_range { 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_handle(r.p_handle), p_owned(false) {} @@ -761,9 +766,9 @@ struct directory_range: input_range { } private: - void open(path const &p); - void close() noexcept; - void read_next(); + OSTD_EXPORT void open(path const &p); + OSTD_EXPORT void close() noexcept; + OSTD_EXPORT void read_next(); directory_entry p_current{}; path p_dir{}; @@ -771,6 +776,61 @@ private: bool p_owned = false; }; +struct recursive_directory_range: input_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>; + + 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 home(); diff --git a/src/posix/path.cc b/src/posix/path.cc index 8721476..4b4e839 100644 --- a/src/posix/path.cc +++ b/src/posix/path.cc @@ -5,13 +5,39 @@ #include #include +#include #include "ostd/path.hh" namespace ostd { 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(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()); if (!d) { /* FIXME: throw */ @@ -23,7 +49,7 @@ void directory_range::open(path const &p) { read_next(); } -void directory_range::close() noexcept { +OSTD_EXPORT void directory_range::close() noexcept { if (!p_owned) { return; } @@ -31,27 +57,78 @@ void directory_range::close() noexcept { p_owned = false; } -void directory_range::read_next() { - struct dirent d; - struct dirent *o; - DIR *dh = static_cast(p_handle); - for (;;) { - if (readdir_r(dh, &d, &o)) { +OSTD_EXPORT void directory_range::read_next() { + dir_read_next(static_cast(p_handle), p_current, p_dir); +} + +/* recursive dir range */ + +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(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(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 */ abort(); } - if (!o) { - p_current.clear(); + if (S_ISDIR(sb.st_mode)) { + /* 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; } - string_range nm{static_cast(o->d_name)}; - if ((nm == ".") || (nm == "..")) { - continue; - } - path p{p_dir}; - p.append(nm); - p_current.assign(std::move(p)); - break; + } + /* empty result - done with a dir, pop */ + closedir(static_cast(p_stack->top())); + p_stack->pop(); + if (!p_stack->empty()) { + p_current.assign(p_dir); + p_dir.remove_name(); + } else { + p_current.clear(); } }