| // -*-c++-*- | |
| // vim: set ft=cpp: | |
| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #pragma once | |
| #include "cmSTL.hxx" // IWYU pragma: keep | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <iterator> | |
| #include <string> | |
| #include <utility> | |
| #include <cm/string_view> | |
| #include <cm/type_traits> | |
| #include <cmext/iterator> | |
| #if defined(CMake_HAVE_CXX_FILESYSTEM) | |
| # include <filesystem> // IWYU pragma: export | |
| #else | |
| # include <cstddef> | |
| # include <iostream> | |
| # include <memory> | |
| # include <cm/iomanip> | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| # include <algorithm> | |
| # endif | |
| #endif | |
| namespace cm { | |
| namespace filesystem { | |
| #if !defined(CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR) | |
| // Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile | |
| // the source_traits for iterator check. So disable it for now. | |
| # define CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR 0 | |
| #endif | |
| namespace internals { | |
| class unicode_helper | |
| { | |
| protected: | |
| using utf8_state = unsigned char; | |
| static utf8_state const s_start = 0; | |
| static utf8_state const s_reject = 8; | |
| static inline bool in_range(std::uint32_t c, std::uint32_t lo, | |
| std::uint32_t hi) | |
| { | |
| return (static_cast<std::uint32_t>(c - lo) < (hi - lo + 1)); | |
| } | |
| static inline bool is_surrogate(std::uint32_t c) | |
| { | |
| return in_range(c, 0xd800, 0xdfff); | |
| } | |
| static inline bool is_high_surrogate(std::uint32_t c) | |
| { | |
| return (c & 0xfffffc00) == 0xd800; | |
| } | |
| static inline bool is_low_surrogate(std::uint32_t c) | |
| { | |
| return (c & 0xfffffc00) == 0xdc00; | |
| } | |
| template <typename Char> | |
| static void append(std::basic_string<Char>& str, std::uint32_t codepoint) | |
| { | |
| if (codepoint <= 0x7f) { | |
| str.push_back(static_cast<Char>(codepoint)); | |
| } else if (codepoint >= 0x80 && codepoint <= 0x7ff) { | |
| str.push_back(static_cast<Char>((codepoint >> 6) + 192)); | |
| str.push_back(static_cast<Char>((codepoint & 0x3f) + 128)); | |
| } else if ((codepoint >= 0x800 && codepoint <= 0xd7ff) || | |
| (codepoint >= 0xe000 && codepoint <= 0xffff)) { | |
| str.push_back(static_cast<Char>((codepoint >> 12) + 224)); | |
| str.push_back(static_cast<Char>(((codepoint & 0xfff) >> 6) + 128)); | |
| str.push_back(static_cast<Char>((codepoint & 0x3f) + 128)); | |
| } else if (codepoint >= 0x10000 && codepoint <= 0x10ffff) { | |
| str.push_back(static_cast<Char>((codepoint >> 18) + 240)); | |
| str.push_back(static_cast<Char>(((codepoint & 0x3ffff) >> 12) + 128)); | |
| str.push_back(static_cast<Char>(((codepoint & 0xfff) >> 6) + 128)); | |
| str.push_back(static_cast<Char>((codepoint & 0x3f) + 128)); | |
| } else { | |
| append(str, 0xfffd); | |
| } | |
| } | |
| static utf8_state decode(utf8_state const state, std::uint8_t const fragment, | |
| std::uint32_t& codepoint); | |
| }; | |
| template <typename Char, typename = void> | |
| class unicode | |
| { | |
| }; | |
| template <typename Char> | |
| class unicode<Char, typename std::enable_if<(sizeof(Char) == 4)>::type> | |
| : public unicode_helper | |
| { | |
| public: | |
| // UTF32 -> UTF8 | |
| template <typename UTF8Char> | |
| static std::basic_string<UTF8Char> to_utf8(wchar_t const* str) | |
| { | |
| std::basic_string<UTF8Char> result; | |
| while (auto c = *str++) { | |
| append(result, c); | |
| } | |
| return result; | |
| } | |
| template <typename UTF8Char> | |
| static std::basic_string<UTF8Char> to_utf8(std::wstring const& str) | |
| { | |
| return unicode<Char>::template to_utf8<UTF8Char>(str.c_str()); | |
| } | |
| template <typename UTF8Char> | |
| static std::basic_string<UTF8Char> to_utf8(wchar_t c) | |
| { | |
| std::basic_string<UTF8Char> result; | |
| append(result, c); | |
| return result; | |
| } | |
| // UTF8 -> UTF32 | |
| template <typename UTF8Char> | |
| static std::wstring from_utf8(UTF8Char const* str, std::size_t length) | |
| { | |
| std::wstring result; | |
| result.reserve(length); | |
| utf8_state state = s_start; | |
| std::uint32_t codepoint = 0; | |
| while (*str) { | |
| if ((state = decode(state, static_cast<std::uint8_t>(*str++), | |
| codepoint)) == s_start) { | |
| result += static_cast<std::wstring::value_type>(codepoint); | |
| codepoint = 0; | |
| } else if (state == s_reject) { | |
| result += static_cast<std::wstring::value_type>(0xfffd); | |
| state = s_start; | |
| codepoint = 0; | |
| } | |
| } | |
| if (state) { | |
| result += static_cast<std::wstring::value_type>(0xfffd); | |
| } | |
| return result; | |
| } | |
| template <typename UTF8Char> | |
| static std::wstring from_utf8(UTF8Char const* str) | |
| { | |
| return unicode<Char>::from_utf8( | |
| str, std::strlen(reinterpret_cast<char const*>(str))); | |
| } | |
| template <typename UTF8Char> | |
| static std::wstring from_utf8(std::basic_string<UTF8Char> const& str) | |
| { | |
| return unicode<Char>::from_utf8(str.c_str(), str.length()); | |
| } | |
| template <typename UTF8Char> | |
| static std::wstring from_utf8(UTF8Char c) | |
| { | |
| std::wstring result; | |
| utf8_state state = s_start; | |
| std::uint32_t codepoint = 0; | |
| if ((state = decode(state, static_cast<std::uint8_t>(c), codepoint)) == | |
| s_start) { | |
| result += static_cast<std::wstring::value_type>(codepoint); | |
| } else { | |
| result += static_cast<std::wstring::value_type>(0xfffd); | |
| } | |
| return result; | |
| } | |
| }; | |
| template <typename Char> | |
| class unicode<Char, typename std::enable_if<(sizeof(Char) == 2)>::type> | |
| : public unicode_helper | |
| { | |
| public: | |
| // UTF16 -> UTF8 | |
| template <typename UTF8Char> | |
| static std::basic_string<UTF8Char> to_utf8(wchar_t const* str) | |
| { | |
| std::basic_string<UTF8Char> result; | |
| for (; *str; ++str) { | |
| std::uint32_t c = *str; | |
| if (is_surrogate(c)) { | |
| ++str; | |
| if (*str && is_high_surrogate(c) && is_low_surrogate(*str)) { | |
| append(result, (std::uint32_t(c) << 10) + *str - 0x35fdc00); | |
| } else { | |
| append(result, 0xfffd); | |
| if (!*str) { | |
| break; | |
| } | |
| } | |
| } else { | |
| append(result, c); | |
| } | |
| } | |
| return result; | |
| } | |
| template <typename UTF8Char> | |
| static std::basic_string<UTF8Char> to_utf8(std::wstring const& str) | |
| { | |
| return unicode<Char>::template to_utf8<UTF8Char>(str.c_str()); | |
| } | |
| template <typename UTF8Char> | |
| static std::basic_string<UTF8Char> to_utf8(wchar_t c) | |
| { | |
| std::basic_string<UTF8Char> result; | |
| if (is_surrogate(c)) { | |
| append(result, 0xfffd); | |
| } else { | |
| append(result, c); | |
| } | |
| return result; | |
| } | |
| // UTF8 -> UTF16 | |
| template <typename UTF8Char> | |
| static std::wstring from_utf8(UTF8Char const* str, std::size_t length) | |
| { | |
| std::wstring result; | |
| result.reserve(length); | |
| utf8_state state = s_start; | |
| std::uint32_t codepoint = 0; | |
| while (*str) { | |
| if ((state = decode(state, static_cast<std::uint8_t>(*str++), | |
| codepoint)) == s_start) { | |
| if (codepoint <= 0xffff) { | |
| result += static_cast<std::wstring::value_type>(codepoint); | |
| } else { | |
| codepoint -= 0x10000; | |
| result += | |
| static_cast<std::wstring::value_type>((codepoint >> 10) + 0xd800); | |
| result += static_cast<std::wstring::value_type>((codepoint & 0x3ff) + | |
| 0xdc00); | |
| } | |
| codepoint = 0; | |
| } else if (state == s_reject) { | |
| result += static_cast<std::wstring::value_type>(0xfffd); | |
| state = s_start; | |
| codepoint = 0; | |
| } | |
| } | |
| if (state) { | |
| result += static_cast<std::wstring::value_type>(0xfffd); | |
| } | |
| return result; | |
| } | |
| template <typename UTF8Char> | |
| static std::wstring from_utf8(UTF8Char const* str) | |
| { | |
| return unicode<Char>::from_utf8( | |
| str, std::strlen(reinterpret_cast<char const*>(str))); | |
| } | |
| template <typename UTF8Char> | |
| static std::wstring from_utf8(std::basic_string<UTF8Char> const& str) | |
| { | |
| return unicode<Char>::from_utf8(str.c_str(), str.length()); | |
| } | |
| template <typename UTF8Char> | |
| static std::wstring from_utf8(UTF8Char c) | |
| { | |
| std::wstring result; | |
| utf8_state state = s_start; | |
| std::uint32_t codepoint = 0; | |
| if ((state = decode(state, static_cast<std::uint8_t>(c), codepoint)) == | |
| s_start) { | |
| if (codepoint <= 0xffff) { | |
| result += static_cast<std::wstring::value_type>(codepoint); | |
| } else { | |
| codepoint -= 0x10000; | |
| result += | |
| static_cast<std::wstring::value_type>((codepoint >> 10) + 0xd800); | |
| result += | |
| static_cast<std::wstring::value_type>((codepoint & 0x3ff) + 0xdc00); | |
| } | |
| } else { | |
| result += static_cast<std::wstring::value_type>(0xfffd); | |
| } | |
| return result; | |
| } | |
| }; | |
| template <typename In, typename Out> | |
| class unicode_converter; | |
| template <> | |
| class unicode_converter<char, wchar_t> | |
| { | |
| public: | |
| std::wstring operator()(std::string const& in) | |
| { | |
| return unicode<wchar_t>::from_utf8(in); | |
| } | |
| std::wstring operator()(std::string&& in) | |
| { | |
| return unicode<wchar_t>::from_utf8(in); | |
| } | |
| std::wstring operator()(char const* in) | |
| { | |
| return unicode<wchar_t>::from_utf8(in); | |
| } | |
| std::wstring operator()(char in) { return unicode<wchar_t>::from_utf8(in); } | |
| }; | |
| template <> | |
| class unicode_converter<wchar_t, char> | |
| { | |
| public: | |
| std::string operator()(std::wstring const& in) | |
| { | |
| return unicode<wchar_t>::to_utf8<char>(in); | |
| } | |
| std::string operator()(std::wstring&& in) | |
| { | |
| return unicode<wchar_t>::to_utf8<char>(in); | |
| } | |
| std::string operator()(wchar_t const* in) | |
| { | |
| return unicode<wchar_t>::to_utf8<char>(in); | |
| } | |
| std::string operator()(wchar_t in) | |
| { | |
| return unicode<wchar_t>::to_utf8<char>(in); | |
| } | |
| }; | |
| template <> | |
| class unicode_converter<char, char> | |
| { | |
| public: | |
| std::string operator()(std::string const& in) { return in; } | |
| std::string operator()(std::string&& in) { return in; } | |
| std::string operator()(char const* in) { return std::string(in); } | |
| std::string operator()(char in) { return std::string(1, in); } | |
| }; | |
| template <> | |
| class unicode_converter<wchar_t, wchar_t> | |
| { | |
| public: | |
| std::wstring operator()(std::wstring const& in) { return in; } | |
| std::wstring operator()(std::wstring&& in) { return in; } | |
| std::wstring operator()(wchar_t const* in) { return std::wstring(in); } | |
| std::wstring operator()(wchar_t in) { return std::wstring(1, in); } | |
| }; | |
| #if defined(__cpp_char8_t) | |
| template <> | |
| class unicode_converter<char, char8_t> | |
| { | |
| public: | |
| std::u8string operator()(std::string const& in) | |
| { | |
| return std::u8string{ reinterpret_cast<char8_t const*>(in.c_str()) }; | |
| } | |
| std::u8string operator()(std::string&& in) | |
| { | |
| return std::u8string{ reinterpret_cast<char8_t const*>(in.c_str()) }; | |
| } | |
| std::u8string operator()(char const* in) | |
| { | |
| return std::u8string{ reinterpret_cast<char8_t const*>(in) }; | |
| } | |
| std::u8string operator()(char in) | |
| { | |
| return std::u8string{ 1, static_cast<char8_t>(in) }; | |
| } | |
| }; | |
| template <> | |
| class unicode_converter<char8_t, char> | |
| { | |
| public: | |
| std::string operator()(std::u8string const& in) | |
| { | |
| return std::string{ reinterpret_cast<char const*>(in.c_str()) }; | |
| } | |
| std::string operator()(std::u8string&& in) | |
| { | |
| return std::string{ reinterpret_cast<char const*>(in.c_str()) }; | |
| } | |
| std::string operator()(char8_t const* in) | |
| { | |
| return std::string{ reinterpret_cast<char const*>(in) }; | |
| } | |
| std::string operator()(char8_t in) | |
| { | |
| return std::string{ 1, static_cast<char>(in) }; | |
| } | |
| }; | |
| template <> | |
| class unicode_converter<wchar_t, char8_t> | |
| { | |
| public: | |
| std::u8string operator()(std::wstring const& in) | |
| { | |
| return unicode<wchar_t>::to_utf8<char8_t>(in); | |
| } | |
| std::u8string operator()(std::wstring&& in) | |
| { | |
| return unicode<wchar_t>::to_utf8<char8_t>(in); | |
| } | |
| std::u8string operator()(wchar_t const* in) | |
| { | |
| return unicode<wchar_t>::to_utf8<char8_t>(in); | |
| } | |
| std::u8string operator()(wchar_t in) | |
| { | |
| return unicode<wchar_t>::to_utf8<char8_t>(in); | |
| } | |
| }; | |
| template <> | |
| class unicode_converter<char8_t, wchar_t> | |
| { | |
| public: | |
| std::wstring operator()(std::u8string const& in) | |
| { | |
| return unicode<wchar_t>::from_utf8(in); | |
| } | |
| std::wstring operator()(std::u8string&& in) | |
| { | |
| return unicode<wchar_t>::from_utf8(in); | |
| } | |
| std::wstring operator()(char8_t const* in) | |
| { | |
| return unicode<wchar_t>::from_utf8(in); | |
| } | |
| std::wstring operator()(char8_t in) | |
| { | |
| return unicode<wchar_t>::from_utf8(in); | |
| } | |
| }; | |
| #endif | |
| template <typename In> | |
| struct string_converter | |
| { | |
| }; | |
| template <> | |
| struct string_converter<char> | |
| { | |
| // some compilers, like gcc 4.8 does not implement the following C++11 | |
| // signature: | |
| // std::string::string(const string&, const Allocator&) | |
| // As workaround, use char* pointer. | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(std::string const& in, | |
| Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<char, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(char const* in, | |
| Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<char, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(char in, Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<char, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(std::string const& in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<char, Char>()(in)); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(std::string&& in) | |
| { | |
| return std::basic_string<Char>( | |
| unicode_converter<char, Char>()(std::move(in))); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(cm::string_view in) | |
| { | |
| return std::basic_string<Char>( | |
| unicode_converter<char, Char>()(std::string{ in.begin(), in.end() })); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(char const* in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<char, Char>()(in)); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(char in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<char, Char>()(in)); | |
| } | |
| template <typename Char, typename Iterator> | |
| static std::basic_string<Char> to(Iterator it) | |
| { | |
| char e = '\0'; | |
| std::string tmp; | |
| for (; *it != e; ++it) { | |
| tmp.push_back(*it); | |
| } | |
| return std::basic_string<Char>( | |
| unicode_converter<char, Char>()(std::move(tmp))); | |
| } | |
| template <typename Char, typename Iterator> | |
| static std::basic_string<Char> to(Iterator begin, Iterator end) | |
| { | |
| return std::basic_string<Char>( | |
| unicode_converter<char, Char>()(std::string{ begin, end })); | |
| } | |
| }; | |
| template <> | |
| struct string_converter<wchar_t> | |
| { | |
| // some compilers, like gcc 4.8 does not implement the following C++11 | |
| // signature: | |
| // std::string::string(const string&, const Allocator&) | |
| // As workaround, use char* pointer. | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(std::wstring const& in, | |
| Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<wchar_t, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(wchar_t const* in, | |
| Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<wchar_t, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(wchar_t in, Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<wchar_t, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(std::wstring const& in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<wchar_t, Char>()(in)); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(std::wstring&& in) | |
| { | |
| return std::basic_string<Char>( | |
| unicode_converter<wchar_t, Char>()(std::move(in))); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(wchar_t const* in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<wchar_t, Char>()(in)); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(wchar_t in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<wchar_t, Char>()(in)); | |
| } | |
| template <typename Char, typename Iterator> | |
| static std::basic_string<Char> to(Iterator it) | |
| { | |
| wchar_t e = '\0'; | |
| std::wstring tmp; | |
| for (; *it != e; ++it) { | |
| tmp.push_back(*it); | |
| } | |
| return std::basic_string<Char>( | |
| unicode_converter<wchar_t, Char>()(std::move(tmp))); | |
| } | |
| template <typename Char, typename Iterator> | |
| static std::basic_string<Char> to(Iterator begin, Iterator end) | |
| { | |
| return std::basic_string<Char>( | |
| unicode_converter<wchar_t, Char>()(std::wstring{ begin, end })); | |
| } | |
| }; | |
| #if defined(__cpp_char8_t) | |
| template <> | |
| struct string_converter<char8_t> | |
| { | |
| // some compilers, like gcc 4.8 does not implement the following C++11 | |
| // signature: | |
| // std::string::string(const string&, const Allocator&) | |
| // As workaround, use char* pointer. | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(std::u8string const& in, | |
| Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<char8_t, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(char8_t const* in, | |
| Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<char8_t, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char, typename Traits, typename Alloc> | |
| static std::basic_string<Char, Traits, Alloc> to(char8_t in, Alloc const& a) | |
| { | |
| return std::basic_string<Char, Traits, Alloc>( | |
| unicode_converter<char8_t, Char>()(in).c_str(), a); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(std::u8string const& in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<char8_t, Char>()(in)); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(std::u8string&& in) | |
| { | |
| return std::basic_string<Char>( | |
| unicode_converter<char8_t, Char>()(std::move(in))); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(char8_t const* in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<char8_t, Char>()(in)); | |
| } | |
| template <typename Char> | |
| static std::basic_string<Char> to(char8_t in) | |
| { | |
| return std::basic_string<Char>(unicode_converter<char8_t, Char>()(in)); | |
| } | |
| template <typename Char, typename Iterator> | |
| static std::basic_string<Char> to(Iterator it) | |
| { | |
| char8_t e = '\0'; | |
| std::u8string tmp; | |
| for (; *it != e; ++it) { | |
| tmp.push_back(*it); | |
| } | |
| return std::basic_string<Char>( | |
| unicode_converter<char8_t, Char>()(std::move(tmp))); | |
| } | |
| template <typename Char, typename Iterator> | |
| static std::basic_string<Char> to(Iterator begin, Iterator end) | |
| { | |
| return std::basic_string<Char>( | |
| unicode_converter<char8_t, Char>()(std::u8string{ begin, end })); | |
| } | |
| }; | |
| #endif | |
| template <typename T, typename = void> | |
| struct source_traits | |
| { | |
| }; | |
| template <typename T, std::size_t N> | |
| struct source_traits<T[N]> | |
| { | |
| using value_type = T; | |
| }; | |
| template <typename T> | |
| struct source_traits<T*> | |
| { | |
| using value_type = cm::decay_t<T>; | |
| }; | |
| template <typename Char, typename Traits, typename Alloc> | |
| struct source_traits<std::basic_string<Char, Traits, Alloc>> | |
| { | |
| using value_type = | |
| typename std::basic_string<Char, Traits, Alloc>::value_type; | |
| }; | |
| template <> | |
| struct source_traits<cm::string_view> | |
| { | |
| using value_type = cm::string_view::value_type; | |
| }; | |
| #if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR | |
| template <typename T> | |
| struct source_traits<T, cm::enable_if_t<cm::is_iterator<T>::value, void>> | |
| { | |
| using value_type = | |
| typename std::iterator_traits<typename std::decay<T>::type>::value_type; | |
| }; | |
| #endif | |
| template <typename T> | |
| struct is_pathable_string : std::false_type | |
| { | |
| }; | |
| template <typename Traits, typename Alloc> | |
| struct is_pathable_string<std::basic_string<char, Traits, Alloc>> | |
| : std::true_type | |
| { | |
| }; | |
| template <typename Traits, typename Alloc> | |
| struct is_pathable_string<std::basic_string<wchar_t, Traits, Alloc>> | |
| : std::true_type | |
| { | |
| }; | |
| template <> | |
| struct is_pathable_string<cm::string_view> : std::true_type | |
| { | |
| }; | |
| template <typename T, typename = void> | |
| struct is_pathable_char_array : std::false_type | |
| { | |
| }; | |
| template <typename T> | |
| struct is_pathable_char_array< | |
| T, | |
| cm::enable_if_t< | |
| std::is_same<char*, typename std::decay<T>::type>::value || | |
| std::is_same<wchar_t*, typename std::decay<T>::type>::value, | |
| void>> | |
| : bool_constant<std::is_same<char*, typename std::decay<T>::type>::value || | |
| std::is_same<wchar_t*, typename std::decay<T>::type>::value> | |
| { | |
| }; | |
| template <typename T, typename = void> | |
| struct is_pathable_iterator : std::false_type | |
| { | |
| }; | |
| template <typename T> | |
| struct is_pathable_iterator< | |
| T, | |
| cm::enable_if_t< | |
| is_input_iterator<T>::value && | |
| (std::is_same<char, | |
| typename std::iterator_traits< | |
| typename std::decay<T>::type>::value_type>::value || | |
| std::is_same<wchar_t, | |
| typename std::iterator_traits< | |
| typename std::decay<T>::type>::value_type>::value), | |
| void>> | |
| : bool_constant< | |
| std::is_same<char, | |
| typename std::iterator_traits< | |
| typename std::decay<T>::type>::value_type>::value || | |
| std::is_same<wchar_t, | |
| typename std::iterator_traits< | |
| typename std::decay<T>::type>::value_type>::value> | |
| { | |
| }; | |
| #if defined(__SUNPRO_CC) && defined(__sparc) | |
| // Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile | |
| // the full 'is_pathable' check. We use it only to improve error messages | |
| // via 'enable_if' when calling methods with incorrect types. Just | |
| // pretend all types are allowed so we can at least compile valid code. | |
| template <typename T> | |
| struct is_pathable : std::true_type | |
| { | |
| }; | |
| #else | |
| template <typename T> | |
| struct is_pathable | |
| : bool_constant<is_pathable_string<T>::value || | |
| is_pathable_char_array<T>::value || | |
| is_pathable_iterator<T>::value> | |
| { | |
| }; | |
| #endif | |
| template <typename In, typename Out> | |
| struct source_converter | |
| { | |
| }; | |
| template <> | |
| struct source_converter<char, char> | |
| { | |
| template <typename Iterator> | |
| static void append_range(std::string& p, Iterator b, Iterator e) | |
| { | |
| if (b == e) { | |
| return; | |
| } | |
| p.append(b, e); | |
| } | |
| template <typename Iterator> | |
| static void append_range(std::string& p, Iterator b) | |
| { | |
| char e = '\0'; | |
| if (*b == e) { | |
| return; | |
| } | |
| for (; *b != e; ++b) { | |
| p.push_back(*b); | |
| } | |
| } | |
| static void append_source(std::string& p, cm::string_view s) | |
| { | |
| append_range(p, s.begin(), s.end()); | |
| } | |
| static void append_source(std::string& p, std::string const& s) | |
| { | |
| append_range(p, s.begin(), s.end()); | |
| } | |
| template <typename Source> | |
| static void append_source(std::string& p, Source const& s) | |
| { | |
| append_range(p, s); | |
| } | |
| static void set_source(std::string& p, std::string&& s) { p = std::move(s); } | |
| static std::string from(cm::string_view s) | |
| { | |
| return std::string{ s.begin(), s.end() }; | |
| } | |
| static std::string from(std::string const& s) { return s; } | |
| static std::string from(std::string&& s) { return s; } | |
| template <typename Source> | |
| static std::string from(Source const& s) | |
| { | |
| std::string result; | |
| append_source(result, s); | |
| return result; | |
| } | |
| template <typename Iterator> | |
| static std::string from(Iterator first, Iterator last) | |
| { | |
| return std::string{ first, last }; | |
| } | |
| }; | |
| template <> | |
| struct source_converter<char, wchar_t> | |
| { | |
| static std::wstring from(std::string const& s) | |
| { | |
| return string_converter<char>::to<wchar_t>(s); | |
| } | |
| static std::wstring from(std::string&& s) | |
| { | |
| return string_converter<char>::to<wchar_t>(std::move(s)); | |
| } | |
| template <typename Source> | |
| static std::wstring from(Source const& s) | |
| { | |
| return string_converter<char>::to<wchar_t>(s); | |
| } | |
| template <typename Iterator> | |
| static std::wstring from(Iterator first, Iterator last) | |
| { | |
| return string_converter<char>::to<wchar_t>(first, last); | |
| } | |
| }; | |
| template <> | |
| struct source_converter<wchar_t, wchar_t> | |
| { | |
| template <typename Iterator> | |
| static void append_range(std::wstring& p, Iterator b, Iterator e) | |
| { | |
| if (b == e) { | |
| return; | |
| } | |
| p.append(b, e); | |
| } | |
| template <typename Iterator> | |
| static void append_range(std::wstring& p, Iterator b) | |
| { | |
| char e = '\0'; | |
| if (*b == e) { | |
| return; | |
| } | |
| for (; *b != e; ++b) { | |
| p.push_back(*b); | |
| } | |
| } | |
| template <typename Source> | |
| static void append_source(std::wstring& p, Source const& s) | |
| { | |
| append_range(p, s); | |
| } | |
| static std::wstring from(std::wstring const& s) { return s; } | |
| static std::wstring from(std::wstring&& s) { return s; } | |
| template <typename Source> | |
| static std::wstring from(Source const& s) | |
| { | |
| std::wstring result; | |
| append_source(result, s); | |
| return result; | |
| } | |
| template <typename Iterator> | |
| static std::wstring from(Iterator first, Iterator last) | |
| { | |
| return std::wstring{ first, last }; | |
| } | |
| }; | |
| template <> | |
| struct source_converter<wchar_t, char> | |
| { | |
| template <typename Iterator> | |
| static void append_range(std::string& p, Iterator b, Iterator e) | |
| { | |
| if (b == e) { | |
| return; | |
| } | |
| std::string dest = string_converter<wchar_t>::to<char>(b, e); | |
| p.append(dest.begin(), dest.end()); | |
| } | |
| template <typename Iterator> | |
| static void append_range(std::string& p, Iterator b) | |
| { | |
| wchar_t e = '\0'; | |
| if (*b == e) { | |
| return; | |
| } | |
| std::string dest = string_converter<wchar_t>::to<char>(b); | |
| p.append(dest.begin(), dest.end()); | |
| } | |
| static void append_source(std::string& p, std::wstring const& s) | |
| { | |
| append_range(p, s.begin(), s.end()); | |
| } | |
| template <typename Source> | |
| static void append_source(std::string& p, Source const& s) | |
| { | |
| append_range(p, s); | |
| } | |
| static void set_source(std::string& p, std::wstring&& s) | |
| { | |
| p = string_converter<wchar_t>::to<char>(std::move(s)); | |
| } | |
| static std::string from(std::wstring const& s) | |
| { | |
| return string_converter<wchar_t>::to<char>(s); | |
| } | |
| static std::string from(std::wstring&& s) | |
| { | |
| return string_converter<wchar_t>::to<char>(std::move(s)); | |
| } | |
| template <typename Source> | |
| static std::string from(Source const& s) | |
| { | |
| return string_converter<wchar_t>::to<char>(s); | |
| } | |
| template <typename Iterator> | |
| static std::string from(Iterator first, Iterator last) | |
| { | |
| return string_converter<wchar_t>::to<char>(first, last); | |
| } | |
| }; | |
| #if defined(__cpp_char8_t) | |
| template <> | |
| struct source_converter<char, char8_t> | |
| { | |
| static std::u8string from(std::string const& s) | |
| { | |
| return string_converter<char>::to<char8_t>(s); | |
| } | |
| static std::u8string from(std::string&& s) | |
| { | |
| return string_converter<char>::to<char8_t>(std::move(s)); | |
| } | |
| template <typename Source> | |
| static std::u8string from(Source const& s) | |
| { | |
| return string_converter<char>::to<char8_t>(s); | |
| } | |
| template <typename Iterator> | |
| static std::u8string from(Iterator first, Iterator last) | |
| { | |
| return string_converter<char>::to<char8_t>(first, last); | |
| } | |
| }; | |
| template <> | |
| struct source_converter<char8_t, char> | |
| { | |
| static std::string from(std::u8string const& s) | |
| { | |
| return string_converter<char8_t>::to<char>(s); | |
| } | |
| static std::string from(std::u8string&& s) | |
| { | |
| return string_converter<char8_t>::to<char>(std::move(s)); | |
| } | |
| template <typename Source> | |
| static std::string from(Source const& s) | |
| { | |
| return string_converter<char8_t>::to<char>(s); | |
| } | |
| template <typename Iterator> | |
| static std::string from(Iterator first, Iterator last) | |
| { | |
| return string_converter<char8_t>::to<char>(first, last); | |
| } | |
| }; | |
| template <> | |
| struct source_converter<wchar_t, char8_t> | |
| { | |
| static std::u8string from(std::wstring const& s) | |
| { | |
| return string_converter<wchar_t>::to<char8_t>(s); | |
| } | |
| static std::u8string from(std::wstring&& s) | |
| { | |
| return string_converter<wchar_t>::to<char8_t>(std::move(s)); | |
| } | |
| template <typename Source> | |
| static std::u8string from(Source const& s) | |
| { | |
| return string_converter<wchar_t>::to<char8_t>(s); | |
| } | |
| template <typename Iterator> | |
| static std::u8string from(Iterator first, Iterator last) | |
| { | |
| return string_converter<wchar_t>::to<char8_t>(first, last); | |
| } | |
| }; | |
| template <> | |
| struct source_converter<char8_t, wchar_t> | |
| { | |
| static std::wstring from(std::u8string const& s) | |
| { | |
| return string_converter<char8_t>::to<wchar_t>(s); | |
| } | |
| static std::wstring from(std::u8string&& s) | |
| { | |
| return string_converter<char8_t>::to<wchar_t>(std::move(s)); | |
| } | |
| template <typename Source> | |
| static std::wstring from(Source const& s) | |
| { | |
| return string_converter<char8_t>::to<wchar_t>(s); | |
| } | |
| template <typename Iterator> | |
| static std::wstring from(Iterator first, Iterator last) | |
| { | |
| return string_converter<char8_t>::to<wchar_t>(first, last); | |
| } | |
| }; | |
| #endif | |
| } | |
| #if defined(CMake_HAVE_CXX_FILESYSTEM) | |
| class path : public std::filesystem::path | |
| { | |
| private: | |
| using base = std::filesystem::path; | |
| // define the char type to convert to | |
| # if defined(__cpp_char8_t) && defined(_WIN32) && !defined(__CYGWIN__) | |
| using target_type = char8_t; | |
| # else | |
| using target_type = base::value_type; | |
| # endif | |
| template <typename Source> | |
| using enable_if_pathable = | |
| enable_if_t<filesystem::internals::is_pathable<Source>::value, path&>; | |
| template <typename Source, typename Target = target_type> | |
| using string_converter = filesystem::internals::source_converter< | |
| typename filesystem::internals::source_traits<Source>::value_type, Target>; | |
| template <typename Iterator, typename Target = target_type> | |
| using range_converter = filesystem::internals::source_converter< | |
| typename std::iterator_traits<Iterator>::value_type, Target>; | |
| public: | |
| class iterator; | |
| using const_iterator = iterator; | |
| // Constructors | |
| // ============ | |
| path() noexcept {} | |
| path(path const& p) | |
| : base(p) | |
| { | |
| } | |
| path(path&& p) noexcept | |
| : base(std::move(p)) | |
| { | |
| } | |
| path(string_type&& source, format fmt = format::auto_format) | |
| : base(string_converter<string_type>::from(std::move(source)), fmt) | |
| { | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path(Source const& source, format fmt = format::auto_format) | |
| : base(string_converter<Source>::from(source), fmt) | |
| { | |
| } | |
| template <typename Iterator, typename = enable_if_pathable<Iterator>> | |
| path(Iterator first, Iterator last, format fmt = format::auto_format) | |
| : base(range_converter<Iterator>::from(first, last), fmt) | |
| { | |
| } | |
| ~path() = default; | |
| // Assignments | |
| // =========== | |
| path& operator=(path const& p) | |
| { | |
| if (this != &p) { | |
| this->base::operator=(static_cast<base const&>(p)); | |
| } | |
| return *this; | |
| } | |
| path& operator=(path&& p) noexcept | |
| { | |
| if (this != &p) { | |
| this->base::operator=(std::move(static_cast<base&&>(p))); | |
| } | |
| return *this; | |
| } | |
| path& operator=(string_type&& source) | |
| { | |
| return this->assign(std::move(source)); | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path& operator=(Source const& source) | |
| { | |
| return this->assign(source); | |
| } | |
| path& assign(string_type&& source) | |
| { | |
| this->base::assign(string_converter<string_type>::from(std::move(source))); | |
| return *this; | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path& assign(Source const& source) | |
| { | |
| this->base::assign(string_converter<Source>::from(source)); | |
| return *this; | |
| } | |
| template <typename Iterator, typename = enable_if_pathable<Iterator>> | |
| path& assign(Iterator first, Iterator last) | |
| { | |
| this->base::assign(range_converter<Iterator>::from(first, last)); | |
| return *this; | |
| } | |
| // Concatenation | |
| // ============= | |
| path& operator/=(path const& p) | |
| { | |
| this->base::operator/=(static_cast<base const&>(p)); | |
| return *this; | |
| } | |
| template <typename Source> | |
| path& operator/=(Source const& source) | |
| { | |
| return this->append(source); | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path& append(Source const& source) | |
| { | |
| this->base::append(string_converter<Source>::from(source)); | |
| return *this; | |
| } | |
| template <typename Iterator, typename = enable_if_pathable<Iterator>> | |
| path& append(Iterator first, Iterator last) | |
| { | |
| this->base::append(range_converter<Iterator>::from(first, last)); | |
| return *this; | |
| } | |
| path& operator+=(path const& p) | |
| { | |
| this->base::operator+=(static_cast<base const&>(p)); | |
| return *this; | |
| } | |
| path& operator+=(string_type const& str) { return this->concat(str); } | |
| path& operator+=(cm::string_view str) | |
| { | |
| this->concat(str.begin(), str.end()); | |
| return *this; | |
| } | |
| path& operator+=(value_type const* str) { return this->concat(str); } | |
| path& operator+=(value_type const c) | |
| { | |
| return this->concat(string_type{ 1, c }); | |
| } | |
| template <typename Source> | |
| path& operator+=(Source const& source) | |
| { | |
| return this->concat(source); | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path& concat(Source const& source) | |
| { | |
| this->base::concat(string_converter<Source>::from(source)); | |
| return *this; | |
| } | |
| template <typename Iterator, typename = enable_if_pathable<Iterator>> | |
| path& concat(Iterator first, Iterator last) | |
| { | |
| this->base::concat(range_converter<Iterator>::from(first, last)); | |
| return *this; | |
| } | |
| // Format observers | |
| // ================ | |
| string_type const& native() const noexcept | |
| { | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| this->native_path_ = this->wstring(); | |
| return this->native_path_; | |
| # else | |
| return this->base::native(); | |
| # endif | |
| } | |
| value_type const* c_str() const noexcept { return this->native().c_str(); } | |
| operator string_type() const { return this->native(); } | |
| std::string string() const | |
| { | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| # if defined(__cpp_char8_t) | |
| return string_converter<std::u8string, std::string::value_type>::from( | |
| this->base::u8string()); | |
| # else | |
| return string_converter<std::wstring, std::string::value_type>::from( | |
| this->base::wstring()); | |
| # endif | |
| # else | |
| return this->base::string(); | |
| # endif | |
| } | |
| std::wstring wstring() const { return this->base::wstring(); } | |
| # if defined(__cpp_char8_t) | |
| std::u8string u8string() const | |
| # else | |
| std::string u8string() const | |
| # endif | |
| { | |
| # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__cpp_char8_t) | |
| return string_converter<std::wstring, std::string::value_type>::from( | |
| this->base::wstring()); | |
| # else | |
| return this->base::u8string(); | |
| # endif | |
| } | |
| std::string generic_string() const | |
| { | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| # if defined(__cpp_char8_t) | |
| return string_converter<std::u8string, std::string::value_type>::from( | |
| this->base::generic_u8string()); | |
| # else | |
| return string_converter<std::wstring, std::string::value_type>::from( | |
| this->base::generic_wstring()); | |
| # endif | |
| # else | |
| return this->base::generic_string(); | |
| # endif | |
| } | |
| std::wstring generic_wstring() const | |
| { | |
| return this->base::generic_wstring(); | |
| } | |
| # if defined(__cpp_char8_t) | |
| std::u8string generic_u8string() const | |
| # else | |
| std::string generic_u8string() const | |
| # endif | |
| { | |
| # if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__cpp_char8_t) | |
| return string_converter<std::wstring, std::string::value_type>::from( | |
| this->base::generic_wstring()); | |
| # else | |
| return this->base::generic_u8string(); | |
| # endif | |
| } | |
| // Generation | |
| // ========== | |
| path lexically_normal() const { return this->base::lexically_normal(); } | |
| path lexically_relative(path const& root) const | |
| { | |
| return this->base::lexically_relative(root); | |
| } | |
| path lexically_proximate(path const& root) const | |
| { | |
| return this->base::lexically_proximate(root); | |
| } | |
| // Decomposition | |
| // ============= | |
| path root_name() const { return this->base::root_name(); } | |
| path root_directory() const { return this->base::root_directory(); } | |
| path root_path() const { return this->base::root_path(); } | |
| path relative_path() const { return this->base::relative_path(); } | |
| path parent_path() const { return this->base::parent_path(); } | |
| path filename() const { return this->base::filename(); } | |
| path stem() const { return this->base::stem(); } | |
| path extension() const { return this->base::extension(); } | |
| // Iterators | |
| // ========= | |
| inline iterator begin() const; | |
| inline iterator end() const; | |
| // Non-members | |
| // =========== | |
| friend inline path operator/(path const& lhs, path const& rhs) | |
| { | |
| path result{ lhs }; | |
| result /= rhs; | |
| return result; | |
| } | |
| private: | |
| friend class iterator; | |
| path(base&& p) | |
| : base(std::move(p)) | |
| { | |
| } | |
| mutable string_type native_path_; | |
| }; | |
| class path::iterator | |
| { | |
| public: | |
| using iterator_category = std::filesystem::path::iterator::iterator_category; | |
| using value_type = path; | |
| using difference_type = std::filesystem::path::iterator::difference_type; | |
| using pointer = path const*; | |
| using reference = path const&; | |
| iterator() = default; | |
| iterator(iterator const& other) | |
| : iterator_(other.iterator_) | |
| { | |
| } | |
| ~iterator() = default; | |
| iterator& operator=(iterator const& other) | |
| { | |
| this->iterator_ = other.iterator_; | |
| return *this; | |
| } | |
| reference operator*() const | |
| { | |
| this->path_element_ = path{ *this->iterator_ }; | |
| return this->path_element_; | |
| } | |
| pointer operator->() const | |
| { | |
| this->path_element_ = path{ *this->iterator_ }; | |
| return &this->path_element_; | |
| } | |
| iterator& operator++() | |
| { | |
| this->iterator_++; | |
| return *this; | |
| } | |
| iterator operator++(int) | |
| { | |
| iterator it(*this); | |
| this->operator++(); | |
| return *this; | |
| } | |
| iterator& operator--() | |
| { | |
| this->iterator_--; | |
| return *this; | |
| } | |
| iterator operator--(int) | |
| { | |
| iterator it(*this); | |
| this->operator--(); | |
| return it; | |
| } | |
| private: | |
| friend class path; | |
| friend bool operator==(iterator const&, iterator const&); | |
| iterator(std::filesystem::path::iterator it) | |
| : iterator_(it) | |
| { | |
| } | |
| std::filesystem::path::iterator iterator_; | |
| mutable path path_element_; | |
| }; | |
| inline path::iterator path::begin() const | |
| { | |
| return iterator{ this->base::begin() }; | |
| } | |
| inline path::iterator path::end() const | |
| { | |
| return iterator{ this->base::end() }; | |
| } | |
| // Non-member functions | |
| // ==================== | |
| inline bool operator==(path::iterator const& lhs, path::iterator const& rhs) | |
| { | |
| return lhs.iterator_ == rhs.iterator_; | |
| } | |
| inline bool operator!=(path::iterator const& lhs, path::iterator const& rhs) | |
| { | |
| return !(lhs == rhs); | |
| } | |
| inline void swap(path& lhs, path& rhs) noexcept | |
| { | |
| lhs.swap(rhs); | |
| } | |
| inline std::size_t hash_value(path const& p) noexcept | |
| { | |
| return std::filesystem::hash_value(p); | |
| } | |
| #else | |
| namespace internals { | |
| class path_parser; | |
| } | |
| class path | |
| { | |
| protected: | |
| using path_type = std::string; | |
| template <typename Source> | |
| using enable_if_pathable = | |
| enable_if_t<internals::is_pathable<Source>::value, path&>; | |
| enum class filename_fragment : unsigned char | |
| { | |
| stem, | |
| extension | |
| }; | |
| public: | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| using value_type = wchar_t; | |
| # else | |
| using value_type = char; | |
| # endif | |
| using string_type = std::basic_string<value_type>; | |
| class iterator; | |
| using const_iterator = iterator; | |
| enum format : unsigned char | |
| { | |
| auto_format, | |
| native_format, | |
| generic_format | |
| }; | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| static constexpr value_type preferred_separator = L'\\'; | |
| # else | |
| static constexpr value_type preferred_separator = '/'; | |
| # endif | |
| // Constructors | |
| // ============ | |
| path() noexcept {} | |
| path(path const& p) | |
| : path_(p.path_) | |
| { | |
| } | |
| path(path&& p) noexcept | |
| : path_(std::move(p.path_)) | |
| { | |
| } | |
| path(string_type&& source, format fmt = auto_format) | |
| { | |
| (void)fmt; | |
| internals::source_converter<value_type, path_type::value_type>::set_source( | |
| this->path_, std::move(source)); | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path(Source const& source, format fmt = auto_format) | |
| { | |
| (void)fmt; | |
| internals::source_converter< | |
| typename internals::source_traits<Source>::value_type, | |
| path_type::value_type>::append_source(this->path_, source); | |
| } | |
| template <typename Iterator, typename = enable_if_pathable<Iterator>> | |
| path(Iterator first, Iterator last, format fmt = auto_format) | |
| { | |
| (void)fmt; | |
| internals::source_converter< | |
| typename std::iterator_traits<Iterator>::value_type, | |
| path_type::value_type>::append_range(this->path_, first, last); | |
| } | |
| ~path() = default; | |
| // Assignments | |
| // =========== | |
| path& operator=(path const& p) | |
| { | |
| if (this != &p) { | |
| this->path_ = p.path_; | |
| } | |
| return *this; | |
| } | |
| path& operator=(path&& p) noexcept | |
| { | |
| if (this != &p) { | |
| this->path_ = std::move(p.path_); | |
| } | |
| return *this; | |
| } | |
| path& operator=(string_type&& source) | |
| { | |
| return this->assign(std::move(source)); | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path& operator=(Source const& source) | |
| { | |
| return this->assign(source); | |
| } | |
| path& assign(string_type&& source) | |
| { | |
| internals::source_converter<value_type, path_type::value_type>::set_source( | |
| this->path_, std::move(source)); | |
| return *this; | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path& assign(Source const& source) | |
| { | |
| this->path_.clear(); | |
| internals::source_converter< | |
| typename internals::source_traits<Source>::value_type, | |
| path_type::value_type>::append_source(this->path_, source); | |
| return *this; | |
| } | |
| template <typename Iterator, typename = enable_if_pathable<Iterator>> | |
| path& assign(Iterator first, Iterator last) | |
| { | |
| this->path_.clear(); | |
| internals::source_converter< | |
| typename std::iterator_traits<Iterator>::value_type, | |
| path_type::value_type>::append_range(this->path_, first, last); | |
| return *this; | |
| } | |
| // Concatenation | |
| // ============= | |
| path& operator/=(path const& p); | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path& append(Source const& source) | |
| { | |
| return this->operator/=(path(source)); | |
| } | |
| template <typename Source> | |
| path& operator/=(Source const& source) | |
| { | |
| return this->append(source); | |
| } | |
| template <typename Iterator, typename = enable_if_pathable<Iterator>> | |
| path& append(Iterator first, Iterator last) | |
| { | |
| return this->operator/=(path(first, last)); | |
| } | |
| path& operator+=(path const& p) | |
| { | |
| this->path_ += p.path_; | |
| return *this; | |
| } | |
| path& operator+=(string_type const& str) | |
| { | |
| this->path_ += | |
| internals::string_converter<value_type>::to<path_type::value_type>(str); | |
| return *this; | |
| } | |
| path& operator+=(cm::string_view str) | |
| { | |
| this->path_.append(str.begin(), str.end()); | |
| return *this; | |
| } | |
| path& operator+=(value_type const* str) | |
| { | |
| this->path_ += | |
| internals::string_converter<value_type>::to<path_type::value_type>(str); | |
| return *this; | |
| } | |
| path& operator+=(value_type const c) | |
| { | |
| this->path_ += | |
| internals::string_converter<value_type>::to<path_type::value_type>(c); | |
| return *this; | |
| } | |
| template <typename Source, typename = enable_if_pathable<Source>> | |
| path& concat(Source const& source) | |
| { | |
| internals::source_converter< | |
| typename internals::source_traits<Source>::value_type, | |
| path_type::value_type>::append_source(this->path_, source); | |
| return *this; | |
| } | |
| template <typename Source> | |
| path& operator+=(Source const& source) | |
| { | |
| return this->concat(source); | |
| } | |
| template <typename Iterator, typename = enable_if_pathable<Iterator>> | |
| path& concat(Iterator first, Iterator last) | |
| { | |
| internals::source_converter< | |
| typename std::iterator_traits<Iterator>::value_type, | |
| path_type::value_type>::append_range(this->path_, first, last); | |
| return *this; | |
| } | |
| // Modifiers | |
| // ========= | |
| void clear() noexcept { this->path_.clear(); } | |
| path& make_preferred() | |
| { | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| std::replace( | |
| this->path_.begin(), this->path_.end(), '/', | |
| static_cast<path_type::value_type>(this->preferred_separator)); | |
| # endif | |
| return *this; | |
| } | |
| path& remove_filename() | |
| { | |
| auto fname = this->get_filename(); | |
| if (!fname.empty()) { | |
| this->path_.erase(fname.data() - | |
| // Avoid C++17 non-const .data() that may reallocate. | |
| static_cast<path_type const&>(this->path_).data()); | |
| } | |
| return *this; | |
| } | |
| path& replace_filename(path const& replacement) | |
| { | |
| this->remove_filename(); | |
| this->operator/=(replacement); | |
| return *this; | |
| } | |
| path& replace_extension(path const& replacement = path()) | |
| { | |
| auto ext = this->get_filename_fragment(filename_fragment::extension); | |
| if (!ext.empty()) { | |
| this->path_.erase(ext.data() - | |
| // Avoid C++17 non-const .data() that may reallocate. | |
| static_cast<path_type const&>(this->path_).data()); | |
| } | |
| if (!replacement.path_.empty()) { | |
| if (replacement.path_[0] != '.') { | |
| this->path_ += '.'; | |
| } | |
| this->path_.append(replacement.path_); | |
| } | |
| return *this; | |
| } | |
| void swap(path& other) noexcept { this->path_.swap(other.path_); } | |
| // Format observers | |
| // ================ | |
| string_type const& native() const noexcept | |
| { | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| this->native_path_ = internals::string_converter< | |
| path_type::value_type>::to<string_type::value_type>(this->path_); | |
| return this->native_path_; | |
| # else | |
| return this->path_; | |
| # endif | |
| } | |
| value_type const* c_str() const noexcept { return this->native().c_str(); } | |
| operator string_type() const { return this->native(); } | |
| template < | |
| typename Char, typename Traits = std::char_traits<Char>, | |
| typename Alloc = std::allocator<Char>, | |
| cm::enable_if_t<(std::is_same<Char, char>::value && | |
| std::is_same<Traits, std::char_traits<char>>::value) || | |
| (std::is_same<Char, wchar_t>::value && | |
| std::is_same<Traits, std::char_traits<wchar_t>>::value), | |
| int> = 1> | |
| std::basic_string<Char, Traits, Alloc> string(Alloc const& a = Alloc()) const | |
| { | |
| return internals::string_converter<path_type::value_type>::to<Char, Traits, | |
| Alloc>( | |
| this->path_, a); | |
| } | |
| std::string string() const { return this->path_; } | |
| std::wstring wstring() const | |
| { | |
| return internals::string_converter<path_type::value_type>::to< | |
| std::wstring::value_type>(this->string()); | |
| } | |
| std::string u8string() const { return this->path_; } | |
| template < | |
| typename Char, typename Traits = std::char_traits<Char>, | |
| typename Alloc = std::allocator<Char>, | |
| cm::enable_if_t<(std::is_same<Char, char>::value && | |
| std::is_same<Traits, std::char_traits<char>>::value) || | |
| (std::is_same<Char, wchar_t>::value && | |
| std::is_same<Traits, std::char_traits<wchar_t>>::value), | |
| int> = 1> | |
| std::basic_string<Char, Traits, Alloc> generic_string( | |
| Alloc const& a = Alloc()) const | |
| { | |
| return internals::string_converter<path_type::value_type>::to<Char, Traits, | |
| Alloc>( | |
| this->get_generic(), a); | |
| } | |
| std::string generic_string() const { return this->get_generic(); } | |
| std::wstring generic_wstring() const | |
| { | |
| return internals::string_converter<path_type::value_type>::to< | |
| std::wstring::value_type>(this->generic_string()); | |
| } | |
| std::string generic_u8string() const { return this->get_generic(); } | |
| // Compare | |
| // ======= | |
| int compare(path const& p) const noexcept | |
| { | |
| return this->compare_path(p.path_); | |
| } | |
| int compare(string_type const& str) const | |
| { | |
| return this->compare_path( | |
| internals::string_converter<value_type>::to<path_type::value_type>(str)); | |
| } | |
| int compare(value_type const* str) const | |
| { | |
| return this->compare_path( | |
| internals::string_converter<value_type>::to<path_type::value_type>(str)); | |
| } | |
| int compare(cm::string_view str) const { return this->compare_path(str); } | |
| // Generation | |
| // ========== | |
| path lexically_normal() const; | |
| path lexically_relative(path const& base) const; | |
| path lexically_proximate(path const& base) const | |
| { | |
| path result = this->lexically_relative(base); | |
| return result.empty() ? *this : result; | |
| } | |
| // Decomposition | |
| // ============= | |
| path root_name() const { return get_root_name(); } | |
| path root_directory() const { return this->get_root_directory(); } | |
| path root_path() const | |
| { | |
| return this->root_name().append(this->get_root_directory()); | |
| } | |
| path relative_path() const { return this->get_relative_path(); } | |
| path parent_path() const { return this->get_parent_path(); } | |
| path filename() const { return this->get_filename(); } | |
| path stem() const | |
| { | |
| return this->get_filename_fragment(filename_fragment::stem); | |
| } | |
| path extension() const | |
| { | |
| return this->get_filename_fragment(filename_fragment::extension); | |
| } | |
| // Queries | |
| // ======= | |
| bool empty() const noexcept { return this->path_.empty(); } | |
| bool has_root_name() const { return !this->get_root_name().empty(); } | |
| bool has_root_directory() const | |
| { | |
| return !this->get_root_directory().empty(); | |
| } | |
| bool has_root_path() const | |
| { | |
| return this->has_root_name() || this->has_root_directory(); | |
| } | |
| bool has_relative_path() const { return !this->get_relative_path().empty(); } | |
| bool has_parent_path() const { return !this->get_parent_path().empty(); } | |
| bool has_filename() const { return !this->get_filename().empty(); } | |
| bool has_stem() const | |
| { | |
| return !this->get_filename_fragment(filename_fragment::stem).empty(); | |
| } | |
| bool has_extension() const | |
| { | |
| return !this->get_filename_fragment(filename_fragment::extension).empty(); | |
| } | |
| bool is_absolute() const | |
| { | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| return this->has_root_name() && this->has_root_directory(); | |
| # else | |
| // For CYGWIN, root_name (i.e. //host or /cygdrive/x) is not considered. | |
| // Same as current GNU g++ implementation (9.3). | |
| return this->has_root_directory(); | |
| # endif | |
| } | |
| bool is_relative() const { return !this->is_absolute(); } | |
| // Iterators | |
| // ========= | |
| inline iterator begin() const; | |
| inline iterator end() const; | |
| // Non-members | |
| // =========== | |
| friend inline bool operator==(path const& lhs, path const& rhs) noexcept | |
| { | |
| return lhs.compare(rhs) == 0; | |
| } | |
| friend inline bool operator!=(path const& lhs, path const& rhs) noexcept | |
| { | |
| return lhs.compare(rhs) != 0; | |
| } | |
| friend inline bool operator<(path const& lhs, path const& rhs) noexcept | |
| { | |
| return lhs.compare(rhs) < 0; | |
| } | |
| friend inline bool operator<=(path const& lhs, path const& rhs) noexcept | |
| { | |
| return lhs.compare(rhs) <= 0; | |
| } | |
| friend inline bool operator>(path const& lhs, path const& rhs) noexcept | |
| { | |
| return lhs.compare(rhs) > 0; | |
| } | |
| friend inline bool operator>=(path const& lhs, path const& rhs) noexcept | |
| { | |
| return lhs.compare(rhs) >= 0; | |
| } | |
| friend inline path operator/(path const& lhs, path const& rhs) | |
| { | |
| path result(lhs); | |
| result /= rhs; | |
| return result; | |
| } | |
| template <typename Char, typename Traits> | |
| friend inline cm::enable_if_t< | |
| (std::is_same<Char, path::value_type>::value && | |
| std::is_same<Traits, std::char_traits<path::value_type>>::value) || | |
| (std::is_same<Char, path::path_type::value_type>::value && | |
| std::is_same<Traits, | |
| std::char_traits<path::path_type::value_type>>::value), | |
| std::basic_ostream<Char, Traits>&> | |
| operator<<(std::basic_ostream<Char, Traits>& os, path const& p) | |
| { | |
| os << cm::quoted(p.string<Char, Traits>()); | |
| return os; | |
| } | |
| template <typename Char, typename Traits> | |
| friend inline cm::enable_if_t< | |
| (std::is_same<Char, path::value_type>::value && | |
| std::is_same<Traits, std::char_traits<path::value_type>>::value) || | |
| (std::is_same<Char, path::path_type::value_type>::value && | |
| std::is_same<Traits, | |
| std::char_traits<path::path_type::value_type>>::value), | |
| std::basic_istream<Char, Traits>&> | |
| operator>>(std::basic_istream<Char, Traits>& is, path& p) | |
| { | |
| std::basic_string<Char, Traits> tmp; | |
| is >> cm::quoted(tmp); | |
| p = tmp; | |
| return is; | |
| } | |
| private: | |
| friend class iterator; | |
| friend std::size_t hash_value(path const& p) noexcept; | |
| path_type get_generic() const; | |
| cm::string_view get_root_name() const; | |
| cm::string_view get_root_directory() const; | |
| cm::string_view get_relative_path() const; | |
| cm::string_view get_parent_path() const; | |
| cm::string_view get_filename() const; | |
| cm::string_view get_filename_fragment(filename_fragment fragment) const; | |
| int compare_path(cm::string_view str) const; | |
| path_type path_; | |
| # if defined(_WIN32) && !defined(__CYGWIN__) | |
| mutable string_type native_path_; | |
| # endif | |
| }; | |
| class path::iterator | |
| { | |
| public: | |
| using iterator_category = std::bidirectional_iterator_tag; | |
| using value_type = path; | |
| using difference_type = std::ptrdiff_t; | |
| using pointer = path const*; | |
| using reference = path const&; | |
| iterator(); | |
| iterator(iterator const& other); | |
| ~iterator(); | |
| iterator& operator=(iterator const& other); | |
| reference operator*() const { return this->path_element_; } | |
| pointer operator->() const { return &this->path_element_; } | |
| iterator& operator++(); | |
| iterator operator++(int) | |
| { | |
| iterator it(*this); | |
| this->operator++(); | |
| return it; | |
| } | |
| iterator& operator--(); | |
| iterator operator--(int) | |
| { | |
| iterator it(*this); | |
| this->operator--(); | |
| return it; | |
| } | |
| private: | |
| friend class path; | |
| friend bool operator==(iterator const&, iterator const&); | |
| iterator(path const* p, bool at_end = false); | |
| path const* path_; | |
| std::unique_ptr<internals::path_parser> parser_; | |
| path path_element_; | |
| }; | |
| inline path::iterator path::begin() const | |
| { | |
| return iterator(this); | |
| } | |
| inline path::iterator path::end() const | |
| { | |
| return iterator(this, true); | |
| } | |
| // Non-member functions | |
| // ==================== | |
| bool operator==(path::iterator const& lhs, path::iterator const& rhs); | |
| inline bool operator!=(path::iterator const& lhs, path::iterator const& rhs) | |
| { | |
| return !(lhs == rhs); | |
| } | |
| inline void swap(path& lhs, path& rhs) noexcept | |
| { | |
| lhs.swap(rhs); | |
| } | |
| std::size_t hash_value(path const& p) noexcept; | |
| #endif | |
| } // namespace filesystem | |
| } // namespace cm |