diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2016-07-26 15:12:54 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2016-07-28 13:51:29 +0200 |
commit | b6f166c4ed98f94bdd2cc82885d61173a101abfd (patch) | |
tree | c6b75cf2efc98624760050173219e977f8620608 /butl/path.ixx | |
parent | 098559ca3552ebd8f80a6d28254f4fa58913b751 (diff) |
Redesign path to store trailing slash for directories
Diffstat (limited to 'butl/path.ixx')
-rw-r--r-- | butl/path.ixx | 352 |
1 files changed, 273 insertions, 79 deletions
diff --git a/butl/path.ixx b/butl/path.ixx index 48d6576..3d1f20c 100644 --- a/butl/path.ixx +++ b/butl/path.ixx @@ -25,41 +25,57 @@ namespace butl } #endif - // @@ Should only enable_if P is basic_path<C, K1>. - // + template <class C, class K1, class K2> + inline basic_path<C, K1> + path_cast_impl (const basic_path<C, K2>& p, basic_path<C, K1>*) + { + typename basic_path<C, K1>::data_type d ( + typename basic_path<C, K1>::string_type (p.path_), p.diff_); + K1::cast (d); + return basic_path<C, K1> (std::move (d)); + } + + template <class C, class K1, class K2> + inline basic_path<C, K1> + path_cast_impl (basic_path<C, K2>&& p, basic_path<C, K1>*) + { + typename basic_path<C, K1>::data_type d (std::move (p.path_), p.diff_); + K1::cast (d); + return basic_path<C, K1> (std::move (d)); + } + template <class P, class C, class K> inline P path_cast (const basic_path<C, K>& p) { - return P (p.path_, false); + return path_cast_impl (p, static_cast<P*> (nullptr)); } template <class P, class C, class K> inline P path_cast (basic_path<C, K>&& p) { - return P (std::move (p.path_), false); + return path_cast_impl (std::move (p), static_cast<P*> (nullptr)); } template <typename C, typename K> inline bool basic_path<C, K>:: simple () const { - return -#ifndef _WIN32 - root () || -#endif - traits::find_separator (this->path_) == string_type::npos; + return empty () || + traits::rfind_separator (this->path_, _size () - 1) == string_type::npos; } template <typename C, typename K> inline bool basic_path<C, K>:: absolute () const { + const string_type& s (this->path_); + #ifdef _WIN32 - return this->path_.size () > 1 && this->path_[1] == ':'; + return s.size () > 1 && s[1] == ':'; #else - return !this->path_.empty () && traits::is_separator (this->path_[0]); + return s.size () != 0 && traits::is_separator (s[0]); #endif } @@ -67,10 +83,12 @@ namespace butl inline bool basic_path<C, K>:: root () const { + const string_type& s (this->path_); + #ifdef _WIN32 - return this->path_.size () == 2 && this->path_[1] == ':'; + return s.size () == 2 && s[1] == ':'; #else - return this->path_.size () == 1 && traits::is_separator (this->path_[0]); + return s.size () == 1 && traits::is_separator (s[0]); #endif } @@ -78,95 +96,116 @@ namespace butl inline bool basic_path<C, K>:: sub (const basic_path& p) const { - size_type n (p.path_.size ()); + // The thinking here is that we can use the full string representations + // (including the trailing slash in "/"). + // + const string_type& ps (p.path_); + size_type pn (ps.size ()); - if (n == 0) + if (pn == 0) return true; - size_type m (this->path_.size ()); + const string_type& s (this->path_); + size_type n (s.size ()); // The second condition guards against the /foo-bar vs /foo case. // - return m >= n && - traits::compare (this->path_.c_str (), n, p.path_.c_str (), n) == 0 && - (traits::is_separator (p.path_.back ()) || // p ends with a separator - m == n || // *this == p - traits::is_separator (this->path_[n])); // next char is a separator + return n >= pn && + traits::compare (s.c_str (), pn, ps.c_str (), pn) == 0 && + (traits::is_separator (ps.back ()) || // p ends with a separator + n == pn || // *this == p + traits::is_separator (s[pn])); // next char is a separator } template <typename C, typename K> inline bool basic_path<C, K>:: sup (const basic_path& p) const { - size_type n (p.path_.size ()); + // The thinking here is that we can use the full string representations + // (including the trailing slash in "/"). + // + const string_type& ps (p.path_); + size_type pn (ps.size ()); - if (n == 0) + if (pn == 0) return true; - size_type m (this->path_.size ()); + const string_type& s (this->path_); + size_type n (s.size ()); // The second condition guards against the /foo-bar vs bar case. // - return m >= n && - traits::compare ( - this->path_.c_str () + m - n, n, p.path_.c_str (), n) == 0 && - (m == n || // *this == p - traits::is_separator (this->path_[m - n - 1])); // prev char separator + return n >= pn && + traits::compare (s.c_str () + n - pn, pn, ps.c_str (), pn) == 0 && + (n == pn || // *this == p + traits::is_separator (s[n - pn - 1])); // previous char is a separator + } + + template <typename C, typename K> + inline basic_path<C, K> basic_path<C, K>:: + leaf () const + { + const string_type& s (this->path_); + size_type n (_size ()); + + size_type p (n != 0 + ? traits::rfind_separator (s, n - 1) + : string_type::npos); + + return p != string_type::npos + ? basic_path (data_type (string_type (s, p + 1), this->diff_)) + : *this; + } + + template <typename C, typename K> + inline typename basic_path<C, K>::dir_type basic_path<C, K>:: + directory () const + { + const string_type& s (this->path_); + size_type n (_size ()); + + size_type p (n != 0 + ? traits::rfind_separator (s, n - 1) + : string_type::npos); + + return p != string_type::npos + ? dir_type (data_type (string_type (s, 0, p + 1))) // Include slash. + : dir_type (); } template <typename C, typename K> inline auto basic_path<C, K>:: begin () const -> iterator { - size_type b, e; + const string_type& s (this->path_); - if (this->path_.empty ()) - b = e = string_type::npos; + size_type b (s.empty () ? string_type::npos : 0); + size_type e (b == 0 ? traits::find_separator (s) : b); -#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); + return iterator (this, b, e); } template <typename C, typename K> inline auto basic_path<C, K>:: end () const -> iterator { - return iterator (this->path_, string_type::npos, string_type::npos); + return iterator (this, string_type::npos, string_type::npos); } template <typename C, typename K> inline basic_path<C, K>:: basic_path (const iterator& b, const iterator& e) + : base_type ( + b == e + ? data_type () + // We need to include the trailing separator but it is implied if + // e == end(). + // + : (e.b_ != string_type::npos + ? data_type (string_type (b.p_->path_, b.b_, e.b_ - b.b_)) + : data_type (string_type (b.p_->path_, b.b_), b.p_->diff_))) { //assert (b.p_ == e.p_); - - if (b != e) - { - this->path_.assign ( - *b.p_, b.b_, (e.b_ != string_type::npos ? e.b_ - b.b_ - 1 : e.b_)); - -#ifndef _WIN32 - if (this->path_.empty ()) - this->path_ = '/'; -#endif - - // No init() should be necessary. - } } template <typename C, typename K> @@ -187,7 +226,7 @@ namespace butl complete (); normalize (); #else - traits::realize (this->path_); + traits::realize (this->path_); // Note: we retail trailing slash. #endif return *this; } @@ -196,24 +235,34 @@ namespace butl inline typename basic_path<C, K>::dir_type basic_path<C, K>:: root_directory () const { - return absolute () #ifdef _WIN32 - // Disambiguate with dir_type(string_type, bool). - // - ? dir_type (this->path_, static_cast<size_type> (2)) + // Note: on Windows we may have "c:" but still need to return "c:\". + // + const string_type& s (this->path_); + + return absolute () + ? dir_type ( + s.size () > 2 + ? data_type (string_type (s, 0, 3)) + : data_type (string_type (s), this->diff_ != 0 ? this->diff_ : 1)) + : dir_type (); #else - ? dir_type ("/") -#endif + return absolute () + ? dir_type (data_type ("/", -1)) : dir_type (); +#endif + } template <typename C, typename K> inline basic_path<C, K> basic_path<C, K>:: base () const { - size_type p (traits::find_extension (this->path_)); + const string_type& s (this->path_); + size_type p (traits::find_extension (s)); + return p != string_type::npos - ? basic_path (this->path_.c_str (), p) + ? basic_path (data_type (string_type (s, 0, p), this->diff_)) : *this; } @@ -221,8 +270,9 @@ namespace butl inline const C* basic_path<C, K>:: extension () const { - size_type p (traits::find_extension (this->path_)); - return p != string_type::npos ? this->path_.c_str () + p + 1 : nullptr; + const string_type& s (this->path_); + size_type p (traits::find_extension (s)); + return p != string_type::npos ? s.c_str () + p + 1 : nullptr; } #ifndef _WIN32 @@ -236,16 +286,160 @@ namespace butl template <typename C, typename K> inline void basic_path<C, K>:: - combine (const C* r, size_type rn) + combine (const C* r, size_type rn, difference_type rd) { - size_type ln (this->path_.size ()); + //assert (rn != 0); - if (ln != 0 && rn != 0) + string_type& l (this->path_); + difference_type& d (this->diff_); + + // Handle the separator. LHS should be empty or already have one. + // + switch (d) { - if (!traits::is_separator (this->path_[ln - 1])) - this->path_ += traits::directory_separator; + case 0: if (!l.empty ()) throw invalid_basic_path<C> (l); break; + case -1: break; // Already in the string. + default: l += path_traits<C>::directory_separators[d - 1]; } + l.append (r, rn); + d = rd; // New trailing separator from RHS. + } + + template <typename C, typename K> + inline void basic_path<C, K>:: + combine (const C* r, size_type rn) + { + // If we do (dir_path / path) then we will end up with path. What should + // we end up if we do (dir_path / "foo") vs (dir_path / "foo/")? We cannot + // choose at runtime what kind of path to return. One (elaborate) option + // would be to handle the trailing slash but also call K::cast() so that + // dir_path gets the canonical trailing slash if one wasn't there. + // + // For now we won't allow the slash and will always add the canonical one + // for dir_path (via cast()). + // + if (traits::find_separator (r, rn) != nullptr) + throw invalid_basic_path<C> (r); + + combine (r, rn, 0); + K::cast (*this); + } + + template <typename C, typename K> + inline basic_path<C, K>& basic_path<C, K>:: + operator/= (basic_path<C, K> const& r) + { + if (r.absolute () && !empty ()) // Allow ('' / '/foo'). + throw invalid_basic_path<C> (r.path_); + + if (!r.empty ()) + combine (r.path_.c_str (), r.path_.size (), r.diff_); + + return *this; + } + + template <typename C, typename K> + inline basic_path<C, K>& basic_path<C, K>:: + operator/= (string_type const& r) + { + if (size_type rn = r.size ()) + combine (r.c_str (), rn); + + return *this; + } + + template <typename C, typename K> + inline basic_path<C, K>& basic_path<C, K>:: + operator/= (const C* r) + { + if (size_type rn = string_type::traits_type::length (r)) + combine (r, rn); + + return *this; + } + + template <typename C, typename K> + inline void basic_path<C, K>:: + append (const C* r, size_type rn) + { + //assert (this->diff_ != -1); // Append to root? this->path_.append (r, rn); } + + template <typename C, typename K> + inline basic_path<C, K>& basic_path<C, K>:: + operator+= (string_type const& s) + { + append (s.c_str (), s.size ()); + return *this; + } + + template <typename C, typename K> + inline basic_path<C, K>& basic_path<C, K>:: + operator+= (const C* s) + { + append (s, string_type::traits_type::length (s)); + return *this; + } + + template <typename C, typename K> + inline basic_path<C, K>& basic_path<C, K>:: + operator+= (C c) + { + append (&c, 1); + return *this; + } + + template <typename C, typename K> + inline auto basic_path<C, K>:: + representation () const& -> string_type + { + string_type r (this->path_); + + if (this->diff_ > 0) + r += path_traits<C>::directory_separators[this->diff_ - 1]; + + return r; + } + + template <typename C, typename K> + inline auto basic_path<C, K>:: + representation () && -> string_type + { + string_type r; + r.swap (this->path_); + + if (this->diff_ > 0) + r += path_traits<C>::directory_separators[this->diff_ - 1]; + + return r; + } + + template <typename C, typename K> + inline C basic_path<C, K>:: + separator () const + { + return (this->diff_ == 0 ? 0 : + this->diff_ == -1 ? this->path_[0] : + path_traits<C>::directory_separators[this->diff_ - 1]); + } + + template <typename C, typename K> + inline auto basic_path<C, K>:: + separator_string () const -> string_type + { + C c (separator ()); + return c == 0 ? string_type () : string_type (1, c); + } + + template <typename C> + inline void dir_path_kind<C>:: + cast (data_type& d) + { + // Add trailing slash if one isn't already there. + // + if (!d.path_.empty () && d.diff_ == 0) + d.diff_ = 1; // Canonical separator is always first. + } } |