From b6f166c4ed98f94bdd2cc82885d61173a101abfd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 26 Jul 2016 15:12:54 +0200 Subject: Redesign path to store trailing slash for directories --- butl/path.txx | 263 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 136 insertions(+), 127 deletions(-) (limited to 'butl/path.txx') diff --git a/butl/path.txx b/butl/path.txx index 94fbd90..1d6995e 100644 --- a/butl/path.txx +++ b/butl/path.txx @@ -8,30 +8,51 @@ namespace butl { template basic_path basic_path:: - leaf () const + leaf (basic_path const& d) const { - size_type p (traits::rfind_separator (this->path_)); + size_type dn (d.path_.size ()); + + if (dn == 0) + return *this; + + const string_type& s (this->path_); - return p != string_type::npos - ? basic_path (this->path_.c_str () + p + 1, this->path_.size () - p - 1) - : *this; + if (!sub (d)) + throw invalid_basic_path (s); + + // If there is implied trailing slash, add it to count. Unless it is + // "matched" by the implied slash on the other side. + // + if (d.diff_ > 0 && dn < s.size ()) + dn++; + + // Preserve trailing slash. + // + return basic_path (data_type (string_type (s, dn, s.size () - dn), + this->diff_)); } template typename basic_path::dir_type basic_path:: - directory () const + directory (basic_path const& l) const { - if (root ()) - return dir_type (); + size_type ln (l.path_.size ()); - size_type p (traits::rfind_separator (this->path_)); + const string_type& s (this->path_); - // Include the trailing slash so that we get correct behavior - // if directory is root. - // - return p != string_type::npos - ? dir_type (this->path_.c_str (), p + 1) - : dir_type (); + if (ln == 0) + { + if (this->diff_ == 0) // Must be a directory. + throw invalid_basic_path (s); + + return dir_type (data_type (string_type (s), this->diff_)); + } + + if (!sup (l)) + throw invalid_basic_path (s); + + return dir_type ( + data_type (string_type (s, 0, s.size () - ln))); // Include slash. } #ifdef _WIN32 @@ -55,85 +76,6 @@ namespace butl #endif template - basic_path& basic_path:: - operator/= (basic_path const& r) - { - if (r.absolute () && !this->path_.empty ()) // Allow ('' / '/foo'). - throw invalid_basic_path (r.path_); - - combine (r.path_.c_str (), r.path_.size ()); - return *this; - } - - template - basic_path& basic_path:: - operator/= (string_type const& r) - { - if (traits::find_separator (r) != string_type::npos) - throw invalid_basic_path (r); - - combine (r.c_str (), r.size ()); - return *this; - } - - template - basic_path& basic_path:: - operator/= (const C* r) - { - size_type rn (string_type::traits_type::length (r)); - - if (traits::find_separator (r, rn) != nullptr) - throw invalid_basic_path (r); - - combine (r, rn); - return *this; - } - - template - basic_path basic_path:: - leaf (basic_path const& d) const - { - size_type n (d.path_.size ()); - - if (n == 0) - return *this; - - if (!sub (d)) - throw invalid_basic_path (this->path_); - - size_type m (this->path_.size ()); - - if (n != m -#ifndef _WIN32 - && !d.root () -#endif - ) - n++; // Skip the directory separator (unless it is POSIX root). - - return basic_path (this->path_.c_str () + n, m - n); - } - - template - typename basic_path::dir_type basic_path:: - directory (basic_path const& l) const - { - size_type n (l.path_.size ()); - - if (n == 0) - return dir_type (this->path_); - - if (!sup (l)) - throw invalid_basic_path (this->path_); - - size_type m (this->path_.size ()); - - if (n != m) - n++; // Skip the directory separator. - - return dir_type (this->path_.c_str (), m - n); - } - - template basic_path basic_path:: relative (basic_path d) const { @@ -144,7 +86,7 @@ namespace butl if (sub (d)) break; - r /= basic_path (".."); + r /= basic_path ("../"); // Roots of the paths do not match. // @@ -162,40 +104,61 @@ namespace butl if (empty ()) return *this; + string_type& s (this->path_); + difference_type& d (this->diff_); + bool abs (absolute ()); typedef std::vector paths; paths ps; - for (size_type b (0), e (traits::find_separator (this->path_)), - n (this->path_.size ());; - e = traits::find_separator (this->path_, b)) + bool tsep (d != 0); // Trailing directory separator. { - string_type s (this->path_, b, e == string_type::npos ? e : e - b); - ps.push_back (s); + size_type n (_size ()); - if (e == string_type::npos) - break; + for (size_type b (0), e (traits::find_separator (s, 0, n)); + ; + e = traits::find_separator (s, b, n)) + { + ps.push_back ( + string_type (s, b, (e == string_type::npos ? n : e) - b)); - ++e; + if (e == string_type::npos) + break; - while (e < n && traits::is_separator (this->path_[e])) ++e; - if (e == n) - break; + // Skip consecutive directory separators. + // + while (e != n && traits::is_separator (s[e])) + ++e; + + if (e == n) + break; - b = e; + b = e; + } + + // If the last component is "." or ".." then this is a directory. + // + if (!tsep) + { + const string_type& l (ps.back ()); + size_type ln (l.size ()); + + if ((ln == 1 && l[0] == '.') || + (ln == 2 && l[0] == '.' && l[1] == '.')) + tsep = true; + } } - // First collapse '.' and '..'. + // Collapse "." and "..". // paths r; - for (typename paths::const_iterator i (ps.begin ()), e (ps.end ()); - i != e; ++i) + for (typename paths::iterator i (ps.begin ()), e (ps.end ()); i != e; ++i) { - string_type const& s (*i); + string_type& s (*i); size_type n (s.size ()); if (n == 1 && s[0] == '.') @@ -222,7 +185,7 @@ namespace butl } } - r.push_back (s); + r.push_back (std::move (s)); } // Reassemble the path. @@ -238,10 +201,20 @@ namespace butl p += traits::directory_separator; } - if (p.empty () && !r.empty ()) - p += traits::directory_separator; // Root directory. + if (tsep && (!p.empty () || abs)) // Distinguish "/"-empty and "."-empty. + { + if (p.empty ()) + { + p += traits::directory_separator; + d = -1; + } + else + d = 1; // Canonical separator is always first. + } + else + d = 0; - this->path_.swap (p); + s.swap (p); return *this; } @@ -257,10 +230,13 @@ namespace butl traits::current (s); } - template - bool basic_path:: - init (string_type& s, bool exact) + template + auto any_path_kind:: + init (string_type&& s, bool exact) -> data_type { + using size_type = typename string_type::size_type; + using difference_type = typename string_type::difference_type; + size_type n (s.size ()); #ifdef _WIN32 @@ -272,25 +248,58 @@ namespace butl (n > 1 && s[0] == '\\' && s[1] == '\\')) { if (exact) - return false; + return data_type (); else throw invalid_basic_path (s); } #endif - // Strip trailing slashes except for the case where the single slash - // represents the root directory. + // Strip trailing slashes. // - for (; n > 1 && traits::is_separator (s[n - 1]); --n) ; + size_type m (n), di (0); + for (size_type i; + m != 0 && (i = path_traits::separator_index (s[m - 1])) != 0; + --m) di = i; - if (n != s.size ()) + difference_type d (0); + if (size_t k = n - m) { - if (!exact) - this->path_.resize (n); + // We can only accomodate one trailing slash in the exact mode. + // + if (exact && k > 1) + return data_type (); - return !exact; + if (m == 0) // The "/" case. + { + ++m; // Keep one slash in the string. + d = -1; + } + else + d = di; + + s.resize (m); } - return true; + return data_type (std::move (s), d); + } + + template + auto dir_path_kind:: + init (string_type&& s, bool exact) -> data_type + { + // If we don't already have the separator then this can't be the exact + // initialization. + // + if (exact && !s.empty () && !path_traits::is_separator (s.back ())) + return data_type (); + + data_type r (any_path_kind::init (std::move (s), exact)); + + // Unless the result is empty, make sure we have the trailing slash. + // + if (!r.path_.empty () && r.diff_ == 0) + r.diff_ = 1; // Canonical separator is always first. + + return r; } } -- cgit v1.1