From 56ce5687567150b0b2cc3e57540d564793ef6bf7 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 28 Apr 2015 16:13:54 +0200 Subject: Add support for iteration over path components --- build/path | 59 +++++++++++++++++++++++++++++++++++++++++++++ build/path.ixx | 35 +++++++++++++++++++++++++++ tests/build/path/driver.cxx | 34 ++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) diff --git a/build/path b/build/path index d153ed7..4c34a58 100644 --- a/build/path +++ b/build/path @@ -6,8 +6,10 @@ #define BUILD_PATH #include +#include // ptrdiff_t #include #include // move +#include #include #include // hash @@ -352,6 +354,63 @@ namespace build basic_path relative (basic_path) const; + // Iteration over path components. + // + public: + struct iterator + { + typedef string_type value_type; + typedef string_type* pointer; + typedef string_type& reference; + typedef std::ptrdiff_t difference_type; + typedef std::forward_iterator_tag iterator_category; + + typedef typename string_type::size_type size_type; + + iterator (): p_ (nullptr) {} + iterator (const string_type& p, size_type b, size_type e) + : p_ (&p), b_ (b), e_ (e) {} + + iterator& + operator++ () + { + b_ = e_; + + if (b_ != string_type::npos) + e_ = traits::find_separator (*p_, ++b_); + + return *this; + } + + iterator + operator++ (int) {iterator r (*this); return ++r;} + + string_type operator* () const + { + return string_type (*p_, b_, (e_ != string_type::npos ? e_ - b_ : e_)); + } + + friend bool + operator== (const iterator& x, const iterator& y) + { + return x.p_ == y.p_ && x.b_ == y.b_ && x.e_ == y.e_; + } + + friend bool + operator!= (const iterator& x, const iterator& y) {return !(x == y);} + + private: + // b != npos && e == npos - last component + // b == npos && e == npos - one past last component (end) + // + const string_type* p_; + size_type b_; + size_type e_; + }; + + iterator begin () const; + iterator end () const; + public: // Normalize the path. This includes collapsing the '.' and '..' // directories if possible, collapsing multiple directory diff --git a/build/path.ixx b/build/path.ixx index ff1ec32..8ee6a33 100644 --- a/build/path.ixx +++ b/build/path.ixx @@ -85,6 +85,41 @@ namespace build } template + inline auto basic_path:: + begin () const -> iterator + { + size_type b, e; + + if (this->path_.empty ()) + b = e = string_type::npos; + +#ifndef _WIN32 + else if (root ()) + { + // We want to return a single empty component. Here we return + // the begin position one past the end. Not sure if this legal. + // + b = 1; + e = string_type::npos; + } +#endif + else + { + b = 0; + e = traits::find_separator (this->path_); + } + + return iterator (this->path_, b, e); + } + + template + inline auto basic_path:: + end () const -> iterator + { + return iterator (this->path_, string_type::npos, string_type::npos); + } + + template inline basic_path& basic_path:: complete () { diff --git a/tests/build/path/driver.cxx b/tests/build/path/driver.cxx index 739146b..3aa624c 100644 --- a/tests/build/path/driver.cxx +++ b/tests/build/path/driver.cxx @@ -77,6 +77,40 @@ main () assert (path ("C:\\foo.txt").base ().string () == "C:\\foo"); #endif + // iteration + // + { + path p; + assert (p.begin () == p.end ()); + } + { + path p ("foo"); + path::iterator i (p.begin ()); + assert (i != p.end () && *i == "foo"); + assert (++i == p.end ()); + } + { + path p ("foo/bar"); + path::iterator i (p.begin ()); + assert (i != p.end () && *i == "foo"); + assert (++i != p.end () && *i == "bar"); + assert (++i == p.end ()); + } + { + path p ("/foo/bar"); + path::iterator i (p.begin ()); + assert (i != p.end () && *i == ""); + assert (++i != p.end () && *i == "foo"); + assert (++i != p.end () && *i == "bar"); + assert (++i == p.end ()); + } + { + path p ("/"); + path::iterator i (p.begin ()); + assert (i != p.end () && *i == ""); + assert (++i == p.end ()); + } + // operator/ // #ifndef _WIN32 -- cgit v1.1