168 changes: 122 additions & 46 deletions libcxx/include/experimental/filesystem
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@
// operational functions
path absolute(const path& p, const path& base=current_path());
path absolute(const path& p);
path absolute(const path& p, error_code &ec);
path canonical(const path& p, const path& base = current_path());
path canonical(const path& p);
path canonical(const path& p, error_code& ec);
path canonical(const path& p, const path& base, error_code& ec);
void copy(const path& from, const path& to);
void copy(const path& from, const path& to, error_code& ec);
Expand Down Expand Up @@ -185,9 +185,17 @@
void permissions(const path& p, perms prms, perm_options opts,
error_code& ec);
path proximate(const path& p, error_code& ec);
path proximate(const path& p, const path& base = current_path());
path proximate(const path& p, const path& base, error_code &ec);
path read_symlink(const path& p);
path read_symlink(const path& p, error_code& ec);
path relative(const path& p, error_code& ec);
path relative(const path& p, const path& base=current_path());
path relative(const path& p, const path& base, error_code& ec);
bool remove(const path& p);
bool remove(const path& p, error_code& ec) _NOEXCEPT;
Expand All @@ -211,12 +219,13 @@
file_status symlink_status(const path& p);
file_status symlink_status(const path& p, error_code& ec) _NOEXCEPT;
path system_complete(const path& p);
path system_complete(const path& p, error_code& ec);
path temp_directory_path();
path temp_directory_path(error_code& ec);
path weakly_canonical(path const& p);
path weakly_canonical(path const& p, error_code& ec);
} } } } // namespaces std::experimental::filesystem::v1
*/
Expand Down Expand Up @@ -796,26 +805,26 @@ public:

private:
template <class _ECharT>
void __append_sep_if_needed(_ECharT __first_or_null) {
const _ECharT __null_val = {};
bool __append_sep = !empty() &&
!__is_separator(__pn_.back()) &&
__first_or_null != __null_val && // non-empty
!__is_separator(__first_or_null);
if (__append_sep)
__pn_ += preferred_separator;
static bool __source_is_absolute(_ECharT __first_or_null) {
return __is_separator(__first_or_null);
}

public:
// appends
path& operator/=(const path& __p) {
_LIBCPP_ASSERT(!__p.has_root_name(),
"cannot append to a path with a root name");
__append_sep_if_needed(__p.empty() ? char{} : __p.__pn_[0]);
if (__p.is_absolute()) {
__pn_ = __p.__pn_;
return *this;
}
if (has_filename())
__pn_ += preferred_separator;
__pn_ += __p.native();
return *this;
}

// FIXME: Use _LIBCPP_DIAGNOSE_WARNING to produce a diagnostic when __src
// is known at compile time to be "/' since the user almost certainly intended
// to append a separator instead of overwriting the path with "/"
template <class _Source>
_LIBCPP_INLINE_VISIBILITY
_EnableIfPathable<_Source>
Expand All @@ -828,7 +837,10 @@ public:
append(const _Source& __src) {
using _Traits = __is_pathable<_Source>;
using _CVT = _PathCVT<_SourceChar<_Source>>;
__append_sep_if_needed(_Traits::__first_or_null(__src));
if (__source_is_absolute(_Traits::__first_or_null(__src)))
__pn_.clear();
else if (has_filename())
__pn_ += preferred_separator;
_CVT::__append_source(__pn_, __src);
return *this;
}
Expand All @@ -838,10 +850,11 @@ public:
typedef typename iterator_traits<_InputIt>::value_type _ItVal;
static_assert(__can_convert_char<_ItVal>::value, "Must convertible");
using _CVT = _PathCVT<_ItVal>;
if (__first != __last) {
__append_sep_if_needed(*__first);
_CVT::__append_range(__pn_, __first, __last);
}
if (__first != __last && __source_is_absolute(*__first))
__pn_.clear();
else if (has_filename())
__pn_ += preferred_separator;
_CVT::__append_range(__pn_, __first, __last);
return *this;
}

Expand Down Expand Up @@ -916,10 +929,9 @@ public:

_LIBCPP_INLINE_VISIBILITY
path& remove_filename() {
if (__pn_.size() == __root_path_raw().size())
clear();
else
__pn_ = __parent_path();
auto __fname = __filename();
if (!__fname.empty())
__pn_.erase(__fname.data() - __pn_.data());
return *this;
}

Expand All @@ -935,6 +947,10 @@ public:
__pn_.swap(__rhs.__pn_);
}

// private helper to allow reserving memory in the path
_LIBCPP_INLINE_VISIBILITY
void __reserve(size_t __s) { __pn_.reserve(__s); }

