Merge topic 'stl-support-filesystem-path'

d654bf3449 STL Support: Add cm::filesystem::path in <cm/filesystem>
e5798126fc STL Support: introduce dedicated configuration file
4408f34cfe STL Support: Add function cm::quoted in <cm/iomanip>

Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !4968
This commit is contained in:
Brad King
2020-07-13 11:59:35 +00:00
committed by Kitware Robot
15 changed files with 3404 additions and 3 deletions

View File

@@ -20,6 +20,8 @@ SortUsingDeclarations: false
SpaceAfterTemplateKeyword: true
IncludeBlocks: Regroup
IncludeCategories:
- Regex: '^[<"]cmSTL\.hxx'
Priority: -2
- Regex: '^[<"]cmConfigure\.h'
Priority: -1
- Regex: '^<queue>'

View File

@@ -108,6 +108,11 @@ if(NOT CMake_TEST_EXTERNAL_CMAKE)
endif()
endif()
# Inform STL library header wrappers whether to use system versions.
configure_file(${CMake_SOURCE_DIR}/Utilities/std/cmSTL.hxx.in
${CMake_BINARY_DIR}/Utilities/cmSTL.hxx
@ONLY)
# set the internal encoding of CMake to UTF-8
set(KWSYS_ENCODING_DEFAULT_CODEPAGE CP_UTF8)

View File

@@ -35,6 +35,9 @@ Available features are:
* From ``C++14``:
* ``<cm/iomanip>``:
``cm::quoted``
* ``<cm/iterator>``:
``cm::make_reverse_iterator``, ``cm::cbegin``, ``cm::cend``,
``cm::rbegin``, ``cm::rend``, ``cm::crbegin``, ``cm::crend``
@@ -53,6 +56,9 @@ Available features are:
* ``<cm/algorithm>``:
``cm::clamp``
* ``cm/filesystem>``:
``cm::filesystem::path``
* ``<cm/iterator>``:
``cm::size``, ``cm::empty``, ``cm::data``

View File

@@ -63,3 +63,8 @@ if(CMake_HAVE_CXX_MAKE_UNIQUE)
set(CMake_HAVE_CXX_UNIQUE_PTR 1)
endif()
cm_check_cxx_feature(unique_ptr)
if (NOT CMAKE_CXX_STANDARD LESS "17")
cm_check_cxx_feature(filesystem)
else()
set(CMake_HAVE_CXX_FILESYSTEM FALSE)
endif()

View File

@@ -0,0 +1,10 @@
#include <filesystem>
int main()
{
std::filesystem::path p1("/a/b/c");
std::filesystem::path p2("/a/b/c");
return p1 == p2 ? 0 : 1;
}

View File

@@ -19,7 +19,6 @@
#cmakedefine HAVE_UNSETENV
#cmakedefine CMAKE_USE_ELF_PARSER
#cmakedefine CMAKE_USE_MACH_PARSER
#cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE
#define CMake_DEFAULT_RECURSION_LIMIT @CMake_DEFAULT_RECURSION_LIMIT@
#define CMAKE_BIN_DIR "/@CMAKE_BIN_DIR@"
#define CMAKE_DATA_DIR "/@CMAKE_DATA_DIR@"

View File

@@ -29,6 +29,9 @@ set(CMakeLib_TESTS
testCMExtMemory.cxx
testCMExtAlgorithm.cxx
)
if (CMake_TEST_FILESYSTEM_PATH OR NOT CMake_HAVE_CXX_FILESYSTEM)
list(APPEND CMakeLib_TESTS testCMFilesystemPath.cxx)
endif()
add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx)

File diff suppressed because it is too large Load Diff

View File

@@ -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

View File

@@ -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

1173
Utilities/std/cm/filesystem Normal file

File diff suppressed because it is too large Load Diff

183
Utilities/std/cm/iomanip Normal file
View File

