mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-27 04:09:36 +03:00
STL Support: Add cm::filesystem::path in <cm/filesystem>
This commit is contained in:
@@ -56,6 +56,9 @@ Available features are:
|
||||
* ``<cm/algorithm>``:
|
||||
``cm::clamp``
|
||||
|
||||
* ``cm/filesystem>``:
|
||||
``cm::filesystem::path``
|
||||
|
||||
* ``<cm/iterator>``:
|
||||
``cm::size``, ``cm::empty``, ``cm::data``
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -5,5 +5,6 @@
|
||||
|
||||
/* Whether CMake is using its own STL implementation. */
|
||||
#cmakedefine CMake_HAVE_CXX_MAKE_UNIQUE
|
||||
#cmakedefine CMake_HAVE_CXX_FILESYSTEM
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user