// native format observers
_LIBCPP_INLINE_VISIBILITY
const string_type& native() const _NOEXCEPT {
Expand Down Expand Up @@ -1023,6 +1039,17 @@ public:
_LIBCPP_INLINE_VISIBILITY bool is_absolute() const { return has_root_directory(); }
_LIBCPP_INLINE_VISIBILITY bool is_relative() const { return !is_absolute(); }

// relative paths
path lexically_normal() const;
path lexically_relative(const path& __base) const;

_LIBCPP_INLINE_VISIBILITY path lexically_proximate(const path& __base) const {
path __result = this->lexically_relative(__base);
if (__result.native().empty())
return *this;
return __result;
}

// iterators
class _LIBCPP_TYPE_VIS iterator;
typedef iterator const_iterator;
Expand Down Expand Up @@ -1277,7 +1304,9 @@ void __throw_filesystem_error(_Args&&...)
// operational functions

_LIBCPP_FUNC_VIS
path __canonical(const path&, const path&, error_code *__ec=nullptr);
path __absolute(const path&, error_code *__ec=nullptr);
_LIBCPP_FUNC_VIS
path __canonical(const path&, error_code *__ec=nullptr);
_LIBCPP_FUNC_VIS
void __copy(const path& __from, const path& __to, copy_options __opt,
error_code *__ec=nullptr);
Expand Down Expand Up @@ -1342,6 +1371,8 @@ _LIBCPP_FUNC_VIS
path __system_complete(const path&, error_code *__ec=nullptr);
_LIBCPP_FUNC_VIS
path __temp_directory_path(error_code *__ec=nullptr);
_LIBCPP_FUNC_VIS
path __weakly_canonical(path const& __p, error_code *__ec=nullptr);

inline _LIBCPP_INLINE_VISIBILITY
path current_path() {
Expand All @@ -1363,24 +1394,24 @@ void current_path(const path& __p, error_code& __ec) _NOEXCEPT {
__current_path(__p, &__ec);
}

_LIBCPP_FUNC_VIS
path absolute(const path&, const path& __p2 = current_path());
inline _LIBCPP_INLINE_VISIBILITY
path absolute(const path& __p) {
return __absolute(__p);
}

inline _LIBCPP_INLINE_VISIBILITY
path canonical(const path& __p, const path& __base = current_path()) {
return __canonical(__p, __base);
path absolute(const path& __p, error_code &__ec) {
return __absolute(__p, &__ec);
}

inline _LIBCPP_INLINE_VISIBILITY
path canonical(const path& __p, error_code& __ec) {
path __base = __current_path(&__ec);
if (__ec) return {};
return __canonical(__p, __base, &__ec);
path canonical(const path& __p) {
return __canonical(__p);
}

inline _LIBCPP_INLINE_VISIBILITY
path canonical(const path& __p, const path& __base, error_code& __ec) {
return __canonical(__p, __base, &__ec);
path canonical(const path& __p, error_code& __ec) {
return __canonical(__p, &__ec);
}

inline _LIBCPP_INLINE_VISIBILITY
Expand Down Expand Up @@ -1492,7 +1523,8 @@ void create_symlink(const path& __to, const path& __new) {
}

inline _LIBCPP_INLINE_VISIBILITY
void create_symlink(const path& __to, const path& __new, error_code& __ec) _NOEXCEPT {
void create_symlink(const path& __to, const path& __new,
error_code& __ec) _NOEXCEPT {
return __create_symlink(__to, __new, &__ec);
}

Expand Down Expand Up @@ -1715,6 +1747,27 @@ void permissions(const path& __p, perms __prms, perm_options __opts,
__permissions(__p, __prms, __opts, &__ec);
}

inline _LIBCPP_INLINE_VISIBILITY
path proximate(const path& __p, const path& __base, error_code& __ec) {
path __tmp = __weakly_canonical(__p, &__ec);
if (__ec)
return {};
path __tmp_base = __weakly_canonical(__base, &__ec);
if (__ec)
return {};
return __tmp.lexically_proximate(__tmp_base);
}

inline _LIBCPP_INLINE_VISIBILITY
path proximate(const path& __p, error_code& __ec) {
return proximate(__p, current_path(), __ec);
}

inline _LIBCPP_INLINE_VISIBILITY
path proximate(const path& __p, const path& __base = current_path()) {
return __weakly_canonical(__p).lexically_proximate(__weakly_canonical(__base));
}

inline _LIBCPP_INLINE_VISIBILITY
path read_symlink(const path& __p) {
return __read_symlink(__p);
Expand All @@ -1725,6 +1778,29 @@ path read_symlink(const path& __p, error_code& __ec) {
return __read_symlink(__p, &__ec);
}


inline _LIBCPP_INLINE_VISIBILITY
path relative(const path& __p, const path& __base, error_code& __ec) {
path __tmp = __weakly_canonical(__p, &__ec);
if (__ec)
return path();
path __tmpbase = __weakly_canonical(__base, &__ec);
if (__ec)
return path();
return __tmp.lexically_relative(__tmpbase);
}

inline _LIBCPP_INLINE_VISIBILITY
path relative(const path& __p, error_code& __ec) {
return relative(__p, current_path(), __ec);
}

inline _LIBCPP_INLINE_VISIBILITY
path relative(const path& __p, const path& __base=current_path()) {
return __weakly_canonical(__p).lexically_relative(__weakly_canonical(__base));
}


inline _LIBCPP_INLINE_VISIBILITY
bool remove(const path& __p) {
return __remove(__p);
Expand Down Expand Up @@ -1796,23 +1872,23 @@ file_status symlink_status(const path& __p, error_code& __ec) _NOEXCEPT {
}

inline _LIBCPP_INLINE_VISIBILITY
path system_complete(const path& __p) {
return __system_complete(__p);
path temp_directory_path() {
return __temp_directory_path();
}

inline _LIBCPP_INLINE_VISIBILITY
path system_complete(const path& __p, error_code& __ec) {
return __system_complete(__p, &__ec);
path temp_directory_path(error_code& __ec) {
return __temp_directory_path(&__ec);
}

inline _LIBCPP_INLINE_VISIBILITY
path temp_directory_path() {
return __temp_directory_path();
path weakly_canonical(path const& __p) {
return __weakly_canonical(__p);
}

inline _LIBCPP_INLINE_VISIBILITY
path temp_directory_path(error_code& __ec) {
return __temp_directory_path(&__ec);
path weakly_canonical(path const& __p, error_code& __ec) {
return __weakly_canonical(__p, &__ec);
}


Expand Down
675 changes: 647 additions & 28 deletions libcxx/src/experimental/filesystem/operations.cpp

Large diffs are not rendered by default.

448 changes: 0 additions & 448 deletions libcxx/src/experimental/filesystem/path.cpp

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,14 @@ void checkBeginEndBasic() {
}
{
path p("//root_name//first_dir////second_dir");
const path expect[] = {"//root_name", "/", "first_dir", "second_dir"};
const path expect[] = {"/", "root_name", "first_dir", "second_dir"};
assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect)));
assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect)));

}
{
path p("////foo/bar/baz///");
const path expect[] = {"/", "foo", "bar", "baz", "."};
const path expect[] = {"/", "foo", "bar", "baz", ""};
assert(checkCollectionsEqual(p.begin(), p.end(), std::begin(expect), std::end(expect)));
assert(checkCollectionsEqualBackwards(p.begin(), p.end(), std::begin(expect), std::end(expect)));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"
#include "verbose_assert.h"


struct AppendOperatorTestcase {
Expand All @@ -45,21 +46,31 @@ const AppendOperatorTestcase Cases[] =
{S(""), S(""), S("")}
, {S("p1"), S("p2"), S("p1/p2")}
, {S("p1/"), S("p2"), S("p1/p2")}
, {S("p1"), S("/p2"), S("p1/p2")}
, {S("p1/"), S("/p2"), S("p1//p2")}
, {S("p1"), S("/p2"), S("/p2")}
, {S("p1/"), S("/p2"), S("/p2")}
, {S("p1"), S("\\p2"), S("p1/\\p2")}
, {S("p1\\"), S("p2"), S("p1\\/p2")}
, {S("p1\\"), S("\\p2"), S("p1\\/\\p2")}
, {S("p1"), S(""), S("p1")}
, {S(""), S("p2"), S("p2")}
, {S("/p1"), S("p2"), S("/p1/p2")}
, {S("/p1"), S("/p2"), S("/p2")}
, {S("/p1/p3"), S("p2"), S("/p1/p3/p2")}
, {S("/p1/p3/"), S("p2"), S("/p1/p3/p2")}
, {S("/p1/"), S("p2"), S("/p1/p2")}
, {S("/p1/p3/"), S("/p2/p4"), S("/p2/p4")}
, {S("/"), S(""), S("/")}
, {S("/p1"), S("/p2/"), S("/p2/")}
, {S("p1"), S(""), S("p1/")}
, {S("p1/"), S(""), S("p1/")}
};


const AppendOperatorTestcase LongLHSCases[] =
{
{S("p1"), S("p2"), S("p1/p2")}
, {S("p1/"), S("p2"), S("p1/p2")}
, {S("p1"), S("/p2"), S("p1/p2")}
, {S("p1"), S("/p2"), S("/p2")}
, {S("/p1"), S("p2"), S("/p1/p2")}
};
#undef S

Expand Down Expand Up @@ -98,7 +109,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
DisableAllocationGuard g;
LHS /= RHS;
}
assert(LHS == E);
ASSERT_PRED(PathEq, LHS , E);
}
// basic_string_view
{
Expand All @@ -108,7 +119,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
DisableAllocationGuard g;
LHS /= RHS;
}
assert(LHS == E);
assert(PathEq(LHS, E));
}
// CharT*
{
Expand All @@ -118,7 +129,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
DisableAllocationGuard g;
LHS /= RHS;
}
assert(LHS == E);
assert(PathEq(LHS, E));
}
{
path LHS(L); PathReserve(LHS, ReserveSize);
Expand All @@ -127,7 +138,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
DisableAllocationGuard g;
LHS.append(RHS, StrEnd(RHS));
}
assert(LHS == E);
assert(PathEq(LHS, E));
}
// input iterator - For non-native char types, appends needs to copy the
// iterator range into a contiguous block of memory before it can perform the
Expand All @@ -143,7 +154,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
if (DisableAllocations) g.requireExactly(0);
LHS /= RHS;
}
assert(LHS == E);
assert(PathEq(LHS, E));
}
{
path LHS(L); PathReserve(LHS, ReserveSize);
Expand All @@ -154,7 +165,7 @@ void doAppendSourceAllocTest(AppendOperatorTestcase const& TC)
if (DisableAllocations) g.requireExactly(0);
LHS.append(RHS, REnd);
}
assert(LHS == E);
assert(PathEq(LHS, E));
}
}