@@ -0,0 +1,183 @@
// -*-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_iomanip
#define cm_iomanip
#include <iomanip> // IWYU pragma: export
#if __cplusplus < 201402L || defined(_MSVC_LANG) && _MSVC_LANG < 201402L
# include <ios>
# include <iostream>
# include <sstream>
# include <string>
# include <type_traits>
#endif
#if __cplusplus < 201703L || defined(_MSVC_LANG) && _MSVC_LANG < 201703L
# include <cm/string_view>
#endif
namespace cm {
#if __cplusplus >= 201402L || defined(_MSVC_LANG) && _MSVC_LANG >= 201402L
using std::quoted;
# if __cplusplus < 201703L || defined(_MSVC_LANG) && _MSVC_LANG < 201703L
inline auto quoted(cm::string_view str, char delim = '"', char escape = '\\')
{
return std::quoted(static_cast<std::string>(str), delim, escape);
}
# endif
#else
namespace internals {
// Struct for delimited strings.
template <typename String, typename Char>
struct quoted_string
{
static_assert(std::is_reference<String>::value ||
std::is_pointer<String>::value,
"String type must be pointer or reference");
quoted_string(String str, Char del, Char esc)
: string_(str)
, delim_{ del }
, escape_{ esc }
{
}
quoted_string& operator=(quoted_string&) = delete;
String string_;
Char delim_;
Char escape_;
};
template <>
struct quoted_string<cm::string_view, char>
{
quoted_string(cm::string_view str, char del, char esc)
: string_(str)
, delim_{ del }
, escape_{ esc }
{
}
quoted_string& operator=(quoted_string&) = delete;
cm::string_view string_;
char delim_;
char escape_;
};
template <typename Char, typename Traits>
std::basic_ostream<Char, Traits>& operator<<(
std::basic_ostream<Char, Traits>& os,
const quoted_string<const Char*, Char>& str)
{
std::basic_ostringstream<Char, Traits> ostr;
ostr << str.delim_;
for (const Char* c = str.string_; *c; ++c) {
if (*c == str.delim_ || *c == str.escape_)
ostr << str.escape_;
ostr << *c;
}
ostr << str.delim_;
return os << ostr.str();
}
template <typename Char, typename Traits, typename String>
std::basic_ostream<Char, Traits>& operator<<(
std::basic_ostream<Char, Traits>& os, const quoted_string<String, Char>& str)
{
std::basic_ostringstream<Char, Traits> ostr;
ostr << str.delim_;
for (auto c : str.string_) {
if (c == str.delim_ || c == str.escape_)
ostr << str.escape_;
ostr << c;
}
ostr << str.delim_;
return os << ostr.str();
}
template <typename Char, typename Traits, typename Alloc>
std::basic_istream<Char, Traits>& operator>>(
std::basic_istream<Char, Traits>& is,
const quoted_string<std::basic_string<Char, Traits, Alloc>&, Char>& str)
{
Char c;
is >> c;
if (!is.good())
return is;
if (c != str.delim_) {
is.unget();
is >> str.string_;
return is;
}
str.string_.clear();
std::ios_base::fmtflags flags =
is.flags(is.flags() & ~std::ios_base::skipws);
do {
is >> c;
if (!is.good())
break;
if (c == str.escape_) {
is >> c;
if (!is.good())
break;
} else if (c == str.delim_)
break;
str.string_ += c;
} while (true);
is.setf(flags);
return is;
}
}
template <typename Char>
inline internals::quoted_string<const Char*, Char> quoted(
const Char* str, Char delim = Char('"'), Char escape = Char('\\'))
{
return internals::quoted_string<const Char*, Char>(str, delim, escape);
}
template <typename Char, typename Traits, typename Alloc>
inline internals::quoted_string<const std::basic_string<Char, Traits, Alloc>&,
Char>
quoted(const std::basic_string<Char, Traits, Alloc>& str,
Char delim = Char('"'), Char escape = Char('\\'))
{
return internals::quoted_string<
const std::basic_string<Char, Traits, Alloc>&, Char>(str, delim, escape);
}
template <typename Char, typename Traits, typename Alloc>
inline internals::quoted_string<std::basic_string<Char, Traits, Alloc>&, Char>
quoted(std::basic_string<Char, Traits, Alloc>& str, Char delim = Char('"'),
Char escape = Char('\\'))
{
return internals::quoted_string<std::basic_string<Char, Traits, Alloc>&,
Char>(str, delim, escape);
}
inline internals::quoted_string<cm::string_view, char> quoted(
cm::string_view str, char delim = '"', char escape = '\\')
{
return internals::quoted_string<cm::string_view, char>(str, delim, escape);
}
#endif
} // namespace cm
#endif

View File

@@ -6,7 +6,10 @@
#ifndef cm_memory
#define cm_memory
#include "cmSTL.hxx" // IWYU pragma: keep
#include <memory> // IWYU pragma: export
#if !defined(CMake_HAVE_CXX_MAKE_UNIQUE)
# include <cstddef>
# include <type_traits>

View File

@@ -0,0 +1,10 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#ifndef cmSTL_hxx
#define cmSTL_hxx
/* Whether CMake is using its own STL implementation. */
#cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE
#cmakedefine CMake_HAVE_CXX_FILESYSTEM
#endif

View File

@@ -479,6 +479,7 @@ if ${cmake_system_mingw}; then
fi
CMAKE_STD_CXX_HEADERS="\
filesystem \
memory \
optional \
shared_mutex \
@@ -486,6 +487,7 @@ CMAKE_STD_CXX_HEADERS="\
utility \
"
CMAKE_STD_CXX_SOURCES="\
fs_path \
string_view \
"
@@ -1252,7 +1254,7 @@ echo "C++ compiler on this system is: ${cmake_cxx_compiler} ${cmake_cxx_flags}"
#-----------------------------------------------------------------------------
# Test CXX features
cmake_cxx_features="make_unique"
cmake_cxx_features="make_unique filesystem"
for feature in ${cmake_cxx_features}; do
eval "cmake_have_cxx_${feature}=0"
@@ -1272,6 +1274,9 @@ for feature in ${cmake_cxx_features}; do
fi
done
cmake_generate_file "${cmake_bootstrap_dir}/cmSTL.hxx" ""
#-----------------------------------------------------------------------------
# Test Make