// file : butl/path.ixx -*- C++ -*- // copyright : Copyright (c) 2014-2016 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:: root () const { const string_type& s (this->path_); #ifdef _WIN32 return s.size () == 2 && s[1] == ':'; #else return s.size () == 1 && traits::is_separator (s[0]); #endif } 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:: complete () { if (relative ()) *this = current () / *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 const C* basic_path:: extension () 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. } }