Expand All @@ -171,76 +182,78 @@ void doAppendSourceTest(AppendOperatorTestcase const& TC)
const Ptr E = TC.expect;
// basic_string
{
path LHS(L);
path Result(L);
Str RHS(R);
path& Ref = (LHS /= RHS);
assert(LHS == E);
assert(&Ref == &LHS);
path& Ref = (Result /= RHS);
ASSERT_EQ(Result, E)
<< DISPLAY(L) << DISPLAY(R);
assert(&Ref == &Result);
}
{
path LHS(L);
Str RHS(R);
path& Ref = LHS.append(RHS);
assert(LHS == E);
assert(PathEq(LHS, E));
assert(&Ref == &LHS);
}
// basic_string_view
{
path LHS(L);
StrView RHS(R);
path& Ref = (LHS /= RHS);
assert(LHS == E);
assert(PathEq(LHS, E));
assert(&Ref == &LHS);
}
{
path LHS(L);
StrView RHS(R);
path& Ref = LHS.append(RHS);
assert(LHS == E);
assert(PathEq(LHS, E));
assert(&Ref == &LHS);
}
// Char*
{
path LHS(L);
Str RHS(R);
path& Ref = (LHS /= RHS);
assert(LHS == E);
assert(PathEq(LHS, E));
assert(&Ref == &LHS);
}
{
path LHS(L);
Ptr RHS(R);
path& Ref = LHS.append(RHS);
assert(LHS == E);
assert(PathEq(LHS, E));
assert(&Ref == &LHS);
}
{
path LHS(L);
Ptr RHS(R);
path& Ref = LHS.append(RHS, StrEnd(RHS));
assert(LHS == E);
ASSERT_PRED(PathEq, LHS, E)
<< DISPLAY(L) << DISPLAY(R);
assert(&Ref == &LHS);
}
// iterators
{
path LHS(L);
InputIter RHS(R);
path& Ref = (LHS /= RHS);
assert(LHS == E);
assert(PathEq(LHS, E));
assert(&Ref == &LHS);
}
{
path LHS(L); InputIter RHS(R);
path& Ref = LHS.append(RHS);
assert(LHS == E);
assert(PathEq(LHS, E));
assert(&Ref == &LHS);
}
{
path LHS(L);
InputIter RHS(R);
InputIter REnd(StrEnd(R));
path& Ref = LHS.append(RHS, REnd);
assert(LHS == E);
assert(PathEq(LHS, E));
assert(&Ref == &LHS);
}
}
Expand Down Expand Up @@ -304,11 +317,14 @@ int main()
using namespace fs;
for (auto const & TC : Cases) {
{
path LHS((const char*)TC.lhs);
path RHS((const char*)TC.rhs);
path& Ref = (LHS /= RHS);
assert(LHS == (const char*)TC.expect);
assert(&Ref == &LHS);
const char* LHS_In = TC.lhs;
const char* RHS_In = TC.rhs;
path LHS(LHS_In);
path RHS(RHS_In);
path& Res = (LHS /= RHS);
ASSERT_PRED(PathEq, Res, (const char*)TC.expect)
<< DISPLAY(LHS_In) << DISPLAY(RHS_In);
assert(&Res == &LHS);
}
doAppendSourceTest<char> (TC);
doAppendSourceTest<wchar_t> (TC);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
//
// size_t hash_value(path const&) noexcept;


#include "filesystem_include.hpp"
#include <type_traits>
#include <vector>
Expand All @@ -35,7 +36,7 @@
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"

#include "verbose_assert.h"

struct PathCompareTest {
const char* LHS;
Expand All @@ -57,8 +58,9 @@ const PathCompareTest CompareTestCases[] =
{"a/b/c", "b/a/c", -1},
{"a/b", "a/b/c", -1},
{"a/b/c", "a/b", 1},
{"a/b/", "a/b/.", 0},
{"a/b//////", "a/b/////.", 0},
{"a/b/", "a/b/.", -1},
{"a/b/", "a/b", 1},
{"a/b//////", "a/b/////.", -1},
{"a/.././b", "a///..//.////b", 0},
{"//foo//bar///baz////", "//foo/bar/baz/", 0}, // duplicate separators
{"///foo/bar", "/foo/bar", 0}, // "///" is not a root directory
Expand Down Expand Up @@ -94,8 +96,13 @@ int main()
int ret2 = normalize_ret(p1.compare(R));
int ret3 = normalize_ret(p1.compare(TC.RHS));
int ret4 = normalize_ret(p1.compare(RV));
assert(ret1 == ret2 && ret1 == ret3 && ret1 == ret4);
assert(ret1 == E);

g.release();
ASSERT_EQ(ret1, ret2);
ASSERT_EQ(ret1, ret3);
ASSERT_EQ(ret1, ret4);
ASSERT_EQ(ret1, E)
<< DISPLAY(TC.LHS) << DISPLAY(TC.RHS);

// check signatures
ASSERT_NOEXCEPT(p1.compare(p2));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
// UNSUPPORTED: clang-3.3, clang-3.4, clang-3.5, clang-3.6, clang-3.7, clang-3.8

#include "filesystem_include.hpp"

#include "test_macros.h"

int main ()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"
#include "assert_checkpoint.h"
#include "verbose_assert.h"

struct ComparePathExact {
bool operator()(std::string const& LHS, std::string const& RHS) const {
return LHS == RHS;
}
};

struct PathDecomposeTestcase
{
Expand All @@ -72,80 +80,89 @@ const PathDecomposeTestcase PathTestCases[] =
, {".", {"."}, "", "", "", ".", "", "."}
, {"..", {".."}, "", "", "", "..", "", ".."}
, {"foo", {"foo"}, "", "", "", "foo", "", "foo"}
, {"/", {"/"}, "/", "", "/", "", "", "/"}
, {"/", {"/"}, "/", "", "/", "", "/", ""}
, {"/foo", {"/", "foo"}, "/", "", "/", "foo", "/", "foo"}
, {"foo/", {"foo", "."}, "", "", "", "foo/", "foo", "."}
, {"/foo/", {"/", "foo", "."}, "/", "", "/", "foo/", "/foo", "."}
, {"foo/", {"foo", ""}, "", "", "", "foo/", "foo", ""}
, {"/foo/", {"/", "foo", ""}, "/", "", "/", "foo/", "/foo", ""}
, {"foo/bar", {"foo","bar"}, "", "", "", "foo/bar", "foo", "bar"}
, {"/foo//bar", {"/","foo","bar"}, "/", "", "/", "foo/bar", "/foo", "bar"}
, {"//net", {"//net"}, "//net", "//net", "", "", "", "//net"}
, {"//net/foo", {"//net", "/", "foo"}, "//net/", "//net", "/", "foo", "//net/", "foo"}
, {"///foo///", {"/", "foo", "."}, "/", "", "/", "foo///", "///foo", "."}
, {"//net", {"/", "net"}, "/", "", "/", "net", "/", "net"}
, {"//net/foo", {"/", "net", "foo"}, "/", "", "/", "net/foo", "/net", "foo"}
, {"///foo///", {"/", "foo", ""}, "/", "", "/", "foo///", "///foo", ""}
, {"///foo///bar", {"/", "foo", "bar"}, "/", "", "/", "foo///bar", "///foo", "bar"}
, {"/.", {"/", "."}, "/", "", "/", ".", "/", "."}
, {"./", {".", "."}, "", "", "", "./", ".", "."}
, {"./", {".", ""}, "", "", "", "./", ".", ""}
, {"/..", {"/", ".."}, "/", "", "/", "..", "/", ".."}
, {"../", {"..", "."}, "", "", "", "../", "..", "."}
, {"../", {"..", ""}, "", "", "", "../", "..", ""}
, {"foo/.", {"foo", "."}, "", "", "", "foo/.", "foo", "."}
, {"foo/..", {"foo", ".."}, "", "", "", "foo/..", "foo", ".."}
, {"foo/./", {"foo", ".", "."}, "", "", "", "foo/./", "foo/.", "."}
, {"foo/./", {"foo", ".", ""}, "", "", "", "foo/./", "foo/.", ""}
, {"foo/./bar", {"foo", ".", "bar"}, "", "", "", "foo/./bar", "foo/.", "bar"}
, {"foo/../", {"foo", "..", "."}, "", "", "", "foo/../", "foo/..", "."}
, {"foo/../", {"foo", "..", ""}, "", "", "", "foo/../", "foo/..", ""}
, {"foo/../bar", {"foo", "..", "bar"}, "", "", "", "foo/../bar", "foo/..", "bar"}
, {"c:", {"c:"}, "", "", "", "c:", "", "c:"}
, {"c:/", {"c:", "."}, "", "", "", "c:/", "c:", "."}
, {"c:/", {"c:", ""}, "", "", "", "c:/", "c:", ""}
, {"c:foo", {"c:foo"}, "", "", "", "c:foo", "", "c:foo"}
, {"c:/foo", {"c:", "foo"}, "", "", "", "c:/foo", "c:", "foo"}
, {"c:foo/", {"c:foo", "."}, "", "", "", "c:foo/", "c:foo", "."}
, {"c:/foo/", {"c:", "foo", "."}, "", "", "", "c:/foo/", "c:/foo", "."}
, {"c:foo/", {"c:foo", ""}, "", "", "", "c:foo/", "c:foo", ""}
, {"c:/foo/", {"c:", "foo", ""}, "", "", "", "c:/foo/", "c:/foo", ""}
, {"c:/foo/bar", {"c:", "foo", "bar"}, "", "", "", "c:/foo/bar", "c:/foo", "bar"}
, {"prn:", {"prn:"}, "", "", "", "prn:", "", "prn:"}
, {"c:\\", {"c:\\"}, "", "", "", "c:\\", "", "c:\\"}
, {"c:\\foo", {"c:\\foo"}, "", "", "", "c:\\foo", "", "c:\\foo"}
, {"c:foo\\", {"c:foo\\"}, "", "", "", "c:foo\\", "", "c:foo\\"}
, {"c:\\foo\\", {"c:\\foo\\"}, "", "", "", "c:\\foo\\", "", "c:\\foo\\"}
, {"c:\\foo/", {"c:\\foo", "."}, "", "", "", "c:\\foo/", "c:\\foo", "."}
, {"c:\\foo/", {"c:\\foo", ""}, "", "", "", "c:\\foo/", "c:\\foo", ""}
, {"c:/foo\\bar", {"c:", "foo\\bar"}, "", "", "", "c:/foo\\bar", "c:", "foo\\bar"}
, {"//", {"//"}, "//", "//", "", "", "", "//"}
, {"//", {"/"}, "/", "", "/", "", "/", ""}
};

void decompPathTest()
{
using namespace fs;
for (auto const & TC : PathTestCases) {
path p(TC.raw);
assert(p == TC.raw);
CHECKPOINT(TC.raw.c_str());
fs::path p(TC.raw);
ASSERT(p == TC.raw);

assert(p.root_path() == TC.root_path);
assert(p.has_root_path() != TC.root_path.empty());
ASSERT_EQ(p.root_path(), TC.root_path);
ASSERT_NEQ(p.has_root_path(), TC.root_path.empty());

assert(p.root_name() == TC.root_name);
assert(p.has_root_name() != TC.root_name.empty());
ASSERT(p.root_name().native().empty())
<< DISPLAY(p.root_name());
ASSERT_EQ(p.root_name(),TC.root_name);
ASSERT_NEQ(p.has_root_name(), TC.root_name.empty());

assert(p.root_directory() == TC.root_directory);
assert(p.has_root_directory() != TC.root_directory.empty());
ASSERT_EQ(p.root_directory(), TC.root_directory);
ASSERT_NEQ(p.has_root_directory(), TC.root_directory.empty());

assert(p.relative_path() == TC.relative_path);
assert(p.has_relative_path() != TC.relative_path.empty());
ASSERT_EQ(p.relative_path(), TC.relative_path);
ASSERT_NEQ(p.has_relative_path(), TC.relative_path.empty());

assert(p.parent_path() == TC.parent_path);
assert(p.has_parent_path() != TC.parent_path.empty());
ASSERT_EQ(p.parent_path(), TC.parent_path);
ASSERT_NEQ(p.has_parent_path(), TC.parent_path.empty());

assert(p.filename() == TC.filename);
assert(p.has_filename() != TC.filename.empty());
ASSERT_EQ(p.filename(), TC.filename);
ASSERT_NEQ(p.has_filename(), TC.filename.empty());

assert(p.is_absolute() == p.has_root_directory());
assert(p.is_relative() != p.is_absolute());
ASSERT_EQ(p.is_absolute(), p.has_root_directory());
ASSERT_NEQ(p.is_relative(), p.is_absolute());
if (p.empty())
ASSERT(p.is_relative());

assert(checkCollectionsEqual(p.begin(), p.end(),
TC.elements.begin(), TC.elements.end()));
ASSERT_COLLECTION_EQ_COMP(
p.begin(), p.end(),
TC.elements.begin(), TC.elements.end(),
ComparePathExact()
);
// check backwards

std::vector<fs::path> Parts;
for (auto it = p.end(); it != p.begin(); )
Parts.push_back(*--it);
assert(checkCollectionsEqual(Parts.begin(), Parts.end(),
TC.elements.rbegin(), TC.elements.rend()));
ASSERT_COLLECTION_EQ_COMP(Parts.begin(), Parts.end(),
TC.elements.rbegin(), TC.elements.rend(),
ComparePathExact());
}
}

Expand All @@ -163,29 +180,32 @@ const FilenameDecompTestcase FilenameTestCases[] =
{"", "", "", ""}
, {".", ".", ".", ""}
, {"..", "..", "..", ""}
, {"/", "/", "/", ""}
, {"/", "", "", ""}
, {"foo", "foo", "foo", ""}
, {"/foo/bar.txt", "bar.txt", "bar", ".txt"}
, {"foo..txt", "foo..txt", "foo.", ".txt"}
, {".profile", ".profile", ".profile", ""}
, {".profile.txt", ".profile.txt", ".profile", ".txt"}
};


void decompFilenameTest()
{
using namespace fs;
for (auto const & TC : FilenameTestCases) {
path p(TC.raw);
assert(p == TC.raw);
CHECKPOINT(TC.raw.c_str());
fs::path p(TC.raw);
ASSERT_EQ(p, TC.raw);
ASSERT_NOEXCEPT(p.empty());

assert(p.filename() == TC.filename);
assert(p.has_filename() != TC.filename.empty());
ASSERT_EQ(p.filename(), TC.filename);
ASSERT_NEQ(p.has_filename(), TC.filename.empty());

assert(p.stem() == TC.stem);
assert(p.has_stem() != TC.stem.empty());
ASSERT_EQ(p.stem(), TC.stem);
ASSERT_NEQ(p.has_stem(), TC.stem.empty());

assert(p.extension() == TC.extension);
assert(p.has_extension() != TC.extension.empty());
ASSERT_EQ(p.extension(), TC.extension);
ASSERT_NEQ(p.has_extension(), TC.extension.empty());
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// class path

// path lexically_normal() const;

#include "filesystem_include.hpp"
#include <type_traits>
#include <vector>
#include <iostream>
#include <cassert>

#include "test_macros.h"
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"


int main() {
// clang-format off
struct {
std::string input;
std::string expect;
} TestCases[] = {
{"", ""},
{"/a/b/c", "/a/b/c"},
{"/a/b//c", "/a/b/c"},
{"foo/./bar/..", "foo/"},
{"foo/.///bar/../", "foo/"},
{"/a/b/", "/a/b/"},
{"a/b", "a/b"},
{"a/b/.", "a/b/"},
{"a/b/./", "a/b/"},
{"a/..", "."},
{".", "."},
{"./", "."},
{"./.", "."},
{"./..", ".."},
{"..", ".."},
{"../..", "../.."},
{"/../", "/"},
{"/../..", "/"},
{"/../../", "/"},
{"..", ".."},
{"../", ".."},
{"/a/b/c/../", "/a/b/"},
{"/a/b/./", "/a/b/"},
{"/a/b/c/../d", "/a/b/d"},
{"/a/b/c/../d/", "/a/b/d/"},
{"//a/", "/a/"},
{"//a/b/", "/a/b/"},
{"//a/b/.", "/a/b/"},
{"//a/..", "/"},
///===---------------------------------------------------------------===//
/// Tests specifically for the clauses under [fs.path.generic]p6
///===---------------------------------------------------------------===//
// p1: If the path is empty, stop.
{"", ""},
// p2: Replace each slash character in the root-name with a preferred
// separator.
{"NO_ROOT_NAME_ON_LINUX", "NO_ROOT_NAME_ON_LINUX"},
// p3: Replace each directory-separator with a preferred-separator.
// [ Note: The generic pathname grammar ([fs.path.generic]) defines
// directory-separator as one or more slashes and preferred-separators.
// — end note ]
{"/", "/"},
{"//", "/"},
{"///", "/"},
{"a/b", "a/b"},
{"a//b", "a/b"},
{"a///b", "a/b"},
{"a/b/", "a/b/"},
{"a/b//", "a/b/"},
{"a/b///", "a/b/"},
{"///a////b//////", "/a/b/"},
// p4: Remove each dot filename and any immediately following directory
// separators
{"foo/.", "foo/"},
{"foo/./bar/.", "foo/bar/"},
{"./foo/././bar/./", "foo/bar/"},
{".///foo//.////./bar/.///", "foo/bar/"},
// p5: As long as any appear, remove a non-dot-dot filename immediately
// followed by a directory-separator and a dot-dot filename, along with
// any immediately following directory separator.
{"foo/..", "."},
{"foo/../", "."},
{"foo/bar/..", "foo/"},
{"foo/bar/../", "foo/"},
{"foo/bar/../..", "."},
{"foo/bar/../../", "."},
{"foo/bar/baz/../..", "foo/"},
{"foo/bar/baz/../../", "foo/"},
{"foo/bar/./..", "foo/"},
{"foo/bar/./../", "foo/"},
// p6: If there is a root-directory, remove all dot-dot filenames and any
// directory-separators immediately following them. [ Note: These dot-dot
// filenames attempt to refer to nonexistent parent directories. — end note ]
{"/..", "/"},
{"/../", "/"},
{"/foo/../..", "/"},
{"/../foo", "/foo"},
{"/../foo/../..", "/"},
// p7: If the last filename is dot-dot, remove any trailing
// directory-separator.
{"../", ".."},
{"../../", "../.."},
{"foo/../bar/../..///", ".."},
{"foo/../bar/..//..///../", "../.."},
// p8: If the path is empty, add a dot
{".", "."},
{"./", "."},
{"foo/..", "."}
};
// clang-format on
int ID = 0;
bool Failed = false;
for (auto& TC : TestCases) {
++ID;
fs::path p(TC.input);
const fs::path output = p.lexically_normal();
if (!PathEq(output, TC.expect)) {
Failed = true;
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
std::cerr << " Input: '" << TC.input << "'\n";
std::cerr << " Expected: '" << TC.expect << "'\n";
std::cerr << " Output: '" << output.native() << "'";
std::cerr << std::endl;
}
}
return Failed;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// class path

// path lexically_relative(const path& p) const;
// path lexically_proximate(const path& p) const;

#include "filesystem_include.hpp"
#include <type_traits>
#include <vector>
#include <iostream>
#include <cassert>

#include "test_macros.h"
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"


int main() {
// clang-format off
struct {
std::string input;
std::string base;
std::string expect;
} TestCases[] = {
{"", "", "."},
{"/", "a", ""},
{"a", "/", ""},
{"//net", "a", ""},
{"a", "//net", ""},
{"//net/", "//net", ""},
{"//net", "//net/", ".."},
{"//base", "a", ""},
{"a", "a", "."},
{"a/b", "a/b", "."},
{"a/b/c/", "a/b/c/", "."},
{"//net", "//net", "."},
{"//net/", "//net/", "."},
{"//net/a/b", "//net/a/b", "."},
{"/a/d", "/a/b/c", "../../d"},
{"/a/b/c", "/a/d", "../b/c"},
{"a/b/c", "a", "b/c"},
{"a/b/c", "a/b/c/x/y", "../.."},
{"a/b/c", "a/b/c", "."},
{"a/b", "c/d", "../../a/b"}
};
// clang-format on
int ID = 0;
bool Failed = false;
for (auto& TC : TestCases) {
++ID;
const fs::path p(TC.input);
const fs::path output = p.lexically_relative(TC.base);
auto ReportErr = [&](const char* Testing, fs::path const& Output,
fs::path const& Expected) {
Failed = true;
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
std::cerr << " Testing: " << Testing << "\n";
std::cerr << " Input: '" << TC.input << "'\n";
std::cerr << " Base: '" << TC.base << "'\n";
std::cerr << " Expected: '" << Expected << "'\n";
std::cerr << " Output: '" << Output.native() << "'";
std::cerr << std::endl;
};
if (!PathEq(output, TC.expect))
ReportErr("path::lexically_relative", output, TC.expect);
const fs::path proximate_output = p.lexically_proximate(TC.base);
// [path.gen] lexically_proximate
// Returns: If the value of lexically_relative(base) is not an empty path,
// return it.Otherwise return *this.
const fs::path proximate_expected = output.native().empty() ? p
: output;
if (!PathEq(proximate_expected, proximate_output))
ReportErr("path::lexically_proximate", proximate_output, proximate_expected);
}
return Failed;
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
#include "min_allocator.h"
#include "filesystem_test_helper.hpp"


MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#include "min_allocator.h"
#include "filesystem_test_helper.hpp"


MultiStringType longString = MKSTR("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/123456789/abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");

int main()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"

#include "verbose_assert.h"

struct RemoveFilenameTestcase {
const char* value;
Expand All @@ -33,27 +33,29 @@ struct RemoveFilenameTestcase {
const RemoveFilenameTestcase TestCases[] =
{
{"", ""}
, {"/", ""}
, {"//", ""}
, {"///", ""}
, {"/", "/"}
, {"//", "//"}
, {"///", "///"}
, {"\\", ""}
, {".", ""}
, {"..", ""}
, {"/foo", "/"}
, {"//foo", ""}
, {"//foo/", ""}
, {"//foo///", ""}
, {"///foo", "/"}
, {"///foo/", "///foo"}
, {"/foo/", "/foo"}
, {"/foo/.", "/foo"}
, {"/foo/..", "/foo"}
, {"/foo/////", "/foo"}
, {"foo/bar", "foo/"}
, {"foo/", "foo/"}
, {"//foo", "//"}
, {"//foo/", "//foo/"}
, {"//foo///", "//foo///"}
, {"///foo", "///"}
, {"///foo/", "///foo/"}
, {"/foo/", "/foo/"}
, {"/foo/.", "/foo/"}
, {"/foo/..", "/foo/"}
, {"/foo/////", "/foo/////"}
, {"/foo\\\\", "/"}
, {"/foo//\\/", "/foo//\\"}
, {"///foo", "/"}
, {"/foo//\\/", "/foo//\\/"}
, {"///foo", "///"}
, {"file.txt", ""}
, {"bar/../baz/./file.txt", "bar/../baz/."}
, {"bar/../baz/./file.txt", "bar/../baz/./"}
};

int main()
Expand All @@ -64,16 +66,8 @@ int main()
path p(p_orig);
assert(p == TC.value);
path& Ref = (p.remove_filename());
assert(p == TC.expect);
ASSERT_EQ(p, TC.expect) << DISPLAY(p_orig);
assert(&Ref == &p);
{
const path parentp = p_orig.parent_path();
if (parentp == p_orig.root_name()) {

assert(p.empty());
} else {
assert(p == parentp);
}
}
assert(!p.has_filename());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"

#include "assert_checkpoint.h"
#include "verbose_assert.h"

struct ReplaceFilenameTestcase {
const char* value;
Expand All @@ -36,9 +37,9 @@ const ReplaceFilenameTestcase TestCases[] =
{"/foo", "/bar", "bar"}
, {"/foo", "/", ""}
, {"foo", "bar", "bar"}
, {"/", "bar", "bar"}
, {"/", "/bar", "bar"}
, {"\\", "bar", "bar"}
, {"///", "bar", "bar"}
, {"///", "///bar", "bar"}
, {"\\\\", "bar", "bar"}
, {"\\/\\", "\\/bar", "bar"}
, {".", "bar", "bar"}
Expand All @@ -52,17 +53,19 @@ int main()
using namespace fs;
for (auto const & TC : TestCases) {
path p(TC.value);
assert(p == TC.value);
ASSERT_EQ(p, TC.value);
path& Ref = (p.replace_filename(TC.filename));
assert(p == TC.expect);
ASSERT_EQ(p, TC.expect)
<< DISPLAY(TC.value)
<< DISPLAY(TC.filename);
assert(&Ref == &p);
// Tests Effects "as-if": remove_filename() append(filename)
{
path p2(TC.value);
path replace(TC.filename);
p2.remove_filename();
p2 /= replace;
assert(p2 == p);
ASSERT_EQ(p, p2);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// enum class perm_options;

#include "filesystem_include.hpp"
#include <type_traits>
#include <cassert>
#include <sys/stat.h>

#include "test_macros.h"
#include "check_bitmask_types.hpp"


constexpr fs::perm_options ME(int val) {
return static_cast<fs::perm_options>(val);
}

int main() {
typedef fs::perm_options E;
static_assert(std::is_enum<E>::value, "");

// Check that E is a scoped enum by checking for conversions.
typedef std::underlying_type<E>::type UT;
static_assert(!std::is_convertible<E, UT>::value, "");

static_assert(std::is_same<UT, unsigned char >::value, ""); // Implementation detail

typedef check_bitmask_type<E, E::replace, E::nofollow> BitmaskTester;
assert(BitmaskTester::check());

static_assert(
E::replace == ME(1) &&
E::add == ME(2) &&
E::remove == ME(4) &&
E::nofollow == ME(8),
"Expected enumeration values do not match");
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,89 +28,30 @@ TEST_SUITE(filesystem_absolute_path_test_suite)
TEST_CASE(absolute_signature_test)
{
const path p; ((void)p);
std::error_code ec;
ASSERT_NOT_NOEXCEPT(absolute(p));
ASSERT_NOT_NOEXCEPT(absolute(p, p));
ASSERT_NOT_NOEXCEPT(absolute(p, ec));
}

// There are 4 cases is the proposal for absolute path.
// Each scope tests one of the cases.
TEST_CASE(absolute_path_test)
{
// has_root_name() && has_root_directory()
{
const path p("//net/foo");
const path base("//net/bar/baz");
TEST_REQUIRE(p.has_root_name());
TEST_REQUIRE(p.has_root_directory());
TEST_CHECK(p.is_absolute());
path ret = absolute(p, base);
TEST_CHECK(ret.is_absolute());
TEST_CHECK(ret == p);
}
// !has_root_name() && has_root_directory()
{
const path p("/foo");
const path base("//net/bar");
TEST_REQUIRE(not p.has_root_name());
TEST_REQUIRE(p.has_root_directory());
TEST_CHECK(p.is_absolute());
// ensure absolute(base) is not recursively called
TEST_REQUIRE(base.has_root_name());
TEST_REQUIRE(base.has_root_directory());

path ret = absolute(p, base);
TEST_CHECK(ret.is_absolute());
TEST_CHECK(ret.has_root_name());
TEST_CHECK(ret.root_name() == path("//net"));
TEST_CHECK(ret.has_root_directory());
TEST_CHECK(ret.root_directory() == path("/"));
TEST_CHECK(ret == path("//net/foo"));
}
// has_root_name() && !has_root_directory()
{
const path p("//net");
const path base("//net/foo/bar");
TEST_REQUIRE(p.has_root_name());
TEST_REQUIRE(not p.has_root_directory());
TEST_CHECK(not p.is_absolute());
// absolute is called recursively on base. The following conditions
// must be true for it to return base unmodified
TEST_REQUIRE(base.has_root_name());
TEST_REQUIRE(base.has_root_directory());
path ret = absolute(p, base);
const path expect("//net/foo/bar");
TEST_CHECK(ret.is_absolute());
TEST_CHECK(ret == path("//net/foo/bar"));
}
// !has_root_name() && !has_root_directory()
{
const path p("bar/baz");
const path base("//net/foo");
TEST_REQUIRE(not p.has_root_name());
TEST_REQUIRE(not p.has_root_directory());
TEST_REQUIRE(base.has_root_name());
TEST_REQUIRE(base.has_root_directory());

path ret = absolute(p, base);
TEST_CHECK(ret.is_absolute());
TEST_CHECK(ret == path("//net/foo/bar/baz"));
}
}

TEST_CASE(absolute_path_with_default_base)
TEST_CASE(basic_test)
{
const path testCases[] = {
"//net/foo", // has_root_name() && has_root_directory()
"/foo", // !has_root_name() && has_root_directory()
"//net", // has_root_name() && !has_root_directory()
"bar/baz" // !has_root_name() && !has_root_directory()
const fs::path cwd = fs::current_path();
const struct {
std::string input;
std::string expect;
} TestCases [] = {
{"", cwd / ""},
{"foo", cwd / "foo"},
{"foo/", cwd / "foo/"},
{"/already_absolute", "/already_absolute"}
};
const path base = current_path();
for (auto& p : testCases) {
const path ret = absolute(p);
const path expect = absolute(p, base);
for (auto& TC : TestCases) {
std::error_code ec = GetTestEC();
const path ret = absolute(TC.input, ec);
TEST_CHECK(!ec);
TEST_CHECK(ret.is_absolute());
TEST_CHECK(ret == expect);
TEST_CHECK(PathEq(ret, TC.expect));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@

// <experimental/filesystem>

// path canonical(const path& p, const path& base = current_path());
// path canonical(const path& p);
// path canonical(const path& p, error_code& ec);
// path canonical(const path& p, const path& base, error_code& ec);

#include "filesystem_include.hpp"
#include <type_traits>
Expand All @@ -25,22 +24,30 @@

using namespace fs;

struct CWDGuard {
path OldCWD;
CWDGuard() : OldCWD(fs::current_path()) { }
~CWDGuard() { fs::current_path(OldCWD); }

CWDGuard(CWDGuard const&) = delete;
CWDGuard& operator=(CWDGuard const&) = delete;
};

TEST_SUITE(filesystem_canonical_path_test_suite)

TEST_CASE(signature_test)
{
const path p; ((void)p);
std::error_code ec; ((void)ec);
ASSERT_NOT_NOEXCEPT(canonical(p));
ASSERT_NOT_NOEXCEPT(canonical(p, p));
ASSERT_NOT_NOEXCEPT(canonical(p, ec));
ASSERT_NOT_NOEXCEPT(canonical(p, p, ec));
}

// There are 4 cases is the proposal for absolute path.
// Each scope tests one of the cases.
TEST_CASE(test_canonical)
{
CWDGuard guard;
// has_root_name() && has_root_directory()
const path Root = StaticEnv::Root;
const path RootName = Root.filename();
Expand All @@ -65,54 +72,51 @@ TEST_CASE(test_canonical)
{ SymlinkName, StaticEnv::File, StaticEnv::Root}
};
for (auto& TC : testCases) {
std::error_code ec;
const path ret = canonical(TC.p, TC.base, ec);
std::error_code ec = GetTestEC();
fs::current_path(TC.base);
const path ret = canonical(TC.p, ec);
TEST_REQUIRE(!ec);
const path ret2 = canonical(TC.p, TC.base);
TEST_CHECK(ret == TC.expect);
TEST_CHECK(ret == ret2);
const path ret2 = canonical(TC.p);
TEST_CHECK(PathEq(ret, TC.expect));
TEST_CHECK(PathEq(ret, ret2));
TEST_CHECK(ret.is_absolute());
}
}

TEST_CASE(test_dne_path)
{
std::error_code ec;
std::error_code ec = GetTestEC();
{
const path ret = canonical(StaticEnv::DNE, ec);
TEST_REQUIRE(ec);
TEST_CHECK(ret == path{});
}
ec.clear();
{
const path ret = canonical(StaticEnv::DNE, StaticEnv::Root, ec);
TEST_CHECK(ec != GetTestEC());
TEST_REQUIRE(ec);
TEST_CHECK(ret == path{});
}
{
TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE));
TEST_CHECK_THROW(filesystem_error, canonical(StaticEnv::DNE, StaticEnv::Root));
}
}

TEST_CASE(test_exception_contains_paths)
{
#ifndef TEST_HAS_NO_EXCEPTIONS
CWDGuard guard;
const path p = "blabla/dne";
const path base = StaticEnv::Root;
try {
canonical(p, base);
canonical(p);
TEST_REQUIRE(false);
} catch (filesystem_error const& err) {
TEST_CHECK(err.path1() == p);
TEST_CHECK(err.path2() == base);
// libc++ provides the current path as the second path in the exception
LIBCPP_ONLY(TEST_CHECK(err.path2() == current_path()));
}
fs::current_path(StaticEnv::Dir);
try {
canonical(p);
TEST_REQUIRE(false);
} catch (filesystem_error const& err) {
TEST_CHECK(err.path1() == p);
TEST_CHECK(err.path2() == current_path());
LIBCPP_ONLY(TEST_CHECK(err.path2() == StaticEnv::Dir));
}
#endif
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
// void last_write_time(const path& p, file_time_type new_type,
// std::error_code& ec) noexcept;


#include "filesystem_include.hpp"
#include <type_traits>
#include <chrono>
Expand All @@ -33,7 +32,6 @@

using namespace fs;


std::pair<std::time_t, std::time_t> GetTimes(path const& p) {
using Clock = file_time_type::clock;
struct ::stat st;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// path proximate(const path& p, error_code &ec)
// path proximate(const path& p, const path& base = current_path())
// path proximate(const path& p, const path& base, error_code& ec);

#include "filesystem_include.hpp"
#include <type_traits>
#include <vector>
#include <iostream>
#include <cassert>

#include "test_macros.h"
#include "test_iterators.h"
#include "count_new.hpp"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"


static int count_path_elems(const fs::path& p) {
int count = 0;
for (auto& elem : p) {
if (elem != "/" && elem != "")
++count;
}
return count;
}

TEST_SUITE(filesystem_proximate_path_test_suite)


TEST_CASE(signature_test)
{
using fs::path;
const path p; ((void)p);
std::error_code ec; ((void)ec);
ASSERT_NOT_NOEXCEPT(proximate(p));
ASSERT_NOT_NOEXCEPT(proximate(p, p));
ASSERT_NOT_NOEXCEPT(proximate(p, ec));
ASSERT_NOT_NOEXCEPT(proximate(p, p, ec));
}

TEST_CASE(basic_test) {
using fs::path;
const path cwd = fs::current_path();
const path parent_cwd = cwd.parent_path();
const path curdir = cwd.filename();
TEST_REQUIRE(!cwd.native().empty());
int cwd_depth = count_path_elems(cwd);
path dot_dot_to_root;
for (int i=0; i < cwd_depth; ++i)
dot_dot_to_root /= "..";
path relative_cwd = cwd.native().substr(1);
// clang-format off
struct {
std::string input;
std::string base;
std::string expect;
} TestCases[] = {
{"", "", "."},
{cwd, "a", ".."},
{parent_cwd, "a", "../.."},
{"a", cwd, "a"},
{"a", parent_cwd, "fs.op.proximate/a"},
{"/", "a", dot_dot_to_root / ".."},
{"/", "a/b", dot_dot_to_root / "../.."},
{"/", "a/b/", dot_dot_to_root / "../../.."},
{"a", "/", relative_cwd / "a"},
{"a/b", "/", relative_cwd / "a/b"},
{"a", "/net", ".." / relative_cwd / "a"},
{"//net/", "//net", "/net/"},
{"//net", "//net/", ".."},
{"//net", "//net", "."},
{"//net/", "//net/", "."},
{"//base", "a", dot_dot_to_root / "../base"},
{"a", "a", "."},
{"a/b", "a/b", "."},
{"a/b/c/", "a/b/c/", "."},
{"//net/a/b", "//net/a/b", "."},
{"/a/d", "/a/b/c", "../../d"},
{"/a/b/c", "/a/d", "../b/c"},
{"a/b/c", "a", "b/c"},
{"a/b/c", "a/b/c/x/y", "../.."},
{"a/b/c", "a/b/c", "."},
{"a/b", "c/d", "../../a/b"}
};
// clang-format on
int ID = 0;
for (auto& TC : TestCases) {
++ID;
std::error_code ec = GetTestEC();
fs::path p(TC.input);
const fs::path output = fs::proximate(p, TC.base, ec);
TEST_CHECK(!ec);
TEST_CHECK(PathEq(output, TC.expect));
if (!PathEq(output, TC.expect)) {
const path canon_input = fs::weakly_canonical(TC.input);
const path canon_base = fs::weakly_canonical(TC.base);
const path lexically_p = canon_input.lexically_proximate(canon_base);
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
std::cerr << " Input: '" << TC.input << "'\n";
std::cerr << " Base: '" << TC.base << "'\n";
std::cerr << " Expected: '" << TC.expect << "'\n";
std::cerr << " Output: '" << output.native() << "'\n";
std::cerr << " Lex Prox: '" << lexically_p.native() << "'\n";
std::cerr << " Canon Input: " << canon_input << "\n";
std::cerr << " Canon Base: " << canon_base << "\n";

std::cerr << std::endl;
}
}
}

TEST_SUITE_END()
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// path proximate(const path& p, error_code &ec)
// path proximate(const path& p, const path& base = current_path())
// path proximate(const path& p, const path& base, error_code& ec);

#include "filesystem_include.hpp"
#include <type_traits>
#include <vector>
#include <iostream>
#include <cassert>

#include "test_macros.h"
#include "test_iterators.h"
#include "count_new.hpp"
#include "rapid-cxx-test.hpp"
#include "filesystem_test_helper.hpp"


TEST_SUITE(filesystem_proximate_path_test_suite)

TEST_CASE(test_signature) {

}
int main() {
// clang-format off
struct {
std::string input;
std::string expect;
} TestCases[] = {
{"", fs::current_path()},
{".", fs::current_path()},
{StaticEnv::File, StaticEnv::File},
{StaticEnv::Dir, StaticEnv::Dir},
{StaticEnv::SymlinkToDir, StaticEnv::Dir},
{StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"},
// FIXME? If the trailing separator occurs in a part of the path that exists,
// it is ommitted. Otherwise it is added to the end of the result.
{StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"},
{StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"},
{StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2},
{StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""},
{StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"},
{StaticEnv::Dir / "../dir1", StaticEnv::Dir},
{StaticEnv::Dir / "./.", StaticEnv::Dir},
{StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"}
};
// clang-format on
int ID = 0;
bool Failed = false;
for (auto& TC : TestCases) {
++ID;
fs::path p(TC.input);
const fs::path output = fs::weakly_canonical(p);
if (output != TC.expect) {
Failed = true;
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
std::cerr << " Input: '" << TC.input << "'\n";
std::cerr << " Expected: '" << TC.expect << "'\n";
std::cerr << " Output: '" << output.native() << "'";
std::cerr << std::endl;
}
}
return Failed;
}

TEST_SUITE_END()

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//===----------------------------------------------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++98, c++03

// <experimental/filesystem>

// path weakly_canonical(const path& p);
// path weakly_canonical(const path& p, error_code& ec);

#include "filesystem_include.hpp"
#include <type_traits>
#include <vector>
#include <iostream>
#include <cassert>

#include "test_macros.h"
#include "test_iterators.h"
#include "count_new.hpp"
#include "filesystem_test_helper.hpp"


int main() {
// clang-format off
struct {
std::string input;
std::string expect;
} TestCases[] = {
{"", fs::current_path()},
{".", fs::current_path()},
{"/", "/"},
{"/foo", "/foo"},
{"/.", "/"},
{"/./", "/"},
{"a/b", fs::current_path() / "a/b"},
{"a", fs::current_path() / "a"},
{"a/b/", fs::current_path() / "a/b/"},
{StaticEnv::File, StaticEnv::File},
{StaticEnv::Dir, StaticEnv::Dir},
{StaticEnv::SymlinkToDir, StaticEnv::Dir},
{StaticEnv::SymlinkToDir / "dir2/.", StaticEnv::Dir / "dir2"},
// FIXME? If the trailing separator occurs in a part of the path that exists,
// it is ommitted. Otherwise it is added to the end of the result.
{StaticEnv::SymlinkToDir / "dir2/./", StaticEnv::Dir / "dir2"},
{StaticEnv::SymlinkToDir / "dir2/DNE/./", StaticEnv::Dir / "dir2/DNE/"},
{StaticEnv::SymlinkToDir / "dir2", StaticEnv::Dir2},
{StaticEnv::SymlinkToDir / "dir2/../dir2/DNE/..", StaticEnv::Dir2 / ""},
{StaticEnv::SymlinkToDir / "dir2/dir3/../DNE/DNE2", StaticEnv::Dir2 / "DNE/DNE2"},
{StaticEnv::Dir / "../dir1", StaticEnv::Dir},
{StaticEnv::Dir / "./.", StaticEnv::Dir},
{StaticEnv::Dir / "DNE/../foo", StaticEnv::Dir / "foo"}
};
// clang-format on
int ID = 0;
bool Failed = false;
for (auto& TC : TestCases) {
++ID;
fs::path p(TC.input);
const fs::path output = fs::weakly_canonical(p);
if (!PathEq(output, TC.expect)) {
Failed = true;
std::cerr << "TEST CASE #" << ID << " FAILED: \n";
std::cerr << " Input: '" << TC.input << "'\n";
std::cerr << " Expected: '" << TC.expect << "'\n";
std::cerr << " Output: '" << output.native() << "'";
std::cerr << std::endl;
}
}
return Failed;
}
5 changes: 4 additions & 1 deletion libcxx/test/support/filesystem_test_helper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <random>
#include <chrono>


// static test helpers

#ifndef LIBCXX_FILESYSTEM_STATIC_TEST_ROOT
Expand Down Expand Up @@ -400,4 +399,8 @@ void SleepFor(std::chrono::seconds dur) {
;
}

inline bool PathEq(fs::path const& LHS, fs::path const& RHS) {
return LHS.native() == RHS.native();
}

#endif /* FILESYSTEM_TEST_HELPER_HPP */
222 changes: 222 additions & 0 deletions libcxx/test/support/verbose_assert.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
#ifndef TEST_SUPPORT_VERBOSE_ASSERT
#define TEST_SUPPORT_VERBOSE_ASSERT

#include <iostream>
#include <cstdio>
#include <sstream>
#include <string>
#include "test_macros.h"

namespace verbose_assert {

typedef std::basic_ostream<char>&(EndLType)(std::basic_ostream<char>&);

template <class Stream, class Tp,
class = decltype(std::declval<Stream&>() << std::declval<Tp const&>())>
std::true_type IsStreamableImp(int);
template <class Stream, class Tp> std::false_type IsStreamableImp(long);

template <class Stream, class Tp>
struct IsStreamable : decltype(IsStreamableImp<Stream, Tp>(0)) {};

template <class Tp, int ST = (IsStreamable<decltype(std::cerr), Tp>::value ? 1
: (IsStreamable<decltype(std::wcerr), Tp>::value ? 2 : -1))>
struct SelectStream {
static_assert(ST == -1, "specialization required for ST != -1");
static void Print(Tp const&) { std::clog << "Value Not Streamable!\n"; }
};

template <class Tp>
struct SelectStream<Tp, 1> {
static void Print(Tp const& val) { std::cerr << val; }
};

template <class Tp>
struct SelectStream<Tp, 2> {
static void Print(Tp const& val) { std::wcerr << val; }
};

struct AssertData {
AssertData(const char* xcheck, const char* xfile, const char* xfunc,
unsigned long xline, bool xpassed = true)
: passed(xpassed), check(xcheck), file(xfile), func(xfunc), line(xline),
msg() {}

AssertData& SetFailed(std::string xmsg = std::string()) {
msg = xmsg;
passed = false;
return *this;
}

void PrintFailed() const {
std::fprintf(stderr, "%s:%lu %s: Assertion '%s' failed.\n", file, line,
func, check);
if (!msg.empty())
std::fprintf(stderr, "%s\n", msg.data());
}

bool passed;
const char* check;
const char* file;
const char* func;
unsigned long line;
std::string msg;
};

// AssertHandler is the class constructed by failing CHECK macros. AssertHandler
// will log information about the failures and abort when it is destructed.
class AssertHandler {
public:
AssertHandler(AssertData const& Data)
: passed(Data.passed) {
if (!passed)
Data.PrintFailed();
}

~AssertHandler() TEST_NOEXCEPT_FALSE {
if (!passed) {
error_log << std::endl;
std::abort();
}
}

class LogType {
friend class AssertHandler;

template <class Tp>
friend LogType& operator<<(LogType& log, Tp const& value) {
if (!log.is_disabled) {
SelectStream<Tp>::Print(value);
}
return log;
}

friend LogType& operator<<(LogType& log, EndLType* m) {
if (!log.is_disabled) {
SelectStream<EndLType*>::Print(m);
}
return log;
}

private:
LogType(bool disable) : is_disabled(disable) {}
bool is_disabled;

LogType(LogType const&);
LogType& operator=(LogType const&);
};

LogType& GetLog() {
if (passed)
return null_log;
return error_log;
}

private:
static LogType null_log;
static LogType error_log;

AssertHandler& operator=(const AssertHandler&) = delete;
AssertHandler(const AssertHandler&) = delete;
AssertHandler() = delete;

private:
bool passed;
};

AssertHandler::LogType AssertHandler::null_log(true);
AssertHandler::LogType AssertHandler::error_log(false);

template <class It1>
std::string PrintRange(const char* Name, It1 F, It1 E) {
std::stringstream ss;
ss << " " << Name << " = [";
while (F != E) {
ss << *F;
++F;
if (F != E)
ss << ", ";
}
ss << "]\n";
return ss.str();
}

template <class Tp, class Up>
std::string PrintMismatch(Tp const& LHS, Up const& RHS, int Elem) {
std::stringstream ss;
ss << " Element " << Elem << " mismatched: `" << LHS << "` != `" << RHS
<< "`!\n";
return ss.str();
};

struct EqualToComp {
template <class Tp, class Up>
bool operator()(Tp const& LHS, Up const& RHS) const {
return LHS == RHS;
}
};

template <class It1, class It2, class Comp>
AssertData CheckCollectionsEqual(It1 F1, It1 E1, It2 F2, It2 E2,
AssertData Data, Comp C = EqualToComp()) {
const It1 F1Orig = F1;
const It2 F2Orig = F2;
bool Failed = false;
std::string ErrorMsg;
int Idx = 0;
while (F1 != E1 && F2 != E2) {
if (!(C(*F1, *F2))) {
ErrorMsg += PrintMismatch(*F1, *F2, Idx);
Failed = true;
break;
}
++Idx;
++F1;
++F2;
}
if (!Failed && (F1 != E1 || F2 != E2)) {
ErrorMsg += " Ranges have different sizes!\n";
Failed = true;
}
if (Failed) {
ErrorMsg += PrintRange("LHS", F1Orig, E1);
ErrorMsg += PrintRange("RHS", F2Orig, E2);
Data.SetFailed(ErrorMsg);
}
return Data;
}
} // namespace verbose_assert

#ifdef __GNUC__
#define ASSERT_FN_NAME() __PRETTY_FUNCTION__
#else
#define ASSERT_FN_NAME() __func__
#endif

#define DISPLAY(...) " " #__VA_ARGS__ " = " << (__VA_ARGS__) << "\n"

#define ASSERT(...) \
::verbose_assert::AssertHandler(::verbose_assert::AssertData( \
#__VA_ARGS__, __FILE__, ASSERT_FN_NAME(), __LINE__,(__VA_ARGS__))).GetLog()

#define ASSERT_EQ(LHS, RHS) \
ASSERT(LHS == RHS) << DISPLAY(LHS) << DISPLAY(RHS)
#define ASSERT_NEQ(LHS, RHS) \
ASSERT(LHS != RHS) << DISPLAY(LHS) << DISPLAY(RHS)
#define ASSERT_PRED(PRED, LHS, RHS) \
ASSERT(PRED(LHS, RHS)) << DISPLAY(LHS) << DISPLAY(RHS)

#define ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, Comp) \
(::verbose_assert::AssertHandler( \
::verbose_assert::CheckCollectionsEqual( \
F1, E1, F2, E2, \
::verbose_assert::AssertData("CheckCollectionsEqual(" #F1 ", " #E1 \
", " #F2 ", " #E2 ")", \
__FILE__, ASSERT_FN_NAME(), __LINE__), \
Comp)) \
.GetLog())

#define ASSERT_COLLECTION_EQ(F1, E1, F2, E2) \
ASSERT_COLLECTION_EQ_COMP(F1, E1, F2, E2, ::verbose_assert::EqualToComp())

#endif
4 changes: 2 additions & 2 deletions libcxx/www/cxx1z_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ <h3>Paper Status</h3>
<tr><td><a href="https://wg21.link/p0180r2">p0180r2</a></td><td>LWG</td><td>Reserve a New Library Namespace for Future Standardization</td><td>Oulu</td><td><i>Nothing to do</i></td><td>n/a</td></tr>
<tr><td><a href="https://wg21.link/p0181r1">p0181r1</a></td><td>LWG</td><td>Ordered by Default</td><td>Oulu</td><td><i>Removed in Kona</i></td><td>n/a</td></tr>
<tr><td><a href="https://wg21.link/p0209r2">p0209r2</a></td><td>LWG</td><td>make_from_tuple: apply for construction</td><td>Oulu</td><td>Complete</td><td>3.9</td></tr>
<tr><td><a href="https://wg21.link/p0219r1">p0219r1</a></td><td>LWG</td><td>Relative Paths for Filesystem</td><td>Oulu</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/p0219r1">p0219r1</a></td><td>LWG</td><td>Relative Paths for Filesystem</td><td>Oulu</td><td>Complete</td><td>7.0</td></tr>
<tr><td><a href="https://wg21.link/p0254r2">p0254r2</a></td><td>LWG</td><td>Integrating std::string_view and std::string</td><td>Oulu</td><td>Complete</td><td>4.0</td></tr>
<tr><td><a href="https://wg21.link/p0258r2">p0258r2</a></td><td>LWG</td><td>has_unique_object_representations</td><td>Oulu</td><td>Complete</td><td>6.0</td></tr>
<tr><td><a href="https://wg21.link/p0295r0">p0295r0</a></td><td>LWG</td><td>Adopt Selected Library Fundamentals V2 Components for C++17</td><td>Oulu</td><td>Complete</td><td>4.0</td></tr>
Expand Down Expand Up @@ -153,7 +153,7 @@ <h3>Paper Status</h3>
<tr><td><a href="https://wg21.link/P0433R2">P0433R2</a></td><td>LWG</td><td>Toward a resolution of US7 and US14: Integrating template deduction for class templates into the standard library</td><td>Kona</td><td><i>In progress</i></td><td>7.0</td></tr>
<tr><td><a href="https://wg21.link/P0452R1">P0452R1</a></td><td>LWG</td><td>Unifying &lt;numeric&gt; Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P0467R2">P0467R2</a></td><td>LWG</td><td>Iterator Concerns for Parallel Algorithms</td><td>Kona</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P0492R2">P0492R2</a></td><td>LWG</td><td>Proposed Resolution of C++17 National Body Comments for Filesystems</td><td>Kona</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P0492R2">P0492R2</a></td><td>LWG</td><td>Proposed Resolution of C++17 National Body Comments for Filesystems</td><td>Kona</td><td>Complete</td><td>7.0</td></tr>
<tr><td><a href="https://wg21.link/P0518R1">P0518R1</a></td><td>LWG</td><td>Allowing copies as arguments to function objects given to parallel algorithms in response to CH11</td><td>Kona</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P0523R1">P0523R1</a></td><td>LWG</td><td>Wording for CH 10: Complexity of parallel algorithms</td><td>Kona</td><td></td><td></td></tr>
<tr><td><a href="https://wg21.link/P0548R1">P0548R1</a></td><td>LWG</td><td>common_type and duration</td><td>Kona</td><td>Complete</td><td>5.0</td></tr>
Expand Down
2 changes: 1 addition & 1 deletion libcxx/www/cxx2a_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ <h3>Library Working group Issues Status</h3>
<tr><td><a href="https://wg21.link/LWG3015">3015</a></td><td><tt>copy_options::<i>unspecified</i></tt> underspecified</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>
<tr><td><a href="https://wg21.link/LWG3017">3017</a></td><td><tt>list splice</tt> functions should use <tt>addressof</tt></td><td>Jacksonville</td><td></td></tr>
<tr><td><a href="https://wg21.link/LWG3020">3020</a></td><td>[networking.ts] Remove spurious nested <tt>value_type</tt> buffer sequence requirement</td><td>Jacksonville</td><td></td></tr>
<tr><td><a href="https://wg21.link/LWG3026">3026</a></td><td><tt>filesystem::weakly_canonical</tt> still defined in terms of <tt>canonical(p, base)</tt></td><td>Jacksonville</td><td></td></tr>
<tr><td><a href="https://wg21.link/LWG3026">3026</a></td><td><tt>filesystem::weakly_canonical</tt> still defined in terms of <tt>canonical(p, base)</tt></td><td>Jacksonville</td><td>Complete</td></tr>
<tr><td><a href="https://wg21.link/LWG3030">3030</a></td><td>Who shall meet the requirements of <tt>try_lock</tt>?</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>
<tr><td><a href="https://wg21.link/LWG3034">3034</a></td><td>P0767R1 breaks previously-standard-layout types</td><td>Jacksonville</td><td>Complete</td></tr>
<tr><td><a href="https://wg21.link/LWG3035">3035</a></td><td><tt>std::allocator</tt>'s constructors should be <tt>constexpr</tt></td><td>Jacksonville</td><td>Complete</td></tr>
Expand Down