mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Add libcxx filesystem modules
This commit is contained in:
parent
a092fda388
commit
0c43c98de1
6 changed files with 2612 additions and 4 deletions
6
Makefile
6
Makefile
|
@ -142,13 +142,10 @@ include libc/thread/thread.mk # │ You can finally call malloc()
|
|||
include libc/zipos/zipos.mk # │
|
||||
include libc/stdio/stdio.mk # │
|
||||
include libc/time/time.mk # │
|
||||
include third_party/libcxx/libcxx.mk # │
|
||||
include net/net.mk # │
|
||||
include third_party/vqsort/vqsort.mk # │
|
||||
include libc/log/log.mk # │
|
||||
include third_party/getopt/getopt.mk # │
|
||||
include third_party/ggml/ggml.mk # │
|
||||
include third_party/radpajama/radpajama.mk # │
|
||||
include third_party/bzip2/bzip2.mk # │
|
||||
include dsp/core/core.mk # │
|
||||
include libc/x/x.mk # │
|
||||
|
@ -166,6 +163,9 @@ include dsp/tty/tty.mk # ├──ONLINE RUNTIME
|
|||
include libc/dns/dns.mk # │ You can communicate with the network
|
||||
include net/http/http.mk # │
|
||||
include third_party/mbedtls/mbedtls.mk # │
|
||||
include third_party/libcxx/libcxx.mk # │
|
||||
include third_party/ggml/ggml.mk # │
|
||||
include third_party/radpajama/radpajama.mk # │
|
||||
include net/https/https.mk # │
|
||||
include third_party/regex/regex.mk #─┘
|
||||
include third_party/tidy/tidy.mk
|
||||
|
|
|
@ -8,16 +8,17 @@
|
|||
# - Backtraces
|
||||
# - Syscall tracing
|
||||
# - Function tracing
|
||||
# - No GDB debugging
|
||||
#
|
||||
ifeq ($(MODE),)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g
|
||||
CONFIG_CCFLAGS += -O2 $(BACKTRACES)
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG
|
||||
TARGET_ARCH ?= -msse3
|
||||
endif
|
||||
ifeq ($(MODE), aarch64)
|
||||
ENABLE_FTRACE = 1
|
||||
CONFIG_OFLAGS ?= -g
|
||||
CONFIG_CCFLAGS += -O2 $(BACKTRACES)
|
||||
CONFIG_CPPFLAGS += -DSYSDEBUG
|
||||
endif
|
||||
|
|
393
third_party/libcxx/directory_iterator.cc
vendored
Normal file
393
third_party/libcxx/directory_iterator.cc
vendored
Normal file
|
@ -0,0 +1,393 @@
|
|||
// clang-format off
|
||||
//===------------------ directory_iterator.cpp ----------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "third_party/libcxx/filesystem"
|
||||
#include "third_party/libcxx/__config"
|
||||
#include "libc/calls/struct/dirent.h"
|
||||
#include "libc/sysv/consts/dt.h"
|
||||
#include "third_party/libcxx/filesystem_common.hh"
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
|
||||
|
||||
namespace detail {
|
||||
namespace {
|
||||
|
||||
#if !defined(_LIBCPP_WIN32API)
|
||||
|
||||
#if defined(DT_BLK)
|
||||
template <class DirEntT, class = decltype(DirEntT::d_type)>
|
||||
static file_type get_file_type(DirEntT* ent, int) {
|
||||
switch (ent->d_type) {
|
||||
case DT_BLK:
|
||||
return file_type::block;
|
||||
case DT_CHR:
|
||||
return file_type::character;
|
||||
case DT_DIR:
|
||||
return file_type::directory;
|
||||
case DT_FIFO:
|
||||
return file_type::fifo;
|
||||
case DT_LNK:
|
||||
return file_type::symlink;
|
||||
case DT_REG:
|
||||
return file_type::regular;
|
||||
case DT_SOCK:
|
||||
return file_type::socket;
|
||||
// Unlike in lstat, hitting "unknown" here simply means that the underlying
|
||||
// filesystem doesn't support d_type. Report is as 'none' so we correctly
|
||||
// set the cache to empty.
|
||||
case DT_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
return file_type::none;
|
||||
}
|
||||
#endif // defined(DT_BLK)
|
||||
|
||||
template <class DirEntT>
|
||||
static file_type get_file_type(DirEntT* ent, long) {
|
||||
return file_type::none;
|
||||
}
|
||||
|
||||
static pair<string_view, file_type> posix_readdir(DIR* dir_stream,
|
||||
error_code& ec) {
|
||||
struct dirent* dir_entry_ptr = nullptr;
|
||||
errno = 0; // zero errno in order to detect errors
|
||||
ec.clear();
|
||||
if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
|
||||
if (errno)
|
||||
ec = capture_errno();
|
||||
return {};
|
||||
} else {
|
||||
return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
static file_type get_file_type(const WIN32_FIND_DATA& data) {
|
||||
//auto attrs = data.dwFileAttributes;
|
||||
// FIXME(EricWF)
|
||||
return file_type::unknown;
|
||||
}
|
||||
static uintmax_t get_file_size(const WIN32_FIND_DATA& data) {
|
||||
return (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow;
|
||||
}
|
||||
static file_time_type get_write_time(const WIN32_FIND_DATA& data) {
|
||||
ULARGE_INTEGER tmp;
|
||||
const FILETIME& time = data.ftLastWriteTime;
|
||||
tmp.u.LowPart = time.dwLowDateTime;
|
||||
tmp.u.HighPart = time.dwHighDateTime;
|
||||
return file_time_type(file_time_type::duration(tmp.QuadPart));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
} // namespace detail
|
||||
|
||||
using detail::ErrorHandler;
|
||||
|
||||
#if defined(_LIBCPP_WIN32API)
|
||||
class __dir_stream {
|
||||
public:
|
||||
__dir_stream() = delete;
|
||||
__dir_stream& operator=(const __dir_stream&) = delete;
|
||||
|
||||
__dir_stream(__dir_stream&& __ds) noexcept : __stream_(__ds.__stream_),
|
||||
__root_(move(__ds.__root_)),
|
||||
__entry_(move(__ds.__entry_)) {
|
||||
__ds.__stream_ = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
__dir_stream(const path& root, directory_options opts, error_code& ec)
|
||||
: __stream_(INVALID_HANDLE_VALUE), __root_(root) {
|
||||
__stream_ = ::FindFirstFile(root.c_str(), &__data_);
|
||||
if (__stream_ == INVALID_HANDLE_VALUE) {
|
||||
ec = error_code(::GetLastError(), generic_category());
|
||||
const bool ignore_permission_denied =
|
||||
bool(opts & directory_options::skip_permission_denied);
|
||||
if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED)
|
||||
ec.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
~__dir_stream() noexcept {
|
||||
if (__stream_ == INVALID_HANDLE_VALUE)
|
||||
return;
|
||||
close();
|
||||
}
|
||||
|
||||
bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; }
|
||||
|
||||
bool advance(error_code& ec) {
|
||||
while (::FindNextFile(__stream_, &__data_)) {
|
||||
if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, ".."))
|
||||
continue;
|
||||
// FIXME: Cache more of this
|
||||
//directory_entry::__cached_data cdata;
|
||||
//cdata.__type_ = get_file_type(__data_);
|
||||
//cdata.__size_ = get_file_size(__data_);
|
||||
//cdata.__write_time_ = get_write_time(__data_);
|
||||
__entry_.__assign_iter_entry(
|
||||
__root_ / __data_.cFileName,
|
||||
directory_entry::__create_iter_result(detail::get_file_type(__data)));
|
||||
return true;
|
||||
}
|
||||
ec = error_code(::GetLastError(), generic_category());
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
error_code close() noexcept {
|
||||
error_code ec;
|
||||
if (!::FindClose(__stream_))
|
||||
ec = error_code(::GetLastError(), generic_category());
|
||||
__stream_ = INVALID_HANDLE_VALUE;
|
||||
return ec;
|
||||
}
|
||||
|
||||
HANDLE __stream_{INVALID_HANDLE_VALUE};
|
||||
WIN32_FIND_DATA __data_;
|
||||
|
||||
public:
|
||||
path __root_;
|
||||
directory_entry __entry_;
|
||||
};
|
||||
#else
|
||||
class __dir_stream {
|
||||
public:
|
||||
__dir_stream() = delete;
|
||||
__dir_stream& operator=(const __dir_stream&) = delete;
|
||||
|
||||
__dir_stream(__dir_stream&& other) noexcept : __stream_(other.__stream_),
|
||||
__root_(move(other.__root_)),
|
||||
__entry_(move(other.__entry_)) {
|
||||
other.__stream_ = nullptr;
|
||||
}
|
||||
|
||||
__dir_stream(const path& root, directory_options opts, error_code& ec)
|
||||
: __stream_(nullptr), __root_(root) {
|
||||
if ((__stream_ = ::opendir(root.c_str())) == nullptr) {
|
||||
ec = detail::capture_errno();
|
||||
const bool allow_eacess =
|
||||
bool(opts & directory_options::skip_permission_denied);
|
||||
if (allow_eacess && ec.value() == EACCES)
|
||||
ec.clear();
|
||||
return;
|
||||
}
|
||||
advance(ec);
|
||||
}
|
||||
|
||||
~__dir_stream() noexcept {
|
||||
if (__stream_)
|
||||
close();
|
||||
}
|
||||
|
||||
bool good() const noexcept { return __stream_ != nullptr; }
|
||||
|
||||
bool advance(error_code& ec) {
|
||||
while (true) {
|
||||
auto str_type_pair = detail::posix_readdir(__stream_, ec);
|
||||
auto& str = str_type_pair.first;
|
||||
if (str == "." || str == "..") {
|
||||
continue;
|
||||
} else if (ec || str.empty()) {
|
||||
close();
|
||||
return false;
|
||||
} else {
|
||||
__entry_.__assign_iter_entry(
|
||||
__root_ / str,
|
||||
directory_entry::__create_iter_result(str_type_pair.second));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
error_code close() noexcept {
|
||||
error_code m_ec;
|
||||
if (::closedir(__stream_) == -1)
|
||||
m_ec = detail::capture_errno();
|
||||
__stream_ = nullptr;
|
||||
return m_ec;
|
||||
}
|
||||
|
||||
DIR* __stream_{nullptr};
|
||||
|
||||
public:
|
||||
path __root_;
|
||||
directory_entry __entry_;
|
||||
};
|
||||
#endif
|
||||
|
||||
// directory_iterator
|
||||
|
||||
directory_iterator::directory_iterator(const path& p, error_code* ec,
|
||||
directory_options opts) {
|
||||
ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p);
|
||||
|
||||
error_code m_ec;
|
||||
__imp_ = make_shared<__dir_stream>(p, opts, m_ec);
|
||||
if (ec)
|
||||
*ec = m_ec;
|
||||
if (!__imp_->good()) {
|
||||
__imp_.reset();
|
||||
if (m_ec)
|
||||
err.report(m_ec);
|
||||
}
|
||||
}
|
||||
|
||||
directory_iterator& directory_iterator::__increment(error_code* ec) {
|
||||
_LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
|
||||
ErrorHandler<void> err("directory_iterator::operator++()", ec);
|
||||
|
||||
error_code m_ec;
|
||||
if (!__imp_->advance(m_ec)) {
|
||||
path root = move(__imp_->__root_);
|
||||
__imp_.reset();
|
||||
if (m_ec)
|
||||
err.report(m_ec, "at root \"%s\"", root);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
directory_entry const& directory_iterator::__dereference() const {
|
||||
_LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
|
||||
return __imp_->__entry_;
|
||||
}
|
||||
|
||||
// recursive_directory_iterator
|
||||
|
||||
struct recursive_directory_iterator::__shared_imp {
|
||||
stack<__dir_stream> __stack_;
|
||||
directory_options __options_;
|
||||
};
|
||||
|
||||
recursive_directory_iterator::recursive_directory_iterator(
|
||||
const path& p, directory_options opt, error_code* ec)
|
||||
: __imp_(nullptr), __rec_(true) {
|
||||
ErrorHandler<void> err("recursive_directory_iterator", ec, &p);
|
||||
|
||||
error_code m_ec;
|
||||
__dir_stream new_s(p, opt, m_ec);
|
||||
if (m_ec)
|
||||
err.report(m_ec);
|
||||
if (m_ec || !new_s.good())
|
||||
return;
|
||||
|
||||
__imp_ = make_shared<__shared_imp>();
|
||||
__imp_->__options_ = opt;
|
||||
__imp_->__stack_.push(move(new_s));
|
||||
}
|
||||
|
||||
void recursive_directory_iterator::__pop(error_code* ec) {
|
||||
_LIBCPP_ASSERT(__imp_, "Popping the end iterator");
|
||||
if (ec)
|
||||
ec->clear();
|
||||
__imp_->__stack_.pop();
|
||||
if (__imp_->__stack_.size() == 0)
|
||||
__imp_.reset();
|
||||
else
|
||||
__advance(ec);
|
||||
}
|
||||
|
||||
directory_options recursive_directory_iterator::options() const {
|
||||
return __imp_->__options_;
|
||||
}
|
||||
|
||||
int recursive_directory_iterator::depth() const {
|
||||
return __imp_->__stack_.size() - 1;
|
||||
}
|
||||
|
||||
const directory_entry& recursive_directory_iterator::__dereference() const {
|
||||
return __imp_->__stack_.top().__entry_;
|
||||
}
|
||||
|
||||
recursive_directory_iterator&
|
||||
recursive_directory_iterator::__increment(error_code* ec) {
|
||||
if (ec)
|
||||
ec->clear();
|
||||
if (recursion_pending()) {
|
||||
if (__try_recursion(ec) || (ec && *ec))
|
||||
return *this;
|
||||
}
|
||||
__rec_ = true;
|
||||
__advance(ec);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void recursive_directory_iterator::__advance(error_code* ec) {
|
||||
ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
|
||||
|
||||
const directory_iterator end_it;
|
||||
auto& stack = __imp_->__stack_;
|
||||
error_code m_ec;
|
||||
while (stack.size() > 0) {
|
||||
if (stack.top().advance(m_ec))
|
||||
return;
|
||||
if (m_ec)
|
||||
break;
|
||||
stack.pop();
|
||||
}
|
||||
|
||||
if (m_ec) {
|
||||
path root = 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) {
|
||||
ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec);
|
||||
|
||||
bool rec_sym = bool(options() & directory_options::follow_directory_symlink);
|
||||
|
||||
auto& curr_it = __imp_->__stack_.top();
|
||||
|
||||
bool skip_rec = false;
|
||||
error_code m_ec;
|
||||
if (!rec_sym) {
|
||||
file_status st(curr_it.__entry_.__get_sym_ft(&m_ec));
|
||||
if (m_ec && status_known(st))
|
||||
m_ec.clear();
|
||||
if (m_ec || is_symlink(st) || !is_directory(st))
|
||||
skip_rec = true;
|
||||
} else {
|
||||
file_status st(curr_it.__entry_.__get_ft(&m_ec));
|
||||
if (m_ec && status_known(st))
|
||||
m_ec.clear();
|
||||
if (m_ec || !is_directory(st))
|
||||
skip_rec = true;
|
||||
}
|
||||
|
||||
if (!skip_rec) {
|
||||
__dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec);
|
||||
if (new_it.good()) {
|
||||
__imp_->__stack_.push(move(new_it));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (m_ec) {
|
||||
const bool allow_eacess =
|
||||
bool(__imp_->__options_ & directory_options::skip_permission_denied);
|
||||
if (m_ec.value() == EACCES && allow_eacess) {
|
||||
if (ec)
|
||||
ec->clear();
|
||||
} else {
|
||||
path at_ent = move(curr_it.__entry_.__p_);
|
||||
__imp_.reset();
|
||||
err.report(m_ec, "attempting recursion into \"%s\"", at_ent);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_LIBCPP_END_NAMESPACE_FILESYSTEM
|
425
third_party/libcxx/filesystem_common.hh
vendored
Normal file
425
third_party/libcxx/filesystem_common.hh
vendored
Normal file
|
@ -0,0 +1,425 @@
|
|||
// clang-format off
|
||||
//===----------------------------------------------------------------------===////
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===////
|
||||
|
||||
#ifndef FILESYSTEM_COMMON_H
|
||||
#define FILESYSTEM_COMMON_H
|
||||
|
||||
#include "third_party/libcxx/__config"
|
||||
#include "third_party/libcxx/filesystem"
|
||||
#include "third_party/libcxx/filesystem_common.hh"
|
||||
#include "third_party/libcxx/array"
|
||||
#include "third_party/libcxx/chrono"
|
||||
#include "third_party/libcxx/cstdlib"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/sysv/consts/at.h"
|
||||
#include "third_party/libcxx/climits"
|
||||
|
||||
#define _LIBCPP_USE_UTIMENSAT
|
||||
|
||||
#if defined(__GNUC__)
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
|
||||
|
||||
namespace detail {
|
||||
namespace {
|
||||
|
||||
static 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;
|
||||
GuardVAList(va_list& target) : target(target), 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::string result;
|
||||
|
||||
array<char, 256> local_buff;
|
||||
size_t size_with_null = local_buff.size();
|
||||
auto ret = ::vsnprintf(local_buff.data(), size_with_null, msg, args_cp);
|
||||
|
||||
args_copy_guard.clear();
|
||||
|
||||
// handle empty expansion
|
||||
if (ret == 0)
|
||||
return result;
|
||||
if (static_cast<size_t>(ret) < size_with_null) {
|
||||
result.assign(local_buff.data(), static_cast<size_t>(ret));
|
||||
return result;
|
||||
}
|
||||
|
||||
// we did not provide a long enough buffer on our first attempt. The
|
||||
// return value is the number of bytes (excluding the null byte) that are
|
||||
// needed for formatting.
|
||||
size_with_null = static_cast<size_t>(ret) + 1;
|
||||
result.__resize_default_init(size_with_null - 1);
|
||||
ret = ::vsnprintf(&result[0], size_with_null, msg, args);
|
||||
_LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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>
|
||||
string format_string(const char* fmt, Args const&... args) {
|
||||
return format_string_imp(fmt, unwrap(args)...);
|
||||
}
|
||||
|
||||
error_code capture_errno() {
|
||||
_LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
|
||||
return error_code(errno, generic_category());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T error_value();
|
||||
template <>
|
||||
_LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {}
|
||||
template <>
|
||||
bool error_value<bool>() {
|
||||
return false;
|
||||
}
|
||||
template <>
|
||||
uintmax_t error_value<uintmax_t>() {
|
||||
return uintmax_t(-1);
|
||||
}
|
||||
template <>
|
||||
_LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() {
|
||||
return file_time_type::min();
|
||||
}
|
||||
template <>
|
||||
path error_value<path>() {
|
||||
return {};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
struct ErrorHandler {
|
||||
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;
|
||||
};
|
||||
|
||||
using chrono::duration;
|
||||
using chrono::duration_cast;
|
||||
|
||||
using TimeSpec = struct ::timespec;
|
||||
using StatT = struct ::stat;
|
||||
|
||||
template <class FileTimeT, class TimeT,
|
||||
bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
|
||||
struct time_util_base {
|
||||
using rep = typename FileTimeT::rep;
|
||||
using fs_duration = typename FileTimeT::duration;
|
||||
using fs_seconds = duration<rep>;
|
||||
using fs_nanoseconds = duration<rep, nano>;
|
||||
using fs_microseconds = duration<rep, micro>;
|
||||
|
||||
static constexpr rep max_seconds =
|
||||
duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
|
||||
|
||||
static constexpr rep max_nsec =
|
||||
duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
|
||||
fs_seconds(max_seconds))
|
||||
.count();
|
||||
|
||||
static constexpr rep min_seconds =
|
||||
duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
|
||||
|
||||
static constexpr rep min_nsec_timespec =
|
||||
duration_cast<fs_nanoseconds>(
|
||||
(FileTimeT::duration::min() - fs_seconds(min_seconds)) +
|
||||
fs_seconds(1))
|
||||
.count();
|
||||
|
||||
private:
|
||||
#if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
|
||||
static constexpr fs_duration get_min_nsecs() {
|
||||
return duration_cast<fs_duration>(
|
||||
fs_nanoseconds(min_nsec_timespec) -
|
||||
duration_cast<fs_nanoseconds>(fs_seconds(1)));
|
||||
}
|
||||
// Static assert that these values properly round trip.
|
||||
static_assert(fs_seconds(min_seconds) + get_min_nsecs() ==
|
||||
FileTimeT::duration::min(),
|
||||
"value doesn't roundtrip");
|
||||
|
||||
static constexpr bool check_range() {
|
||||
// This kinda sucks, but it's what happens when we don't have __int128_t.
|
||||
if (sizeof(TimeT) == sizeof(rep)) {
|
||||
typedef duration<long long, ratio<3600 * 24 * 365> > Years;
|
||||
return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) &&
|
||||
duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250);
|
||||
}
|
||||
return max_seconds >= numeric_limits<TimeT>::max() &&
|
||||
min_seconds <= numeric_limits<TimeT>::min();
|
||||
}
|
||||
static_assert(check_range(), "the representable range is unacceptable small");
|
||||
#endif
|
||||
};
|
||||
|
||||
template <class FileTimeT, class TimeT>
|
||||
struct time_util_base<FileTimeT, TimeT, true> {
|
||||
using rep = typename FileTimeT::rep;
|
||||
using fs_duration = typename FileTimeT::duration;
|
||||
using fs_seconds = duration<rep>;
|
||||
using fs_nanoseconds = duration<rep, nano>;
|
||||
using fs_microseconds = duration<rep, micro>;
|
||||
|
||||
static const rep max_seconds;
|
||||
static const rep max_nsec;
|
||||
static const rep min_seconds;
|
||||
static const rep min_nsec_timespec;
|
||||
};
|
||||
|
||||
template <class FileTimeT, class TimeT>
|
||||
const typename FileTimeT::rep
|
||||
time_util_base<FileTimeT, TimeT, true>::max_seconds =
|
||||
duration_cast<fs_seconds>(FileTimeT::duration::max()).count();
|
||||
|
||||
template <class FileTimeT, class TimeT>
|
||||
const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec =
|
||||
duration_cast<fs_nanoseconds>(FileTimeT::duration::max() -
|
||||
fs_seconds(max_seconds))
|
||||
.count();
|
||||
|
||||
template <class FileTimeT, class TimeT>
|
||||
const typename FileTimeT::rep
|
||||
time_util_base<FileTimeT, TimeT, true>::min_seconds =
|
||||
duration_cast<fs_seconds>(FileTimeT::duration::min()).count();
|
||||
|
||||
template <class FileTimeT, class TimeT>
|
||||
const typename FileTimeT::rep
|
||||
time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec =
|
||||
duration_cast<fs_nanoseconds>((FileTimeT::duration::min() -
|
||||
fs_seconds(min_seconds)) +
|
||||
fs_seconds(1))
|
||||
.count();
|
||||
|
||||
template <class FileTimeT, class TimeT, class TimeSpecT>
|
||||
struct time_util : time_util_base<FileTimeT, TimeT> {
|
||||
using Base = time_util_base<FileTimeT, TimeT>;
|
||||
using Base::max_nsec;
|
||||
using Base::max_seconds;
|
||||
using Base::min_nsec_timespec;
|
||||
using Base::min_seconds;
|
||||
|
||||
using typename Base::fs_duration;
|
||||
using typename Base::fs_microseconds;
|
||||
using typename Base::fs_nanoseconds;
|
||||
using typename Base::fs_seconds;
|
||||
|
||||
public:
|
||||
template <class CType, class ChronoType>
|
||||
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out,
|
||||
ChronoType time) {
|
||||
using Lim = numeric_limits<CType>;
|
||||
if (time > Lim::max() || time < Lim::min())
|
||||
return false;
|
||||
*out = static_cast<CType>(time);
|
||||
return true;
|
||||
}
|
||||
|
||||
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) {
|
||||
if (tm.tv_sec >= 0) {
|
||||
return tm.tv_sec < max_seconds ||
|
||||
(tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec);
|
||||
} else if (tm.tv_sec == (min_seconds - 1)) {
|
||||
return tm.tv_nsec >= min_nsec_timespec;
|
||||
} else {
|
||||
return tm.tv_sec >= min_seconds;
|
||||
}
|
||||
}
|
||||
|
||||
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) {
|
||||
auto secs = duration_cast<fs_seconds>(tm.time_since_epoch());
|
||||
auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs);
|
||||
if (nsecs.count() < 0) {
|
||||
secs = secs + fs_seconds(1);
|
||||
nsecs = nsecs + fs_seconds(1);
|
||||
}
|
||||
using TLim = numeric_limits<TimeT>;
|
||||
if (secs.count() >= 0)
|
||||
return secs.count() <= TLim::max();
|
||||
return secs.count() >= TLim::min();
|
||||
}
|
||||
|
||||
static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT
|
||||
convert_from_timespec(TimeSpecT tm) {
|
||||
if (tm.tv_sec >= 0 || tm.tv_nsec == 0) {
|
||||
return FileTimeT(fs_seconds(tm.tv_sec) +
|
||||
duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec)));
|
||||
} else { // tm.tv_sec < 0
|
||||
auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) -
|
||||
fs_nanoseconds(tm.tv_nsec));
|
||||
auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec;
|
||||
return FileTimeT(Dur);
|
||||
}
|
||||
}
|
||||
|
||||
template <class SubSecT>
|
||||
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool
|
||||
set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) {
|
||||
auto dur = tp.time_since_epoch();
|
||||
auto sec_dur = duration_cast<fs_seconds>(dur);
|
||||
auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur);
|
||||
// The tv_nsec and tv_usec fields must not be negative so adjust accordingly
|
||||
if (subsec_dur.count() < 0) {
|
||||
if (sec_dur.count() > min_seconds) {
|
||||
sec_dur = sec_dur - fs_seconds(1);
|
||||
subsec_dur = subsec_dur + fs_seconds(1);
|
||||
} else {
|
||||
subsec_dur = fs_nanoseconds::zero();
|
||||
}
|
||||
}
|
||||
return checked_set(sec_out, sec_dur.count()) &&
|
||||
checked_set(subsec_out, subsec_dur.count());
|
||||
}
|
||||
static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest,
|
||||
FileTimeT tp) {
|
||||
if (!is_representable(tp))
|
||||
return false;
|
||||
return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp);
|
||||
}
|
||||
};
|
||||
|
||||
using fs_time = time_util<file_time_type, time_t, TimeSpec>;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
|
||||
TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
|
||||
#else
|
||||
TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
|
||||
TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
|
||||
#endif
|
||||
|
||||
// allow the utimes implementation to compile even it we're not going
|
||||
// to use it.
|
||||
|
||||
bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
|
||||
error_code& ec) {
|
||||
using namespace chrono;
|
||||
auto Convert = [](long nsec) {
|
||||
using int_type = decltype(std::declval< ::timeval>().tv_usec);
|
||||
auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count();
|
||||
return static_cast<int_type>(dur);
|
||||
};
|
||||
struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)},
|
||||
{TS[1].tv_sec, Convert(TS[1].tv_nsec)}};
|
||||
if (::utimes(p.c_str(), ConvertedTS) == -1) {
|
||||
ec = capture_errno();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(_LIBCPP_USE_UTIMENSAT)
|
||||
bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
|
||||
error_code& ec) {
|
||||
if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) {
|
||||
ec = capture_errno();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
|
||||
error_code& ec) {
|
||||
#if !defined(_LIBCPP_USE_UTIMENSAT)
|
||||
return posix_utimes(p, TS, ec);
|
||||
#else
|
||||
return posix_utimensat(p, TS, ec);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // end namespace detail
|
||||
|
||||
_LIBCPP_END_NAMESPACE_FILESYSTEM
|
||||
|
||||
#endif // FILESYSTEM_COMMON_H
|
4
third_party/libcxx/libcxx.mk
vendored
4
third_party/libcxx/libcxx.mk
vendored
|
@ -75,6 +75,7 @@ THIRD_PARTY_LIBCXX_A_HDRS = \
|
|||
third_party/libcxx/execution \
|
||||
third_party/libcxx/experimental/__config \
|
||||
third_party/libcxx/filesystem \
|
||||
third_party/libcxx/filesystem_common.hh \
|
||||
third_party/libcxx/forward_list \
|
||||
third_party/libcxx/fstream \
|
||||
third_party/libcxx/functional \
|
||||
|
@ -144,6 +145,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC = \
|
|||
third_party/libcxx/chrono.cc \
|
||||
third_party/libcxx/condition_variable.cc \
|
||||
third_party/libcxx/condition_variable_destructor.cc \
|
||||
third_party/libcxx/directory_iterator.cc \
|
||||
third_party/libcxx/exception.cc \
|
||||
third_party/libcxx/functional.cc \
|
||||
third_party/libcxx/future.cc \
|
||||
|
@ -157,6 +159,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC = \
|
|||
third_party/libcxx/memory.cc \
|
||||
third_party/libcxx/mutex.cc \
|
||||
third_party/libcxx/new.cc \
|
||||
third_party/libcxx/operations.cc \
|
||||
third_party/libcxx/optional.cc \
|
||||
third_party/libcxx/random.cc \
|
||||
third_party/libcxx/regex.cc \
|
||||
|
@ -188,6 +191,7 @@ THIRD_PARTY_LIBCXX_A_DIRECTDEPS = \
|
|||
LIBC_NEXGEN32E \
|
||||
LIBC_RUNTIME \
|
||||
LIBC_STDIO \
|
||||
LIBC_SOCK \
|
||||
LIBC_STR \
|
||||
LIBC_SYSV \
|
||||
LIBC_TIME \
|
||||
|
|
1785
third_party/libcxx/operations.cc
vendored
Normal file
1785
third_party/libcxx/operations.cc
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue