aboutsummaryrefslogtreecommitdiff
path: root/libbutl/path.ixx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-05-01 16:08:43 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-05-01 16:59:24 +0300
commit61377c582e0f2675baa5f5e6e30a35d1a4164b33 (patch)
tree11cdca992834d7f7f197f72856712fbcb3020e3d /libbutl/path.ixx
parent442c1a6790e52baa0c081f310d4d9e9b6f1ff638 (diff)
Add hxx extension for headers and lib prefix for library dir
Diffstat (limited to 'libbutl/path.ixx')
-rw-r--r--libbutl/path.ixx508
1 files changed, 508 insertions, 0 deletions
diff --git a/libbutl/path.ixx b/libbutl/path.ixx
new file mode 100644
index 0000000..c140d1e
--- /dev/null
+++ b/libbutl/path.ixx
@@ -0,0 +1,508 @@
+// file : libbutl/path.ixx -*- C++ -*-
+// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifdef _WIN32
+# include <cwctype> // towlower(), towupper()
+#endif
+
+namespace butl
+{
+#ifdef _WIN32
+ template <>
+ inline char path_traits<char>::
+ tolower (char c)
+ {
+ return lcase (c);
+ }
+
+ template <>
+ inline wchar_t path_traits<wchar_t>::
+ tolower (wchar_t c)
+ {
+ return std::towlower (c);
+ }
+
+ template <>
+ inline char path_traits<char>::
+ toupper (char c)
+ {
+ return ucase (c);
+ }
+
+ template <>
+ inline wchar_t path_traits<wchar_t>::
+ toupper (wchar_t c)
+ {
+ return std::towupper (c);
+ }
+#endif
+
+ 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.tsep_);
+ 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.tsep_);
+ 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 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 path_cast_impl (std::move (p), static_cast<P*> (nullptr));
+ }
+
+ template <typename C, typename K>
+ inline bool basic_path<C, K>::
+ simple () const
+ {
+ 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
+ {
+ return traits::absolute (this->path_);
+ }
+
+ template <typename C, typename K>
+ inline bool basic_path<C, K>::
+ current () const
+ {
+ return traits::current (this->path_);
+ }
+
+ template <typename C, typename K>
+ inline bool basic_path<C, K>::
+ parent () const
+ {
+ return traits::parent (this->path_);
+ }
+
+ template <typename C, typename K>
+ inline bool basic_path<C, K>::
+ root () const
+ {
+ return traits::root (this->path_);
+ }
+
+ template <typename C, typename K>
+ inline bool basic_path<C, K>::
+ sub (const basic_path& p) const
+ {
+ // 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 (pn == 0)
+ return true;
+
+ const string_type& s (this->path_);
+ size_type n (s.size ());
+
+ // The second condition guards against the /foo-bar vs /foo case.
+ //
+ 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
+ {
+ // 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 (pn == 0)
+ return true;
+
+ const string_type& s (this->path_);
+ size_type n (s.size ());
+
+ // The second condition guards against the /foo-bar vs bar case.
+ //
+ 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->tsep_))
+ : *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
+ {
+ const string_type& s (this->path_);
+
+ size_type b (s.empty () ? string_type::npos : 0);
+ size_type e (b == 0 ? traits::find_separator (s) : b);
+
+ return iterator (this, b, e);
+ }
+
+ template <typename C, typename K>
+ inline auto basic_path<C, K>::
+ end () const -> iterator
+ {
+ 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_->tsep_)))
+ {
+ //assert (b.p_ == e.p_);
+ }
+
+ template <typename C, typename K>
+ inline basic_path<C, K>& basic_path<C, K>::
+ canonicalize ()
+ {
+ traits::canonicalize (this->path_);
+
+ if (this->tsep_ > 1) // Non-canonical trailing separator.
+ this->tsep_ = 1;
+
+ return *this;
+ }
+
+ template <typename C, typename K>
+ inline basic_path<C, K>& basic_path<C, K>::
+ complete ()
+ {
+ if (relative ())
+ *this = current_directory () / *this;
+
+ return *this;
+ }
+
+ template <typename C, typename K>
+ inline basic_path<C, K>& basic_path<C, K>::
+ realize ()
+ {
+#ifdef _WIN32
+ // This is not exactly the semantics of realpath(3). In particular, we
+ // don't fail if the path does not exist. But we could have seeing that
+ // we actualize it.
+ //
+ complete ();
+ normalize (true);
+#else
+ traits::realize (this->path_); // Note: we retain the trailing slash.
+#endif
+ return *this;
+ }
+
+ template <typename C, typename K>
+ inline typename basic_path<C, K>::dir_type basic_path<C, K>::
+ root_directory () const
+ {
+#ifdef _WIN32
+ // 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->tsep_ != 0 ? this->tsep_ : 1))
+ : dir_type ();
+#else
+ 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
+ {
+ const string_type& s (this->path_);
+ size_type p (traits::find_extension (s));
+
+ return p != string_type::npos
+ ? basic_path (data_type (string_type (s, 0, p), this->tsep_))
+ : *this;
+ }
+
+ template <typename C, typename K>
+ inline typename basic_path<C, K>::string_type basic_path<C, K>::
+ extension () const
+ {
+ const string_type& s (this->path_);
+ size_type p (traits::find_extension (s));
+ return p != string_type::npos
+ ? string_type (s.c_str () + p + 1)
+ : string_type ();
+ }
+
+ template <typename C, typename K>
+ inline const C* basic_path<C, K>::
+ extension_cstring () const
+ {
+ 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
+ template <typename C, typename K>
+ inline typename basic_path<C, K>::string_type basic_path<C, K>::
+ posix_string () const&
+ {
+ return string ();
+ }
+
+ template <typename C, typename K>
+ inline typename basic_path<C, K>::string_type basic_path<C, K>::
+ posix_string () &&
+ {
+ return std::move (*this).string ();
+ }
+
+ template <typename C, typename K>
+ inline typename basic_path<C, K>::string_type basic_path<C, K>::
+ posix_representation () const&
+ {
+ return representation ();
+ }
+
+ template <typename C, typename K>
+ inline typename basic_path<C, K>::string_type basic_path<C, K>::
+ posix_representation () &&
+ {
+ return std::move (*this).representation ();
+ }
+#endif
+
+ template <typename C, typename K>
+ inline void basic_path<C, K>::
+ combine (const C* r, size_type rn, difference_type rts)
+ {
+ //assert (rn != 0);
+
+ string_type& l (this->path_);
+ difference_type& ts (this->tsep_);
+
+ // Handle the separator. LHS should be empty or already have one.
+ //
+ switch (ts)
+ {
+ 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[ts - 1];
+ }
+
+ l.append (r, rn);
+ ts = rts; // 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.tsep_);
+
+ 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->tsep_ != -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->tsep_ > 0)
+ r += path_traits<C>::directory_separators[this->tsep_ - 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->tsep_ > 0)
+ r += path_traits<C>::directory_separators[this->tsep_ - 1];
+
+ return r;
+ }
+
+ template <typename C, typename K>
+ inline C basic_path<C, K>::
+ separator () const
+ {
+ return (this->tsep_ == 0 ? 0 :
+ this->tsep_ == -1 ? this->path_[0] :
+ path_traits<C>::directory_separators[this->tsep_ - 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.tsep_ == 0)
+ d.tsep_ = 1; // Canonical separator is always first.
+ }
+}