From 61377c582e0f2675baa5f5e6e30a35d1a4164b33 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 1 May 2017 16:08:43 +0300 Subject: Add hxx extension for headers and lib prefix for library dir --- libbutl/path.ixx | 508 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 508 insertions(+) create mode 100644 libbutl/path.ixx (limited to 'libbutl/path.ixx') 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 // towlower(), towupper() +#endif + +namespace butl +{ +#ifdef _WIN32 + template <> + inline char path_traits:: + tolower (char c) + { + return lcase (c); + } + + template <> + inline wchar_t path_traits:: + tolower (wchar_t c) + { + return std::towlower (c); + } + + template <> + inline char path_traits:: + toupper (char c) + { + return ucase (c); + } + + template <> + inline wchar_t path_traits:: + toupper (wchar_t c) + { + return std::towupper (c); + } +#endif + + template + inline basic_path + path_cast_impl (const basic_path& p, basic_path*) + { + typename basic_path::data_type d ( + typename basic_path::string_type (p.path_), p.tsep_); + K1::cast (d); + return basic_path (std::move (d)); + } + + template + inline basic_path + path_cast_impl (basic_path&& p, basic_path*) + { + typename basic_path::data_type d (std::move (p.path_), p.tsep_); + K1::cast (d); + return basic_path (std::move (d)); + } + + template + inline P + path_cast (const basic_path& p) + { + return path_cast_impl (p, static_cast (nullptr)); + } + + template + inline P + path_cast (basic_path&& p) + { + return path_cast_impl (std::move (p), static_cast (nullptr)); + } + + template + inline bool basic_path:: + simple () const + { + return empty () || + traits::rfind_separator (this->path_, _size () - 1) == string_type::npos; + } + + template + inline bool basic_path:: + absolute () const + { + return traits::absolute (this->path_); + } + + template + inline bool basic_path:: + current () const + { + return traits::current (this->path_); + } + + template + inline bool basic_path:: + parent () const + { + return traits::parent (this->path_); + } + + template + inline bool basic_path:: + root () const + { + return traits::root (this->path_); + } + + template + inline bool basic_path:: + 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 + inline bool basic_path:: + 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 + inline basic_path basic_path:: + 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 + inline typename basic_path::dir_type basic_path:: + 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 + inline auto basic_path:: + 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 + inline auto basic_path:: + end () const -> iterator + { + return iterator (this, string_type::npos, string_type::npos); + } + + template + inline basic_path:: + 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 + inline basic_path& basic_path:: + canonicalize () + { + traits::canonicalize (this->path_); + + if (this->tsep_ > 1) // Non-canonical trailing separator. + this->tsep_ = 1; + + return *this; + } + + template + inline basic_path& basic_path:: + complete () + { + if (relative ()) + *this = current_directory () / *this; + + return *this; + } + + template + inline basic_path& basic_path:: + 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 + inline typename basic_path::dir_type basic_path:: + 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 + inline basic_path basic_path:: + 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 + inline typename basic_path::string_type basic_path:: + 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 + inline const C* basic_path:: + 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 + inline typename basic_path::string_type basic_path:: + posix_string () const& + { + return string (); + } + + template + inline typename basic_path::string_type basic_path:: + posix_string () && + { + return std::move (*this).string (); + } + + template + inline typename basic_path::string_type basic_path:: + posix_representation () const& + { + return representation (); + } + + template + inline typename basic_path::string_type basic_path:: + posix_representation () && + { + return std::move (*this).representation (); + } +#endif + + template + inline void basic_path:: + 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 (l); break; + case -1: break; // Already in the string. + default: l += path_traits::directory_separators[ts - 1]; + } + + l.append (r, rn); + ts = rts; // New trailing separator from RHS. + } + + template + inline void basic_path:: + 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 (r); + + combine (r, rn, 0); + K::cast (*this); + } + + template + inline basic_path& basic_path:: + operator/= (basic_path const& r) + { + if (r.absolute () && !empty ()) // Allow ('' / '/foo'). + throw invalid_basic_path (r.path_); + + if (!r.empty ()) + combine (r.path_.c_str (), r.path_.size (), r.tsep_); + + return *this; + } + + template + inline basic_path& basic_path:: + operator/= (string_type const& r) + { + if (size_type rn = r.size ()) + combine (r.c_str (), rn); + + return *this; + } + + template + inline basic_path& basic_path:: + operator/= (const C* r) + { + if (size_type rn = string_type::traits_type::length (r)) + combine (r, rn); + + return *this; + } + + template + inline void basic_path:: + append (const C* r, size_type rn) + { + //assert (this->tsep_ != -1); // Append to root? + this->path_.append (r, rn); + } + + template + inline basic_path& basic_path:: + operator+= (string_type const& s) + { + append (s.c_str (), s.size ()); + return *this; + } + + template + inline basic_path& basic_path:: + operator+= (const C* s) + { + append (s, string_type::traits_type::length (s)); + return *this; + } + + template + inline basic_path& basic_path:: + operator+= (C c) + { + append (&c, 1); + return *this; + } + + template + inline auto basic_path:: + representation () const& -> string_type + { + string_type r (this->path_); + + if (this->tsep_ > 0) + r += path_traits::directory_separators[this->tsep_ - 1]; + + return r; + } + + template + inline auto basic_path:: + representation () && -> string_type + { + string_type r; + r.swap (this->path_); + + if (this->tsep_ > 0) + r += path_traits::directory_separators[this->tsep_ - 1]; + + return r; + } + + template + inline C basic_path:: + separator () const + { + return (this->tsep_ == 0 ? 0 : + this->tsep_ == -1 ? this->path_[0] : + path_traits::directory_separators[this->tsep_ - 1]); + } + + template + inline auto basic_path:: + separator_string () const -> string_type + { + C c (separator ()); + return c == 0 ? string_type () : string_type (1, c); + } + + template + inline void dir_path_kind:: + 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. + } +} -- cgit v1.1