aboutsummaryrefslogtreecommitdiff
path: root/butl/path.ixx
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2016-07-26 15:12:54 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2016-07-28 13:51:29 +0200
commitb6f166c4ed98f94bdd2cc82885d61173a101abfd (patch)
treec6b75cf2efc98624760050173219e977f8620608 /butl/path.ixx
parent098559ca3552ebd8f80a6d28254f4fa58913b751 (diff)
Redesign path to store trailing slash for directories
Diffstat (limited to 'butl/path.ixx')
-rw-r--r--butl/path.ixx352
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.
+ }
}