| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| |
| #pragma once |
| |
| #include "cmConfigure.h" // IWYU pragma: keep |
| |
| #include <cstddef> |
| #include <string> |
| #include <utility> |
| |
| #include <cm/filesystem> |
| #include <cm/string_view> |
| #include <cm/type_traits> |
| #include <cmext/string_view> |
| |
| namespace detail { |
| #if defined(__SUNPRO_CC) && defined(__sparc) |
| // Oracle DeveloperStudio C++ compiler on Solaris/Sparc fails to compile |
| // the full 'is_pathable' and 'is_move_pathable' checks. 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 |
| { |
| }; |
| |
| template <typename T> |
| struct is_move_pathable : std::true_type |
| { |
| }; |
| |
| #else |
| template <typename T, typename = void> |
| struct is_pathable : std::false_type |
| { |
| }; |
| |
| template <> |
| struct is_pathable<cm::filesystem::path> : std::true_type |
| { |
| }; |
| template <> |
| struct is_pathable<std::string> : std::true_type |
| { |
| }; |
| template <> |
| struct is_pathable<cm::string_view> : std::true_type |
| { |
| }; |
| template <> |
| struct is_pathable<cm::static_string_view> : std::true_type |
| { |
| }; |
| template <typename T> |
| struct is_pathable< |
| T, |
| cm::enable_if_t<std::is_same<char*, typename std::decay<T>::type>::value, |
| void>> |
| : cm::bool_constant<std::is_same<char*, typename std::decay<T>::type>::value> |
| { |
| }; |
| |
| template <typename T> |
| struct is_move_pathable : std::false_type |
| { |
| }; |
| |
| template <> |
| struct is_move_pathable<cm::filesystem::path> : std::true_type |
| { |
| }; |
| template <> |
| struct is_move_pathable<std::string> : std::true_type |
| { |
| }; |
| #endif |
| } |
| |
| class cmCMakePath |
| { |
| private: |
| template <typename Source> |
| using enable_if_move_pathable = |
| cm::enable_if_t<detail::is_move_pathable<Source>::value, cmCMakePath&>; |
| |
| template <typename Source> |
| using enable_if_pathable = |
| cm::enable_if_t<detail::is_pathable<Source>::value, cmCMakePath&>; |
| |
| public: |
| using value_type = cm::filesystem::path::value_type; |
| using string_type = cm::filesystem::path::string_type; |
| |
| enum format : unsigned char |
| { |
| auto_format = |
| static_cast<unsigned char>(cm::filesystem::path::format::auto_format), |
| native_format = |
| static_cast<unsigned char>(cm::filesystem::path::format::native_format), |
| generic_format = |
| static_cast<unsigned char>(cm::filesystem::path::format::generic_format) |
| }; |
| |
| class iterator; |
| using const_iterator = iterator; |
| |
| cmCMakePath() noexcept = default; |
| |
| cmCMakePath(const cmCMakePath&) = default; |
| |
| cmCMakePath(cmCMakePath&& path) noexcept |
| : Path(std::forward<cm::filesystem::path>(path.Path)) |
| { |
| } |
| |
| cmCMakePath(cm::filesystem::path path) noexcept |
| : Path(std::move(path)) |
| { |
| } |
| cmCMakePath(cm::string_view source, format fmt = generic_format) noexcept |
| : Path(FormatPath(source, fmt)) |
| { |
| } |
| template <typename Source, typename = enable_if_move_pathable<Source>> |
| cmCMakePath(Source source, format fmt = generic_format) |
| : Path(FormatPath(std::move(source), fmt)) |
| { |
| } |
| |
| template <typename Source, typename = enable_if_move_pathable<Source>> |
| cmCMakePath& Assign(Source&& source) |
| { |
| this->Path = std::forward<Source>(source); |
| return *this; |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& Assign(const Source& source) |
| { |
| this->Path = source; |
| return *this; |
| } |
| |
| cmCMakePath& operator=(const cmCMakePath& path) |
| { |
| if (this != &path) { |
| this->Path = path.Path; |
| } |
| return *this; |
| } |
| cmCMakePath& operator=(cmCMakePath&& path) noexcept |
| { |
| if (this != &path) { |
| this->Path = std::move(path.Path); |
| } |
| return *this; |
| } |
| template <typename Source, typename = enable_if_move_pathable<Source>> |
| cmCMakePath& operator=(Source&& source) |
| { |
| this->Assign(std::forward<Source>(source)); |
| return *this; |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& operator=(const Source& source) |
| { |
| this->Assign(source); |
| return *this; |
| } |
| |
| // Concatenation |
| cmCMakePath& Append(const cmCMakePath& path) |
| { |
| return this->Append(path.Path); |
| } |
| cmCMakePath& Append(const cm::filesystem::path& path) |
| { |
| this->Path /= path; |
| // filesystem::path::append use preferred_separator ('\' on Windows) |
| // so convert back to '/' |
| this->Path = this->Path.generic_string(); |
| return *this; |
| } |
| |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& Append(const Source& source) |
| { |
| return this->Append(cm::filesystem::path(source)); |
| } |
| |
| cmCMakePath& operator/=(const cmCMakePath& path) |
| { |
| return this->Append(path); |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& operator/=(const Source& source) |
| { |
| return this->Append(source); |
| } |
| |
| cmCMakePath& Concat(const cmCMakePath& path) |
| { |
| this->Path += path.Path; |
| return *this; |
| } |
| cmCMakePath& Concat(cm::static_string_view source) |
| { |
| this->Path.concat(std::string(source)); |
| return *this; |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& Concat(const Source& source) |
| { |
| this->Path.concat(source); |
| return *this; |
| } |
| |
| cmCMakePath& operator+=(const cmCMakePath& path) |
| { |
| return this->Concat(path); |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& operator+=(const Source& source) |
| { |
| return this->Concat(source); |
| } |
| |
| // Manipulation |
| void Clear() noexcept { this->Path.clear(); } |
| |
| cmCMakePath& RemoveFileName() |
| { |
| this->Path.remove_filename(); |
| return *this; |
| } |
| |
| cmCMakePath& ReplaceFileName(const cmCMakePath& filename) |
| { |
| if (this->Path.has_filename()) { |
| this->Path.replace_filename(filename.Path); |
| } |
| return *this; |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& ReplaceFileName(const Source& filename) |
| { |
| if (this->Path.has_filename()) { |
| this->Path.replace_filename(filename); |
| } |
| return *this; |
| } |
| |
| cmCMakePath& ReplaceExtension(const cmCMakePath& extension = cmCMakePath()) |
| { |
| this->Path.replace_extension(extension.Path); |
| return *this; |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& ReplaceExtension(const Source& extension) |
| { |
| this->Path.replace_extension(extension); |
| return *this; |
| } |
| |
| cmCMakePath& ReplaceWideExtension( |
| const cmCMakePath& extension = cmCMakePath()) |
| { |
| return this->ReplaceWideExtension( |
| static_cast<cm::string_view>(extension.Path.string())); |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath& ReplaceWideExtension(const Source& extension) |
| { |
| return this->ReplaceWideExtension(cm::string_view(extension)); |
| } |
| cmCMakePath& ReplaceWideExtension(cm::string_view extension); |
| |
| cmCMakePath& RemoveExtension() |
| { |
| if (this->Path.has_extension()) { |
| this->ReplaceExtension(cm::string_view("")); |
| } |
| return *this; |
| } |
| |
| cmCMakePath& RemoveWideExtension() |
| { |
| if (this->Path.has_extension()) { |
| this->ReplaceWideExtension(cm::string_view("")); |
| } |
| return *this; |
| } |
| |
| void swap(cmCMakePath& other) noexcept { this->Path.swap(other.Path); } |
| |
| // Observers |
| std::string String() const { return this->Path.string(); } |
| std::wstring WString() const { return this->Path.wstring(); } |
| |
| string_type Native() const |
| { |
| string_type path; |
| this->GetNativePath(path); |
| |
| return path; |
| } |
| std::string NativeString() const |
| { |
| std::string path; |
| this->GetNativePath(path); |
| |
| return path; |
| } |
| std::wstring NativeWString() const |
| { |
| std::wstring path; |
| this->GetNativePath(path); |
| |
| return path; |
| } |
| std::string GenericString() const { return this->Path.generic_string(); } |
| std::wstring GenericWString() const { return this->Path.generic_wstring(); } |
| |
| // Decomposition |
| cmCMakePath GetRootName() const { return this->Path.root_name(); } |
| cmCMakePath GetRootDirectory() const { return this->Path.root_directory(); } |
| cmCMakePath GetRootPath() const { return this->Path.root_path(); } |
| cmCMakePath GetFileName() const { return this->Path.filename(); } |
| cmCMakePath GetExtension() const { return this->Path.extension(); } |
| cmCMakePath GetWideExtension() const; |
| cmCMakePath GetStem() const { return this->Path.stem(); } |
| cmCMakePath GetNarrowStem() const; |
| |
| cmCMakePath GetRelativePath() const { return this->Path.relative_path(); } |
| cmCMakePath GetParentPath() const { return this->Path.parent_path(); } |
| |
| // Generation |
| cmCMakePath Normal() const |
| { |
| auto path = this->Path.lexically_normal(); |
| // filesystem::path:lexically_normal use preferred_separator ('\') on |
| // Windows) so convert back to '/' |
| return path.generic_string(); |
| } |
| |
| cmCMakePath Relative(const cmCMakePath& base) const |
| { |
| return this->Relative(base.Path); |
| } |
| cmCMakePath Relative(const cm::filesystem::path& base) const |
| { |
| auto path = this->Path.lexically_relative(base); |
| // filesystem::path:lexically_relative use preferred_separator ('\') on |
| // Windows) so convert back to '/' |
| return path.generic_string(); |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath Relative(const Source& base) const |
| { |
| return this->Relative(cm::filesystem::path(base)); |
| } |
| |
| cmCMakePath Proximate(const cmCMakePath& base) const |
| { |
| return this->Proximate(base.Path); |
| } |
| cmCMakePath Proximate(const cm::filesystem::path& base) const |
| { |
| auto path = this->Path.lexically_proximate(base); |
| // filesystem::path::lexically_proximate use preferred_separator ('\') on |
| // Windows) so convert back to '/' |
| return path.generic_string(); |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath Proximate(const Source& base) const |
| { |
| return this->Proximate(cm::filesystem::path(base)); |
| } |
| |
| cmCMakePath Absolute(const cmCMakePath& base) const |
| { |
| return this->Absolute(base.Path); |
| } |
| template <typename Source, typename = enable_if_pathable<Source>> |
| cmCMakePath Absolute(const Source& base) const |
| { |
| return this->Absolute(cm::filesystem::path(base)); |
| } |
| cmCMakePath Absolute(const cm::filesystem::path& base) const; |
| |
| // Comparison |
| int Compare(const cmCMakePath& path) const noexcept |
| { |
| return this->Path.compare(path.Path); |
| } |
| |
| // Query |
| bool IsEmpty() const noexcept { return this->Path.empty(); } |
| |
| bool HasRootPath() const { return this->Path.has_root_path(); } |
| bool HasRootName() const { return this->Path.has_root_name(); } |
| bool HasRootDirectory() const { return this->Path.has_root_directory(); } |
| bool HasRelativePath() const { return this->Path.has_relative_path(); } |
| bool HasParentPath() const { return this->Path.has_parent_path(); } |
| bool HasFileName() const { return this->Path.has_filename(); } |
| bool HasStem() const { return this->Path.has_stem(); } |
| bool HasExtension() const { return this->Path.has_extension(); } |
| |
| bool IsAbsolute() const { return this->Path.is_absolute(); } |
| bool IsRelative() const { return this->Path.is_relative(); } |
| bool IsPrefix(const cmCMakePath& path) const; |
| |
| // Iterators |
| // ========= |
| inline iterator begin() const; |
| inline iterator end() const; |
| |
| // Non-members |
| // =========== |
| friend inline bool operator==(const cmCMakePath& lhs, |
| const cmCMakePath& rhs) noexcept |
| { |
| return lhs.Compare(rhs) == 0; |
| } |
| friend inline bool operator!=(const cmCMakePath& lhs, |
| const cmCMakePath& rhs) noexcept |
| { |
| return lhs.Compare(rhs) != 0; |
| } |
| |
| friend inline cmCMakePath operator/(const cmCMakePath& lhs, |
| const cmCMakePath& rhs) |
| { |
| cmCMakePath result(lhs); |
| result /= rhs; |
| |
| return result; |
| } |
| |
| private: |
| friend std::size_t hash_value(const cmCMakePath& path) noexcept; |
| |
| static std::string FormatPath(std::string path, format fmt = generic_format); |
| static std::string FormatPath(cm::string_view path, |
| format fmt = generic_format) |
| { |
| return FormatPath(std::string(path), fmt); |
| } |
| |
| void GetNativePath(std::string& path) const; |
| void GetNativePath(std::wstring& path) const; |
| |
| cm::filesystem::path Path; |
| }; |
| |
| class cmCMakePath::iterator |
| { |
| public: |
| using iterator_category = cm::filesystem::path::iterator::iterator_category; |
| |
| using value_type = cmCMakePath; |
| using difference_type = cm::filesystem::path::iterator::difference_type; |
| using pointer = const cmCMakePath*; |
| using reference = const cmCMakePath&; |
| |
| iterator() = default; |
| |
| iterator(const iterator& other) |
| : Iterator(other.Iterator) |
| , Path(other.Path) |
| , PathElement(*this->Iterator) |
| { |
| } |
| |
| ~iterator() = default; |
| |
| iterator& operator=(const iterator& other) |
| { |
| if (this != &other) { |
| this->Iterator = other.Iterator; |
| this->Path = other.Path; |
| this->PathElement = *this->Iterator; |
| } |
| |
| return *this; |
| } |
| |
| reference operator*() const { return this->PathElement; } |
| |
| pointer operator->() const { return &this->PathElement; } |
| |
| iterator& operator++() |
| { |
| ++this->Iterator; |
| this->PathElement = *this->Iterator; |
| |
| return *this; |
| } |
| |
| iterator operator++(int) |
| { |
| iterator it(*this); |
| this->operator++(); |
| return it; |
| } |
| |
| iterator& operator--() |
| { |
| --this->Iterator; |
| this->PathElement = *this->Iterator; |
| |
| return *this; |
| } |
| |
| iterator operator--(int) |
| { |
| iterator it(*this); |
| this->operator--(); |
| return it; |
| } |
| |
| private: |
| friend class cmCMakePath; |
| friend bool operator==(const iterator&, const iterator&); |
| |
| iterator(const cmCMakePath* path, const cm::filesystem::path::iterator& it) |
| : Iterator(it) |
| , Path(path) |
| , PathElement(*this->Iterator) |
| { |
| } |
| |
| cm::filesystem::path::iterator Iterator; |
| const cmCMakePath* Path = nullptr; |
| cmCMakePath PathElement; |
| }; |
| |
| inline cmCMakePath::iterator cmCMakePath::begin() const |
| { |
| return iterator(this, this->Path.begin()); |
| } |
| inline cmCMakePath::iterator cmCMakePath::end() const |
| { |
| return iterator(this, this->Path.end()); |
| } |
| |
| // Non-member functions |
| // ==================== |
| inline bool operator==(const cmCMakePath::iterator& lhs, |
| const cmCMakePath::iterator& rhs) |
| { |
| return lhs.Path == rhs.Path && lhs.Path != nullptr && |
| lhs.Iterator == rhs.Iterator; |
| } |
| |
| inline bool operator!=(const cmCMakePath::iterator& lhs, |
| const cmCMakePath::iterator& rhs) |
| { |
| return !(lhs == rhs); |
| } |
| |
| inline void swap(cmCMakePath& lhs, cmCMakePath& rhs) noexcept |
| { |
| lhs.swap(rhs); |
| } |
| |
| inline std::size_t hash_value(const cmCMakePath& path) noexcept |
| { |
| return cm::filesystem::hash_value(path.Path); |
| } |