diff options
author | Marc Chevrier <marc.chevrier@gmail.com> | 2020-06-21 12:28:37 (GMT) |
---|---|---|
committer | Marc Chevrier <marc.chevrier@gmail.com> | 2020-07-09 15:02:42 (GMT) |
commit | d654bf3449a24d412378af6c3aea53fa2899ad7d (patch) | |
tree | 7b5ac126f179e5513bbbc48fa03d3c598b02fec1 /Utilities | |
parent | e5798126fcfd08c7ca0f0f8d1fb627b04f655198 (diff) | |
download | CMake-d654bf3449a24d412378af6c3aea53fa2899ad7d.zip CMake-d654bf3449a24d412378af6c3aea53fa2899ad7d.tar.gz CMake-d654bf3449a24d412378af6c3aea53fa2899ad7d.tar.bz2 |
STL Support: Add cm::filesystem::path in <cm/filesystem>
Diffstat (limited to 'Utilities')
-rw-r--r-- | Utilities/std/CMakeLists.txt | 4 | ||||
-rw-r--r-- | Utilities/std/cm/bits/fs_path.cxx | 989 | ||||
-rw-r--r-- | Utilities/std/cm/filesystem | 1173 | ||||
-rw-r--r-- | Utilities/std/cmSTL.hxx.in | 1 |
4 files changed, 2166 insertions, 1 deletions
diff --git a/Utilities/std/CMakeLists.txt b/Utilities/std/CMakeLists.txt index 17a7aaa..23d9104 100644 --- a/Utilities/std/CMakeLists.txt +++ b/Utilities/std/CMakeLists.txt @@ -4,7 +4,9 @@ set(CMAKE_CXX_EXTENSIONS FALSE) # source files for CMake std library -set(SRCS cm/bits/string_view.cxx +set(SRCS cm/bits/fs_path.cxx + cm/bits/string_view.cxx + cm/filesystem cm/memory cm/optional cm/shared_mutex diff --git a/Utilities/std/cm/bits/fs_path.cxx b/Utilities/std/cm/bits/fs_path.cxx new file mode 100644 index 0000000..71386bb --- /dev/null +++ b/Utilities/std/cm/bits/fs_path.cxx @@ -0,0 +1,989 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ + +#include <cm/filesystem> // IWYU pragma: associated + +#if !defined(CMake_HAVE_CXX_FILESYSTEM) + +# include <algorithm> +# include <cassert> +# include <cstddef> +# include <cstdlib> +# include <functional> +# include <string> +# include <utility> +# include <vector> +# if defined(_WIN32) +# include <cctype> +# include <iterator> +# endif + +# include <cm/memory> +# include <cm/string_view> +# include <cmext/string_view> + +namespace cm { +namespace filesystem { +namespace internals { + +class path_parser +{ +# if defined(__SUNPRO_CC) && defined(__sparc) + // Oracle DeveloperStudio C++ compiler generates wrong code if enum size + // is different than the default. + using enum_size = int; +# else + using enum_size = unsigned char; +# endif + + enum class state : enum_size + { + before_begin, + in_root_name, + in_root_dir, + in_filename, + trailing_separator, + at_end + }; + + using pointer = char const*; + +public: + enum class seek_position : enum_size + { + root_name = static_cast<enum_size>(state::in_root_name), + root_directory = static_cast<enum_size>(state::in_root_dir) + }; + enum class peek_fragment : enum_size + { + remainder, + path + }; + + path_parser(cm::string_view path, bool set_at_end = false) + : State(set_at_end ? state::at_end : state::before_begin) + , Path(path) + { + } + + path_parser(const path_parser&) = default; + + ~path_parser() = default; + + void reset() noexcept { this->set_state(state::before_begin); } + + void increment() noexcept + { + const pointer start = this->next_token(); + const pointer end = this->after_end(); + + if (start == end) { + this->set_state(state::at_end); + return; + } + + switch (this->State) { + case state::before_begin: { + auto pos = this->consume_root_name(start, end); + if (pos) { + this->set_state(state::in_root_name); + } else { + pos = this->consume_separator(start, end); + if (pos) { + this->set_state(state::in_root_dir); + } else { + this->consume_filename(start, end); + this->set_state(state::in_filename); + } + } + break; + } + case state::in_root_name: { + auto pos = this->consume_separator(start, end); + if (pos) { + this->set_state(state::in_root_dir); + } else { + this->consume_filename(start, end); + this->set_state(state::in_filename); + } + break; + } + case state::in_root_dir: { + this->consume_filename(start, end); + this->set_state(state::in_filename); + break; + } + case state::in_filename: { + auto posSep = this->consume_separator(start, end); + if (posSep != end) { + auto pos = this->consume_filename(posSep, end); + if (pos) { + return; + } + } + set_state(state::trailing_separator); + break; + } + case state::trailing_separator: { + this->set_state(state::at_end); + break; + } + case state::at_end: + // unreachable + std::abort(); + } + } + + void decrement() noexcept + { + const pointer rstart = this->current_token() - 1; + const pointer rend = this->before_start(); + + if (rstart == rend) { + this->set_state(state::before_begin); + return; + } + + switch (this->State) { + case state::at_end: { + auto posSep = this->consume_separator(rstart, rend); + if (posSep) { + if (posSep == rend) { + this->set_state(state::in_root_dir); + } else { + auto pos = this->consume_root_name(posSep, rend, true); + if (pos == rend) { + this->set_state(state::in_root_dir); + } else { + this->set_state(state::trailing_separator); + } + } + } else { + auto pos = this->consume_root_name(rstart, rend); + if (pos == rend) { + this->set_state(state::in_root_name); + } else { + this->consume_filename(rstart, rend); + this->set_state(state::in_filename); + } + } + break; + } + case state::trailing_separator: { + this->consume_filename(rstart, rend); + this->set_state(state::in_filename); + break; + } + case state::in_filename: { + auto posSep = this->consume_separator(rstart, rend); + if (posSep == rend) { + this->set_state(state::in_root_dir); + } else { + auto pos = this->consume_root_name(posSep, rend, true); + if (pos == rend) { + this->set_state(state::in_root_dir); + } else { + this->consume_filename(posSep, rend); + this->set_state(state::in_filename); + } + } + break; + } + case state::in_root_dir: { + auto pos = this->consume_root_name(rstart, rend); + if (pos) { + this->set_state(state::in_root_name); + } + break; + } + case state::in_root_name: + case state::before_begin: { + // unreachable + std::abort(); + } + } + } + + path_parser& operator++() noexcept + { + this->increment(); + return *this; + } + + path_parser& operator--() noexcept + { + this->decrement(); + return *this; + } + + cm::string_view operator*() const noexcept + { + switch (this->State) { + case state::before_begin: + case state::at_end: + return cm::string_view(); + case state::trailing_separator: + return ""; + case state::in_root_dir: + case state::in_root_name: + case state::in_filename: + return this->Entry; + default: + // unreachable + std::abort(); + } + } + + void seek(seek_position position) + { + state s = static_cast<state>(static_cast<int>(position)); + + while (this->State <= s) { + this->increment(); + } + } + + cm::string_view peek(peek_fragment fragment) + { + if (fragment == peek_fragment::remainder) { + // peek-up remain part of the initial path + return { this->Entry.data(), + std::size_t(&this->Path.back() - this->Entry.data() + 1) }; + } + if (fragment == peek_fragment::path) { + // peek-up full path until current position + return { this->Path.data(), + std::size_t(&this->Entry.back() - this->Path.data() + 1) }; + } + return {}; + } + + bool in_root_name() const { return this->State == state::in_root_name; } + bool in_root_directory() const { return this->State == state::in_root_dir; } + bool at_end() const { return this->State == state::at_end; } + + bool at_start() const { return this->Entry.data() == this->Path.data(); } + +private: + void set_state(state newState) noexcept + { + this->State = newState; + if (newState == state::before_begin || newState == state::at_end) { + this->Entry = {}; + } + } + + pointer before_start() const noexcept { return this->Path.data() - 1; } + pointer after_end() const noexcept + { + return this->Path.data() + this->Path.size(); + } + + pointer current_token() const noexcept + { + switch (this->State) { + case state::before_begin: + case state::in_root_name: + return &this->Path.front(); + case state::in_root_dir: + case state::in_filename: + case state::trailing_separator: + return &this->Entry.front(); + case state::at_end: + return &this->Path.back() + 1; + default: + // unreachable + std::abort(); + } + } + pointer next_token() const noexcept + { + switch (this->State) { + case state::before_begin: + return this->Path.data(); + case state::in_root_name: + case state::in_root_dir: + case state::in_filename: + return &this->Entry.back() + 1; + case state::trailing_separator: + case state::at_end: + return after_end(); + default: + // unreachable + std::abort(); + } + } + + pointer consume_separator(pointer ptr, pointer end) noexcept + { + if (ptr == end || + (*ptr != '/' +# if defined(_WIN32) + && *ptr != '\\' +# endif + )) { + return nullptr; + } + const auto step = ptr < end ? 1 : -1; + ptr += step; + while (ptr != end && + (*ptr == '/' +# if defined(_WIN32) + || *ptr == ' \\' +# endif + )) { + ptr += step; + } + if (step == 1) { + this->Entry = cm::string_view(ptr - 1, 1); + } else { + this->Entry = cm::string_view(ptr + 1, 1); + } + + return ptr; + } + + pointer consume_filename(pointer ptr, pointer end) noexcept + { + auto start = ptr; + + if (ptr == end || *ptr == '/' +# if defined(_WIN32) + || *ptr == '\\' +# endif + ) { + return nullptr; + } + const auto step = ptr < end ? 1 : -1; + ptr += step; + while (ptr != end && *ptr != '/' +# if defined(_WIN32) + && *ptr != '\\' +# endif + ) { + ptr += step; + } + +# if defined(_WIN32) + if (step == -1 && (start - ptr) >= 2 && ptr == end) { + // rollback drive name consumption, if any + if (this->is_drive_name(ptr + 1)) { + ptr += 2; + } + if (ptr == start) { + return nullptr; + } + } +# endif + + if (step == 1) { + this->Entry = cm::string_view(start, ptr - start); + } else { + this->Entry = cm::string_view(ptr + 1, start - ptr); + } + + return ptr; + } + +# if defined(_WIN32) + bool is_drive_name(pointer ptr) + { + return std::toupper(ptr[0]) >= 'A' && std::toupper(ptr[0]) <= 'Z' && + ptr[1] == ':'; + } +# endif + + pointer consume_root_name(pointer ptr, pointer end, + bool check_only = false) noexcept + { +# if defined(_WIN32) + if (ptr < end) { + if ((end - ptr) >= 2 && this->is_drive_name(ptr)) { + // Drive letter (X:) is a root name + if (!check_only) { + this->Entry = cm::string_view(ptr, 2); + } + return ptr + 2; + } + if ((end - ptr) > 2 && (ptr[0] == '/' || ptr[0] == '\\') && + (ptr[1] == '/' || ptr[1] == '\\') && + (ptr[2] != '/' && ptr[2] != '\\')) { + // server name (//server) is a root name + auto pos = std::find(ptr + 2, end, '/'); + if (!check_only) { + this->Entry = cm::string_view(ptr, pos - ptr); + } + return pos; + } + } else { + if ((ptr - end) >= 2 && this->is_drive_name(ptr - 1)) { + // Drive letter (X:) is a root name + if (!check_only) { + this->Entry = cm::string_view(ptr - 1, 2); + } + return ptr - 2; + } + if ((ptr - end) > 2 && (ptr[0] != '/' && ptr[0] != '\\')) { + std::reverse_iterator<pointer> start(ptr); + std::reverse_iterator<pointer> stop(end); + auto res = std::find_if(start, stop, + [](char c) { return c == '/' || c == '\\'; }); + pointer pos = res.base() - 1; + if ((pos - 1) > end && (pos[-1] == '/' || pos[-1] == '\\')) { + // server name (//server) is a root name + if (!check_only) { + this->Entry = cm::string_view(pos - 1, ptr - pos + 2); + } + return pos - 2; + } + } + } +# else + (void)ptr; + (void)end; + (void)check_only; +# endif + return nullptr; + } + + state State; + const cm::string_view Path; + cm::string_view Entry; +}; + +// class unicode_helper +void unicode_helper::append(std::string& 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); + } +} + +unicode_helper::utf8_state unicode_helper::decode(const utf8_state state, + const std::uint8_t fragment, + std::uint32_t& codepoint) +{ + const std::uint32_t utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, + 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, + 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, + 0x99999999u, 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, + 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, + 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, + 0u, 0u, + }; + std::uint8_t category = fragment < 128 + ? 0 + : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; + codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) + : (0xffu >> category) & fragment); + return state == s_reject + ? s_reject + : static_cast<utf8_state>( + (utf8_state_info[category + 16] >> (state << 2)) & 0xf); +} + +} // internals + +// Class path +path& path::operator/=(const path& p) +{ + if (p.is_absolute() || + (p.has_root_name() && p.get_root_name() != this->get_root_name())) { + this->path_ = p.path_; + return *this; + } + if (p.has_root_directory()) { + this->path_ = static_cast<std::string>(this->get_root_name()); + this->path_ += static_cast<std::string>(p.get_root_directory()); + } else if (this->has_filename()) { + this->path_ += this->preferred_separator; +# if defined(_WIN32) + // special case: "//host" / "b" => "//host/b" + } else if (this->has_root_name() && !this->has_root_directory()) { + if (this->path_.length() >= 3 && + (this->path_[0] == '/' || this->path_[0] == '\\') && + (this->path_[1] == '/' || this->path_[1] == '\\') && + (this->path_[2] != '/' || this->path_[2] != '\\')) { + this->path_ += this->preferred_separator; + } +# endif + } + + this->path_ += p.get_relative_path(); + return *this; +} + +path path::lexically_normal() const +{ + if (this->path_.empty()) { + return *this; + } + + const cm::string_view dot = "."_s; + const cm::string_view dotdot = ".."_s; + + std::vector<cm::string_view> root_parts; + std::vector<cm::string_view> parts; + bool root_directory_defined = false; + bool need_final_separator = false; + std::size_t path_size = 0; + + internals::path_parser parser(this->path_); + ++parser; + while (!parser.at_end()) { + auto part = *parser; + + if (parser.in_root_name() || parser.in_root_directory()) { + if (parser.in_root_directory()) { + root_directory_defined = true; + } + root_parts.push_back(part); + path_size += part.size(); + } else if (part == dotdot) { + if (!parts.empty() && parts.back() != dotdot) { + need_final_separator = true; + path_size -= parts.back().size(); + parts.pop_back(); + } else if ((parts.empty() || parts.back() == dotdot) && + !root_directory_defined) { + parts.push_back(dotdot); + path_size += 2; + } + + } else if (part == dot || part.empty()) { + need_final_separator = true; + if (part.empty()) { + parts.push_back(part); + } + } else { + // filename + need_final_separator = false; + parts.push_back(part); + path_size += part.size(); + } + ++parser; + } + + // no final separator if last element of path is ".." + need_final_separator = + need_final_separator && !parts.empty() && parts.back() != dotdot; + + // build final path + //// compute final size of path + path_size += parts.size() + (need_final_separator ? 1 : 0); + + std::string np; + np.reserve(path_size); + for (const auto& p : root_parts) { + np += p; + } + // convert any slash to the preferred_separator + if (static_cast<std::string::value_type>(this->preferred_separator) != '/') { + std::replace( + np.begin(), np.end(), '/', + static_cast<std::string::value_type>(this->preferred_separator)); + } + for (const auto& p : parts) { + if (!p.empty()) { + np += p; + np += static_cast<std::string::value_type>(this->preferred_separator); + } + } + if (!parts.empty() && !need_final_separator) { + // remove extra separator + np.pop_back(); + } + if (np.empty()) { + np.assign(1, '.'); + } + + return path(std::move(np)); +} + +path path::lexically_relative(const path& base) const +{ + internals::path_parser parser(this->path_); + ++parser; + internals::path_parser parserbase(base.path_); + ++parserbase; + cm::string_view this_root_name, base_root_name; + cm::string_view this_root_dir, base_root_dir; + + if (parser.in_root_name()) { + this_root_name = *parser; + ++parser; + } + if (parser.in_root_directory()) { + this_root_dir = *parser; + ++parser; + } + if (parserbase.in_root_name()) { + base_root_name = *parserbase; + ++parserbase; + } + if (parserbase.in_root_directory()) { + base_root_dir = *parserbase; + ++parserbase; + } + + auto is_path_absolute = [](cm::string_view rn, cm::string_view rd) -> bool { +# if defined(_WIN32) + return !rn.empty() && !rd.empty(); +# else + (void)rn; + return !rd.empty(); +# endif + }; + + if (this_root_name != base_root_name || + is_path_absolute(this_root_name, this_root_dir) != + is_path_absolute(base_root_name, base_root_dir) || + (this_root_dir.empty() && !base_root_dir.empty())) { + return path(); + } + +# if defined(_WIN32) + // LWG3070 handle special case: filename can also be a root-name + auto is_drive_name = [](cm::string_view item) -> bool { + return item.length() == 2 && item[1] == ':'; + }; + parser.reset(); + parser.seek(internals::path_parser::seek_position::root_directory); + while (!parser.at_end()) { + if (is_drive_name(*parser)) { + return path(); + } + ++parser; + } + parserbase.reset(); + parserbase.seek(internals::path_parser::seek_position::root_directory); + while (!parserbase.at_end()) { + if (is_drive_name(*parserbase)) { + return path(); + } + ++parserbase; + } +# endif + + const cm::string_view dot = "."_s; + const cm::string_view dotdot = ".."_s; + + auto a = this->begin(), aend = this->end(); + auto b = base.begin(), bend = base.end(); + while (a != aend && b != bend && a->string() == b->string()) { + ++a; + ++b; + } + + int count = 0; + for (; b != bend; ++b) { + auto part = *b; + if (part == dotdot) { + --count; + } else if (part.string() != dot && !part.empty()) { + ++count; + } + } + + if (count == 0 && (a == this->end() || a->empty())) { + return path(dot); + } + if (count >= 0) { + path result; + path p_dotdot(dotdot); + for (int i = 0; i < count; ++i) { + result /= p_dotdot; + } + for (; a != aend; ++a) { + result /= *a; + } + return result; + } + // count < 0 + return path(); +} + +path::path_type path::get_generic() const +{ + auto gen_path = this->path_; + auto start = gen_path.begin(); +# if defined(_WIN32) + std::replace(gen_path.begin(), gen_path.end(), '\\', '/'); + // preserve special syntax for root_name ('//server' or '//?') + if (gen_path.length() > 2 && gen_path[2] != '/') { + start += 2; + } +# endif + // remove duplicate separators + auto new_end = std::unique(start, gen_path.end(), [](char lhs, char rhs) { + return lhs == rhs && lhs == '/'; + }); + gen_path.erase(new_end, gen_path.end()); + return gen_path; +} + +cm::string_view path::get_root_name() const +{ + internals::path_parser parser(this->path_); + ++parser; + if (parser.in_root_name()) { + return *parser; + } + return {}; +} + +cm::string_view path::get_root_directory() const +{ + internals::path_parser parser(this->path_); + ++parser; + if (parser.in_root_name()) { + ++parser; + } + if (parser.in_root_directory()) { + return *parser; + } + return {}; +} + +cm::string_view path::get_relative_path() const +{ + internals::path_parser parser(this->path_); + parser.seek(internals::path_parser::seek_position::root_directory); + if (parser.at_end()) { + return {}; + } + return parser.peek(internals::path_parser::peek_fragment::remainder); +} + +cm::string_view path::get_parent_path() const +{ + if (!this->has_relative_path()) { + return this->path_; + } + + // peek-up full path minus last element + internals::path_parser parser(this->path_, true); + --parser; + if (parser.at_start()) { + return {}; + } + --parser; + return parser.peek(internals::path_parser::peek_fragment::path); +} + +cm::string_view path::get_filename() const +{ + { + internals::path_parser parser(this->path_); + parser.seek(internals::path_parser::seek_position::root_directory); + if (parser.at_end()) { + return {}; + } + } + { + internals::path_parser parser(this->path_, true); + return *(--parser); + } +} + +cm::string_view path::get_filename_fragment(filename_fragment fragment) const +{ + auto file = this->get_filename(); + + if (file == "." || file == ".." || file.empty()) { + return fragment == filename_fragment::stem ? file : cm::string_view{}; + } + + auto pos = file.find_last_of('.'); + if (pos == cm::string_view::npos || pos == 0) { + return fragment == filename_fragment::stem ? file : cm::string_view{}; + } + return fragment == filename_fragment::stem ? file.substr(0, pos) + : file.substr(pos); +} + +int path::compare_path(cm::string_view str) const +{ + internals::path_parser this_pp(this->path_); + ++this_pp; + internals::path_parser other_pp(str); + ++other_pp; + + // compare root_name part + { + bool compare_root_names = false; + cm::string_view this_root_name, other_root_name; + int res; + + if (this_pp.in_root_name()) { + compare_root_names = true; + this_root_name = *this_pp; + ++this_pp; + } + if (other_pp.in_root_name()) { + compare_root_names = true; + other_root_name = *other_pp; + ++other_pp; + } + if (compare_root_names && + (res = this_root_name.compare(other_root_name) != 0)) { + return res; + } + } + + // compare root_directory part + { + if (!this_pp.in_root_directory() && other_pp.in_root_directory()) { + return -1; + } else if (this_pp.in_root_directory() && !other_pp.in_root_directory()) { + return 1; + } + if (this_pp.in_root_directory()) { + ++this_pp; + } + if (other_pp.in_root_directory()) { + ++other_pp; + } + } + + // compare various parts of the paths + while (!this_pp.at_end() && !other_pp.at_end()) { + int res; + if ((res = (*this_pp).compare(*other_pp)) != 0) { + return res; + } + ++this_pp; + ++other_pp; + } + + // final step + if (this_pp.at_end() && !other_pp.at_end()) { + return -1; + } else if (!this_pp.at_end() && other_pp.at_end()) { + return 1; + } + + return 0; +} + +// Class path::iterator +path::iterator::iterator() + : path_(nullptr) +{ +} +path::iterator::iterator(const iterator& other) +{ + this->path_ = other.path_; + if (other.parser_) { + this->parser_ = cm::make_unique<internals::path_parser>(*other.parser_); + this->path_element_ = path(**this->parser_); + } +} +path::iterator::iterator(const path* p, bool at_end) + : path_(p) + , parser_(cm::make_unique<internals::path_parser>(p->path_, at_end)) +{ + if (!at_end) { + ++(*this->parser_); + this->path_element_ = path(**this->parser_); + } +} + +path::iterator::~iterator() = default; + +path::iterator& path::iterator::operator=(const iterator& other) +{ + this->path_ = other.path_; + if (other.parser_) { + this->parser_ = cm::make_unique<internals::path_parser>(*other.parser_); + this->path_element_ = path(**this->parser_); + } + + return *this; +} + +path::iterator& path::iterator::operator++() +{ + assert(this->parser_); + + if (this->parser_) { + assert(!this->parser_->at_end()); + + if (!this->parser_->at_end()) { + ++(*this->parser_); + if (this->parser_->at_end()) { + this->path_element_ = path(); + } else { + this->path_element_ = path(**this->parser_); + } + } + } + + return *this; +} + +path::iterator& path::iterator::operator--() +{ + assert(this->parser_); + + if (this->parser_) { + assert(!this->parser_->at_start()); + + if (!this->parser_->at_start()) { + --(*this->parser_); + this->path_element_ = path(**this->parser_); + } + } + + return *this; +} + +bool operator==(const path::iterator& lhs, const path::iterator& rhs) +{ + return lhs.path_ == rhs.path_ && lhs.parser_ != nullptr && + ((lhs.parser_->at_end() && rhs.parser_->at_end()) || + (lhs.parser_->at_start() && rhs.parser_->at_start()) || + ((**lhs.parser_).data() == (**rhs.parser_).data())); +} + +std::size_t hash_value(const path& p) noexcept +{ + internals::path_parser parser(p.path_); + std::hash<cm::string_view> hasher; + std::size_t value = 0; + + while (!parser.at_end()) { + value = hasher(*parser) + 0x9e3779b9 + (value << 6) + (value >> 2); + ++parser; + } + + return value; +} +} // filesystem +} // cm + +#else + +// Avoid empty translation unit. +void cm_filesystem_path_cxx() +{ +} + +#endif diff --git a/Utilities/std/cm/filesystem b/Utilities/std/cm/filesystem new file mode 100644 index 0000000..d7ade34 --- /dev/null +++ b/Utilities/std/cm/filesystem @@ -0,0 +1,1173 @@ +// -*-c++-*- +// vim: set ft=cpp: + +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef cm_filesystem +#define cm_filesystem + +#include "cmSTL.hxx" // IWYU pragma: keep + +#if defined(CMake_HAVE_CXX_FILESYSTEM) + +# include <filesystem> // IWYU pragma: export + +#else + +# include <cstddef> +# include <cstdint> +# include <iostream> +# include <iterator> +# include <memory> +# include <string> +# include <utility> + +# include <cm/iomanip> +# include <cm/string_view> +# include <cm/type_traits> +# include <cmext/iterator> + +# if defined(_WIN32) +# include <algorithm> +# endif + +#endif + +namespace cm { +namespace filesystem { + +#if defined(CMake_HAVE_CXX_FILESYSTEM) + +using std::filesystem::path; +using std::filesystem::swap; +using std::filesystem::hash_value; + +#else + +# 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 path_parser; + +class unicode_helper +{ +protected: + using utf8_state = unsigned char; + static const utf8_state s_start = 0; + static const utf8_state 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; + } + + static void append(std::string& str, std::uint32_t codepoint); + + static utf8_state decode(const utf8_state state, const std::uint8_t 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 + static std::string to_utf8(const std::wstring& str) + { + std::string result; + for (auto c : str) { + append(result, c); + } + return result; + } + static std::string to_utf8(Char c) + { + std::string result; + append(result, c); + return result; + } + + // UTF8 -> UTF32 + static std::wstring from_utf8(const std::string& str) + { + std::wstring result; + result.reserve(str.length()); + auto iter = str.begin(); + utf8_state state = s_start; + std::uint32_t codepoint = 0; + while (iter < str.end()) { + if ((state = decode(state, static_cast<std::uint8_t>(*iter++), + 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; + } + static std::wstring from_utf8(char 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 + static std::string to_utf8(const std::wstring& str) + { + std::string result; + for (auto iter = str.begin(); iter != str.end(); ++iter) { + std::uint32_t c = *iter; + if (is_surrogate(c)) { + ++iter; + if (iter != str.end() && is_high_surrogate(c) && + is_low_surrogate(*iter)) { + append(result, (std::uint32_t(c) << 10) + *iter - 0x35fdc00); + } else { + append(result, 0xfffd); + if (iter == str.end()) { + break; + } + } + } else { + append(result, c); + } + } + return result; + } + static std::string to_utf8(Char c) + { + std::string result; + if (is_surrogate(c)) { + append(result, 0xfffd); + } else { + append(result, c); + } + return result; + } + + // UTF8 -> UTF16 + static std::wstring from_utf8(const std::string& str) + { + std::wstring result; + result.reserve(str.length()); + auto iter = str.begin(); + utf8_state state = s_start; + std::uint32_t codepoint = 0; + while (iter < str.end()) { + if ((state = decode(state, static_cast<std::uint8_t>(*iter++), + 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; + } + static std::wstring from_utf8(char 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()(const std::string& in) + { + return unicode<wchar_t>::from_utf8(in); + } + std::wstring operator()(const char* 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()(const std::wstring& in) + { + return unicode<wchar_t>::to_utf8(in); + } + std::string operator()(const wchar_t* in) + { + return unicode<wchar_t>::to_utf8(in); + } + std::string operator()(wchar_t in) { return unicode<wchar_t>::to_utf8(in); } +}; +template <> +class unicode_converter<char, char> +{ +public: + std::string operator()(const std::string& in) { return in; } + std::string operator()(const char* 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()(const std::wstring& in) { return in; } + std::wstring operator()(const wchar_t* in) { return std::wstring(in); } + std::wstring operator()(wchar_t in) { return std::wstring(1, in); } +}; + +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(const std::string& in, + const Alloc& 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(const char* in, + const Alloc& 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, const Alloc& 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(const std::string& in) + { + return std::basic_string<Char>(unicode_converter<char, Char>()(in)); + } + template <typename Char> + static std::basic_string<Char> to(const char* 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 <> +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(const std::wstring& in, + const Alloc& 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(const wchar_t* in, + const Alloc& 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, const Alloc& 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(const std::wstring& in) + { + return std::basic_string<Char>(unicode_converter<wchar_t, Char>()(in)); + } + template <typename Char> + static std::basic_string<Char> to(const wchar_t* 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 T, typename = void> +struct source_traits +{ +}; + +template <typename T, std::size_t N> +struct source_traits<T[N]> +{ + using value_type = 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 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, const cm::string_view s) + { + append_range(p, s.begin(), s.end()); + } + template <typename Traits, typename Alloc> + static void append_source(std::string& p, + const std::basic_string<char, Traits, Alloc>& s) + { + append_range(p, s.begin(), s.end()); + } + template <typename Source> + static void append_source(std::string& p, const Source& s) + { + append_range(p, s); + } + + static void set_source(std::string& p, std::string&& s) { p = std::move(s); } +}; + +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::wstring tmp(b, e); + std::string dest = string_converter<wchar_t>::to<char>(tmp); + 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::wstring tmp; + for (; *b != e; ++b) { + tmp.push_back(*b); + } + + std::string dest = string_converter<wchar_t>::to<char>(tmp); + p.append(dest.begin(), dest.end()); + } + + template <typename Traits, typename Alloc> + static void append_source(std::string& p, + const std::basic_string<wchar_t, Traits, Alloc>& s) + { + append_range(p, s.begin(), s.end()); + } + template <typename Source> + static void append_source(std::string& p, const Source& s) + { + append_range(p, s); + } + + static void set_source(std::string& p, std::wstring&& s) + { + p = string_converter<wchar_t>::to<char>(s); + } +}; + +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 +} + +class path +{ + 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) + 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) + static constexpr value_type preferred_separator = L'\\'; +# else + static constexpr value_type preferred_separator = '/'; +# endif + + // Constructors + // ============ + path() noexcept {} + path(const path& 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(const Source& 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(const 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=(const path& 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(source); } + template <typename Source, typename = enable_if_pathable<Source>> + path& operator=(const Source& 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(const Source& 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/=(const path& p); + + template <typename Source, typename = enable_if_pathable<Source>> + path& append(const Source& source) + { + return this->operator/=(path(source)); + } + template <typename Source> + path& operator/=(const Source& 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+=(const path& p) + { + this->path_ += p.path_; + return *this; + } + path& operator+=(const string_type& 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+=(const value_type* str) + { + this->path_ += + internals::string_converter<value_type>::to<path_type::value_type>(str); + return *this; + } + path& operator+=(const value_type 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(const Source& 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+=(const Source& 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) + 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() - this->path_.data()); + } + return *this; + } + + path& replace_filename(const path& replacement) + { + this->remove_filename(); + this->operator/=(replacement); + return *this; + } + + path& replace_extension(const path& replacement = path()) + { + auto ext = this->get_filename_fragment(filename_fragment::extension); + if (!ext.empty()) { + this->path_.erase(ext.data() - 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 + // ================ + const string_type& native() const noexcept + { +# if defined(_WIN32) + 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 + } + const value_type* 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(const Alloc& a = Alloc()) const + { + return internals::string_converter<path_type::value_type>::to<Char, Traits, + Alloc>( + this->path_, a); + } + const std::string string() const { return this->path_; } + std::wstring wstring() const + { + std::string out = this->string(); + return internals::string_converter<path_type::value_type>::to< + std::wstring::value_type>(out); + } + + 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( + const Alloc& 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 + { + auto dest = this->generic_string(); + return internals::string_converter<path_type::value_type>::to< + std::wstring::value_type>(dest); + } + + // Compare + // ======= + int compare(const path& p) const noexcept + { + return this->compare_path(p.path_); + } + int compare(const string_type& str) const + { + return this->compare_path( + internals::string_converter<value_type>::to<path_type::value_type>(str)); + } + int compare(const value_type* 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(const path& base) const; + + path lexically_proximate(const path& 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) + return this->has_root_name() && this->has_root_directory(); +# else + 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==(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) == 0; + } + friend inline bool operator!=(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) != 0; + } + friend inline bool operator<(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + friend inline bool operator<=(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + friend inline bool operator>(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + friend inline bool operator>=(const path& lhs, const path& rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + + friend inline path operator/(const path& lhs, const path& 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, const path& 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(const path& 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) + 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 = const path*; + using reference = const path&; + + iterator(); + iterator(const iterator& other); + + ~iterator(); + + iterator& operator=(const iterator& 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==(const iterator&, const iterator&); + + iterator(const path* p, bool at_end = false); + + const path* 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); +} + +bool operator==(const path::iterator& lhs, const path::iterator& rhs); + +inline bool operator!=(const path::iterator& lhs, const path::iterator& rhs) +{ + return !(lhs == rhs); +} + +// Non-member functions +// ==================== +inline void swap(path& lhs, path& rhs) noexcept +{ + lhs.swap(rhs); +} + +std::size_t hash_value(const path& p) noexcept; + +#endif + +} // namespace filesystem +} // namespace cm + +#endif diff --git a/Utilities/std/cmSTL.hxx.in b/Utilities/std/cmSTL.hxx.in index 28fe226..9c8605c 100644 --- a/Utilities/std/cmSTL.hxx.in +++ b/Utilities/std/cmSTL.hxx.in @@ -5,5 +5,6 @@ /* Whether CMake is using its own STL implementation. */ #cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE +#cmakedefine CMake_HAVE_CXX_FILESYSTEM #endif |