STL Support: Add cm::filesystem::path in <cm/filesystem>

This commit is contained in:
Marc Chevrier
2020-07-09 17:02:42 +02:00
parent e5798126fc
commit d654bf3449
10 changed files with 3196 additions and 2 deletions
+3
View File
@@ -56,6 +56,9 @@ Available features are:
* ``<cm/algorithm>``:
``cm::clamp``
* ``cm/filesystem>``:
``cm::filesystem::path``
* ``<cm/iterator>``:
``cm::size``, ``cm::empty``, ``cm::data``
+5
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()
+10
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;
}
+3
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)
+1006
View File
@@ -0,0 +1,1006 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include <algorithm>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <cm/filesystem>
#include <cm/iomanip>
namespace {
namespace fs = cm::filesystem;
void checkResult(bool success)
{
if (!success) {
std::cout << " => failed";
}
std::cout << std::endl;
}
bool testConstructors()
{
std::cout << "testConstructors()";
bool result = true;
{
fs::path p;
if (p != fs::path()) {
result = false;
}
}
{
fs::path p1("/a/b/c");
fs::path p2("/a/b/c");
if (p1 != p2) {
result = false;
}
if (p1.string() != p2.string()) {
result = false;
}
if (p1.string() != "/a/b/c") {
result = false;
}
}
{
std::string s("/a/b/c");
fs::path p1(s);
fs::path p2(s.begin(), s.end());
if (p1 != p2) {
result = false;
}
if (p1.string() != s || p2.string() != s) {
result = false;
}
#if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR
std::string s2(s);
s2 += '\0';
fs::path p3(s2.begin());
if (p1 != p3 || p3.string() != s) {
result = false;
}
#endif
}
{
std::wstring s(L"/a/b/c");
fs::path p1(s);
fs::path p2(s.begin(), s.end());
if (p1 != p2) {
result = false;
}
if (p1.wstring() != s || p2.wstring() != s) {
result = false;
}
#if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR
std::wstring s2(s);
s2 += L'\0';
fs::path p3(s2.begin());
if (p1 != p3 || p3.wstring() != s) {
result = false;
}
#endif
}
{
std::string s("/a/b/c");
fs::path::string_type ws;
for (auto c : s) {
ws += fs::path::value_type(c);
}
fs::path p1(ws);
fs::path p2(ws.begin(), ws.end());
if (p1 != p2) {
result = false;
}
if (p1.native() != ws || p2.native() != ws) {
result = false;
}
#if CM_FILESYSTEM_SOURCE_TRAITS_ITERATOR
fs::path::string_type ws2(ws);
ws2 += fs::path::value_type('\0');
fs::path p3(ws2.begin());
if (p1 != p3 || p3.native() != ws) {
result = false;
}
#endif
}
checkResult(result);
return result;
}
bool testConcatenation()
{
std::cout << "testConcatenation()";
bool result = true;
{
fs::path p("/a/b");
p /= "c";
if (!(p.string() == "/a/b/c" || p.string() == "/a/b\\c")) {
result = false;
}
p += "d";
if (!(p.string() == "/a/b/cd" || p.string() == "/a/b\\cd")) {
result = false;
}
fs::path p2("x/y");
p /= p2;
if (!(p.string() == "/a/b/cd/x/y" || p.string() == "/a/b\\cd\\x/y")) {
result = false;
}
p = p / p2;
if (!(p.string() == "/a/b/cd/x/y/x/y" ||
p.string() == "/a/b\\cd\\x/y\\x/y")) {
result = false;
}
}
{
fs::path p("a");
p /= "";
if (!(p.string() == "a/" || p.string() == "a\\")) {
result = false;
}
p /= "/b";
if (p.string() != "/b") {
result = false;
}
}
#if defined(_WIN32)
{
fs::path p("a");
p /= "c:/b";
if (p.string() != "c:/b") {
result = false;
}
p = fs::path("a") / "c:";
if (p.string() != "c:") {
result = false;
}
p = fs::path("c:") / "";
if (p.string() != "c:") {
result = false;
}
p = fs::path("c:a") / "/b";
if (p.string() != "c:/b") {
result = false;
}
p = fs::path("c:a") / "c:b";
if (p.string() != "c:a\\b") {
result = false;
}
p = fs::path("//host") / "b";
if (p.string() != "//host\\b") {
result = false;
}
p = fs::path("//host/") / "b";
if (p.string() != "//host/b") {
result = false;
}
}
#endif
checkResult(result);
return result;
}
bool testModifiers()
{
std::cout << "testModifiers()";
bool result = true;
{
std::string s("a///b/");
fs::path p(s);
std::replace(
s.begin(), s.end(), '/',
static_cast<std::string::value_type>(fs::path::preferred_separator));
p.make_preferred();
if (p.string() != s) {
result = false;
}
}
{
fs::path p("a/b/c.e.f");
p.remove_filename();
if (p.string() != "a/b/") {
result = false;
}
p.remove_filename();
if (p.string() != "a/b/") {
result = false;
}
}
{
fs::path p("a/b/c.e.f");
p.replace_filename("x.y");
if (p.string() != "a/b/x.y") {
result = false;
}
}
{
fs::path p("a/b/c.e.f");
p.replace_extension(".x");
if (p.string() != "a/b/c.e.x") {
result = false;
}
p.replace_extension(".y");
if (p.string() != "a/b/c.e.y") {
result = false;
}
p.replace_extension();
if (p.string() != "a/b/c.e") {
result = false;
}
p = "/a/b";
p.replace_extension(".x");
if (p.string() != "/a/b.x") {
result = false;
}
p = "/a/b/";
p.replace_extension(".x");
if (p.string() != "/a/b/.x") {
result = false;
}
}
checkResult(result);
return result;
}
bool testObservers()
{
std::cout << "testObservers()";
bool result = true;
{
std::string s("a/b/c");
fs::path p(s);
fs::path::string_type st;
for (auto c : s) {
st += static_cast<fs::path::value_type>(c);
}
if (p.native() != st || static_cast<fs::path::string_type>(p) != st ||
p.c_str() != st) {
result = false;
}
}
{
std::string s("a//b//c");
std::wstring ws(L"a//b//c");
fs::path p(s);
if (p.string() != s || p.wstring() != ws) {
result = false;
}
}
{
std::string s("a/b/c");
std::wstring ws;
for (auto c : s) {
ws += static_cast<std::wstring::value_type>(c);
}
std::string ns(s);
std::replace(
ns.begin(), ns.end(), '/',
static_cast<std::string::value_type>(fs::path::preferred_separator));
fs::path p(ns);
if (p.generic_string() != s || p.generic_wstring() != ws) {
result = false;
}
}
checkResult(result);
return result;
}
bool testCompare()
{
std::cout << "testCompare()";
bool result = true;
{
std::string s("a/b/c");
fs::path p1(s);
fs::path p2(s);
if (p1.compare(p2) != 0) {
result = false;
}
p2 = "a/b";
if (p1.compare(p2) <= 0) {
result = false;
}
p2 = "a/d";
if (p1.compare(p2) >= 0) {
result = false;
}
p2 = "a/b/d";
if (p1.compare(p2) >= 0) {
result = false;
}
p2 = "a/b/a";
if (p1.compare(p2) <= 0) {
result = false;
}
p2 = "a/b/c/d";
if (p1.compare(p2) >= 0) {
result = false;
}
p1 = "a";
p2 = "b";
if (p1.compare(p2) == 0) {
result = false;
}
}
{
// LWG 3096 (https://cplusplus.github.io/LWG/issue3096)
// fs::path p1("/a/");
// fs::path p2("/a/.");
// if (p1.compare(p2) != 0) {
// result = false;
// }
}
checkResult(result);
return result;
}
bool testGeneration()
{
std::cout << "testGeneration()";
bool result = true;
{
fs::path p("a/./b/..");
if (p.lexically_normal().generic_string() != "a/") {
result = false;
}
p = "a/.///b/../";
if (p.lexically_normal().generic_string() != "a/") {
result = false;
}
}
#if defined(_WIN32)
{
fs::path p("//host/./b/..");
if (p.lexically_normal().string() != "\\\\host\\") {
result = false;
}
p = "//host/.///b/../";
if (p.lexically_normal().string() != "\\\\host\\") {
result = false;
}
p = "c://a/.///b/../";
if (p.lexically_normal().string() != "c:\\a\\") {
result = false;
}
}
#endif
{
if (fs::path("/a//d").lexically_relative("/a/b/c") != "../../d") {
result = false;
}
if (fs::path("/a//b///c").lexically_relative("/a/d") != "../b/c") {
result = false;
}
if (fs::path("a/b/c").lexically_relative("a") != "b/c") {
result = false;
}
if (fs::path("a/b/c").lexically_relative("a/b/c/x/y") != "../..") {
result = false;
}
if (fs::path("a/b/c").lexically_relative("a/b/c") != ".") {
result = false;
}
if (fs::path("a/b").lexically_relative("c/d") != "../../a/b") {
result = false;
}
}
{
#if defined(_WIN32)
if (fs::path("/a/d").lexically_relative("e/d/c") != "/a/d") {
result = false;
}
if (!fs::path("c:/a/d").lexically_relative("e/d/c").empty()) {
result = false;
}
#else
if (!fs::path("/a/d").lexically_relative("e/d/c").empty()) {
result = false;
}
#endif
}
{
#if defined(_WIN32)
if (fs::path("c:/a/d").lexically_proximate("e/d/c") != "c:/a/d") {
result = false;
}
#else
if (fs::path("/a/d").lexically_proximate("e/d/c") != "/a/d") {
result = false;
}
#endif
if (fs::path("/a/d").lexically_proximate("/a/b/c") != "../../d") {
result = false;
}
}
// LWG 3070
{
#if defined(_WIN32)
if (!fs::path("/a:/b:").lexically_relative("/a:/c:").empty()) {
result = false;
}
if (fs::path("c:/a/b").lexically_relative("c:/a/d") != "../b") {
result = false;
}
if (!fs::path("c:/a/b:").lexically_relative("c:/a/d").empty()) {
result = false;
}
if (!fs::path("c:/a/b").lexically_relative("c:/a/d:").empty()) {
result = false;
}
#else
if (fs::path("/a:/b:").lexically_relative("/a:/c:") != "../b:") {
result = false;
}
#endif
}
// LWG 3096
{
if (fs::path("/a").lexically_relative("/a/.") != ".") {
result = false;
}
if (fs::path("/a").lexically_relative("/a/") != ".") {
result = false;
}
if (fs::path("a/b/c").lexically_relative("a/b/c") != ".") {
result = false;
}
if (fs::path("a/b/c").lexically_relative("a/b/c/") != ".") {
result = false;
}
if (fs::path("a/b/c").lexically_relative("a/b/c/.") != ".") {
result = false;
}
if (fs::path("a/b/c/").lexically_relative("a/b/c") != ".") {
result = false;
}
if (fs::path("a/b/c/.").lexically_relative("a/b/c") != ".") {
result = false;
}
if (fs::path("a/b/c/.").lexically_relative("a/b/c/") != ".") {
result = false;
}
}
checkResult(result);
return result;
}
bool testDecomposition()
{
std::cout << "testDecomposition()";
bool result = true;
{
if (!fs::path("/a/b").root_name().empty()) {
result = false;
}
#if defined(_WIN32)
if (fs::path("c:/a/b").root_name() != "c:") {
result = false;
}
if (fs::path("c:a/b").root_name() != "c:") {
result = false;
}
if (fs::path("c:").root_name() != "c:") {
result = false;
}
if (fs::path("//host/b").root_name() != "//host") {
result = false;
}
if (fs::path("//host").root_name() != "//host") {
result = false;
}
#endif
}
{
if (!fs::path("a/b").root_directory().empty()) {
result = false;
}
if (fs::path("/a/b").root_directory() != "/") {
result = false;
}
#if defined(_WIN32)
if (!fs::path("c:a/b").root_directory().empty()) {
result = false;
}
if (fs::path("/a/b").root_directory() != "/") {
result = false;
}
if (fs::path("c:/a/b").root_directory() != "/") {
result = false;
}
if (fs::path("//host/b").root_directory() != "/") {
result = false;
}
#endif
}
{
if (!fs::path("a/b").root_path().empty()) {
result = false;
}
if (fs::path("/a/b").root_path() != "/") {
result = false;
}
#if defined(_WIN32)
if (fs::path("c:a/b").root_path() != "c:") {
result = false;
}
if (fs::path("/a/b").root_path() != "/") {
result = false;
}
if (fs::path("c:/a/b").root_path() != "c:/") {
result = false;
}
if (fs::path("//host/b").root_path() != "//host/") {
result = false;
}
#endif
}
{
if (!fs::path("/").relative_path().empty()) {
result = false;
}
if (fs::path("a/b").relative_path() != "a/b") {
result = false;
}
if (fs::path("/a/b").relative_path() != "a/b") {
result = false;
}
#if defined(_WIN32)
if (fs::path("c:a/b").relative_path() != "a/b") {
result = false;
}
if (fs::path("/a/b").relative_path() != "a/b") {
result = false;
}
if (fs::path("c:/a/b").relative_path() != "a/b") {
result = false;
}
if (fs::path("//host/b").relative_path() != "b") {
result = false;
}
#endif
}
{
if (fs::path("/a/b").parent_path() != "/a") {
result = false;
}
if (fs::path("/a/b/").parent_path() != "/a/b") {
result = false;
}
if (fs::path("/a/b/.").parent_path() != "/a/b") {
result = false;
}
if (fs::path("/").parent_path() != "/") {
result = false;
}
#if defined(_WIN32)
if (fs::path("c:/a/b").parent_path() != "c:/a") {
result = false;
}
if (fs::path("c:/").parent_path() != "c:/") {
result = false;
}
if (fs::path("c:").parent_path() != "c:") {
result = false;
}
if (fs::path("//host/").parent_path() != "//host/") {
result = false;
}
if (fs::path("//host").parent_path() != "//host") {
result = false;
}
#endif
}
{
if (fs::path("/a/b.txt").filename() != "b.txt") {
result = false;
}
if (fs::path("/a/.b").filename() != ".b") {
result = false;
}
if (fs::path("/foo/bar/").filename() != "") {
result = false;
}
if (fs::path("/foo/.").filename() != ".") {
result = false;
}
if (fs::path("/foo/..").filename() != "..") {
result = false;
}
if (fs::path(".").filename() != ".") {
result = false;
}
if (fs::path("..").filename() != "..") {
result = false;
}
if (!fs::path("/").filename().empty()) {
result = false;
}
#if defined(_WIN32)
if (fs::path("c:a").filename() != "a") {
result = false;
}
if (fs::path("c:/a").filename() != "a") {
result = false;
}
if (!fs::path("c:").filename().empty()) {
result = false;
}
if (!fs::path("c:/").filename().empty()) {
result = false;
}
if (!fs::path("//host").filename().empty()) {
result = false;
}
#endif
}
{
if (fs::path("/a/b.txt").stem() != "b") {
result = false;
}
if (fs::path("/a/b.c.txt").stem() != "b.c") {
result = false;
}
if (fs::path("/a/.b").stem() != ".b") {
result = false;
}
if (fs::path("/a/b").stem() != "b") {
result = false;
}
if (fs::path("/a/b/.").stem() != ".") {
result = false;
}
if (fs::path("/a/b/..").stem() != "..") {
result = false;
}
if (!fs::path("/a/b/").stem().empty()) {
result = false;
}
#if defined(_WIN32)
if (!fs::path("c:/a/b/").stem().empty()) {
result = false;
}
if (!fs::path("c:/").stem().empty()) {
result = false;
}
if (!fs::path("c:").stem().empty()) {
result = false;
}
if (!fs::path("//host/").stem().empty()) {
result = false;
}
if (!fs::path("//host").stem().empty()) {
result = false;
}
#endif
}
{
if (fs::path("/a/b.txt").extension() != ".txt") {
result = false;
}
if (fs::path("/a/b.").extension() != ".") {
result = false;
}
if (!fs::path("/a/b").extension().empty()) {
result = false;
}
if (fs::path("/a/b.txt/b.cc").extension() != ".cc") {
result = false;
}
if (fs::path("/a/b.txt/b.").extension() != ".") {
result = false;
}
if (!fs::path("/a/b.txt/b").extension().empty()) {
result = false;
}
if (!fs::path("/a/.").extension().empty()) {
result = false;
}
if (!fs::path("/a/..").extension().empty()) {
result = false;
}
if (!fs::path("/a/.hidden").extension().empty()) {
result = false;
}
if (fs::path("/a/..b").extension() != ".b") {
result = false;
}
}
checkResult(result);
return result;
}
bool testQueries()
{
std::cout << "testQueries()";
bool result = true;
{
if (fs::path("/a/b").has_root_name()) {
result = false;
}
fs::path p("/a/b");
if (!p.has_root_directory() || !p.has_root_path()) {
result = false;
}
if (!fs::path("/a/b").has_root_path() || fs::path("a/b").has_root_path()) {
result = false;
}
if (!fs::path("/a/b").has_relative_path() ||
fs::path("/").has_relative_path()) {
result = false;
}
if (!fs::path("/a/b").has_parent_path() ||
!fs::path("/").has_parent_path() || fs::path("a").has_parent_path()) {
result = false;
}
if (!fs::path("/a/b").has_filename() || !fs::path("a.b").has_filename() ||
fs::path("/a/").has_filename() || fs::path("/").has_filename()) {
result = false;
}
if (!fs::path("/a/b").has_stem() || !fs::path("a.b").has_stem() ||
!fs::path("/.a").has_stem() || fs::path("/a/").has_stem() ||
fs::path("/").has_stem()) {
result = false;
}
if (!fs::path("/a/b.c").has_extension() ||
!fs::path("a.b").has_extension() || fs::path("/.a").has_extension() ||
fs::path("/a/").has_extension() || fs::path("/").has_extension()) {
result = false;
}
#if defined(_WIN32)
p = "c:/a/b";
if (!fs::path("c:/a/b").has_root_name() || !p.has_root_directory() ||
!p.has_root_path()) {
result = false;
}
p = "c:a/b";
if (!p.has_root_name() || p.has_root_directory() || !p.has_root_path()) {
result = false;
}
p = "//host/b";
if (!p.has_root_name() || !p.has_root_directory() || !p.has_root_path()) {
result = false;
}
p = "//host";
if (!p.has_root_name() || p.has_root_directory() || !p.has_root_path()) {
result = false;
}
if (!fs::path("c:/a/b").has_relative_path() ||
!fs::path("c:a/b").has_relative_path() ||
!fs::path("//host/b").has_relative_path()) {
result = false;
}
if (!fs::path("c:/a/b").has_parent_path() ||
!fs::path("c:/").has_parent_path() ||
!fs::path("c:").has_parent_path() ||
!fs::path("//host/").has_parent_path() ||
!fs::path("//host").has_parent_path()) {
result = false;
}
#endif
}
{
#if defined(_WIN32)
fs::path p("c:/a");
#else
fs::path p("/a");
#endif
if (!p.is_absolute() || p.is_relative()) {
result = false;
}
p = "a/b";
if (p.is_absolute() || !p.is_relative()) {
result = false;
}
#if defined(_WIN32)
p = "c:/a/b";
if (!p.is_absolute() || p.is_relative()) {
result = false;
}
p = "//host/b";
if (!p.is_absolute() || p.is_relative()) {
result = false;
}
p = "/a";
if (p.is_absolute() || !p.is_relative()) {
result = false;
}
p = "c:a";
if (p.is_absolute() || !p.is_relative()) {
result = false;
}
#endif
}
checkResult(result);
return result;
}
bool testIterators()
{
std::cout << "testIterators()";
bool result = true;
{
fs::path p("/a/b/");
#if defined(_WIN32)
std::vector<fs::path::string_type> ref{ L"/", L"a", L"b", L"" };
#else
std::vector<fs::path::string_type> ref{ "/", "a", "b", "" };
#endif
std::vector<fs::path::string_type> res;
for (auto i = p.begin(), e = p.end(); i != e; ++i) {
res.push_back(*i);
}
if (res != ref) {
result = false;
}
res.clear();
for (const auto& e : p) {
res.push_back(e);
}
if (res != ref) {
result = false;
}
}
{
fs::path p("/a/b/");
#if defined(_WIN32)
std::vector<fs::path::string_type> ref{ L"", L"b", L"a", L"/" };
#else
std::vector<fs::path::string_type> ref{ "", "b", "a", "/" };
#endif
std::vector<fs::path::string_type> res;
auto i = p.end(), b = p.begin();
do {
res.push_back(*--i);
} while (i != b);
if (res != ref) {
result = false;
}
}
checkResult(result);
return result;
}
bool testNonMemberFunctions()
{
std::cout << "testNonMemberFunctions()";
bool result = true;
{
fs::path p1("/a/b/");
fs::path p2("/c/d");
fs::swap(p1, p2);
if (p1.string() != "/c/d" || p2.string() != "/a/b/")
result = false;
}
{
auto h1 = fs::hash_value(fs::path("/a//b//"));
auto h2 = fs::hash_value(fs::path("/a/b/"));
if (h1 != h2)
result = false;
}
{
fs::path p1("/a/b/");
fs::path p2("/c/d");
if (p1 == p2)
result = false;
p1 = "/a//b//";
p2 = "/a/b/";
if (p1 != p2)
result = false;
}
{
fs::path p = "/a";
p = p / "b" / "c";
if (p.generic_string() != "/a/b/c") {
result = false;
}
fs::path::string_type ref;
ref += fs::path::value_type('/');
ref += fs::path::value_type('a');
ref += fs::path::preferred_separator;
ref += fs::path::value_type('b');
ref += fs::path::preferred_separator;
ref += fs::path::value_type('c');
if (p.native() != ref) {
result = false;
}
}
{
fs::path p("/a b\\c/");
std::ostringstream oss;
oss << p;
if (oss.str() != "\"/a b\\\\c/\"") {
result = false;
}
std::istringstream iss(oss.str());
fs::path p2;
iss >> p2;
if (p2 != p) {
result = false;
}
}
checkResult(result);
return result;
}
}
int testCMFilesystemPath(int /*unused*/, char* /*unused*/ [])
{
int result = 0;
if (!testConstructors()) {
result = 1;
}
if (!testConcatenation()) {
result = 1;
}
if (!testModifiers()) {
result = 1;
}
if (!testObservers()) {
result = 1;
}
if (!testCompare()) {
result = 1;
}
if (!testGeneration()) {
result = 1;
}
if (!testDecomposition()) {
result = 1;
}
if (!testQueries()) {
result = 1;
}
if (!testIterators()) {
result = 1;
}
if (!testNonMemberFunctions()) {
result = 1;
}
return result;
}
+3 -1
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
+989
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
View File
@@ -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
+1
View File
@@ -5,5 +5,6 @@
/* Whether CMake is using its own STL implementation. */
#cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE
#cmakedefine CMake_HAVE_CXX_FILESYSTEM
#endif
+3 -1
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"