Implement filesystem_error::what() and improve reporting.
This patch implements the `what()` for filesystem errors. The message includes the 'what_arg', any paths that were specified, and the error code message. Additionally this patch refactors how errors are created, making it easier to report them correctly. git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@337664 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -1265,40 +1265,51 @@ public:
|
|||||||
_LIBCPP_INLINE_VISIBILITY
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
filesystem_error(const string& __what, error_code __ec)
|
filesystem_error(const string& __what, error_code __ec)
|
||||||
: system_error(__ec, __what),
|
: system_error(__ec, __what),
|
||||||
__paths_(make_shared<_Storage>(path(), path()))
|
__storage_(make_shared<_Storage>(path(), path())) {
|
||||||
{}
|
__create_what(0);
|
||||||
|
}
|
||||||
|
|
||||||
_LIBCPP_INLINE_VISIBILITY
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
filesystem_error(const string& __what, const path& __p1, error_code __ec)
|
filesystem_error(const string& __what, const path& __p1, error_code __ec)
|
||||||
: system_error(__ec, __what),
|
: system_error(__ec, __what),
|
||||||
__paths_(make_shared<_Storage>(__p1, path()))
|
__storage_(make_shared<_Storage>(__p1, path())) {
|
||||||
{}
|
__create_what(1);
|
||||||
|
}
|
||||||
|
|
||||||
_LIBCPP_INLINE_VISIBILITY
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
filesystem_error(const string& __what, const path& __p1, const path& __p2,
|
filesystem_error(const string& __what, const path& __p1, const path& __p2,
|
||||||
error_code __ec)
|
error_code __ec)
|
||||||
: system_error(__ec, __what),
|
: system_error(__ec, __what),
|
||||||
__paths_(make_shared<_Storage>(__p1, __p2))
|
__storage_(make_shared<_Storage>(__p1, __p2)) {
|
||||||
{}
|
__create_what(2);
|
||||||
|
|
||||||
_LIBCPP_INLINE_VISIBILITY
|
|
||||||
const path& path1() const _NOEXCEPT {
|
|
||||||
return __paths_->first;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_LIBCPP_INLINE_VISIBILITY
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
const path& path2() const _NOEXCEPT {
|
const path& path1() const _NOEXCEPT { return __storage_->__p1_; }
|
||||||
return __paths_->second;
|
|
||||||
}
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
|
const path& path2() const _NOEXCEPT { return __storage_->__p2_; }
|
||||||
|
|
||||||
~filesystem_error() override; // key function
|
~filesystem_error() override; // key function
|
||||||
|
|
||||||
// TODO(ericwf): Create a custom error message.
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
//const char* what() const _NOEXCEPT;
|
const char* what() const _NOEXCEPT override {
|
||||||
|
return __storage_->__what_.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
_LIBCPP_FUNC_VIS
|
||||||
typedef pair<path, path> _Storage;
|
void __create_what(int __num_paths);
|
||||||
shared_ptr<_Storage> __paths_;
|
|
||||||
|
private:
|
||||||
|
struct _Storage {
|
||||||
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
|
_Storage(const path& __p1, const path& __p2) : __p1_(__p1), __p2_(__p2) {}
|
||||||
|
|
||||||
|
path __p1_;
|
||||||
|
path __p2_;
|
||||||
|
string __what_;
|
||||||
|
};
|
||||||
|
shared_ptr<_Storage> __storage_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class... _Args>
|
template <class... _Args>
|
||||||
@@ -1315,7 +1326,6 @@ void __throw_filesystem_error(_Args&&...)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// operational functions
|
// operational functions
|
||||||
|
|
||||||
_LIBCPP_FUNC_VIS
|
_LIBCPP_FUNC_VIS
|
||||||
@@ -2226,12 +2236,13 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (__ec && (!__allow_dne || !__is_dne_error(__ec)))
|
if (__ec && (!__allow_dne || !__is_dne_error(__ec)))
|
||||||
__throw_filesystem_error(__msg, __p_, _Path{}, __ec);
|
__throw_filesystem_error(__msg, __p_, __ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
_LIBCPP_INLINE_VISIBILITY
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
void __refresh(error_code* __ec = nullptr) {
|
void __refresh(error_code* __ec = nullptr) {
|
||||||
__handle_error("refresh", __ec, __do_refresh(), /*allow_dne*/ true);
|
__handle_error("in directory_entry::refresh", __ec, __do_refresh(),
|
||||||
|
/*allow_dne*/ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_LIBCPP_INLINE_VISIBILITY
|
_LIBCPP_INLINE_VISIBILITY
|
||||||
@@ -2322,11 +2333,11 @@ private:
|
|||||||
case _RefreshNonSymlink: {
|
case _RefreshNonSymlink: {
|
||||||
error_code __m_ec;
|
error_code __m_ec;
|
||||||
file_status __st(__get_ft(&__m_ec));
|
file_status __st(__get_ft(&__m_ec));
|
||||||
__handle_error("directory_entry::file_size", __ec, __m_ec);
|
__handle_error("in directory_entry::file_size", __ec, __m_ec);
|
||||||
if (_VSTD_FS::exists(__st) && !_VSTD_FS::is_regular_file(__st)) {
|
if (_VSTD_FS::exists(__st) && !_VSTD_FS::is_regular_file(__st)) {
|
||||||
errc __err_kind = _VSTD_FS::is_directory(__st) ? errc::is_a_directory
|
errc __err_kind = _VSTD_FS::is_directory(__st) ? errc::is_a_directory
|
||||||
: errc::not_supported;
|
: errc::not_supported;
|
||||||
__handle_error("directory_entry::file_size", __ec,
|
__handle_error("in directory_entry::file_size", __ec,
|
||||||
make_error_code(__err_kind));
|
make_error_code(__err_kind));
|
||||||
}
|
}
|
||||||
return __data_.__size_;
|
return __data_.__size_;
|
||||||
@@ -2347,7 +2358,7 @@ private:
|
|||||||
case _RefreshNonSymlink: {
|
case _RefreshNonSymlink: {
|
||||||
error_code __m_ec;
|
error_code __m_ec;
|
||||||
(void)__get_ft(&__m_ec);
|
(void)__get_ft(&__m_ec);
|
||||||
__handle_error("directory_entry::hard_link_count", __ec, __m_ec);
|
__handle_error("in directory_entry::hard_link_count", __ec, __m_ec);
|
||||||
return __data_.__nlink_;
|
return __data_.__nlink_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2366,10 +2377,10 @@ private:
|
|||||||
case _RefreshNonSymlink: {
|
case _RefreshNonSymlink: {
|
||||||
error_code __m_ec;
|
error_code __m_ec;
|
||||||
file_status __st(__get_ft(&__m_ec));
|
file_status __st(__get_ft(&__m_ec));
|
||||||
__handle_error("directory_entry::last_write_time", __ec, __m_ec);
|
__handle_error("in directory_entry::last_write_time", __ec, __m_ec);
|
||||||
if (_VSTD_FS::exists(__st) &&
|
if (_VSTD_FS::exists(__st) &&
|
||||||
__data_.__write_time_ == file_time_type::min())
|
__data_.__write_time_ == file_time_type::min())
|
||||||
__handle_error("directory_entry::last_write_time", __ec,
|
__handle_error("in directory_entry::last_write_time", __ec,
|
||||||
make_error_code(errc::value_too_large));
|
make_error_code(errc::value_too_large));
|
||||||
return __data_.__write_time_;
|
return __data_.__write_time_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ static file_time_type get_write_time(const WIN32_FIND_DATA& data) {
|
|||||||
} // namespace
|
} // namespace
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
using detail::set_or_throw;
|
using detail::ErrorHandler;
|
||||||
|
|
||||||
#if defined(_LIBCPP_WIN32API)
|
#if defined(_LIBCPP_WIN32API)
|
||||||
class __dir_stream {
|
class __dir_stream {
|
||||||
@@ -231,27 +231,30 @@ public:
|
|||||||
directory_iterator::directory_iterator(const path& p, error_code *ec,
|
directory_iterator::directory_iterator(const path& p, error_code *ec,
|
||||||
directory_options opts)
|
directory_options opts)
|
||||||
{
|
{
|
||||||
std::error_code m_ec;
|
ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);
|
||||||
__imp_ = make_shared<__dir_stream>(p, opts, m_ec);
|
|
||||||
if (ec) *ec = m_ec;
|
std::error_code m_ec;
|
||||||
if (!__imp_->good()) {
|
__imp_ = make_shared<__dir_stream>(p, opts, m_ec);
|
||||||
__imp_.reset();
|
if (ec)
|
||||||
if (m_ec)
|
*ec = m_ec;
|
||||||
set_or_throw(m_ec, ec,
|
if (!__imp_->good()) {
|
||||||
"directory_iterator::directory_iterator(...)", p);
|
__imp_.reset();
|
||||||
}
|
if (m_ec)
|
||||||
|
err.report(m_ec);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
directory_iterator& directory_iterator::__increment(error_code *ec)
|
directory_iterator& directory_iterator::__increment(error_code *ec)
|
||||||
{
|
{
|
||||||
_LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
|
_LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
|
||||||
|
ErrorHandler<void> err("directory_iterator::operator++()", ec);
|
||||||
|
|
||||||
std::error_code m_ec;
|
std::error_code m_ec;
|
||||||
if (!__imp_->advance(m_ec)) {
|
if (!__imp_->advance(m_ec)) {
|
||||||
__imp_.reset();
|
path root = std::move(__imp_->__root_);
|
||||||
if (m_ec)
|
__imp_.reset();
|
||||||
set_or_throw(m_ec, ec, "directory_iterator::operator++()");
|
if (m_ec)
|
||||||
} else {
|
err.report(m_ec, "at root \"%s\"", root);
|
||||||
if (ec) ec->clear();
|
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
|
|
||||||
@@ -273,15 +276,18 @@ recursive_directory_iterator::recursive_directory_iterator(const path& p,
|
|||||||
directory_options opt, error_code *ec)
|
directory_options opt, error_code *ec)
|
||||||
: __imp_(nullptr), __rec_(true)
|
: __imp_(nullptr), __rec_(true)
|
||||||
{
|
{
|
||||||
if (ec) ec->clear();
|
ErrorHandler<void> err("recursive_directory_iterator", ec, &p);
|
||||||
std::error_code m_ec;
|
|
||||||
__dir_stream new_s(p, opt, m_ec);
|
|
||||||
if (m_ec) set_or_throw(m_ec, ec, "recursive_directory_iterator", p);
|
|
||||||
if (m_ec || !new_s.good()) return;
|
|
||||||
|
|
||||||
__imp_ = _VSTD::make_shared<__shared_imp>();
|
std::error_code m_ec;
|
||||||
__imp_->__options_ = opt;
|
__dir_stream new_s(p, opt, m_ec);
|
||||||
__imp_->__stack_.push(_VSTD::move(new_s));
|
if (m_ec)
|
||||||
|
err.report(m_ec);
|
||||||
|
if (m_ec || !new_s.good())
|
||||||
|
return;
|
||||||
|
|
||||||
|
__imp_ = _VSTD::make_shared<__shared_imp>();
|
||||||
|
__imp_->__options_ = opt;
|
||||||
|
__imp_->__stack_.push(_VSTD::move(new_s));
|
||||||
}
|
}
|
||||||
|
|
||||||
void recursive_directory_iterator::__pop(error_code* ec)
|
void recursive_directory_iterator::__pop(error_code* ec)
|
||||||
@@ -321,42 +327,50 @@ recursive_directory_iterator::__increment(error_code *ec)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void recursive_directory_iterator::__advance(error_code* ec) {
|
void recursive_directory_iterator::__advance(error_code* ec) {
|
||||||
// REQUIRES: ec must be cleared before calling this function.
|
ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
|
||||||
const directory_iterator end_it;
|
|
||||||
auto& stack = __imp_->__stack_;
|
const directory_iterator end_it;
|
||||||
std::error_code m_ec;
|
auto& stack = __imp_->__stack_;
|
||||||
while (stack.size() > 0) {
|
std::error_code m_ec;
|
||||||
if (stack.top().advance(m_ec))
|
while (stack.size() > 0) {
|
||||||
return;
|
if (stack.top().advance(m_ec))
|
||||||
if (m_ec) break;
|
return;
|
||||||
stack.pop();
|
|
||||||
}
|
|
||||||
__imp_.reset();
|
|
||||||
if (m_ec)
|
if (m_ec)
|
||||||
set_or_throw(m_ec, ec, "recursive_directory_iterator::operator++()");
|
break;
|
||||||
|
stack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_ec) {
|
||||||
|
path root = std::move(stack.top().__root_);
|
||||||
|
__imp_.reset();
|
||||||
|
err.report(m_ec, "at root \"%s\"", root);
|
||||||
|
} else {
|
||||||
|
__imp_.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool recursive_directory_iterator::__try_recursion(error_code *ec) {
|
bool recursive_directory_iterator::__try_recursion(error_code *ec) {
|
||||||
bool rec_sym =
|
ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
|
||||||
bool(options() & directory_options::follow_directory_symlink);
|
|
||||||
|
|
||||||
auto& curr_it = __imp_->__stack_.top();
|
bool rec_sym = bool(options() & directory_options::follow_directory_symlink);
|
||||||
|
|
||||||
bool skip_rec = false;
|
auto& curr_it = __imp_->__stack_.top();
|
||||||
std::error_code m_ec;
|
|
||||||
if (!rec_sym) {
|
bool skip_rec = false;
|
||||||
file_status st = curr_it.__entry_.symlink_status(m_ec);
|
std::error_code m_ec;
|
||||||
if (m_ec && status_known(st))
|
if (!rec_sym) {
|
||||||
m_ec.clear();
|
file_status st = curr_it.__entry_.symlink_status(m_ec);
|
||||||
if (m_ec || is_symlink(st) || !is_directory(st))
|
if (m_ec && status_known(st))
|
||||||
skip_rec = true;
|
m_ec.clear();
|
||||||
} else {
|
if (m_ec || is_symlink(st) || !is_directory(st))
|
||||||
file_status st = curr_it.__entry_.status(m_ec);
|
skip_rec = true;
|
||||||
if (m_ec && status_known(st))
|
} else {
|
||||||
m_ec.clear();
|
file_status st = curr_it.__entry_.status(m_ec);
|
||||||
if (m_ec || !is_directory(st))
|
if (m_ec && status_known(st))
|
||||||
skip_rec = true;
|
m_ec.clear();
|
||||||
}
|
if (m_ec || !is_directory(st))
|
||||||
|
skip_rec = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!skip_rec) {
|
if (!skip_rec) {
|
||||||
__dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
|
__dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
|
||||||
@@ -371,9 +385,9 @@ bool recursive_directory_iterator::__try_recursion(error_code *ec) {
|
|||||||
if (m_ec.value() == EACCES && allow_eacess) {
|
if (m_ec.value() == EACCES && allow_eacess) {
|
||||||
if (ec) ec->clear();
|
if (ec) ec->clear();
|
||||||
} else {
|
} else {
|
||||||
|
path at_ent = std::move(curr_it.__entry_.__p_);
|
||||||
__imp_.reset();
|
__imp_.reset();
|
||||||
set_or_throw(m_ec, ec,
|
err.report(m_ec, "attempting recursion into \"%s\"", at_ent);
|
||||||
"recursive_directory_iterator::operator++()");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#define FILESYSTEM_COMMON_H
|
#define FILESYSTEM_COMMON_H
|
||||||
|
|
||||||
#include "experimental/__config"
|
#include "experimental/__config"
|
||||||
|
#include "array"
|
||||||
#include "chrono"
|
#include "chrono"
|
||||||
#include "cstdlib"
|
#include "cstdlib"
|
||||||
#include "climits"
|
#include "climits"
|
||||||
@@ -66,26 +67,149 @@ _LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
|
|||||||
namespace detail {
|
namespace detail {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
static std::string format_string_imp(const char* msg, ...) {
|
||||||
|
// we might need a second shot at this, so pre-emptivly make a copy
|
||||||
|
struct GuardVAList {
|
||||||
|
va_list& target;
|
||||||
|
bool active = true;
|
||||||
|
void clear() {
|
||||||
|
if (active)
|
||||||
|
va_end(target);
|
||||||
|
active = false;
|
||||||
|
}
|
||||||
|
~GuardVAList() {
|
||||||
|
if (active)
|
||||||
|
va_end(target);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
va_list args;
|
||||||
|
va_start(args, msg);
|
||||||
|
GuardVAList args_guard = {args};
|
||||||
|
|
||||||
|
va_list args_cp;
|
||||||
|
va_copy(args_cp, args);
|
||||||
|
GuardVAList args_copy_guard = {args_cp};
|
||||||
|
|
||||||
|
std::array<char, 256> local_buff;
|
||||||
|
std::size_t size = local_buff.size();
|
||||||
|
auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
|
||||||
|
|
||||||
|
args_copy_guard.clear();
|
||||||
|
|
||||||
|
// handle empty expansion
|
||||||
|
if (ret == 0)
|
||||||
|
return std::string{};
|
||||||
|
if (static_cast<std::size_t>(ret) < size)
|
||||||
|
return std::string(local_buff.data());
|
||||||
|
|
||||||
|
// we did not provide a long enough buffer on our first attempt.
|
||||||
|
// add 1 to size to account for null-byte in size cast to prevent overflow
|
||||||
|
size = static_cast<std::size_t>(ret) + 1;
|
||||||
|
auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
|
||||||
|
ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
|
||||||
|
return std::string(buff_ptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* unwrap(string const& s) { return s.c_str(); }
|
||||||
|
const char* unwrap(path const& p) { return p.native().c_str(); }
|
||||||
|
template <class Arg>
|
||||||
|
Arg const& unwrap(Arg const& a) {
|
||||||
|
static_assert(!is_class<Arg>::value, "cannot pass class here");
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
std::string format_string(const char* fmt, Args const&... args) {
|
||||||
|
return format_string_imp(fmt, unwrap(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
std::error_code capture_errno() {
|
std::error_code capture_errno() {
|
||||||
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
|
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
|
||||||
return std::error_code(errno, std::generic_category());
|
return std::error_code(errno, std::generic_category());
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
|
template <class T>
|
||||||
const char* msg, path const& p = {}, path const& p2 = {}) {
|
T error_value();
|
||||||
if (ec) {
|
template <>
|
||||||
*ec = m_ec;
|
constexpr void error_value<void>() {}
|
||||||
} else {
|
template <>
|
||||||
string msg_s("std::experimental::filesystem::");
|
constexpr bool error_value<bool>() {
|
||||||
msg_s += msg;
|
return false;
|
||||||
__throw_filesystem_error(msg_s, p, p2, m_ec);
|
}
|
||||||
}
|
template <>
|
||||||
|
constexpr uintmax_t error_value<uintmax_t>() {
|
||||||
|
return uintmax_t(-1);
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
constexpr file_time_type error_value<file_time_type>() {
|
||||||
|
return file_time_type::min();
|
||||||
|
}
|
||||||
|
template <>
|
||||||
|
path error_value<path>() {
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_or_throw(std::error_code* ec, const char* msg, path const& p = {},
|
template <class T>
|
||||||
path const& p2 = {}) {
|
struct ErrorHandler {
|
||||||
return set_or_throw(capture_errno(), ec, msg, p, p2);
|
const char* func_name;
|
||||||
}
|
error_code* ec = nullptr;
|
||||||
|
const path* p1 = nullptr;
|
||||||
|
const path* p2 = nullptr;
|
||||||
|
|
||||||
|
ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr,
|
||||||
|
const path* p2 = nullptr)
|
||||||
|
: func_name(fname), ec(ec), p1(p1), p2(p2) {
|
||||||
|
if (ec)
|
||||||
|
ec->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
T report(const error_code& m_ec) const {
|
||||||
|
if (ec) {
|
||||||
|
*ec = m_ec;
|
||||||
|
return error_value<T>();
|
||||||
|
}
|
||||||
|
string what = string("in ") + func_name;
|
||||||
|
switch (bool(p1) + bool(p2)) {
|
||||||
|
case 0:
|
||||||
|
__throw_filesystem_error(what, m_ec);
|
||||||
|
case 1:
|
||||||
|
__throw_filesystem_error(what, *p1, m_ec);
|
||||||
|
case 2:
|
||||||
|
__throw_filesystem_error(what, *p1, *p2, m_ec);
|
||||||
|
}
|
||||||
|
_LIBCPP_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
T report(const error_code& m_ec, const char* msg, Args const&... args) const {
|
||||||
|
if (ec) {
|
||||||
|
*ec = m_ec;
|
||||||
|
return error_value<T>();
|
||||||
|
}
|
||||||
|
string what =
|
||||||
|
string("in ") + func_name + ": " + format_string(msg, args...);
|
||||||
|
switch (bool(p1) + bool(p2)) {
|
||||||
|
case 0:
|
||||||
|
__throw_filesystem_error(what, m_ec);
|
||||||
|
case 1:
|
||||||
|
__throw_filesystem_error(what, *p1, m_ec);
|
||||||
|
case 2:
|
||||||
|
__throw_filesystem_error(what, *p1, *p2, m_ec);
|
||||||
|
}
|
||||||
|
_LIBCPP_UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
T report(errc const& err) const { return report(make_error_code(err)); }
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
T report(errc const& err, const char* msg, Args const&... args) const {
|
||||||
|
return report(make_error_code(err), msg, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ErrorHandler(ErrorHandler const&) = delete;
|
||||||
|
ErrorHandler& operator=(ErrorHandler const&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
namespace time_util {
|
namespace time_util {
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "experimental/filesystem"
|
#include "experimental/filesystem"
|
||||||
|
#include "array"
|
||||||
#include "iterator"
|
#include "iterator"
|
||||||
#include "fstream"
|
#include "fstream"
|
||||||
#include "random" /* for unique_path */
|
#include "random" /* for unique_path */
|
||||||
@@ -364,7 +365,8 @@ file_status create_file_status(std::error_code& m_ec, path const& p,
|
|||||||
if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
|
if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
|
||||||
return file_status(file_type::not_found);
|
return file_status(file_type::not_found);
|
||||||
} else if (m_ec) {
|
} else if (m_ec) {
|
||||||
set_or_throw(m_ec, ec, "posix_stat", p);
|
ErrorHandler<void> err("posix_stat", ec, &p);
|
||||||
|
err.report(m_ec, "failed to determine attributes for the specified path");
|
||||||
return file_status(file_type::none);
|
return file_status(file_type::none);
|
||||||
}
|
}
|
||||||
// else
|
// else
|
||||||
@@ -453,10 +455,30 @@ file_status FileDescriptor::refresh_status(std::error_code& ec) {
|
|||||||
}
|
}
|
||||||
}} // end namespace detail
|
}} // end namespace detail
|
||||||
|
|
||||||
using detail::set_or_throw;
|
using detail::capture_errno;
|
||||||
using parser::string_view_t;
|
using detail::ErrorHandler;
|
||||||
using parser::PathParser;
|
using detail::StatT;
|
||||||
using parser::createView;
|
using parser::createView;
|
||||||
|
using parser::PathParser;
|
||||||
|
using parser::string_view_t;
|
||||||
|
|
||||||
|
void filesystem_error::__create_what(int __num_paths) {
|
||||||
|
const char* derived_what = system_error::what();
|
||||||
|
__storage_->__what_ = [&]() -> string {
|
||||||
|
const char* p1 = path1().native().empty() ? "\"\"" : path1().c_str();
|
||||||
|
const char* p2 = path2().native().empty() ? "\"\"" : path2().c_str();
|
||||||
|
switch (__num_paths) {
|
||||||
|
default:
|
||||||
|
return detail::format_string("filesystem error: %s", derived_what);
|
||||||
|
case 1:
|
||||||
|
return detail::format_string("filesystem error: %s [%s]", derived_what,
|
||||||
|
p1);
|
||||||
|
case 2:
|
||||||
|
return detail::format_string("filesystem error: %s [%s] [%s]",
|
||||||
|
derived_what, p1, p2);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
|
||||||
static path __do_absolute(const path& p, path *cwd, std::error_code *ec) {
|
static path __do_absolute(const path& p, path *cwd, std::error_code *ec) {
|
||||||
if (ec) ec->clear();
|
if (ec) ec->clear();
|
||||||
@@ -476,48 +498,46 @@ path __absolute(const path& p, std::error_code *ec) {
|
|||||||
path __canonical(path const & orig_p, std::error_code *ec)
|
path __canonical(path const & orig_p, std::error_code *ec)
|
||||||
{
|
{
|
||||||
path cwd;
|
path cwd;
|
||||||
|
ErrorHandler<path> err("canonical", ec, &orig_p, &cwd);
|
||||||
|
|
||||||
path p = __do_absolute(orig_p, &cwd, ec);
|
path p = __do_absolute(orig_p, &cwd, ec);
|
||||||
char buff[PATH_MAX + 1];
|
char buff[PATH_MAX + 1];
|
||||||
char *ret;
|
char *ret;
|
||||||
if ((ret = ::realpath(p.c_str(), buff)) == nullptr) {
|
if ((ret = ::realpath(p.c_str(), buff)) == nullptr)
|
||||||
set_or_throw(ec, "canonical", orig_p, cwd);
|
return err.report(capture_errno());
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (ec) ec->clear();
|
|
||||||
return {ret};
|
return {ret};
|
||||||
}
|
}
|
||||||
|
|
||||||
void __copy(const path& from, const path& to, copy_options options,
|
void __copy(const path& from, const path& to, copy_options options,
|
||||||
std::error_code *ec)
|
std::error_code *ec)
|
||||||
{
|
{
|
||||||
const bool sym_status = bool(options &
|
ErrorHandler<void> err("copy", ec, &from, &to);
|
||||||
(copy_options::create_symlinks | copy_options::skip_symlinks));
|
|
||||||
|
|
||||||
const bool sym_status2 = bool(options &
|
const bool sym_status = bool(
|
||||||
copy_options::copy_symlinks);
|
options & (copy_options::create_symlinks | copy_options::skip_symlinks));
|
||||||
|
|
||||||
std::error_code m_ec1;
|
const bool sym_status2 = bool(options & copy_options::copy_symlinks);
|
||||||
struct ::stat f_st = {};
|
|
||||||
const file_status f = sym_status || sym_status2
|
|
||||||
? detail::posix_lstat(from, f_st, &m_ec1)
|
|
||||||
: detail::posix_stat(from, f_st, &m_ec1);
|
|
||||||
if (m_ec1)
|
|
||||||
return set_or_throw(m_ec1, ec, "copy", from, to);
|
|
||||||
|
|
||||||
struct ::stat t_st = {};
|
std::error_code m_ec1;
|
||||||
const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1)
|
struct ::stat f_st = {};
|
||||||
: detail::posix_stat(to, t_st, &m_ec1);
|
const file_status f = sym_status || sym_status2
|
||||||
|
? detail::posix_lstat(from, f_st, &m_ec1)
|
||||||
|
: detail::posix_stat(from, f_st, &m_ec1);
|
||||||
|
if (m_ec1)
|
||||||
|
return err.report(m_ec1);
|
||||||
|
|
||||||
if (not status_known(t))
|
struct ::stat t_st = {};
|
||||||
return set_or_throw(m_ec1, ec, "copy", from, to);
|
const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1)
|
||||||
|
: detail::posix_stat(to, t_st, &m_ec1);
|
||||||
|
|
||||||
if (!exists(f) || is_other(f) || is_other(t)
|
if (not status_known(t))
|
||||||
|| (is_directory(f) && is_regular_file(t))
|
return err.report(m_ec1);
|
||||||
|| detail::stat_equivalent(f_st, t_st))
|
|
||||||
{
|
if (!exists(f) || is_other(f) || is_other(t) ||
|
||||||
return set_or_throw(make_error_code(errc::function_not_supported),
|
(is_directory(f) && is_regular_file(t)) ||
|
||||||
ec, "copy", from, to);
|
detail::stat_equivalent(f_st, t_st)) {
|
||||||
}
|
return err.report(errc::function_not_supported);
|
||||||
|
}
|
||||||
|
|
||||||
if (ec) ec->clear();
|
if (ec) ec->clear();
|
||||||
|
|
||||||
@@ -527,8 +547,7 @@ void __copy(const path& from, const path& to, copy_options options,
|
|||||||
} else if (not exists(t)) {
|
} else if (not exists(t)) {
|
||||||
__copy_symlink(from, to, ec);
|
__copy_symlink(from, to, ec);
|
||||||
} else {
|
} else {
|
||||||
set_or_throw(make_error_code(errc::file_exists),
|
return err.report(errc::file_exists);
|
||||||
ec, "copy", from, to);
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -550,7 +569,7 @@ void __copy(const path& from, const path& to, copy_options options,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (is_directory(f) && bool(copy_options::create_symlinks & options)) {
|
else if (is_directory(f) && bool(copy_options::create_symlinks & options)) {
|
||||||
return set_or_throw(make_error_code(errc::is_a_directory), ec, "copy");
|
return err.report(errc::is_a_directory);
|
||||||
}
|
}
|
||||||
else if (is_directory(f) && (bool(copy_options::recursive & options) ||
|
else if (is_directory(f) && (bool(copy_options::recursive & options) ||
|
||||||
copy_options::none == options)) {
|
copy_options::none == options)) {
|
||||||
@@ -565,7 +584,9 @@ void __copy(const path& from, const path& to, copy_options options,
|
|||||||
if (ec && *ec) { return; }
|
if (ec && *ec) { return; }
|
||||||
std::error_code m_ec2;
|
std::error_code m_ec2;
|
||||||
for (; it != directory_iterator(); it.increment(m_ec2)) {
|
for (; it != directory_iterator(); it.increment(m_ec2)) {
|
||||||
if (m_ec2) return set_or_throw(m_ec2, ec, "copy", from, to);
|
if (m_ec2) {
|
||||||
|
return err.report(m_ec2);
|
||||||
|
}
|
||||||
__copy(it->path(), to / it->path().filename(),
|
__copy(it->path(), to / it->path().filename(),
|
||||||
options | copy_options::__in_recursive_copy, ec);
|
options | copy_options::__in_recursive_copy, ec);
|
||||||
if (ec && *ec) { return; }
|
if (ec && *ec) { return; }
|
||||||
@@ -672,28 +693,20 @@ bool __copy_file(const path& from, const path& to, copy_options options,
|
|||||||
std::error_code *ec)
|
std::error_code *ec)
|
||||||
{
|
{
|
||||||
using detail::FileDescriptor;
|
using detail::FileDescriptor;
|
||||||
using detail::StatT;
|
ErrorHandler<bool> err("copy_file", ec, &to, &from);
|
||||||
|
|
||||||
if (ec)
|
|
||||||
ec->clear();
|
|
||||||
|
|
||||||
auto Error = [&](const error_code& error_ec) {
|
|
||||||
set_or_throw(error_ec, ec, "copy_file", from, to);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::error_code m_ec;
|
std::error_code m_ec;
|
||||||
FileDescriptor from_fd =
|
FileDescriptor from_fd =
|
||||||
FileDescriptor::create_with_status(&from, m_ec, O_RDONLY | O_NONBLOCK);
|
FileDescriptor::create_with_status(&from, m_ec, O_RDONLY | O_NONBLOCK);
|
||||||
if (m_ec)
|
if (m_ec)
|
||||||
return Error(m_ec);
|
return err.report(m_ec);
|
||||||
|
|
||||||
auto from_st = from_fd.get_status();
|
auto from_st = from_fd.get_status();
|
||||||
StatT const& from_stat = from_fd.get_stat();
|
StatT const& from_stat = from_fd.get_stat();
|
||||||
if (!is_regular_file(from_st)) {
|
if (!is_regular_file(from_st)) {
|
||||||
if (not m_ec)
|
if (not m_ec)
|
||||||
m_ec = make_error_code(errc::not_supported);
|
m_ec = make_error_code(errc::not_supported);
|
||||||
return Error(m_ec);
|
return err.report(m_ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool skip_existing = bool(copy_options::skip_existing & options);
|
const bool skip_existing = bool(copy_options::skip_existing & options);
|
||||||
@@ -704,14 +717,14 @@ bool __copy_file(const path& from, const path& to, copy_options options,
|
|||||||
StatT to_stat_path;
|
StatT to_stat_path;
|
||||||
file_status to_st = detail::posix_stat(to, to_stat_path, &m_ec);
|
file_status to_st = detail::posix_stat(to, to_stat_path, &m_ec);
|
||||||
if (!status_known(to_st))
|
if (!status_known(to_st))
|
||||||
return Error(m_ec);
|
return err.report(m_ec);
|
||||||
|
|
||||||
const bool to_exists = exists(to_st);
|
const bool to_exists = exists(to_st);
|
||||||
if (to_exists && !is_regular_file(to_st))
|
if (to_exists && !is_regular_file(to_st))
|
||||||
return Error(make_error_code(errc::not_supported));
|
return err.report(make_error_code(errc::not_supported));
|
||||||
|
|
||||||
if (to_exists && detail::stat_equivalent(from_stat, to_stat_path))
|
if (to_exists && detail::stat_equivalent(from_stat, to_stat_path))
|
||||||
return Error(make_error_code(errc::file_exists));
|
return err.report(make_error_code(errc::file_exists));
|
||||||
|
|
||||||
if (to_exists && skip_existing)
|
if (to_exists && skip_existing)
|
||||||
return false;
|
return false;
|
||||||
@@ -729,7 +742,7 @@ bool __copy_file(const path& from, const path& to, copy_options options,
|
|||||||
}
|
}
|
||||||
if (!to_exists || overwrite_existing)
|
if (!to_exists || overwrite_existing)
|
||||||
return true;
|
return true;
|
||||||
return Error(make_error_code(errc::file_exists));
|
return err.report(errc::file_exists);
|
||||||
}();
|
}();
|
||||||
if (!ShouldCopy)
|
if (!ShouldCopy)
|
||||||
return false;
|
return false;
|
||||||
@@ -742,25 +755,25 @@ bool __copy_file(const path& from, const path& to, copy_options options,
|
|||||||
FileDescriptor to_fd = FileDescriptor::create_with_status(
|
FileDescriptor to_fd = FileDescriptor::create_with_status(
|
||||||
&to, m_ec, to_open_flags, from_stat.st_mode);
|
&to, m_ec, to_open_flags, from_stat.st_mode);
|
||||||
if (m_ec)
|
if (m_ec)
|
||||||
return Error(m_ec);
|
return err.report(m_ec);
|
||||||
|
|
||||||
if (to_exists) {
|
if (to_exists) {
|
||||||
// Check that the file we initially stat'ed is equivalent to the one
|
// Check that the file we initially stat'ed is equivalent to the one
|
||||||
// we opened.
|
// we opened.
|
||||||
// FIXME: report this better.
|
// FIXME: report this better.
|
||||||
if (!detail::stat_equivalent(to_stat_path, to_fd.get_stat()))
|
if (!detail::stat_equivalent(to_stat_path, to_fd.get_stat()))
|
||||||
return Error(make_error_code(errc::bad_file_descriptor));
|
return err.report(errc::bad_file_descriptor);
|
||||||
|
|
||||||
// Set the permissions and truncate the file we opened.
|
// Set the permissions and truncate the file we opened.
|
||||||
if (!detail::posix_fchmod(to_fd, from_stat, m_ec))
|
if (!detail::posix_fchmod(to_fd, from_stat, m_ec))
|
||||||
return Error(m_ec);
|
return err.report(m_ec);
|
||||||
if (!detail::posix_ftruncate(to_fd, 0, m_ec))
|
if (!detail::posix_ftruncate(to_fd, 0, m_ec))
|
||||||
return Error(m_ec);
|
return err.report(m_ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!copy_file_impl(from_fd, to_fd, m_ec)) {
|
if (!copy_file_impl(from_fd, to_fd, m_ec)) {
|
||||||
// FIXME: Remove the dest file if we failed, and it didn't exist previously.
|
// FIXME: Remove the dest file if we failed, and it didn't exist previously.
|
||||||
return Error(m_ec);
|
return err.report(m_ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -779,213 +792,192 @@ void __copy_symlink(const path& existing_symlink, const path& new_symlink,
|
|||||||
|
|
||||||
bool __create_directories(const path& p, std::error_code *ec)
|
bool __create_directories(const path& p, std::error_code *ec)
|
||||||
{
|
{
|
||||||
std::error_code m_ec;
|
ErrorHandler<bool> err("create_directories", ec, &p);
|
||||||
auto const st = detail::posix_stat(p, &m_ec);
|
|
||||||
if (!status_known(st)) {
|
|
||||||
set_or_throw(m_ec, ec, "create_directories", p);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (is_directory(st)) {
|
|
||||||
if (ec) ec->clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (exists(st)) {
|
|
||||||
set_or_throw(make_error_code(errc::file_exists),
|
|
||||||
ec, "create_directories", p);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const path parent = p.parent_path();
|
std::error_code m_ec;
|
||||||
if (!parent.empty()) {
|
auto const st = detail::posix_stat(p, &m_ec);
|
||||||
const file_status parent_st = status(parent, m_ec);
|
if (!status_known(st))
|
||||||
if (not status_known(parent_st)) {
|
return err.report(m_ec);
|
||||||
set_or_throw(m_ec, ec, "create_directories", p);
|
else if (is_directory(st))
|
||||||
return false;
|
return false;
|
||||||
}
|
else if (exists(st))
|
||||||
if (not exists(parent_st)) {
|
return err.report(errc::file_exists);
|
||||||
__create_directories(parent, ec);
|
|
||||||
if (ec && *ec) { return false; }
|
const path parent = p.parent_path();
|
||||||
}
|
if (!parent.empty()) {
|
||||||
|
const file_status parent_st = status(parent, m_ec);
|
||||||
|
if (not status_known(parent_st))
|
||||||
|
return err.report(m_ec);
|
||||||
|
if (not exists(parent_st)) {
|
||||||
|
__create_directories(parent, ec);
|
||||||
|
if (ec && *ec) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return __create_directory(p, ec);
|
return __create_directory(p, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __create_directory(const path& p, std::error_code *ec)
|
bool __create_directory(const path& p, std::error_code *ec)
|
||||||
{
|
{
|
||||||
if (ec) ec->clear();
|
ErrorHandler<bool> err("create_directory", ec, &p);
|
||||||
if (::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0)
|
|
||||||
return true;
|
if (::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0)
|
||||||
if (errno != EEXIST || !is_directory(p))
|
return true;
|
||||||
set_or_throw(ec, "create_directory", p);
|
if (errno != EEXIST || !is_directory(p))
|
||||||
return false;
|
err.report(capture_errno());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __create_directory(path const & p, path const & attributes,
|
bool __create_directory(path const & p, path const & attributes,
|
||||||
std::error_code *ec)
|
std::error_code *ec)
|
||||||
{
|
{
|
||||||
struct ::stat attr_stat;
|
ErrorHandler<bool> err("create_directory", ec, &p, &attributes);
|
||||||
std::error_code mec;
|
|
||||||
auto st = detail::posix_stat(attributes, attr_stat, &mec);
|
StatT attr_stat;
|
||||||
if (!status_known(st)) {
|
std::error_code mec;
|
||||||
set_or_throw(mec, ec, "create_directory", p, attributes);
|
auto st = detail::posix_stat(attributes, attr_stat, &mec);
|
||||||
return false;
|
if (!status_known(st))
|
||||||
}
|
return err.report(mec);
|
||||||
if (ec) ec->clear();
|
|
||||||
if (::mkdir(p.c_str(), attr_stat.st_mode) == 0)
|
if (::mkdir(p.c_str(), attr_stat.st_mode) == 0)
|
||||||
return true;
|
return true;
|
||||||
if (errno != EEXIST || !is_directory(p))
|
if (errno != EEXIST || !is_directory(p))
|
||||||
set_or_throw(ec, "create_directory", p, attributes);
|
err.report(capture_errno());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __create_directory_symlink(path const & from, path const & to,
|
void __create_directory_symlink(path const& from, path const& to,
|
||||||
std::error_code *ec){
|
std::error_code* ec) {
|
||||||
if (::symlink(from.c_str(), to.c_str()) != 0)
|
ErrorHandler<void> err("create_directory_symlink", ec, &from, &to);
|
||||||
set_or_throw(ec, "create_directory_symlink", from, to);
|
if (::symlink(from.c_str(), to.c_str()) != 0)
|
||||||
else if (ec)
|
return err.report(capture_errno());
|
||||||
ec->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __create_hard_link(const path& from, const path& to, std::error_code *ec){
|
void __create_hard_link(const path& from, const path& to, std::error_code *ec){
|
||||||
if (::link(from.c_str(), to.c_str()) == -1)
|
ErrorHandler<void> err("create_hard_link", ec, &from, &to);
|
||||||
set_or_throw(ec, "create_hard_link", from, to);
|
if (::link(from.c_str(), to.c_str()) == -1)
|
||||||
else if (ec)
|
return err.report(capture_errno());
|
||||||
ec->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __create_symlink(path const & from, path const & to, std::error_code *ec) {
|
void __create_symlink(path const & from, path const & to, std::error_code *ec) {
|
||||||
|
ErrorHandler<void> err("create_symlink", ec, &from, &to);
|
||||||
if (::symlink(from.c_str(), to.c_str()) == -1)
|
if (::symlink(from.c_str(), to.c_str()) == -1)
|
||||||
set_or_throw(ec, "create_symlink", from, to);
|
return err.report(capture_errno());
|
||||||
else if (ec)
|
|
||||||
ec->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
path __current_path(std::error_code *ec) {
|
path __current_path(std::error_code *ec) {
|
||||||
auto size = ::pathconf(".", _PC_PATH_MAX);
|
ErrorHandler<path> err("current_path", ec);
|
||||||
_LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size");
|
|
||||||
|
|
||||||
auto buff = std::unique_ptr<char[]>(new char[size + 1]);
|
auto size = ::pathconf(".", _PC_PATH_MAX);
|
||||||
char* ret;
|
_LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size");
|
||||||
if ((ret = ::getcwd(buff.get(), static_cast<size_t>(size))) == nullptr) {
|
|
||||||
set_or_throw(ec, "current_path");
|
auto buff = std::unique_ptr<char[]>(new char[size + 1]);
|
||||||
return {};
|
char* ret;
|
||||||
}
|
if ((ret = ::getcwd(buff.get(), static_cast<size_t>(size))) == nullptr)
|
||||||
if (ec) ec->clear();
|
return err.report(capture_errno(), "call to getcwd failed");
|
||||||
return {buff.get()};
|
|
||||||
|
return {buff.get()};
|
||||||
}
|
}
|
||||||
|
|
||||||
void __current_path(const path& p, std::error_code *ec) {
|
void __current_path(const path& p, std::error_code *ec) {
|
||||||
if (::chdir(p.c_str()) == -1)
|
ErrorHandler<void> err("current_path", ec, &p);
|
||||||
set_or_throw(ec, "current_path", p);
|
if (::chdir(p.c_str()) == -1)
|
||||||
else if (ec)
|
err.report(capture_errno());
|
||||||
ec->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __equivalent(const path& p1, const path& p2, std::error_code *ec)
|
bool __equivalent(const path& p1, const path& p2, std::error_code *ec)
|
||||||
{
|
{
|
||||||
auto make_unsupported_error = [&]() {
|
ErrorHandler<bool> err("equivalent", ec, &p1, &p2);
|
||||||
set_or_throw(make_error_code(errc::not_supported), ec,
|
|
||||||
"equivalent", p1, p2);
|
std::error_code ec1, ec2;
|
||||||
return false;
|
StatT st1 = {}, st2 = {};
|
||||||
};
|
auto s1 = detail::posix_stat(p1.native(), st1, &ec1);
|
||||||
std::error_code ec1, ec2;
|
if (!exists(s1))
|
||||||
struct ::stat st1 = {};
|
return err.report(errc::not_supported);
|
||||||
struct ::stat st2 = {};
|
auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
|
||||||
auto s1 = detail::posix_stat(p1.native(), st1, &ec1);
|
if (!exists(s2))
|
||||||
if (!exists(s1))
|
return err.report(errc::not_supported);
|
||||||
return make_unsupported_error();
|
|
||||||
auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
|
return detail::stat_equivalent(st1, st2);
|
||||||
if (!exists(s2))
|
|
||||||
return make_unsupported_error();
|
|
||||||
if (ec) ec->clear();
|
|
||||||
return detail::stat_equivalent(st1, st2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::uintmax_t __file_size(const path& p, std::error_code *ec)
|
std::uintmax_t __file_size(const path& p, std::error_code *ec)
|
||||||
{
|
{
|
||||||
std::error_code m_ec;
|
ErrorHandler<uintmax_t> err("file_size", ec, &p);
|
||||||
struct ::stat st;
|
|
||||||
file_status fst = detail::posix_stat(p, st, &m_ec);
|
std::error_code m_ec;
|
||||||
if (!exists(fst) || !is_regular_file(fst)) {
|
struct ::stat st;
|
||||||
errc error_kind =
|
file_status fst = detail::posix_stat(p, st, &m_ec);
|
||||||
is_directory(fst) ? errc::is_a_directory : errc::not_supported;
|
if (!exists(fst) || !is_regular_file(fst)) {
|
||||||
if (!m_ec)
|
errc error_kind =
|
||||||
m_ec = make_error_code(error_kind);
|
is_directory(fst) ? errc::is_a_directory : errc::not_supported;
|
||||||
set_or_throw(m_ec, ec, "file_size", p);
|
if (!m_ec)
|
||||||
return static_cast<uintmax_t>(-1);
|
m_ec = make_error_code(error_kind);
|
||||||
}
|
return err.report(m_ec);
|
||||||
|
}
|
||||||
// is_regular_file(p) == true
|
// is_regular_file(p) == true
|
||||||
if (ec) ec->clear();
|
|
||||||
return static_cast<std::uintmax_t>(st.st_size);
|
return static_cast<std::uintmax_t>(st.st_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::uintmax_t __hard_link_count(const path& p, std::error_code *ec)
|
std::uintmax_t __hard_link_count(const path& p, std::error_code *ec)
|
||||||
{
|
{
|
||||||
std::error_code m_ec;
|
ErrorHandler<uintmax_t> err("hard_link_count", ec, &p);
|
||||||
struct ::stat st;
|
|
||||||
detail::posix_stat(p, st, &m_ec);
|
std::error_code m_ec;
|
||||||
if (m_ec) {
|
StatT st;
|
||||||
set_or_throw(m_ec, ec, "hard_link_count", p);
|
detail::posix_stat(p, st, &m_ec);
|
||||||
return static_cast<std::uintmax_t>(-1);
|
if (m_ec)
|
||||||
}
|
return err.report(m_ec);
|
||||||
if (ec) ec->clear();
|
return static_cast<std::uintmax_t>(st.st_nlink);
|
||||||
return static_cast<std::uintmax_t>(st.st_nlink);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool __fs_is_empty(const path& p, std::error_code *ec)
|
bool __fs_is_empty(const path& p, std::error_code *ec)
|
||||||
{
|
{
|
||||||
if (ec) ec->clear();
|
ErrorHandler<bool> err("is_empty", ec, &p);
|
||||||
std::error_code m_ec;
|
|
||||||
struct ::stat pst;
|
|
||||||
auto st = detail::posix_stat(p, pst, &m_ec);
|
|
||||||
if (m_ec) {
|
|
||||||
set_or_throw(m_ec, ec, "is_empty", p);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (!is_directory(st) && !is_regular_file(st)) {
|
|
||||||
m_ec = make_error_code(errc::not_supported);
|
|
||||||
set_or_throw(m_ec, ec, "is_empty");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (is_directory(st)) {
|
|
||||||
auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p);
|
|
||||||
if (ec && *ec)
|
|
||||||
return false;
|
|
||||||
return it == directory_iterator{};
|
|
||||||
}
|
|
||||||
else if (is_regular_file(st))
|
|
||||||
return static_cast<std::uintmax_t>(pst.st_size) == 0;
|
|
||||||
|
|
||||||
_LIBCPP_UNREACHABLE();
|
std::error_code m_ec;
|
||||||
|
StatT pst;
|
||||||
|
auto st = detail::posix_stat(p, pst, &m_ec);
|
||||||
|
if (m_ec)
|
||||||
|
return err.report(m_ec);
|
||||||
|
else if (!is_directory(st) && !is_regular_file(st))
|
||||||
|
return err.report(errc::not_supported);
|
||||||
|
else if (is_directory(st)) {
|
||||||
|
auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p);
|
||||||
|
if (ec && *ec)
|
||||||
|
return false;
|
||||||
|
return it == directory_iterator{};
|
||||||
|
} else if (is_regular_file(st))
|
||||||
|
return static_cast<std::uintmax_t>(pst.st_size) == 0;
|
||||||
|
|
||||||
|
_LIBCPP_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
static file_time_type __extract_last_write_time(const path& p,
|
static file_time_type __extract_last_write_time(const path& p, const StatT& st,
|
||||||
const struct ::stat& st,
|
|
||||||
error_code* ec) {
|
error_code* ec) {
|
||||||
using detail::FSTime;
|
using detail::FSTime;
|
||||||
|
ErrorHandler<file_time_type> err("last_write_time", ec, &p);
|
||||||
|
|
||||||
auto ts = detail::extract_mtime(st);
|
auto ts = detail::extract_mtime(st);
|
||||||
if (!FSTime::is_representable(ts)) {
|
if (!FSTime::is_representable(ts))
|
||||||
set_or_throw(make_error_code(errc::value_too_large), ec, "last_write_time",
|
return err.report(errc::value_too_large);
|
||||||
p);
|
|
||||||
return file_time_type::min();
|
|
||||||
}
|
|
||||||
return FSTime::convert_timespec(ts);
|
return FSTime::convert_timespec(ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
file_time_type __last_write_time(const path& p, std::error_code *ec)
|
file_time_type __last_write_time(const path& p, std::error_code *ec)
|
||||||
{
|
{
|
||||||
using namespace ::std::chrono;
|
using namespace ::std::chrono;
|
||||||
|
ErrorHandler<file_time_type> err("last_write_time", ec, &p);
|
||||||
|
|
||||||
std::error_code m_ec;
|
std::error_code m_ec;
|
||||||
struct ::stat st;
|
StatT st;
|
||||||
detail::posix_stat(p, st, &m_ec);
|
detail::posix_stat(p, st, &m_ec);
|
||||||
if (m_ec) {
|
if (m_ec)
|
||||||
set_or_throw(m_ec, ec, "last_write_time", p);
|
return err.report(m_ec);
|
||||||
return file_time_type::min();
|
|
||||||
}
|
|
||||||
if (ec) ec->clear();
|
|
||||||
return __extract_last_write_time(p, st, ec);
|
return __extract_last_write_time(p, st, ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -995,6 +987,8 @@ void __last_write_time(const path& p, file_time_type new_time,
|
|||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
using namespace detail;
|
using namespace detail;
|
||||||
|
|
||||||
|
ErrorHandler<void> err("last_write_time", ec, &p);
|
||||||
|
|
||||||
std::error_code m_ec;
|
std::error_code m_ec;
|
||||||
TimeStructArray tbuf;
|
TimeStructArray tbuf;
|
||||||
#if !defined(_LIBCXX_USE_UTIMENSAT)
|
#if !defined(_LIBCXX_USE_UTIMENSAT)
|
||||||
@@ -1003,99 +997,92 @@ void __last_write_time(const path& p, file_time_type new_time,
|
|||||||
// ::utimes
|
// ::utimes
|
||||||
struct ::stat st;
|
struct ::stat st;
|
||||||
file_status fst = detail::posix_stat(p, st, &m_ec);
|
file_status fst = detail::posix_stat(p, st, &m_ec);
|
||||||
if (m_ec && !status_known(fst)) {
|
if (m_ec && !status_known(fst))
|
||||||
set_or_throw(m_ec, ec, "last_write_time", p);
|
return err.report(m_ec);
|
||||||
return;
|
|
||||||
}
|
|
||||||
SetTimeStructTo(tbuf[0], detail::extract_atime(st));
|
SetTimeStructTo(tbuf[0], detail::extract_atime(st));
|
||||||
#else
|
#else
|
||||||
tbuf[0].tv_sec = 0;
|
tbuf[0].tv_sec = 0;
|
||||||
tbuf[0].tv_nsec = UTIME_OMIT;
|
tbuf[0].tv_nsec = UTIME_OMIT;
|
||||||
#endif
|
#endif
|
||||||
if (SetTimeStructTo(tbuf[1], new_time)) {
|
if (SetTimeStructTo(tbuf[1], new_time))
|
||||||
set_or_throw(make_error_code(errc::invalid_argument), ec,
|
return err.report(errc::invalid_argument);
|
||||||
"last_write_time", p);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetFileTimes(p, tbuf, m_ec);
|
SetFileTimes(p, tbuf, m_ec);
|
||||||
if (m_ec)
|
if (m_ec)
|
||||||
set_or_throw(m_ec, ec, "last_write_time", p);
|
return err.report(m_ec);
|
||||||
else if (ec)
|
|
||||||
ec->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void __permissions(const path& p, perms prms, perm_options opts,
|
void __permissions(const path& p, perms prms, perm_options opts,
|
||||||
std::error_code *ec)
|
std::error_code *ec)
|
||||||
{
|
{
|
||||||
auto has_opt = [&](perm_options o) { return bool(o & opts); };
|
ErrorHandler<void> err("permissions", ec, &p);
|
||||||
const bool resolve_symlinks = !has_opt(perm_options::nofollow);
|
|
||||||
const bool add_perms = has_opt(perm_options::add);
|
|
||||||
const bool remove_perms = has_opt(perm_options::remove);
|
|
||||||
_LIBCPP_ASSERT(
|
|
||||||
(add_perms + remove_perms + has_opt(perm_options::replace)) == 1,
|
|
||||||
"One and only one of the perm_options constants replace, add, or remove "
|
|
||||||
"is present in opts");
|
|
||||||
|
|
||||||
bool set_sym_perms = false;
|
auto has_opt = [&](perm_options o) { return bool(o & opts); };
|
||||||
prms &= perms::mask;
|
const bool resolve_symlinks = !has_opt(perm_options::nofollow);
|
||||||
if (!resolve_symlinks || (add_perms || remove_perms)) {
|
const bool add_perms = has_opt(perm_options::add);
|
||||||
std::error_code m_ec;
|
const bool remove_perms = has_opt(perm_options::remove);
|
||||||
file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec)
|
_LIBCPP_ASSERT(
|
||||||
: detail::posix_lstat(p, &m_ec);
|
(add_perms + remove_perms + has_opt(perm_options::replace)) == 1,
|
||||||
set_sym_perms = is_symlink(st);
|
"One and only one of the perm_options constants replace, add, or remove "
|
||||||
if (m_ec) return set_or_throw(m_ec, ec, "permissions", p);
|
"is present in opts");
|
||||||
_LIBCPP_ASSERT(st.permissions() != perms::unknown,
|
|
||||||
"Permissions unexpectedly unknown");
|
bool set_sym_perms = false;
|
||||||
if (add_perms)
|
prms &= perms::mask;
|
||||||
prms |= st.permissions();
|
if (!resolve_symlinks || (add_perms || remove_perms)) {
|
||||||
else if (remove_perms)
|
std::error_code m_ec;
|
||||||
prms = st.permissions() & ~prms;
|
file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec)
|
||||||
}
|
: detail::posix_lstat(p, &m_ec);
|
||||||
|
set_sym_perms = is_symlink(st);
|
||||||
|
if (m_ec)
|
||||||
|
return err.report(m_ec);
|
||||||
|
_LIBCPP_ASSERT(st.permissions() != perms::unknown,
|
||||||
|
"Permissions unexpectedly unknown");
|
||||||
|
if (add_perms)
|
||||||
|
prms |= st.permissions();
|
||||||
|
else if (remove_perms)
|
||||||
|
prms = st.permissions() & ~prms;
|
||||||
|
}
|
||||||
const auto real_perms = detail::posix_convert_perms(prms);
|
const auto real_perms = detail::posix_convert_perms(prms);
|
||||||
|
|
||||||
# if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
|
# if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
|
||||||
const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0;
|
const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0;
|
||||||
if (::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) {
|
if (::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) {
|
||||||
return set_or_throw(ec, "permissions", p);
|
return err.report(capture_errno());
|
||||||
}
|
}
|
||||||
# else
|
# else
|
||||||
if (set_sym_perms)
|
if (set_sym_perms)
|
||||||
return set_or_throw(make_error_code(errc::operation_not_supported),
|
return err.report(errc::operation_not_supported);
|
||||||
ec, "permissions", p);
|
|
||||||
if (::chmod(p.c_str(), real_perms) == -1) {
|
if (::chmod(p.c_str(), real_perms) == -1) {
|
||||||
return set_or_throw(ec, "permissions", p);
|
return err.report(capture_errno());
|
||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
if (ec) ec->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
path __read_symlink(const path& p, std::error_code *ec) {
|
path __read_symlink(const path& p, std::error_code *ec) {
|
||||||
char buff[PATH_MAX + 1];
|
ErrorHandler<path> err("read_symlink", ec, &p);
|
||||||
std::error_code m_ec;
|
|
||||||
::ssize_t ret;
|
char buff[PATH_MAX + 1];
|
||||||
if ((ret = ::readlink(p.c_str(), buff, PATH_MAX)) == -1) {
|
std::error_code m_ec;
|
||||||
set_or_throw(ec, "read_symlink", p);
|
::ssize_t ret;
|
||||||
return {};
|
if ((ret = ::readlink(p.c_str(), buff, PATH_MAX)) == -1) {
|
||||||
}
|
return err.report(capture_errno());
|
||||||
|
}
|
||||||
_LIBCPP_ASSERT(ret <= PATH_MAX, "TODO");
|
_LIBCPP_ASSERT(ret <= PATH_MAX, "TODO");
|
||||||
_LIBCPP_ASSERT(ret > 0, "TODO");
|
_LIBCPP_ASSERT(ret > 0, "TODO");
|
||||||
if (ec) ec->clear();
|
|
||||||
buff[ret] = 0;
|
buff[ret] = 0;
|
||||||
return {buff};
|
return {buff};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool __remove(const path& p, std::error_code *ec) {
|
bool __remove(const path& p, std::error_code *ec) {
|
||||||
if (ec) ec->clear();
|
ErrorHandler<bool> err("remove", ec, &p);
|
||||||
|
if (::remove(p.c_str()) == -1) {
|
||||||
if (::remove(p.c_str()) == -1) {
|
if (errno != ENOENT)
|
||||||
if (errno != ENOENT)
|
err.report(capture_errno());
|
||||||
set_or_throw(ec, "remove", p);
|
return false;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1123,45 +1110,39 @@ std::uintmax_t remove_all_impl(path const & p, std::error_code& ec)
|
|||||||
} // end namespace
|
} // end namespace
|
||||||
|
|
||||||
std::uintmax_t __remove_all(const path& p, std::error_code *ec) {
|
std::uintmax_t __remove_all(const path& p, std::error_code *ec) {
|
||||||
if (ec) ec->clear();
|
ErrorHandler<uintmax_t> err("remove_all", ec, &p);
|
||||||
|
|
||||||
std::error_code mec;
|
std::error_code mec;
|
||||||
auto count = remove_all_impl(p, mec);
|
auto count = remove_all_impl(p, mec);
|
||||||
if (mec) {
|
if (mec) {
|
||||||
if (mec == errc::no_such_file_or_directory) {
|
if (mec == errc::no_such_file_or_directory)
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
return err.report(mec);
|
||||||
set_or_throw(mec, ec, "remove_all", p);
|
}
|
||||||
return static_cast<std::uintmax_t>(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __rename(const path& from, const path& to, std::error_code *ec) {
|
void __rename(const path& from, const path& to, std::error_code *ec) {
|
||||||
if (::rename(from.c_str(), to.c_str()) == -1)
|
ErrorHandler<void> err("rename", ec, &from, &to);
|
||||||
set_or_throw(ec, "rename", from, to);
|
if (::rename(from.c_str(), to.c_str()) == -1)
|
||||||
else if (ec)
|
err.report(capture_errno());
|
||||||
ec->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __resize_file(const path& p, std::uintmax_t size, std::error_code *ec) {
|
void __resize_file(const path& p, std::uintmax_t size, std::error_code *ec) {
|
||||||
if (::truncate(p.c_str(), static_cast<::off_t>(size)) == -1)
|
ErrorHandler<void> err("resize_file", ec, &p);
|
||||||
set_or_throw(ec, "resize_file", p);
|
if (::truncate(p.c_str(), static_cast< ::off_t>(size)) == -1)
|
||||||
else if (ec)
|
return err.report(capture_errno());
|
||||||
ec->clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
space_info __space(const path& p, std::error_code *ec) {
|
space_info __space(const path& p, std::error_code *ec) {
|
||||||
space_info si;
|
ErrorHandler<void> err("space", ec, &p);
|
||||||
struct statvfs m_svfs = {};
|
space_info si;
|
||||||
if (::statvfs(p.c_str(), &m_svfs) == -1) {
|
struct statvfs m_svfs = {};
|
||||||
set_or_throw(ec, "space", p);
|
if (::statvfs(p.c_str(), &m_svfs) == -1) {
|
||||||
si.capacity = si.free = si.available =
|
err.report(capture_errno());
|
||||||
static_cast<std::uintmax_t>(-1);
|
si.capacity = si.free = si.available = static_cast<std::uintmax_t>(-1);
|
||||||
return si;
|
return si;
|
||||||
}
|
}
|
||||||
if (ec) ec->clear();
|
|
||||||
// Multiply with overflow checking.
|
// Multiply with overflow checking.
|
||||||
auto do_mult = [&](std::uintmax_t& out, std::uintmax_t other) {
|
auto do_mult = [&](std::uintmax_t& out, std::uintmax_t other) {
|
||||||
out = other * m_svfs.f_frsize;
|
out = other * m_svfs.f_frsize;
|
||||||
@@ -1183,6 +1164,8 @@ file_status __symlink_status(const path& p, std::error_code *ec) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
path __temp_directory_path(std::error_code* ec) {
|
path __temp_directory_path(std::error_code* ec) {
|
||||||
|
ErrorHandler<path> err("temp_directory_path", ec);
|
||||||
|
|
||||||
const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
|
const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
|
||||||
const char* ret = nullptr;
|
const char* ret = nullptr;
|
||||||
|
|
||||||
@@ -1194,20 +1177,21 @@ path __temp_directory_path(std::error_code* ec) {
|
|||||||
|
|
||||||
path p(ret);
|
path p(ret);
|
||||||
std::error_code m_ec;
|
std::error_code m_ec;
|
||||||
if (!exists(p, m_ec) || !is_directory(p, m_ec)) {
|
file_status st = detail::posix_stat(p, &m_ec);
|
||||||
if (!m_ec || m_ec == make_error_code(errc::no_such_file_or_directory))
|
if (!status_known(st))
|
||||||
m_ec = make_error_code(errc::not_a_directory);
|
return err.report(m_ec, "cannot access path \"%s\"", p);
|
||||||
set_or_throw(m_ec, ec, "temp_directory_path");
|
|
||||||
return {};
|
if (!exists(st) || !is_directory(st))
|
||||||
}
|
return err.report(errc::not_a_directory, "path \"%s\" is not a directory",
|
||||||
|
p);
|
||||||
|
|
||||||
if (ec)
|
|
||||||
ec->clear();
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
path __weakly_canonical(const path& p, std::error_code *ec) {
|
path __weakly_canonical(const path& p, std::error_code *ec) {
|
||||||
|
ErrorHandler<path> err("weakly_canonical", ec, &p);
|
||||||
|
|
||||||
if (p.empty())
|
if (p.empty())
|
||||||
return __canonical("", ec);
|
return __canonical("", ec);
|
||||||
|
|
||||||
@@ -1223,8 +1207,7 @@ path __weakly_canonical(const path& p, std::error_code *ec) {
|
|||||||
std::error_code m_ec;
|
std::error_code m_ec;
|
||||||
file_status st = __status(tmp, &m_ec);
|
file_status st = __status(tmp, &m_ec);
|
||||||
if (!status_known(st)) {
|
if (!status_known(st)) {
|
||||||
set_or_throw(m_ec, ec, "weakly_canonical", p);
|
return err.report(m_ec);
|
||||||
return {};
|
|
||||||
} else if (exists(st)) {
|
} else if (exists(st)) {
|
||||||
result = __canonical(tmp, ec);
|
result = __canonical(tmp, ec);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -80,7 +80,8 @@ TEST_CASE(last_write_time_not_representable_error) {
|
|||||||
TEST_CHECK(last_write_time(file, ec) == file_time_type::min());
|
TEST_CHECK(last_write_time(file, ec) == file_time_type::min());
|
||||||
TEST_CHECK(ErrorIs(ec, expected_err));
|
TEST_CHECK(ErrorIs(ec, expected_err));
|
||||||
|
|
||||||
ExceptionChecker CheckExcept(file, expected_err);
|
ExceptionChecker CheckExcept(file, expected_err,
|
||||||
|
"directory_entry::last_write_time");
|
||||||
TEST_CHECK_THROW_RESULT(fs::filesystem_error, CheckExcept,
|
TEST_CHECK_THROW_RESULT(fs::filesystem_error, CheckExcept,
|
||||||
ent.last_write_time());
|
ent.last_write_time());
|
||||||
|
|
||||||
|
|||||||
@@ -168,7 +168,8 @@ TEST_CASE(refresh_cannot_resolve) {
|
|||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
TEST_CHECK(ent.path() == file);
|
TEST_CHECK(ent.path() == file);
|
||||||
|
|
||||||
ExceptionChecker Checker(file, std::errc::permission_denied);
|
ExceptionChecker Checker(file, std::errc::permission_denied,
|
||||||
|
"directory_entry::refresh");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
|
||||||
}
|
}
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
@@ -182,7 +183,8 @@ TEST_CASE(refresh_cannot_resolve) {
|
|||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
TEST_CHECK(ent.path() == sym_in_dir);
|
TEST_CHECK(ent.path() == sym_in_dir);
|
||||||
|
|
||||||
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
|
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
|
||||||
|
"directory_entry::refresh");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.refresh());
|
||||||
}
|
}
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ TEST_CASE(not_regular_file) {
|
|||||||
TEST_CHECK(ec == other_ec);
|
TEST_CHECK(ec == other_ec);
|
||||||
TEST_CHECK(ErrorIs(ec, TC.expected_err));
|
TEST_CHECK(ErrorIs(ec, TC.expected_err));
|
||||||
|
|
||||||
ExceptionChecker Checker(p, TC.expected_err);
|
ExceptionChecker Checker(p, TC.expected_err, "directory_entry::file_size");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +134,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
||||||
|
|
||||||
ExceptionChecker Checker(StaticEnv::DNE,
|
ExceptionChecker Checker(StaticEnv::DNE,
|
||||||
std::errc::no_such_file_or_directory);
|
std::errc::no_such_file_or_directory,
|
||||||
|
"directory_entry::file_size");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
||||||
}
|
}
|
||||||
// test a dead symlink
|
// test a dead symlink
|
||||||
@@ -156,7 +157,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
||||||
|
|
||||||
ExceptionChecker Checker(StaticEnv::BadSymlink,
|
ExceptionChecker Checker(StaticEnv::BadSymlink,
|
||||||
std::errc::no_such_file_or_directory);
|
std::errc::no_such_file_or_directory,
|
||||||
|
"directory_entry::file_size");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
||||||
}
|
}
|
||||||
// test a file w/o appropriate permissions.
|
// test a file w/o appropriate permissions.
|
||||||
@@ -174,7 +176,7 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
|
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(file, std::errc::permission_denied);
|
ExceptionChecker Checker(file, std::errc::permission_denied, "file_size");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
@@ -199,7 +201,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
|
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
|
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
|
||||||
|
"file_size");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
@@ -224,7 +227,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
|
TEST_CHECK(ent.file_size(ec) == uintmax_t(-1));
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
|
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
|
||||||
|
"file_size");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.file_size());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
|
|||||||
@@ -133,7 +133,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
||||||
|
|
||||||
ExceptionChecker Checker(StaticEnv::DNE,
|
ExceptionChecker Checker(StaticEnv::DNE,
|
||||||
std::errc::no_such_file_or_directory);
|
std::errc::no_such_file_or_directory,
|
||||||
|
"directory_entry::hard_link_count");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
||||||
}
|
}
|
||||||
// test a dead symlink
|
// test a dead symlink
|
||||||
@@ -155,7 +156,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
||||||
|
|
||||||
ExceptionChecker Checker(StaticEnv::BadSymlink,
|
ExceptionChecker Checker(StaticEnv::BadSymlink,
|
||||||
std::errc::no_such_file_or_directory);
|
std::errc::no_such_file_or_directory,
|
||||||
|
"directory_entry::hard_link_count");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
||||||
}
|
}
|
||||||
// test a file w/o appropriate permissions.
|
// test a file w/o appropriate permissions.
|
||||||
@@ -173,7 +175,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
|
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(file, std::errc::permission_denied);
|
ExceptionChecker Checker(file, std::errc::permission_denied,
|
||||||
|
"hard_link_count");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
@@ -198,7 +201,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
|
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
|
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
|
||||||
|
"hard_link_count");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
@@ -223,7 +227,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
|
TEST_CHECK(ent.hard_link_count(ec) == uintmax_t(-1));
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
|
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
|
||||||
|
"hard_link_count");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.hard_link_count());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
||||||
|
|
||||||
ExceptionChecker Checker(StaticEnv::DNE,
|
ExceptionChecker Checker(StaticEnv::DNE,
|
||||||
std::errc::no_such_file_or_directory);
|
std::errc::no_such_file_or_directory,
|
||||||
|
"directory_entry::last_write_time");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
||||||
}
|
}
|
||||||
// test a dead symlink
|
// test a dead symlink
|
||||||
@@ -128,7 +129,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
TEST_CHECK(ErrorIs(ec, std::errc::no_such_file_or_directory));
|
||||||
|
|
||||||
ExceptionChecker Checker(StaticEnv::BadSymlink,
|
ExceptionChecker Checker(StaticEnv::BadSymlink,
|
||||||
std::errc::no_such_file_or_directory);
|
std::errc::no_such_file_or_directory,
|
||||||
|
"directory_entry::last_write_time");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
||||||
}
|
}
|
||||||
// test a file w/o appropriate permissions.
|
// test a file w/o appropriate permissions.
|
||||||
@@ -146,7 +148,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
|
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(file, std::errc::permission_denied);
|
ExceptionChecker Checker(file, std::errc::permission_denied,
|
||||||
|
"last_write_time");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
@@ -171,7 +174,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
|
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied);
|
ExceptionChecker Checker(sym_in_dir, std::errc::permission_denied,
|
||||||
|
"last_write_time");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
@@ -196,7 +200,8 @@ TEST_CASE(error_reporting) {
|
|||||||
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
|
TEST_CHECK(ent.last_write_time(ec) == file_time_type::min());
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
TEST_CHECK(ErrorIs(ec, std::errc::permission_denied));
|
||||||
|
|
||||||
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied);
|
ExceptionChecker Checker(sym_out_of_dir, std::errc::permission_denied,
|
||||||
|
"last_write_time");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ent.last_write_time());
|
||||||
|
|
||||||
permissions(dir, old_perms);
|
permissions(dir, old_perms);
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
#include "rapid-cxx-test.hpp"
|
#include "rapid-cxx-test.hpp"
|
||||||
#include "filesystem_test_helper.hpp"
|
#include "filesystem_test_helper.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
using namespace fs;
|
using namespace fs;
|
||||||
|
|
||||||
TEST_SUITE(recursive_directory_iterator_increment_tests)
|
TEST_SUITE(recursive_directory_iterator_increment_tests)
|
||||||
@@ -290,18 +292,15 @@ TEST_CASE(test_PR35078)
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
bool SeenNestedFile = false;
|
bool SeenNestedFile = false;
|
||||||
recursive_directory_iterator it = SetupState(true, SeenNestedFile);
|
recursive_directory_iterator it = SetupState(false, SeenNestedFile);
|
||||||
TEST_REQUIRE(it != endIt);
|
TEST_REQUIRE(it != endIt);
|
||||||
TEST_REQUIRE(*it == nestedDir);
|
TEST_REQUIRE(*it == nestedDir);
|
||||||
ec = GetTestEC();
|
|
||||||
it.increment(ec);
|
ExceptionChecker Checker(std::errc::permission_denied,
|
||||||
TEST_CHECK(!ec);
|
"recursive_directory_iterator::operator++()",
|
||||||
if (SeenNestedFile) {
|
format_string("attempting recursion into \"%s\"",
|
||||||
TEST_CHECK(it == endIt);
|
nestedDir.native()));
|
||||||
} else {
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, ++it);
|
||||||
TEST_REQUIRE(it != endIt);
|
|
||||||
TEST_CHECK(*it == nestedFile);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ TEST_CASE(test_error_reporting) {
|
|||||||
TEST_CHECK(fs::copy_file(file, file, copy_options::overwrite_existing,
|
TEST_CHECK(fs::copy_file(file, file, copy_options::overwrite_existing,
|
||||||
ec) == false);
|
ec) == false);
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::file_exists));
|
TEST_CHECK(ErrorIs(ec, std::errc::file_exists));
|
||||||
ExceptionChecker Checker(file, file, std::errc::file_exists);
|
ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing));
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing));
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ TEST_CASE(test_error_reporting) {
|
|||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
TEST_CHECK(fs::copy_file(file, file2, ec) == false);
|
TEST_CHECK(fs::copy_file(file, file2, ec) == false);
|
||||||
TEST_CHECK(ErrorIs(ec, std::errc::file_exists));
|
TEST_CHECK(ErrorIs(ec, std::errc::file_exists));
|
||||||
ExceptionChecker Checker(file, file, std::errc::file_exists);
|
ExceptionChecker Checker(file, file, std::errc::file_exists, "copy_file");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing));
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, copy_file(file, file, copy_options::overwrite_existing));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,14 +69,15 @@ TEST_CASE(file_size_error_cases)
|
|||||||
{StaticEnv::Dir, std::errc::is_a_directory},
|
{StaticEnv::Dir, std::errc::is_a_directory},
|
||||||
{StaticEnv::SymlinkToDir, std::errc::is_a_directory},
|
{StaticEnv::SymlinkToDir, std::errc::is_a_directory},
|
||||||
{StaticEnv::BadSymlink, std::errc::no_such_file_or_directory},
|
{StaticEnv::BadSymlink, std::errc::no_such_file_or_directory},
|
||||||
{StaticEnv::DNE, std::errc::no_such_file_or_directory}};
|
{StaticEnv::DNE, std::errc::no_such_file_or_directory},
|
||||||
|
{"", std::errc::no_such_file_or_directory}};
|
||||||
const uintmax_t expect = static_cast<uintmax_t>(-1);
|
const uintmax_t expect = static_cast<uintmax_t>(-1);
|
||||||
for (auto& TC : TestCases) {
|
for (auto& TC : TestCases) {
|
||||||
std::error_code ec = GetTestEC();
|
std::error_code ec = GetTestEC();
|
||||||
TEST_CHECK(file_size(TC.p, ec) == expect);
|
TEST_CHECK(file_size(TC.p, ec) == expect);
|
||||||
TEST_CHECK(ErrorIs(ec, TC.expected_err));
|
TEST_CHECK(ErrorIs(ec, TC.expected_err));
|
||||||
|
|
||||||
ExceptionChecker Checker(TC.p, TC.expected_err);
|
ExceptionChecker Checker(TC.p, TC.expected_err, "file_size");
|
||||||
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, file_size(TC.p));
|
TEST_CHECK_THROW_RESULT(filesystem_error, Checker, file_size(TC.p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,11 @@
|
|||||||
#include <random>
|
#include <random>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include "test_macros.h"
|
||||||
#include "rapid-cxx-test.hpp"
|
#include "rapid-cxx-test.hpp"
|
||||||
|
#include "format_string.hpp"
|
||||||
|
|
||||||
// static test helpers
|
// static test helpers
|
||||||
|
|
||||||
@@ -442,25 +445,77 @@ inline bool PathEq(fs::path const& LHS, fs::path const& RHS) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ExceptionChecker {
|
struct ExceptionChecker {
|
||||||
std::vector<std::errc> expected_err_list;
|
std::errc expected_err;
|
||||||
fs::path expected_path1;
|
fs::path expected_path1;
|
||||||
fs::path expected_path2;
|
fs::path expected_path2;
|
||||||
|
unsigned num_paths;
|
||||||
|
const char* func_name;
|
||||||
|
std::string opt_message;
|
||||||
|
|
||||||
template <class... ErrcT>
|
explicit ExceptionChecker(std::errc first_err, const char* func_name,
|
||||||
explicit ExceptionChecker(fs::path p, std::errc first_err, ErrcT... rest_err)
|
std::string opt_msg = {})
|
||||||
: expected_err_list({first_err, rest_err...}), expected_path1(p) {}
|
: expected_err{first_err}, num_paths(0), func_name(func_name),
|
||||||
|
opt_message(opt_msg) {}
|
||||||
|
explicit ExceptionChecker(fs::path p, std::errc first_err,
|
||||||
|
const char* func_name, std::string opt_msg = {})
|
||||||
|
: expected_err(first_err), expected_path1(p), num_paths(1),
|
||||||
|
func_name(func_name), opt_message(opt_msg) {}
|
||||||
|
|
||||||
template <class... ErrcT>
|
|
||||||
explicit ExceptionChecker(fs::path p1, fs::path p2, std::errc first_err,
|
explicit ExceptionChecker(fs::path p1, fs::path p2, std::errc first_err,
|
||||||
ErrcT... rest_err)
|
const char* func_name, std::string opt_msg = {})
|
||||||
: expected_err_list({first_err, rest_err...}), expected_path1(p1),
|
: expected_err(first_err), expected_path1(p1), expected_path2(p2),
|
||||||
expected_path2(p2) {}
|
num_paths(2), func_name(func_name), opt_message(opt_msg) {}
|
||||||
|
|
||||||
void operator()(fs::filesystem_error const& Err) const {
|
void operator()(fs::filesystem_error const& Err) {
|
||||||
TEST_CHECK(ErrorIsImp(Err.code(), expected_err_list));
|
TEST_CHECK(ErrorIsImp(Err.code(), {expected_err}));
|
||||||
TEST_CHECK(Err.path1() == expected_path1);
|
TEST_CHECK(Err.path1() == expected_path1);
|
||||||
TEST_CHECK(Err.path2() == expected_path2);
|
TEST_CHECK(Err.path2() == expected_path2);
|
||||||
|
LIBCPP_ONLY(check_libcxx_string(Err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void check_libcxx_string(fs::filesystem_error const& Err) {
|
||||||
|
std::string message = std::make_error_code(expected_err).message();
|
||||||
|
|
||||||
|
std::string additional_msg = "";
|
||||||
|
if (!opt_message.empty()) {
|
||||||
|
additional_msg = opt_message + ": ";
|
||||||
|
}
|
||||||
|
auto transform_path = [](const fs::path& p) {
|
||||||
|
if (p.native().empty())
|
||||||
|
return "\"\"";
|
||||||
|
return p.c_str();
|
||||||
|
};
|
||||||
|
std::string format = [&]() -> std::string {
|
||||||
|
switch (num_paths) {
|
||||||
|
case 0:
|
||||||
|
return format_string("filesystem error: in %s: %s%s", func_name,
|
||||||
|
additional_msg, message);
|
||||||
|
case 1:
|
||||||
|
return format_string("filesystem error: in %s: %s%s [%s]", func_name,
|
||||||
|
additional_msg, message,
|
||||||
|
transform_path(expected_path1));
|
||||||
|
case 2:
|
||||||
|
return format_string("filesystem error: in %s: %s%s [%s] [%s]",
|
||||||
|
func_name, additional_msg, message,
|
||||||
|
transform_path(expected_path1),
|
||||||
|
transform_path(expected_path2));
|
||||||
|
default:
|
||||||
|
TEST_CHECK(false && "unexpected case");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
TEST_CHECK(format == Err.what());
|
||||||
|
if (format != Err.what()) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"filesystem_error::what() does not match expected output:\n");
|
||||||
|
fprintf(stderr, " expected: \"%s\"\n", format.c_str());
|
||||||
|
fprintf(stderr, " actual: \"%s\"\n\n", Err.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExceptionChecker(ExceptionChecker const&) = delete;
|
||||||
|
ExceptionChecker& operator=(ExceptionChecker const&) = delete;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* FILESYSTEM_TEST_HELPER_HPP */
|
#endif /* FILESYSTEM_TEST_HELPER_HPP */
|
||||||
|
|||||||
68
test/support/format_string.hpp
Normal file
68
test/support/format_string.hpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#ifndef TEST_SUPPORT_FORMAT_STRING_HPP
|
||||||
|
#define TEST_SUPPORT_FORMAT_STRING_HPP
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace format_string_detail {
|
||||||
|
inline std::string format_string_imp(const char* msg, ...) {
|
||||||
|
// we might need a second shot at this, so pre-emptivly make a copy
|
||||||
|
struct GuardVAList {
|
||||||
|
va_list& target;
|
||||||
|
bool active = true;
|
||||||
|
void clear() {
|
||||||
|
if (active)
|
||||||
|
va_end(target);
|
||||||
|
active = false;
|
||||||
|
}
|
||||||
|
~GuardVAList() {
|
||||||
|
if (active)
|
||||||
|
va_end(target);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
va_list args;
|
||||||
|
va_start(args, msg);
|
||||||
|
GuardVAList args_guard = {args};
|
||||||
|
|
||||||
|
va_list args_cp;
|
||||||
|
va_copy(args_cp, args);
|
||||||
|
GuardVAList args_copy_guard = {args_cp};
|
||||||
|
|
||||||
|
std::array<char, 256> local_buff;
|
||||||
|
std::size_t size = local_buff.size();
|
||||||
|
auto ret = ::vsnprintf(local_buff.data(), size, msg, args_cp);
|
||||||
|
|
||||||
|
args_copy_guard.clear();
|
||||||
|
|
||||||
|
// handle empty expansion
|
||||||
|
if (ret == 0)
|
||||||
|
return std::string{};
|
||||||
|
if (static_cast<std::size_t>(ret) < size)
|
||||||
|
return std::string(local_buff.data());
|
||||||
|
|
||||||
|
// we did not provide a long enough buffer on our first attempt.
|
||||||
|
// add 1 to size to account for null-byte in size cast to prevent overflow
|
||||||
|
size = static_cast<std::size_t>(ret) + 1;
|
||||||
|
auto buff_ptr = std::unique_ptr<char[]>(new char[size]);
|
||||||
|
ret = ::vsnprintf(buff_ptr.get(), size, msg, args);
|
||||||
|
return std::string(buff_ptr.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* unwrap(std::string& s) { return s.c_str(); }
|
||||||
|
template <class Arg>
|
||||||
|
Arg const& unwrap(Arg& a) {
|
||||||
|
static_assert(!std::is_class<Arg>::value, "cannot pass class here");
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace format_string_detail
|
||||||
|
|
||||||
|
template <class... Args>
|
||||||
|
std::string format_string(const char* fmt, Args const&... args) {
|
||||||
|
return format_string_detail::format_string_imp(
|
||||||
|
fmt, format_string_detail::unwrap(const_cast<Args&>(args))...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // TEST_SUPPORT_FORMAT_STRING_HPP
|
||||||
@@ -151,7 +151,7 @@
|
|||||||
<tr><td><a href="https://wg21.link/LWG2953">2953</a></td><td>LWG 2853 should apply to deque::erase too</td><td>Albuquerque</td><td></td></tr>
|
<tr><td><a href="https://wg21.link/LWG2953">2953</a></td><td>LWG 2853 should apply to deque::erase too</td><td>Albuquerque</td><td></td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG2958">2958</a></td><td>Moves improperly defined as deleted</td><td>Albuquerque</td><td></td></tr>
|
<tr><td><a href="https://wg21.link/LWG2958">2958</a></td><td>Moves improperly defined as deleted</td><td>Albuquerque</td><td></td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG2964">2964</a></td><td>Apparently redundant requirement for dynamic_pointer_cast</td><td>Albuquerque</td><td></td></tr>
|
<tr><td><a href="https://wg21.link/LWG2964">2964</a></td><td>Apparently redundant requirement for dynamic_pointer_cast</td><td>Albuquerque</td><td></td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG2965">2965</a></td><td>Non-existing path::native_string() in filesystem_error::what() specification</td><td>Albuquerque</td><td></td></tr>
|
<tr><td><a href="https://wg21.link/LWG2965">2965</a></td><td>Non-existing path::native_string() in filesystem_error::what() specification</td><td>Albuquerque</td><td>Nothing to do</td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG2972">2972</a></td><td>What is is_trivially_destructible_v<int>?</td><td>Albuquerque</td><td>Complete</td></tr>
|
<tr><td><a href="https://wg21.link/LWG2972">2972</a></td><td>What is is_trivially_destructible_v<int>?</td><td>Albuquerque</td><td>Complete</td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG2976">2976</a></td><td>Dangling uses_allocator specialization for packaged_task</td><td>Albuquerque</td><td>Complete</td></tr>
|
<tr><td><a href="https://wg21.link/LWG2976">2976</a></td><td>Dangling uses_allocator specialization for packaged_task</td><td>Albuquerque</td><td>Complete</td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG2977">2977</a></td><td>unordered_meow::merge() has incorrect Throws: clause</td><td>Albuquerque</td><td></td></tr>
|
<tr><td><a href="https://wg21.link/LWG2977">2977</a></td><td>unordered_meow::merge() has incorrect Throws: clause</td><td>Albuquerque</td><td></td></tr>
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
<tr><td><a href="https://wg21.link/LWG3039">3039</a></td><td>Unnecessary <tt>decay</tt> in <tt>thread</tt> and <tt>packaged_task</tt></td><td>Jacksonville</td><td>Complete</td></tr>
|
<tr><td><a href="https://wg21.link/LWG3039">3039</a></td><td>Unnecessary <tt>decay</tt> in <tt>thread</tt> and <tt>packaged_task</tt></td><td>Jacksonville</td><td>Complete</td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG3041">3041</a></td><td>Unnecessary <tt>decay</tt> in <tt>reference_wrapper</tt></td><td>Jacksonville</td><td>Complete</td></tr>
|
<tr><td><a href="https://wg21.link/LWG3041">3041</a></td><td>Unnecessary <tt>decay</tt> in <tt>reference_wrapper</tt></td><td>Jacksonville</td><td>Complete</td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG3042">3042</a></td><td><tt>is_literal_type_v</tt> should be inline</td><td>Jacksonville</td><td>Complete</td></tr>
|
<tr><td><a href="https://wg21.link/LWG3042">3042</a></td><td><tt>is_literal_type_v</tt> should be inline</td><td>Jacksonville</td><td>Complete</td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG3043">3043</a></td><td>Bogus postcondition for <tt>filesystem_error</tt> constructor</td><td>Jacksonville</td><td></td></tr>
|
<tr><td><a href="https://wg21.link/LWG3043">3043</a></td><td>Bogus postcondition for <tt>filesystem_error</tt> constructor</td><td>Jacksonville</td><td>Complete</td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG3045">3045</a></td><td><tt>atomic<<i>floating-point</i>></tt> doesn't have <tt>value_type</tt> or <tt>difference_type</tt></td><td>Jacksonville</td><td></td></tr>
|
<tr><td><a href="https://wg21.link/LWG3045">3045</a></td><td><tt>atomic<<i>floating-point</i>></tt> doesn't have <tt>value_type</tt> or <tt>difference_type</tt></td><td>Jacksonville</td><td></td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG3048">3048</a></td><td><tt>transform_reduce(exec, first1, last1, first2, init)</tt> discards execution policy</td><td>Jacksonville</td><td></td></tr>
|
<tr><td><a href="https://wg21.link/LWG3048">3048</a></td><td><tt>transform_reduce(exec, first1, last1, first2, init)</tt> discards execution policy</td><td>Jacksonville</td><td></td></tr>
|
||||||
<tr><td><a href="https://wg21.link/LWG3051">3051</a></td><td>Floating point classifications were inadvertently changed in P0175</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>
|
<tr><td><a href="https://wg21.link/LWG3051">3051</a></td><td>Floating point classifications were inadvertently changed in P0175</td><td>Jacksonville</td><td><i>Nothing to do</i></td></tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user