aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/cxxsupp/libcxx/src/filesystem
diff options
context:
space:
mode:
authorhiddenpath <hiddenpath@yandex-team.com>2024-02-21 23:16:42 +0300
committerhiddenpath <hiddenpath@yandex-team.com>2024-02-21 23:33:25 +0300
commit9052eb5cc304b8da8885fc4e3364ebddc16945f3 (patch)
tree3c252f6161dd0745c7732d74c9304c000645ab47 /contrib/libs/cxxsupp/libcxx/src/filesystem
parentf5eb715f103692e7c7536e13bef3f281fd78e5e7 (diff)
downloadydb-9052eb5cc304b8da8885fc4e3364ebddc16945f3.tar.gz
Update libcxx to llvmorg-17.0.6
Update libcxx to llvmorg-17.0.6 c871ef572c71b4fef22d4a9e65bcebc57e625aea
Diffstat (limited to 'contrib/libs/cxxsupp/libcxx/src/filesystem')
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/directory_entry.cpp74
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/directory_iterator.cpp18
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/error.h238
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/file_descriptor.h298
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_clock.cpp64
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_error.cpp37
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/format_string.h77
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/operations.cpp1166
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/path.cpp460
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/path_parser.h372
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/posix_compat.h124
-rw-r--r--contrib/libs/cxxsupp/libcxx/src/filesystem/time_utils.h (renamed from contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_common.h)382
12 files changed, 1786 insertions, 1524 deletions
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/directory_entry.cpp b/contrib/libs/cxxsupp/libcxx/src/filesystem/directory_entry.cpp
new file mode 100644
index 0000000000..4910d390d1
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/directory_entry.cpp
@@ -0,0 +1,74 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__config>
+#include <cstdint>
+#include <filesystem>
+#include <system_error>
+
+#include "file_descriptor.h"
+#include "posix_compat.h"
+#include "time_utils.h"
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+error_code directory_entry::__do_refresh() noexcept {
+ __data_.__reset();
+ error_code failure_ec;
+
+ detail::StatT full_st;
+ file_status st = detail::posix_lstat(__p_, full_st, &failure_ec);
+ if (!status_known(st)) {
+ __data_.__reset();
+ return failure_ec;
+ }
+
+ if (!_VSTD_FS::exists(st) || !_VSTD_FS::is_symlink(st)) {
+ __data_.__cache_type_ = directory_entry::_RefreshNonSymlink;
+ __data_.__type_ = st.type();
+ __data_.__non_sym_perms_ = st.permissions();
+ } else { // we have a symlink
+ __data_.__sym_perms_ = st.permissions();
+ // Get the information about the linked entity.
+ // Ignore errors from stat, since we don't want errors regarding symlink
+ // resolution to be reported to the user.
+ error_code ignored_ec;
+ st = detail::posix_stat(__p_, full_st, &ignored_ec);
+
+ __data_.__type_ = st.type();
+ __data_.__non_sym_perms_ = st.permissions();
+
+ // If we failed to resolve the link, then only partially populate the
+ // cache.
+ if (!status_known(st)) {
+ __data_.__cache_type_ = directory_entry::_RefreshSymlinkUnresolved;
+ return error_code{};
+ }
+ // Otherwise, we resolved the link, potentially as not existing.
+ // That's OK.
+ __data_.__cache_type_ = directory_entry::_RefreshSymlink;
+ }
+
+ if (_VSTD_FS::is_regular_file(st))
+ __data_.__size_ = static_cast<uintmax_t>(full_st.st_size);
+
+ if (_VSTD_FS::exists(st)) {
+ __data_.__nlink_ = static_cast<uintmax_t>(full_st.st_nlink);
+
+ // Attempt to extract the mtime, and fail if it's not representable using
+ // file_time_type. For now we ignore the error, as we'll report it when
+ // the value is actually used.
+ error_code ignored_ec;
+ __data_.__write_time_ =
+ detail::__extract_last_write_time(__p_, full_st, &ignored_ec);
+ }
+
+ return failure_ec;
+}
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/directory_iterator.cpp b/contrib/libs/cxxsupp/libcxx/src/filesystem/directory_iterator.cpp
index 8d069cc080..151fb2fb62 100644
--- a/contrib/libs/cxxsupp/libcxx/src/filesystem/directory_iterator.cpp
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/directory_iterator.cpp
@@ -11,8 +11,18 @@
#include <errno.h>
#include <filesystem>
#include <stack>
+#include <utility>
-#include "filesystem_common.h"
+#include "error.h"
+#include "file_descriptor.h"
+
+#if defined(_LIBCPP_WIN32API)
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <windows.h>
+#else
+# include <dirent.h> // for DIR & friends
+#endif
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
@@ -182,7 +192,7 @@ directory_iterator::directory_iterator(const path& p, error_code* ec,
}
directory_iterator& directory_iterator::__increment(error_code* ec) {
- _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator");
+ _LIBCPP_ASSERT_UNCATEGORIZED(__imp_, "Attempting to increment an invalid iterator");
ErrorHandler<void> err("directory_iterator::operator++()", ec);
error_code m_ec;
@@ -196,7 +206,7 @@ directory_iterator& directory_iterator::__increment(error_code* ec) {
}
directory_entry const& directory_iterator::__dereference() const {
- _LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator");
+ _LIBCPP_ASSERT_UNCATEGORIZED(__imp_, "Attempting to dereference an invalid iterator");
return __imp_->__entry_;
}
@@ -225,7 +235,7 @@ recursive_directory_iterator::recursive_directory_iterator(
}
void recursive_directory_iterator::__pop(error_code* ec) {
- _LIBCPP_ASSERT(__imp_, "Popping the end iterator");
+ _LIBCPP_ASSERT_UNCATEGORIZED(__imp_, "Popping the end iterator");
if (ec)
ec->clear();
__imp_->__stack_.pop();
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/error.h b/contrib/libs/cxxsupp/libcxx/src/filesystem/error.h
new file mode 100644
index 0000000000..965e0eadcd
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/error.h
@@ -0,0 +1,238 @@
+//===----------------------------------------------------------------------===////
+//
+// 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_ERROR_H
+#define FILESYSTEM_ERROR_H
+
+#include <__assert>
+#include <__config>
+#include <cerrno>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdint>
+#include <filesystem>
+#include <string>
+#include <system_error>
+#include <utility> // __libcpp_unreachable
+
+#include "format_string.h"
+
+#if defined(_LIBCPP_WIN32API)
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <windows.h> // ERROR_* macros
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+namespace detail {
+
+#if defined(_LIBCPP_WIN32API)
+
+inline errc __win_err_to_errc(int err) {
+ constexpr struct {
+ DWORD win;
+ errc errc;
+ } win_error_mapping[] = {
+ {ERROR_ACCESS_DENIED, errc::permission_denied},
+ {ERROR_ALREADY_EXISTS, errc::file_exists},
+ {ERROR_BAD_NETPATH, errc::no_such_file_or_directory},
+ {ERROR_BAD_PATHNAME, errc::no_such_file_or_directory},
+ {ERROR_BAD_UNIT, errc::no_such_device},
+ {ERROR_BROKEN_PIPE, errc::broken_pipe},
+ {ERROR_BUFFER_OVERFLOW, errc::filename_too_long},
+ {ERROR_BUSY, errc::device_or_resource_busy},
+ {ERROR_BUSY_DRIVE, errc::device_or_resource_busy},
+ {ERROR_CANNOT_MAKE, errc::permission_denied},
+ {ERROR_CANTOPEN, errc::io_error},
+ {ERROR_CANTREAD, errc::io_error},
+ {ERROR_CANTWRITE, errc::io_error},
+ {ERROR_CURRENT_DIRECTORY, errc::permission_denied},
+ {ERROR_DEV_NOT_EXIST, errc::no_such_device},
+ {ERROR_DEVICE_IN_USE, errc::device_or_resource_busy},
+ {ERROR_DIR_NOT_EMPTY, errc::directory_not_empty},
+ {ERROR_DIRECTORY, errc::invalid_argument},
+ {ERROR_DISK_FULL, errc::no_space_on_device},
+ {ERROR_FILE_EXISTS, errc::file_exists},
+ {ERROR_FILE_NOT_FOUND, errc::no_such_file_or_directory},
+ {ERROR_HANDLE_DISK_FULL, errc::no_space_on_device},
+ {ERROR_INVALID_ACCESS, errc::permission_denied},
+ {ERROR_INVALID_DRIVE, errc::no_such_device},
+ {ERROR_INVALID_FUNCTION, errc::function_not_supported},
+ {ERROR_INVALID_HANDLE, errc::invalid_argument},
+ {ERROR_INVALID_NAME, errc::no_such_file_or_directory},
+ {ERROR_INVALID_PARAMETER, errc::invalid_argument},
+ {ERROR_LOCK_VIOLATION, errc::no_lock_available},
+ {ERROR_LOCKED, errc::no_lock_available},
+ {ERROR_NEGATIVE_SEEK, errc::invalid_argument},
+ {ERROR_NOACCESS, errc::permission_denied},
+ {ERROR_NOT_ENOUGH_MEMORY, errc::not_enough_memory},
+ {ERROR_NOT_READY, errc::resource_unavailable_try_again},
+ {ERROR_NOT_SAME_DEVICE, errc::cross_device_link},
+ {ERROR_NOT_SUPPORTED, errc::not_supported},
+ {ERROR_OPEN_FAILED, errc::io_error},
+ {ERROR_OPEN_FILES, errc::device_or_resource_busy},
+ {ERROR_OPERATION_ABORTED, errc::operation_canceled},
+ {ERROR_OUTOFMEMORY, errc::not_enough_memory},
+ {ERROR_PATH_NOT_FOUND, errc::no_such_file_or_directory},
+ {ERROR_READ_FAULT, errc::io_error},
+ {ERROR_REPARSE_TAG_INVALID, errc::invalid_argument},
+ {ERROR_RETRY, errc::resource_unavailable_try_again},
+ {ERROR_SEEK, errc::io_error},
+ {ERROR_SHARING_VIOLATION, errc::permission_denied},
+ {ERROR_TOO_MANY_OPEN_FILES, errc::too_many_files_open},
+ {ERROR_WRITE_FAULT, errc::io_error},
+ {ERROR_WRITE_PROTECT, errc::permission_denied},
+ };
+
+ for (const auto &pair : win_error_mapping)
+ if (pair.win == static_cast<DWORD>(err))
+ return pair.errc;
+ return errc::invalid_argument;
+}
+
+#endif // _LIBCPP_WIN32API
+
+inline error_code capture_errno() {
+ _LIBCPP_ASSERT_UNCATEGORIZED(errno != 0, "Expected errno to be non-zero");
+ return error_code(errno, generic_category());
+}
+
+#if defined(_LIBCPP_WIN32API)
+inline error_code make_windows_error(int err) {
+ return make_error_code(__win_err_to_errc(err));
+}
+#endif
+
+template <class T>
+T error_value();
+template <>
+inline _LIBCPP_CONSTEXPR_SINCE_CXX14 void error_value<void>() {}
+template <>
+inline bool error_value<bool>() {
+ return false;
+}
+#if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
+template <>
+inline size_t error_value<size_t>() {
+ return size_t(-1);
+}
+#endif
+template <>
+inline uintmax_t error_value<uintmax_t>() {
+ return uintmax_t(-1);
+}
+template <>
+inline _LIBCPP_CONSTEXPR_SINCE_CXX14 file_time_type error_value<file_time_type>() {
+ return file_time_type::min();
+}
+template <>
+inline 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& ec) const {
+ if (ec_) {
+ *ec_ = ec;
+ return error_value<T>();
+ }
+ string what = string("in ") + func_name_;
+ switch (bool(p1_) + bool(p2_)) {
+ case 0:
+ __throw_filesystem_error(what, ec);
+ case 1:
+ __throw_filesystem_error(what, *p1_, ec);
+ case 2:
+ __throw_filesystem_error(what, *p1_, *p2_, ec);
+ }
+ __libcpp_unreachable();
+ }
+
+ _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 0)
+ void report_impl(const error_code& ec, const char* msg, va_list ap) const {
+ if (ec_) {
+ *ec_ = ec;
+ return;
+ }
+ string what =
+ string("in ") + func_name_ + ": " + detail::vformat_string(msg, ap);
+ switch (bool(p1_) + bool(p2_)) {
+ case 0:
+ __throw_filesystem_error(what, ec);
+ case 1:
+ __throw_filesystem_error(what, *p1_, ec);
+ case 2:
+ __throw_filesystem_error(what, *p1_, *p2_, ec);
+ }
+ __libcpp_unreachable();
+ }
+
+ _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
+ T report(const error_code& ec, const char* msg, ...) const {
+ va_list ap;
+ va_start(ap, msg);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ try {
+#endif // _LIBCPP_HAS_NO_EXCEPTIONS
+ report_impl(ec, msg, ap);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ } catch (...) {
+ va_end(ap);
+ throw;
+ }
+#endif // _LIBCPP_HAS_NO_EXCEPTIONS
+ va_end(ap);
+ return error_value<T>();
+ }
+
+ T report(errc const& err) const {
+ return report(make_error_code(err));
+ }
+
+ _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
+ T report(errc const& err, const char* msg, ...) const {
+ va_list ap;
+ va_start(ap, msg);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ try {
+#endif // _LIBCPP_HAS_NO_EXCEPTIONS
+ report_impl(make_error_code(err), msg, ap);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ } catch (...) {
+ va_end(ap);
+ throw;
+ }
+#endif // _LIBCPP_HAS_NO_EXCEPTIONS
+ va_end(ap);
+ return error_value<T>();
+ }
+
+private:
+ ErrorHandler(ErrorHandler const&) = delete;
+ ErrorHandler& operator=(ErrorHandler const&) = delete;
+};
+
+} // end namespace detail
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
+
+#endif // FILESYSTEM_ERROR_H
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/file_descriptor.h b/contrib/libs/cxxsupp/libcxx/src/filesystem/file_descriptor.h
new file mode 100644
index 0000000000..d3a668fa2e
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/file_descriptor.h
@@ -0,0 +1,298 @@
+//===----------------------------------------------------------------------===////
+//
+// 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_FILE_DESCRIPTOR_H
+#define FILESYSTEM_FILE_DESCRIPTOR_H
+
+#include <__config>
+#include <cstdint>
+#include <filesystem>
+#include <string_view>
+#include <system_error>
+#include <utility>
+
+#include "error.h"
+#include "posix_compat.h"
+#include "time_utils.h"
+
+#if defined(_LIBCPP_WIN32API)
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <windows.h>
+#else
+# include <dirent.h> // for DIR & friends
+# include <fcntl.h> // values for fchmodat
+# include <sys/stat.h>
+# include <sys/statvfs.h>
+# include <unistd.h>
+#endif // defined(_LIBCPP_WIN32API)
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+namespace detail {
+
+#if !defined(_LIBCPP_WIN32API)
+
+#if defined(DT_BLK)
+template <class DirEntT, class = decltype(DirEntT::d_type)>
+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>
+file_type get_file_type(DirEntT*, long) {
+ return file_type::none;
+}
+
+inline 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 // _LIBCPP_WIN32API
+
+inline file_type get_file_type(const WIN32_FIND_DATAW& data) {
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
+ data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
+ return file_type::symlink;
+ if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ return file_type::directory;
+ return file_type::regular;
+}
+inline uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {
+ return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;
+}
+inline file_time_type get_write_time(const WIN32_FIND_DATAW& 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 // !_LIBCPP_WIN32API
+
+// POSIX HELPERS
+
+using value_type = path::value_type;
+using string_type = path::string_type;
+
+struct FileDescriptor {
+ const path& name;
+ int fd = -1;
+ StatT m_stat;
+ file_status m_status;
+
+ template <class... Args>
+ static FileDescriptor create(const path* p, error_code& ec, Args... args) {
+ ec.clear();
+ int fd;
+#ifdef _LIBCPP_WIN32API
+ // TODO: most of the filesystem implementation uses native Win32 calls
+ // (mostly via posix_compat.h). However, here we use the C-runtime APIs to
+ // open a file, because we subsequently pass the C-runtime fd to
+ // `std::[io]fstream::__open(int fd)` in order to implement copy_file.
+ //
+ // Because we're calling the windows C-runtime, win32 error codes are
+ // translated into C error numbers by the C runtime, and returned in errno,
+ // rather than being accessible directly via GetLastError.
+ //
+ // Ideally copy_file should be calling the Win32 CopyFile2 function, which
+ // works on paths, not open files -- at which point this FileDescriptor type
+ // will no longer be needed on windows at all.
+ fd = ::_wopen(p->c_str(), args...);
+#else
+ fd = open(p->c_str(), args...);
+#endif
+
+ if (fd == -1) {
+ ec = capture_errno();
+ return FileDescriptor{p};
+ }
+ return FileDescriptor(p, fd);
+ }
+
+ template <class... Args>
+ static FileDescriptor create_with_status(const path* p, error_code& ec,
+ Args... args) {
+ FileDescriptor fd = create(p, ec, args...);
+ if (!ec)
+ fd.refresh_status(ec);
+
+ return fd;
+ }
+
+ file_status get_status() const { return m_status; }
+ StatT const& get_stat() const { return m_stat; }
+
+ bool status_known() const { return _VSTD_FS::status_known(m_status); }
+
+ file_status refresh_status(error_code& ec);
+
+ void close() noexcept {
+ if (fd != -1) {
+#ifdef _LIBCPP_WIN32API
+ ::_close(fd);
+#else
+ ::close(fd);
+#endif
+ // FIXME: shouldn't this return an error_code?
+ }
+ fd = -1;
+ }
+
+ FileDescriptor(FileDescriptor&& other)
+ : name(other.name), fd(other.fd), m_stat(other.m_stat),
+ m_status(other.m_status) {
+ other.fd = -1;
+ other.m_status = file_status{};
+ }
+
+ ~FileDescriptor() { close(); }
+
+ FileDescriptor(FileDescriptor const&) = delete;
+ FileDescriptor& operator=(FileDescriptor const&) = delete;
+
+private:
+ explicit FileDescriptor(const path* p, int descriptor = -1) : name(*p), fd(descriptor) {}
+};
+
+inline perms posix_get_perms(const StatT& st) noexcept {
+ return static_cast<perms>(st.st_mode) & perms::mask;
+}
+
+inline file_status create_file_status(error_code& m_ec, path const& p,
+ const StatT& path_stat, error_code* ec) {
+ if (ec)
+ *ec = m_ec;
+ if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
+ return file_status(file_type::not_found);
+ } else if (m_ec) {
+ 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);
+ }
+ // else
+
+ file_status fs_tmp;
+ auto const mode = path_stat.st_mode;
+ if (S_ISLNK(mode))
+ fs_tmp.type(file_type::symlink);
+ else if (S_ISREG(mode))
+ fs_tmp.type(file_type::regular);
+ else if (S_ISDIR(mode))
+ fs_tmp.type(file_type::directory);
+ else if (S_ISBLK(mode))
+ fs_tmp.type(file_type::block);
+ else if (S_ISCHR(mode))
+ fs_tmp.type(file_type::character);
+ else if (S_ISFIFO(mode))
+ fs_tmp.type(file_type::fifo);
+ else if (S_ISSOCK(mode))
+ fs_tmp.type(file_type::socket);
+ else
+ fs_tmp.type(file_type::unknown);
+
+ fs_tmp.permissions(detail::posix_get_perms(path_stat));
+ return fs_tmp;
+}
+
+inline file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) {
+ error_code m_ec;
+ if (detail::stat(p.c_str(), &path_stat) == -1)
+ m_ec = detail::capture_errno();
+ return create_file_status(m_ec, p, path_stat, ec);
+}
+
+inline file_status posix_stat(path const& p, error_code* ec) {
+ StatT path_stat;
+ return posix_stat(p, path_stat, ec);
+}
+
+inline file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) {
+ error_code m_ec;
+ if (detail::lstat(p.c_str(), &path_stat) == -1)
+ m_ec = detail::capture_errno();
+ return create_file_status(m_ec, p, path_stat, ec);
+}
+
+inline file_status posix_lstat(path const& p, error_code* ec) {
+ StatT path_stat;
+ return posix_lstat(p, path_stat, ec);
+}
+
+// http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html
+inline bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) {
+ if (detail::ftruncate(fd.fd, to_size) == -1) {
+ ec = capture_errno();
+ return true;
+ }
+ ec.clear();
+ return false;
+}
+
+inline bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) {
+ if (detail::fchmod(fd.fd, st.st_mode) == -1) {
+ ec = capture_errno();
+ return true;
+ }
+ ec.clear();
+ return false;
+}
+
+inline bool stat_equivalent(const StatT& st1, const StatT& st2) {
+ return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
+}
+
+inline file_status FileDescriptor::refresh_status(error_code& ec) {
+ // FD must be open and good.
+ m_status = file_status{};
+ m_stat = {};
+ error_code m_ec;
+ if (detail::fstat(fd, &m_stat) == -1)
+ m_ec = capture_errno();
+ m_status = create_file_status(m_ec, name, m_stat, &ec);
+ return m_status;
+}
+
+} // end namespace detail
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
+
+#endif // FILESYSTEM_FILE_DESCRIPTOR_H
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_clock.cpp b/contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_clock.cpp
new file mode 100644
index 0000000000..fbb19ac68d
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_clock.cpp
@@ -0,0 +1,64 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__config>
+#include <chrono>
+#include <filesystem>
+#include <time.h>
+
+#if defined(_LIBCPP_WIN32API)
+# include "time_utils.h"
+#endif
+
+#if defined(_LIBCPP_WIN32API)
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <windows.h>
+#endif
+
+#if __has_include(<unistd.h>)
+# include <unistd.h> // _POSIX_TIMERS
+#endif
+
+#if __has_include(<sys/time.h>)
+# include <sys/time.h> // for gettimeofday and timeval
+#endif
+
+#if defined(__APPLE__) || defined (__gnu_hurd__) || (defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0)
+# define _LIBCPP_HAS_CLOCK_GETTIME
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+const bool _FilesystemClock::is_steady;
+
+_FilesystemClock::time_point _FilesystemClock::now() noexcept {
+ typedef chrono::duration<rep> __secs;
+#if defined(_LIBCPP_WIN32API)
+ typedef chrono::duration<rep, nano> __nsecs;
+ FILETIME time;
+ GetSystemTimeAsFileTime(&time);
+ detail::TimeSpec tp = detail::filetime_to_timespec(time);
+ return time_point(__secs(tp.tv_sec) +
+ chrono::duration_cast<duration>(__nsecs(tp.tv_nsec)));
+#elif defined(_LIBCPP_HAS_CLOCK_GETTIME)
+ typedef chrono::duration<rep, nano> __nsecs;
+ struct timespec tp;
+ if (0 != clock_gettime(CLOCK_REALTIME, &tp))
+ __throw_system_error(errno, "clock_gettime(CLOCK_REALTIME) failed");
+ return time_point(__secs(tp.tv_sec) +
+ chrono::duration_cast<duration>(__nsecs(tp.tv_nsec)));
+#else
+ typedef chrono::duration<rep, micro> __microsecs;
+ timeval tv;
+ gettimeofday(&tv, 0);
+ return time_point(__secs(tv.tv_sec) + __microsecs(tv.tv_usec));
+#endif
+}
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_error.cpp b/contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_error.cpp
new file mode 100644
index 0000000000..5faed3b89e
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_error.cpp
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__config>
+#include <__utility/unreachable.h>
+#include <filesystem>
+#include <system_error>
+
+#include "format_string.h"
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+filesystem_error::~filesystem_error() {}
+
+void filesystem_error::__create_what(int __num_paths) {
+ const char* derived_what = system_error::what();
+ __storage_->__what_ = [&]() -> string {
+ switch (__num_paths) {
+ case 0:
+ return detail::format_string("filesystem error: %s", derived_what);
+ case 1:
+ return detail::format_string("filesystem error: %s [" PATH_CSTR_FMT "]",
+ derived_what, path1().c_str());
+ case 2:
+ return detail::format_string("filesystem error: %s [" PATH_CSTR_FMT "] [" PATH_CSTR_FMT "]",
+ derived_what, path1().c_str(), path2().c_str());
+ }
+ __libcpp_unreachable();
+ }();
+}
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/format_string.h b/contrib/libs/cxxsupp/libcxx/src/filesystem/format_string.h
new file mode 100644
index 0000000000..215d42421b
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/format_string.h
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===////
+//
+// 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_FORMAT_STRING_H
+#define FILESYSTEM_FORMAT_STRING_H
+
+#include <__assert>
+#include <__config>
+#include <array>
+#include <cstdarg>
+#include <cstddef>
+#include <cstdio>
+#include <string>
+
+#if defined(_LIBCPP_WIN32API)
+# define PATHSTR(x) (L##x)
+# define PATH_CSTR_FMT "\"%ls\""
+#else
+# define PATHSTR(x) (x)
+# define PATH_CSTR_FMT "\"%s\""
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+namespace detail {
+
+inline _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 0) string vformat_string(const char* msg, va_list ap) {
+ array<char, 256> buf;
+
+ va_list apcopy;
+ va_copy(apcopy, ap);
+ int ret = ::vsnprintf(buf.data(), buf.size(), msg, apcopy);
+ va_end(apcopy);
+
+ string result;
+ if (static_cast<size_t>(ret) < buf.size()) {
+ result.assign(buf.data(), static_cast<size_t>(ret));
+ } else {
+ // 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_t 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, ap);
+ _LIBCPP_ASSERT_UNCATEGORIZED(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
+ }
+ return result;
+}
+
+inline _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) string format_string(const char* msg, ...) {
+ string ret;
+ va_list ap;
+ va_start(ap, msg);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ try {
+#endif // _LIBCPP_HAS_NO_EXCEPTIONS
+ ret = detail::vformat_string(msg, ap);
+#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+ } catch (...) {
+ va_end(ap);
+ throw;
+ }
+#endif // _LIBCPP_HAS_NO_EXCEPTIONS
+ va_end(ap);
+ return ret;
+}
+
+} // end namespace detail
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
+
+#endif // FILESYSTEM_FORMAT_STRING_H
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/operations.cpp b/contrib/libs/cxxsupp/libcxx/src/filesystem/operations.cpp
index f9b74120e0..61f2cc572e 100644
--- a/contrib/libs/cxxsupp/libcxx/src/filesystem/operations.cpp
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/operations.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include <__assert>
+#include <__config>
#include <__utility/unreachable.h>
#include <array>
#include <climits>
@@ -17,9 +18,11 @@
#include <type_traits>
#include <vector>
-#include "filesystem_common.h"
-
+#include "error.h"
+#include "file_descriptor.h"
+#include "path_parser.h"
#include "posix_compat.h"
+#include "time_utils.h"
#if defined(_LIBCPP_WIN32API)
# define WIN32_LEAN_AND_MEAN
@@ -45,599 +48,12 @@
# define _LIBCPP_FILESYSTEM_USE_FSTREAM
#endif
-#if !defined(CLOCK_REALTIME) && !defined(_LIBCPP_WIN32API)
-# include <sys/time.h> // for gettimeofday and timeval
-#endif
-
#if defined(__ELF__) && defined(_LIBCPP_LINK_RT_LIB)
# pragma comment(lib, "rt")
#endif
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
-namespace {
-
-bool isSeparator(path::value_type C) {
- if (C == '/')
- return true;
-#if defined(_LIBCPP_WIN32API)
- if (C == '\\')
- return true;
-#endif
- return false;
-}
-
-bool isDriveLetter(path::value_type C) {
- return (C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z');
-}
-
-namespace parser {
-
-using string_view_t = path::__string_view;
-using string_view_pair = pair<string_view_t, string_view_t>;
-using PosPtr = path::value_type const*;
-
-struct PathParser {
- enum ParserState : unsigned char {
- // Zero is a special sentinel value used by default constructed iterators.
- PS_BeforeBegin = path::iterator::_BeforeBegin,
- PS_InRootName = path::iterator::_InRootName,
- PS_InRootDir = path::iterator::_InRootDir,
- PS_InFilenames = path::iterator::_InFilenames,
- PS_InTrailingSep = path::iterator::_InTrailingSep,
- PS_AtEnd = path::iterator::_AtEnd
- };
-
- const string_view_t Path;
- string_view_t RawEntry;
- ParserState State;
-
-private:
- PathParser(string_view_t P, ParserState State) noexcept : Path(P),
- State(State) {}
-
-public:
- PathParser(string_view_t P, string_view_t E, unsigned char S)
- : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
- // S cannot be '0' or PS_BeforeBegin.
- }
-
- static PathParser CreateBegin(string_view_t P) noexcept {
- PathParser PP(P, PS_BeforeBegin);
- PP.increment();
- return PP;
- }
-
- static PathParser CreateEnd(string_view_t P) noexcept {
- PathParser PP(P, PS_AtEnd);
- return PP;
- }
-
- PosPtr peek() const noexcept {
- auto TkEnd = getNextTokenStartPos();
- auto End = getAfterBack();
- return TkEnd == End ? nullptr : TkEnd;
- }
-
- void increment() noexcept {
- const PosPtr End = getAfterBack();
- const PosPtr Start = getNextTokenStartPos();
- if (Start == End)
- return makeState(PS_AtEnd);
-
- switch (State) {
- case PS_BeforeBegin: {
- PosPtr TkEnd = consumeRootName(Start, End);
- if (TkEnd)
- return makeState(PS_InRootName, Start, TkEnd);
- }
- _LIBCPP_FALLTHROUGH();
- case PS_InRootName: {
- PosPtr TkEnd = consumeAllSeparators(Start, End);
- if (TkEnd)
- return makeState(PS_InRootDir, Start, TkEnd);
- else
- return makeState(PS_InFilenames, Start, consumeName(Start, End));
- }
- case PS_InRootDir:
- return makeState(PS_InFilenames, Start, consumeName(Start, End));
-
- case PS_InFilenames: {
- PosPtr SepEnd = consumeAllSeparators(Start, End);
- if (SepEnd != End) {
- PosPtr TkEnd = consumeName(SepEnd, End);
- if (TkEnd)
- return makeState(PS_InFilenames, SepEnd, TkEnd);
- }
- return makeState(PS_InTrailingSep, Start, SepEnd);
- }
-
- case PS_InTrailingSep:
- return makeState(PS_AtEnd);
-
- case PS_AtEnd:
- __libcpp_unreachable();
- }
- }
-
- void decrement() noexcept {
- const PosPtr REnd = getBeforeFront();
- const PosPtr RStart = getCurrentTokenStartPos() - 1;
- if (RStart == REnd) // we're decrementing the begin
- return makeState(PS_BeforeBegin);
-
- switch (State) {
- case PS_AtEnd: {
- // Try to consume a trailing separator or root directory first.
- if (PosPtr SepEnd = consumeAllSeparators(RStart, REnd)) {
- if (SepEnd == REnd)
- return makeState(PS_InRootDir, Path.data(), RStart + 1);
- PosPtr TkStart = consumeRootName(SepEnd, REnd);
- if (TkStart == REnd)
- return makeState(PS_InRootDir, RStart, RStart + 1);
- return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1);
- } else {
- PosPtr TkStart = consumeRootName(RStart, REnd);
- if (TkStart == REnd)
- return makeState(PS_InRootName, TkStart + 1, RStart + 1);
- TkStart = consumeName(RStart, REnd);
- return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
- }
- }
- case PS_InTrailingSep:
- return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1,
- RStart + 1);
- case PS_InFilenames: {
- PosPtr SepEnd = consumeAllSeparators(RStart, REnd);
- if (SepEnd == REnd)
- return makeState(PS_InRootDir, Path.data(), RStart + 1);
- PosPtr TkStart = consumeRootName(SepEnd ? SepEnd : RStart, REnd);
- if (TkStart == REnd) {
- if (SepEnd)
- return makeState(PS_InRootDir, SepEnd + 1, RStart + 1);
- return makeState(PS_InRootName, TkStart + 1, RStart + 1);
- }
- TkStart = consumeName(SepEnd, REnd);
- return makeState(PS_InFilenames, TkStart + 1, SepEnd + 1);
- }
- case PS_InRootDir:
- return makeState(PS_InRootName, Path.data(), RStart + 1);
- case PS_InRootName:
- case PS_BeforeBegin:
- __libcpp_unreachable();
- }
- }
-
- /// \brief Return a view with the "preferred representation" of the current
- /// element. For example trailing separators are represented as a '.'
- string_view_t operator*() const noexcept {
- switch (State) {
- case PS_BeforeBegin:
- case PS_AtEnd:
- return PATHSTR("");
- case PS_InRootDir:
- if (RawEntry[0] == '\\')
- return PATHSTR("\\");
- else
- return PATHSTR("/");
- case PS_InTrailingSep:
- return PATHSTR("");
- case PS_InRootName:
- case PS_InFilenames:
- return RawEntry;
- }
- __libcpp_unreachable();
- }
-
- explicit operator bool() const noexcept {
- return State != PS_BeforeBegin && State != PS_AtEnd;
- }
-
- PathParser& operator++() noexcept {
- increment();
- return *this;
- }
-
- PathParser& operator--() noexcept {
- decrement();
- return *this;
- }
-
- bool atEnd() const noexcept {
- return State == PS_AtEnd;
- }
-
- bool inRootDir() const noexcept {
- return State == PS_InRootDir;
- }
-
- bool inRootName() const noexcept {
- return State == PS_InRootName;
- }
-
- bool inRootPath() const noexcept {
- return inRootName() || inRootDir();
- }
-
-private:
- void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
- State = NewState;
- RawEntry = string_view_t(Start, End - Start);
- }
- void makeState(ParserState NewState) noexcept {
- State = NewState;
- RawEntry = {};
- }
-
- PosPtr getAfterBack() const noexcept { return Path.data() + Path.size(); }
-
- PosPtr getBeforeFront() const noexcept { return Path.data() - 1; }
-
- /// \brief Return a pointer to the first character after the currently
- /// lexed element.
- PosPtr getNextTokenStartPos() const noexcept {
- switch (State) {
- case PS_BeforeBegin:
- return Path.data();
- case PS_InRootName:
- case PS_InRootDir:
- case PS_InFilenames:
- return &RawEntry.back() + 1;
- case PS_InTrailingSep:
- case PS_AtEnd:
- return getAfterBack();
- }
- __libcpp_unreachable();
- }
-
- /// \brief Return a pointer to the first character in the currently lexed
- /// element.
- PosPtr getCurrentTokenStartPos() const noexcept {
- switch (State) {
- case PS_BeforeBegin:
- case PS_InRootName:
- return &Path.front();
- case PS_InRootDir:
- case PS_InFilenames:
- case PS_InTrailingSep:
- return &RawEntry.front();
- case PS_AtEnd:
- return &Path.back() + 1;
- }
- __libcpp_unreachable();
- }
-
- // Consume all consecutive separators.
- PosPtr consumeAllSeparators(PosPtr P, PosPtr End) const noexcept {
- if (P == nullptr || P == End || !isSeparator(*P))
- return nullptr;
- const int Inc = P < End ? 1 : -1;
- P += Inc;
- while (P != End && isSeparator(*P))
- P += Inc;
- return P;
- }
-
- // Consume exactly N separators, or return nullptr.
- PosPtr consumeNSeparators(PosPtr P, PosPtr End, int N) const noexcept {
- PosPtr Ret = consumeAllSeparators(P, End);
- if (Ret == nullptr)
- return nullptr;
- if (P < End) {
- if (Ret == P + N)
- return Ret;
- } else {
- if (Ret == P - N)
- return Ret;
- }
- return nullptr;
- }
-
- PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
- PosPtr Start = P;
- if (P == nullptr || P == End || isSeparator(*P))
- return nullptr;
- const int Inc = P < End ? 1 : -1;
- P += Inc;
- while (P != End && !isSeparator(*P))
- P += Inc;
- if (P == End && Inc < 0) {
- // Iterating backwards and consumed all the rest of the input.
- // Check if the start of the string would have been considered
- // a root name.
- PosPtr RootEnd = consumeRootName(End + 1, Start);
- if (RootEnd)
- return RootEnd - 1;
- }
- return P;
- }
-
- PosPtr consumeDriveLetter(PosPtr P, PosPtr End) const noexcept {
- if (P == End)
- return nullptr;
- if (P < End) {
- if (P + 1 == End || !isDriveLetter(P[0]) || P[1] != ':')
- return nullptr;
- return P + 2;
- } else {
- if (P - 1 == End || !isDriveLetter(P[-1]) || P[0] != ':')
- return nullptr;
- return P - 2;
- }
- }
-
- PosPtr consumeNetworkRoot(PosPtr P, PosPtr End) const noexcept {
- if (P == End)
- return nullptr;
- if (P < End)
- return consumeName(consumeNSeparators(P, End, 2), End);
- else
- return consumeNSeparators(consumeName(P, End), End, 2);
- }
-
-#if defined(_LIBCPP_WIN32API)
- PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
- if (PosPtr Ret = consumeDriveLetter(P, End))
- return Ret;
- if (PosPtr Ret = consumeNetworkRoot(P, End))
- return Ret;
- return nullptr;
- }
-#else
- PosPtr consumeRootName(PosPtr /*P*/, PosPtr /*End*/) const noexcept {
- return nullptr;
- }
-#endif
-};
-
-string_view_pair separate_filename(string_view_t const& s) {
- if (s == PATHSTR(".") || s == PATHSTR("..") || s.empty())
- return string_view_pair{s, PATHSTR("")};
- auto pos = s.find_last_of('.');
- if (pos == string_view_t::npos || pos == 0)
- return string_view_pair{s, string_view_t{}};
- return string_view_pair{s.substr(0, pos), s.substr(pos)};
-}
-
-string_view_t createView(PosPtr S, PosPtr E) noexcept {
- return {S, static_cast<size_t>(E - S) + 1};
-}
-
-} // namespace parser
-} // namespace
-
-// POSIX HELPERS
-
-#if defined(_LIBCPP_WIN32API)
-namespace detail {
-
-errc __win_err_to_errc(int err) {
- constexpr struct {
- DWORD win;
- errc errc;
- } win_error_mapping[] = {
- {ERROR_ACCESS_DENIED, errc::permission_denied},
- {ERROR_ALREADY_EXISTS, errc::file_exists},
- {ERROR_BAD_NETPATH, errc::no_such_file_or_directory},
- {ERROR_BAD_PATHNAME, errc::no_such_file_or_directory},
- {ERROR_BAD_UNIT, errc::no_such_device},
- {ERROR_BROKEN_PIPE, errc::broken_pipe},
- {ERROR_BUFFER_OVERFLOW, errc::filename_too_long},
- {ERROR_BUSY, errc::device_or_resource_busy},
- {ERROR_BUSY_DRIVE, errc::device_or_resource_busy},
- {ERROR_CANNOT_MAKE, errc::permission_denied},
- {ERROR_CANTOPEN, errc::io_error},
- {ERROR_CANTREAD, errc::io_error},
- {ERROR_CANTWRITE, errc::io_error},
- {ERROR_CURRENT_DIRECTORY, errc::permission_denied},
- {ERROR_DEV_NOT_EXIST, errc::no_such_device},
- {ERROR_DEVICE_IN_USE, errc::device_or_resource_busy},
- {ERROR_DIR_NOT_EMPTY, errc::directory_not_empty},
- {ERROR_DIRECTORY, errc::invalid_argument},
- {ERROR_DISK_FULL, errc::no_space_on_device},
- {ERROR_FILE_EXISTS, errc::file_exists},
- {ERROR_FILE_NOT_FOUND, errc::no_such_file_or_directory},
- {ERROR_HANDLE_DISK_FULL, errc::no_space_on_device},
- {ERROR_INVALID_ACCESS, errc::permission_denied},
- {ERROR_INVALID_DRIVE, errc::no_such_device},
- {ERROR_INVALID_FUNCTION, errc::function_not_supported},
- {ERROR_INVALID_HANDLE, errc::invalid_argument},
- {ERROR_INVALID_NAME, errc::no_such_file_or_directory},
- {ERROR_INVALID_PARAMETER, errc::invalid_argument},
- {ERROR_LOCK_VIOLATION, errc::no_lock_available},
- {ERROR_LOCKED, errc::no_lock_available},
- {ERROR_NEGATIVE_SEEK, errc::invalid_argument},
- {ERROR_NOACCESS, errc::permission_denied},
- {ERROR_NOT_ENOUGH_MEMORY, errc::not_enough_memory},
- {ERROR_NOT_READY, errc::resource_unavailable_try_again},
- {ERROR_NOT_SAME_DEVICE, errc::cross_device_link},
- {ERROR_NOT_SUPPORTED, errc::not_supported},
- {ERROR_OPEN_FAILED, errc::io_error},
- {ERROR_OPEN_FILES, errc::device_or_resource_busy},
- {ERROR_OPERATION_ABORTED, errc::operation_canceled},
- {ERROR_OUTOFMEMORY, errc::not_enough_memory},
- {ERROR_PATH_NOT_FOUND, errc::no_such_file_or_directory},
- {ERROR_READ_FAULT, errc::io_error},
- {ERROR_REPARSE_TAG_INVALID, errc::invalid_argument},
- {ERROR_RETRY, errc::resource_unavailable_try_again},
- {ERROR_SEEK, errc::io_error},
- {ERROR_SHARING_VIOLATION, errc::permission_denied},
- {ERROR_TOO_MANY_OPEN_FILES, errc::too_many_files_open},
- {ERROR_WRITE_FAULT, errc::io_error},
- {ERROR_WRITE_PROTECT, errc::permission_denied},
- };
-
- for (const auto &pair : win_error_mapping)
- if (pair.win == static_cast<DWORD>(err))
- return pair.errc;
- return errc::invalid_argument;
-}
-
-} // namespace detail
-#endif
-
-namespace detail {
-namespace {
-
-using value_type = path::value_type;
-using string_type = path::string_type;
-
-struct FileDescriptor {
- const path& name;
- int fd = -1;
- StatT m_stat;
- file_status m_status;
-
- template <class... Args>
- static FileDescriptor create(const path* p, error_code& ec, Args... args) {
- ec.clear();
- int fd;
- if ((fd = detail::open(p->c_str(), args...)) == -1) {
- ec = capture_errno();
- return FileDescriptor{p};
- }
- return FileDescriptor(p, fd);
- }
-
- template <class... Args>
- static FileDescriptor create_with_status(const path* p, error_code& ec,
- Args... args) {
- FileDescriptor fd = create(p, ec, args...);
- if (!ec)
- fd.refresh_status(ec);
-
- return fd;
- }
-
- file_status get_status() const { return m_status; }
- StatT const& get_stat() const { return m_stat; }
-
- bool status_known() const { return _VSTD_FS::status_known(m_status); }
-
- file_status refresh_status(error_code& ec);
-
- void close() noexcept {
- if (fd != -1)
- detail::close(fd);
- fd = -1;
- }
-
- FileDescriptor(FileDescriptor&& other)
- : name(other.name), fd(other.fd), m_stat(other.m_stat),
- m_status(other.m_status) {
- other.fd = -1;
- other.m_status = file_status{};
- }
-
- ~FileDescriptor() { close(); }
-
- FileDescriptor(FileDescriptor const&) = delete;
- FileDescriptor& operator=(FileDescriptor const&) = delete;
-
-private:
- explicit FileDescriptor(const path* p, int fd = -1) : name(*p), fd(fd) {}
-};
-
-perms posix_get_perms(const StatT& st) noexcept {
- return static_cast<perms>(st.st_mode) & perms::mask;
-}
-
-file_status create_file_status(error_code& m_ec, path const& p,
- const StatT& path_stat, error_code* ec) {
- if (ec)
- *ec = m_ec;
- if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
- return file_status(file_type::not_found);
- } else if (m_ec) {
- 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);
- }
- // else
-
- file_status fs_tmp;
- auto const mode = path_stat.st_mode;
- if (S_ISLNK(mode))
- fs_tmp.type(file_type::symlink);
- else if (S_ISREG(mode))
- fs_tmp.type(file_type::regular);
- else if (S_ISDIR(mode))
- fs_tmp.type(file_type::directory);
- else if (S_ISBLK(mode))
- fs_tmp.type(file_type::block);
- else if (S_ISCHR(mode))
- fs_tmp.type(file_type::character);
- else if (S_ISFIFO(mode))
- fs_tmp.type(file_type::fifo);
- else if (S_ISSOCK(mode))
- fs_tmp.type(file_type::socket);
- else
- fs_tmp.type(file_type::unknown);
-
- fs_tmp.permissions(detail::posix_get_perms(path_stat));
- return fs_tmp;
-}
-
-file_status posix_stat(path const& p, StatT& path_stat, error_code* ec) {
- error_code m_ec;
- if (detail::stat(p.c_str(), &path_stat) == -1)
- m_ec = detail::capture_errno();
- return create_file_status(m_ec, p, path_stat, ec);
-}
-
-file_status posix_stat(path const& p, error_code* ec) {
- StatT path_stat;
- return posix_stat(p, path_stat, ec);
-}
-
-file_status posix_lstat(path const& p, StatT& path_stat, error_code* ec) {
- error_code m_ec;
- if (detail::lstat(p.c_str(), &path_stat) == -1)
- m_ec = detail::capture_errno();
- return create_file_status(m_ec, p, path_stat, ec);
-}
-
-file_status posix_lstat(path const& p, error_code* ec) {
- StatT path_stat;
- return posix_lstat(p, path_stat, ec);
-}
-
-// http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html
-bool posix_ftruncate(const FileDescriptor& fd, off_t to_size, error_code& ec) {
- if (detail::ftruncate(fd.fd, to_size) == -1) {
- ec = capture_errno();
- return true;
- }
- ec.clear();
- return false;
-}
-
-bool posix_fchmod(const FileDescriptor& fd, const StatT& st, error_code& ec) {
- if (detail::fchmod(fd.fd, st.st_mode) == -1) {
- ec = capture_errno();
- return true;
- }
- ec.clear();
- return false;
-}
-
-bool stat_equivalent(const StatT& st1, const StatT& st2) {
- return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
-}
-
-file_status FileDescriptor::refresh_status(error_code& ec) {
- // FD must be open and good.
- m_status = file_status{};
- m_stat = {};
- error_code m_ec;
- if (detail::fstat(fd, &m_stat) == -1)
- m_ec = capture_errno();
- m_status = create_file_status(m_ec, name, m_stat, &ec);
- return m_status;
-}
-} // namespace
-} // end namespace detail
-
using detail::capture_errno;
using detail::ErrorHandler;
using detail::StatT;
@@ -646,51 +62,6 @@ using parser::createView;
using parser::PathParser;
using parser::string_view_t;
-const bool _FilesystemClock::is_steady;
-
-_FilesystemClock::time_point _FilesystemClock::now() noexcept {
- typedef chrono::duration<rep> __secs;
-#if defined(_LIBCPP_WIN32API)
- typedef chrono::duration<rep, nano> __nsecs;
- FILETIME time;
- GetSystemTimeAsFileTime(&time);
- TimeSpec tp = detail::filetime_to_timespec(time);
- return time_point(__secs(tp.tv_sec) +
- chrono::duration_cast<duration>(__nsecs(tp.tv_nsec)));
-#elif defined(CLOCK_REALTIME)
- typedef chrono::duration<rep, nano> __nsecs;
- struct timespec tp;
- if (0 != clock_gettime(CLOCK_REALTIME, &tp))
- __throw_system_error(errno, "clock_gettime(CLOCK_REALTIME) failed");
- return time_point(__secs(tp.tv_sec) +
- chrono::duration_cast<duration>(__nsecs(tp.tv_nsec)));
-#else
- typedef chrono::duration<rep, micro> __microsecs;
- timeval tv;
- gettimeofday(&tv, 0);
- return time_point(__secs(tv.tv_sec) + __microsecs(tv.tv_usec));
-#endif // CLOCK_REALTIME
-}
-
-filesystem_error::~filesystem_error() {}
-
-void filesystem_error::__create_what(int __num_paths) {
- const char* derived_what = system_error::what();
- __storage_->__what_ = [&]() -> string {
- switch (__num_paths) {
- case 0:
- return detail::format_string("filesystem error: %s", derived_what);
- case 1:
- return detail::format_string("filesystem error: %s [" PATH_CSTR_FMT "]",
- derived_what, path1().c_str());
- case 2:
- return detail::format_string("filesystem error: %s [" PATH_CSTR_FMT "] [" PATH_CSTR_FMT "]",
- derived_what, path1().c_str(), path2().c_str());
- }
- __libcpp_unreachable();
- }();
-}
-
static path __do_absolute(const path& p, path* cwd, error_code* ec) {
if (ec)
ec->clear();
@@ -979,7 +350,7 @@ bool __copy_file(const path& from, const path& to, copy_options options,
return err.report(m_ec);
}
- if (!copy_file_impl(from_fd, to_fd, m_ec)) {
+ if (!detail::copy_file_impl(from_fd, to_fd, m_ec)) {
// FIXME: Remove the dest file if we failed, and it didn't exist previously.
return err.report(m_ec);
}
@@ -1108,7 +479,7 @@ path __current_path(error_code* ec) {
Deleter deleter = &::free;
#else
auto size = ::pathconf(".", _PC_PATH_MAX);
- _LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size");
+ _LIBCPP_ASSERT_UNCATEGORIZED(size >= 0, "pathconf returned a 0 as max size");
auto buff = unique_ptr<path::value_type[]>(new path::value_type[size + 1]);
path::value_type* ptr = buff.get();
@@ -1197,18 +568,6 @@ bool __fs_is_empty(const path& p, error_code* ec) {
__libcpp_unreachable();
}
-static file_time_type __extract_last_write_time(const path& p, const StatT& st,
- error_code* ec) {
- using detail::fs_time;
- ErrorHandler<file_time_type> err("last_write_time", ec, &p);
-
- auto ts = detail::extract_mtime(st);
- if (!fs_time::is_representable(ts))
- return err.report(errc::value_too_large);
-
- return fs_time::convert_from_timespec(ts);
-}
-
file_time_type __last_write_time(const path& p, error_code* ec) {
using namespace chrono;
ErrorHandler<file_time_type> err("last_write_time", ec, &p);
@@ -1218,7 +577,7 @@ file_time_type __last_write_time(const path& p, error_code* ec) {
detail::posix_stat(p, st, &m_ec);
if (m_ec)
return err.report(m_ec);
- return __extract_last_write_time(p, st, ec);
+ return detail::__extract_last_write_time(p, st, ec);
}
void __last_write_time(const path& p, file_time_type new_time, error_code* ec) {
@@ -1268,7 +627,7 @@ void __permissions(const path& p, perms prms, perm_options opts,
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(
+ _LIBCPP_ASSERT_UNCATEGORIZED(
(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");
@@ -1282,7 +641,7 @@ void __permissions(const path& p, perms prms, perm_options opts,
set_sym_perms = is_symlink(st);
if (m_ec)
return err.report(m_ec);
- _LIBCPP_ASSERT(st.permissions() != perms::unknown,
+ _LIBCPP_ASSERT_UNCATEGORIZED(st.permissions() != perms::unknown,
"Permissions unexpectedly unknown");
if (add_perms)
prms |= st.permissions();
@@ -1328,7 +687,7 @@ path __read_symlink(const path& p, error_code* ec) {
detail::SSizeT ret;
if ((ret = detail::readlink(p.c_str(), buff.get(), size)) == -1)
return err.report(capture_errno());
- _LIBCPP_ASSERT(ret > 0, "TODO");
+ _LIBCPP_ASSERT_UNCATEGORIZED(ret > 0, "TODO");
if (static_cast<size_t>(ret) >= size)
return err.report(errc::value_too_large);
buff[ret] = 0;
@@ -1554,8 +913,13 @@ path __temp_directory_path(error_code* ec) {
for (auto& ep : env_paths)
if ((ret = getenv(ep)))
break;
- if (ret == nullptr)
+ if (ret == nullptr) {
+#if defined(__ANDROID__)
+ ret = "/data/local/tmp";
+#else
ret = "/tmp";
+#endif
+ }
path p(ret);
#endif
@@ -1608,500 +972,4 @@ path __weakly_canonical(const path& p, error_code* ec) {
return result.lexically_normal();
}
-///////////////////////////////////////////////////////////////////////////////
-// path definitions
-///////////////////////////////////////////////////////////////////////////////
-
-constexpr path::value_type path::preferred_separator;
-
-path& path::replace_extension(path const& replacement) {
- path p = extension();
- if (not p.empty()) {
- __pn_.erase(__pn_.size() - p.native().size());
- }
- if (!replacement.empty()) {
- if (replacement.native()[0] != '.') {
- __pn_ += PATHSTR(".");
- }
- __pn_.append(replacement.__pn_);
- }
- return *this;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// path.decompose
-
-string_view_t path::__root_name() const {
- auto PP = PathParser::CreateBegin(__pn_);
- if (PP.State == PathParser::PS_InRootName)
- return *PP;
- return {};
-}
-
-string_view_t path::__root_directory() const {
- auto PP = PathParser::CreateBegin(__pn_);
- if (PP.State == PathParser::PS_InRootName)
- ++PP;
- if (PP.State == PathParser::PS_InRootDir)
- return *PP;
- return {};
-}
-
-string_view_t path::__root_path_raw() const {
- auto PP = PathParser::CreateBegin(__pn_);
- if (PP.State == PathParser::PS_InRootName) {
- auto NextCh = PP.peek();
- if (NextCh && isSeparator(*NextCh)) {
- ++PP;
- return createView(__pn_.data(), &PP.RawEntry.back());
- }
- return PP.RawEntry;
- }
- if (PP.State == PathParser::PS_InRootDir)
- return *PP;
- return {};
-}
-
-static bool ConsumeRootName(PathParser *PP) {
- static_assert(PathParser::PS_BeforeBegin == 1 &&
- PathParser::PS_InRootName == 2,
- "Values for enums are incorrect");
- while (PP->State <= PathParser::PS_InRootName)
- ++(*PP);
- return PP->State == PathParser::PS_AtEnd;
-}
-
-static bool ConsumeRootDir(PathParser* PP) {
- static_assert(PathParser::PS_BeforeBegin == 1 &&
- PathParser::PS_InRootName == 2 &&
- PathParser::PS_InRootDir == 3, "Values for enums are incorrect");
- while (PP->State <= PathParser::PS_InRootDir)
- ++(*PP);
- return PP->State == PathParser::PS_AtEnd;
-}
-
-string_view_t path::__relative_path() const {
- auto PP = PathParser::CreateBegin(__pn_);
- if (ConsumeRootDir(&PP))
- return {};
- return createView(PP.RawEntry.data(), &__pn_.back());
-}
-
-string_view_t path::__parent_path() const {
- if (empty())
- return {};
- // Determine if we have a root path but not a relative path. In that case
- // return *this.
- {
- auto PP = PathParser::CreateBegin(__pn_);
- if (ConsumeRootDir(&PP))
- return __pn_;
- }
- // Otherwise remove a single element from the end of the path, and return
- // a string representing that path
- {
- auto PP = PathParser::CreateEnd(__pn_);
- --PP;
- if (PP.RawEntry.data() == __pn_.data())
- return {};
- --PP;
- return createView(__pn_.data(), &PP.RawEntry.back());
- }
-}
-
-string_view_t path::__filename() const {
- if (empty())
- return {};
- {
- PathParser PP = PathParser::CreateBegin(__pn_);
- if (ConsumeRootDir(&PP))
- return {};
- }
- return *(--PathParser::CreateEnd(__pn_));
-}
-
-string_view_t path::__stem() const {
- return parser::separate_filename(__filename()).first;
-}
-
-string_view_t path::__extension() const {
- return parser::separate_filename(__filename()).second;
-}
-
-////////////////////////////////////////////////////////////////////////////
-// path.gen
-
-enum PathPartKind : unsigned char {
- PK_None,
- PK_RootSep,
- PK_Filename,
- PK_Dot,
- PK_DotDot,
- PK_TrailingSep
-};
-
-static PathPartKind ClassifyPathPart(string_view_t Part) {
- if (Part.empty())
- return PK_TrailingSep;
- if (Part == PATHSTR("."))
- return PK_Dot;
- if (Part == PATHSTR(".."))
- return PK_DotDot;
- if (Part == PATHSTR("/"))
- return PK_RootSep;
-#if defined(_LIBCPP_WIN32API)
- if (Part == PATHSTR("\\"))
- return PK_RootSep;
-#endif
- return PK_Filename;
-}
-
-path path::lexically_normal() const {
- if (__pn_.empty())
- return *this;
-
- using PartKindPair = pair<string_view_t, PathPartKind>;
- vector<PartKindPair> Parts;
- // Guess as to how many elements the path has to avoid reallocating.
- Parts.reserve(32);
-
- // Track the total size of the parts as we collect them. This allows the
- // resulting path to reserve the correct amount of memory.
- size_t NewPathSize = 0;
- auto AddPart = [&](PathPartKind K, string_view_t P) {
- NewPathSize += P.size();
- Parts.emplace_back(P, K);
- };
- auto LastPartKind = [&]() {
- if (Parts.empty())
- return PK_None;
- return Parts.back().second;
- };
-
- bool MaybeNeedTrailingSep = false;
- // Build a stack containing the remaining elements of the path, popping off
- // elements which occur before a '..' entry.
- for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {
- auto Part = *PP;
- PathPartKind Kind = ClassifyPathPart(Part);
- switch (Kind) {
- case PK_Filename:
- case PK_RootSep: {
- // Add all non-dot and non-dot-dot elements to the stack of elements.
- AddPart(Kind, Part);
- MaybeNeedTrailingSep = false;
- break;
- }
- case PK_DotDot: {
- // Only push a ".." element if there are no elements preceding the "..",
- // or if the preceding element is itself "..".
- auto LastKind = LastPartKind();
- if (LastKind == PK_Filename) {
- NewPathSize -= Parts.back().first.size();
- Parts.pop_back();
- } else if (LastKind != PK_RootSep)
- AddPart(PK_DotDot, PATHSTR(".."));
- MaybeNeedTrailingSep = LastKind == PK_Filename;
- break;
- }
- case PK_Dot:
- case PK_TrailingSep: {
- MaybeNeedTrailingSep = true;
- break;
- }
- case PK_None:
- __libcpp_unreachable();
- }
- }
- // [fs.path.generic]p6.8: If the path is empty, add a dot.
- if (Parts.empty())
- return PATHSTR(".");
-
- // [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
- // trailing directory-separator.
- bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;
-
- path Result;
- Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);
- for (auto& PK : Parts)
- Result /= PK.first;
-
- if (NeedTrailingSep)
- Result /= PATHSTR("");
-
- Result.make_preferred();
- return Result;
-}
-
-static int DetermineLexicalElementCount(PathParser PP) {
- int Count = 0;
- for (; PP; ++PP) {
- auto Elem = *PP;
- if (Elem == PATHSTR(".."))
- --Count;
- else if (Elem != PATHSTR(".") && Elem != PATHSTR(""))
- ++Count;
- }
- return Count;
-}
-
-path path::lexically_relative(const path& base) const {
- { // perform root-name/root-directory mismatch checks
- auto PP = PathParser::CreateBegin(__pn_);
- auto PPBase = PathParser::CreateBegin(base.__pn_);
- auto CheckIterMismatchAtBase = [&]() {
- return PP.State != PPBase.State &&
- (PP.inRootPath() || PPBase.inRootPath());
- };
- if (PP.inRootName() && PPBase.inRootName()) {
- if (*PP != *PPBase)
- return {};
- } else if (CheckIterMismatchAtBase())
- return {};
-
- if (PP.inRootPath())
- ++PP;
- if (PPBase.inRootPath())
- ++PPBase;
- if (CheckIterMismatchAtBase())
- return {};
- }
-
- // Find the first mismatching element
- auto PP = PathParser::CreateBegin(__pn_);
- auto PPBase = PathParser::CreateBegin(base.__pn_);
- while (PP && PPBase && PP.State == PPBase.State && *PP == *PPBase) {
- ++PP;
- ++PPBase;
- }
-
- // If there is no mismatch, return ".".
- if (!PP && !PPBase)
- return ".";
-
- // Otherwise, determine the number of elements, 'n', which are not dot or
- // dot-dot minus the number of dot-dot elements.
- int ElemCount = DetermineLexicalElementCount(PPBase);
- if (ElemCount < 0)
- return {};
-
- // if n == 0 and (a == end() || a->empty()), returns path("."); otherwise
- if (ElemCount == 0 && (PP.atEnd() || *PP == PATHSTR("")))
- return PATHSTR(".");
-
- // return a path constructed with 'n' dot-dot elements, followed by the
- // elements of '*this' after the mismatch.
- path Result;
- // FIXME: Reserve enough room in Result that it won't have to re-allocate.
- while (ElemCount--)
- Result /= PATHSTR("..");
- for (; PP; ++PP)
- Result /= *PP;
- return Result;
-}
-
-////////////////////////////////////////////////////////////////////////////
-// path.comparisons
-static int CompareRootName(PathParser *LHS, PathParser *RHS) {
- if (!LHS->inRootName() && !RHS->inRootName())
- return 0;
-
- auto GetRootName = [](PathParser *Parser) -> string_view_t {
- return Parser->inRootName() ? **Parser : PATHSTR("");
- };
- int res = GetRootName(LHS).compare(GetRootName(RHS));
- ConsumeRootName(LHS);
- ConsumeRootName(RHS);
- return res;
-}
-
-static int CompareRootDir(PathParser *LHS, PathParser *RHS) {
- if (!LHS->inRootDir() && RHS->inRootDir())
- return -1;
- else if (LHS->inRootDir() && !RHS->inRootDir())
- return 1;
- else {
- ConsumeRootDir(LHS);
- ConsumeRootDir(RHS);
- return 0;
- }
-}
-
-static int CompareRelative(PathParser *LHSPtr, PathParser *RHSPtr) {
- auto &LHS = *LHSPtr;
- auto &RHS = *RHSPtr;
-
- int res;
- while (LHS && RHS) {
- if ((res = (*LHS).compare(*RHS)) != 0)
- return res;
- ++LHS;
- ++RHS;
- }
- return 0;
-}
-
-static int CompareEndState(PathParser *LHS, PathParser *RHS) {
- if (LHS->atEnd() && !RHS->atEnd())
- return -1;
- else if (!LHS->atEnd() && RHS->atEnd())
- return 1;
- return 0;
-}
-
-int path::__compare(string_view_t __s) const {
- auto LHS = PathParser::CreateBegin(__pn_);
- auto RHS = PathParser::CreateBegin(__s);
- int res;
-
- if ((res = CompareRootName(&LHS, &RHS)) != 0)
- return res;
-
- if ((res = CompareRootDir(&LHS, &RHS)) != 0)
- return res;
-
- if ((res = CompareRelative(&LHS, &RHS)) != 0)
- return res;
-
- return CompareEndState(&LHS, &RHS);
-}
-
-////////////////////////////////////////////////////////////////////////////
-// path.nonmembers
-size_t hash_value(const path& __p) noexcept {
- auto PP = PathParser::CreateBegin(__p.native());
- size_t hash_value = 0;
- hash<string_view_t> hasher;
- while (PP) {
- hash_value = __hash_combine(hash_value, hasher(*PP));
- ++PP;
- }
- return hash_value;
-}
-
-////////////////////////////////////////////////////////////////////////////
-// path.itr
-path::iterator path::begin() const {
- auto PP = PathParser::CreateBegin(__pn_);
- iterator it;
- it.__path_ptr_ = this;
- it.__state_ = static_cast<path::iterator::_ParserState>(PP.State);
- it.__entry_ = PP.RawEntry;
- it.__stashed_elem_.__assign_view(*PP);
- return it;
-}
-
-path::iterator path::end() const {
- iterator it{};
- it.__state_ = path::iterator::_AtEnd;
- it.__path_ptr_ = this;
- return it;
-}
-
-path::iterator& path::iterator::__increment() {
- PathParser PP(__path_ptr_->native(), __entry_, __state_);
- ++PP;
- __state_ = static_cast<_ParserState>(PP.State);
- __entry_ = PP.RawEntry;
- __stashed_elem_.__assign_view(*PP);
- return *this;
-}
-
-path::iterator& path::iterator::__decrement() {
- PathParser PP(__path_ptr_->native(), __entry_, __state_);
- --PP;
- __state_ = static_cast<_ParserState>(PP.State);
- __entry_ = PP.RawEntry;
- __stashed_elem_.__assign_view(*PP);
- return *this;
-}
-
-#if defined(_LIBCPP_WIN32API)
-////////////////////////////////////////////////////////////////////////////
-// Windows path conversions
-size_t __wide_to_char(const wstring &str, char *out, size_t outlen) {
- if (str.empty())
- return 0;
- ErrorHandler<size_t> err("__wide_to_char", nullptr);
- UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
- BOOL used_default = FALSE;
- int ret = WideCharToMultiByte(codepage, 0, str.data(), str.size(), out,
- outlen, nullptr, &used_default);
- if (ret <= 0 || used_default)
- return err.report(errc::illegal_byte_sequence);
- return ret;
-}
-
-size_t __char_to_wide(const string &str, wchar_t *out, size_t outlen) {
- if (str.empty())
- return 0;
- ErrorHandler<size_t> err("__char_to_wide", nullptr);
- UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
- int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(),
- str.size(), out, outlen);
- if (ret <= 0)
- return err.report(errc::illegal_byte_sequence);
- return ret;
-}
-#endif
-
-
-///////////////////////////////////////////////////////////////////////////////
-// directory entry definitions
-///////////////////////////////////////////////////////////////////////////////
-
-error_code directory_entry::__do_refresh() noexcept {
- __data_.__reset();
- error_code failure_ec;
-
- StatT full_st;
- file_status st = detail::posix_lstat(__p_, full_st, &failure_ec);
- if (!status_known(st)) {
- __data_.__reset();
- return failure_ec;
- }
-
- if (!_VSTD_FS::exists(st) || !_VSTD_FS::is_symlink(st)) {
- __data_.__cache_type_ = directory_entry::_RefreshNonSymlink;
- __data_.__type_ = st.type();
- __data_.__non_sym_perms_ = st.permissions();
- } else { // we have a symlink
- __data_.__sym_perms_ = st.permissions();
- // Get the information about the linked entity.
- // Ignore errors from stat, since we don't want errors regarding symlink
- // resolution to be reported to the user.
- error_code ignored_ec;
- st = detail::posix_stat(__p_, full_st, &ignored_ec);
-
- __data_.__type_ = st.type();
- __data_.__non_sym_perms_ = st.permissions();
-
- // If we failed to resolve the link, then only partially populate the
- // cache.
- if (!status_known(st)) {
- __data_.__cache_type_ = directory_entry::_RefreshSymlinkUnresolved;
- return error_code{};
- }
- // Otherwise, we resolved the link, potentially as not existing.
- // That's OK.
- __data_.__cache_type_ = directory_entry::_RefreshSymlink;
- }
-
- if (_VSTD_FS::is_regular_file(st))
- __data_.__size_ = static_cast<uintmax_t>(full_st.st_size);
-
- if (_VSTD_FS::exists(st)) {
- __data_.__nlink_ = static_cast<uintmax_t>(full_st.st_nlink);
-
- // Attempt to extract the mtime, and fail if it's not representable using
- // file_time_type. For now we ignore the error, as we'll report it when
- // the value is actually used.
- error_code ignored_ec;
- __data_.__write_time_ =
- __extract_last_write_time(__p_, full_st, &ignored_ec);
- }
-
- return failure_ec;
-}
-
_LIBCPP_END_NAMESPACE_FILESYSTEM
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/path.cpp b/contrib/libs/cxxsupp/libcxx/src/filesystem/path.cpp
new file mode 100644
index 0000000000..82f1ba7ba6
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/path.cpp
@@ -0,0 +1,460 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__config>
+#include <filesystem>
+#include <vector>
+
+#include "error.h"
+#include "path_parser.h"
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+using detail::ErrorHandler;
+using parser::createView;
+using parser::PathParser;
+using parser::string_view_t;
+
+///////////////////////////////////////////////////////////////////////////////
+// path definitions
+///////////////////////////////////////////////////////////////////////////////
+
+constexpr path::value_type path::preferred_separator;
+
+path& path::replace_extension(path const& replacement) {
+ path p = extension();
+ if (not p.empty()) {
+ __pn_.erase(__pn_.size() - p.native().size());
+ }
+ if (!replacement.empty()) {
+ if (replacement.native()[0] != '.') {
+ __pn_ += PATHSTR(".");
+ }
+ __pn_.append(replacement.__pn_);
+ }
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// path.decompose
+
+string_view_t path::__root_name() const {
+ auto PP = PathParser::CreateBegin(__pn_);
+ if (PP.State == PathParser::PS_InRootName)
+ return *PP;
+ return {};
+}
+
+string_view_t path::__root_directory() const {
+ auto PP = PathParser::CreateBegin(__pn_);
+ if (PP.State == PathParser::PS_InRootName)
+ ++PP;
+ if (PP.State == PathParser::PS_InRootDir)
+ return *PP;
+ return {};
+}
+
+string_view_t path::__root_path_raw() const {
+ auto PP = PathParser::CreateBegin(__pn_);
+ if (PP.State == PathParser::PS_InRootName) {
+ auto NextCh = PP.peek();
+ if (NextCh && isSeparator(*NextCh)) {
+ ++PP;
+ return createView(__pn_.data(), &PP.RawEntry.back());
+ }
+ return PP.RawEntry;
+ }
+ if (PP.State == PathParser::PS_InRootDir)
+ return *PP;
+ return {};
+}
+
+static bool ConsumeRootName(PathParser *PP) {
+ static_assert(PathParser::PS_BeforeBegin == 1 &&
+ PathParser::PS_InRootName == 2,
+ "Values for enums are incorrect");
+ while (PP->State <= PathParser::PS_InRootName)
+ ++(*PP);
+ return PP->State == PathParser::PS_AtEnd;
+}
+
+static bool ConsumeRootDir(PathParser* PP) {
+ static_assert(PathParser::PS_BeforeBegin == 1 &&
+ PathParser::PS_InRootName == 2 &&
+ PathParser::PS_InRootDir == 3, "Values for enums are incorrect");
+ while (PP->State <= PathParser::PS_InRootDir)
+ ++(*PP);
+ return PP->State == PathParser::PS_AtEnd;
+}
+
+string_view_t path::__relative_path() const {
+ auto PP = PathParser::CreateBegin(__pn_);
+ if (ConsumeRootDir(&PP))
+ return {};
+ return createView(PP.RawEntry.data(), &__pn_.back());
+}
+
+string_view_t path::__parent_path() const {
+ if (empty())
+ return {};
+ // Determine if we have a root path but not a relative path. In that case
+ // return *this.
+ {
+ auto PP = PathParser::CreateBegin(__pn_);
+ if (ConsumeRootDir(&PP))
+ return __pn_;
+ }
+ // Otherwise remove a single element from the end of the path, and return
+ // a string representing that path
+ {
+ auto PP = PathParser::CreateEnd(__pn_);
+ --PP;
+ if (PP.RawEntry.data() == __pn_.data())
+ return {};
+ --PP;
+ return createView(__pn_.data(), &PP.RawEntry.back());
+ }
+}
+
+string_view_t path::__filename() const {
+ if (empty())
+ return {};
+ {
+ PathParser PP = PathParser::CreateBegin(__pn_);
+ if (ConsumeRootDir(&PP))
+ return {};
+ }
+ return *(--PathParser::CreateEnd(__pn_));
+}
+
+string_view_t path::__stem() const {
+ return parser::separate_filename(__filename()).first;
+}
+
+string_view_t path::__extension() const {
+ return parser::separate_filename(__filename()).second;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// path.gen
+
+enum PathPartKind : unsigned char {
+ PK_None,
+ PK_RootSep,
+ PK_Filename,
+ PK_Dot,
+ PK_DotDot,
+ PK_TrailingSep
+};
+
+static PathPartKind ClassifyPathPart(string_view_t Part) {
+ if (Part.empty())
+ return PK_TrailingSep;
+ if (Part == PATHSTR("."))
+ return PK_Dot;
+ if (Part == PATHSTR(".."))
+ return PK_DotDot;
+ if (Part == PATHSTR("/"))
+ return PK_RootSep;
+#if defined(_LIBCPP_WIN32API)
+ if (Part == PATHSTR("\\"))
+ return PK_RootSep;
+#endif
+ return PK_Filename;
+}
+
+path path::lexically_normal() const {
+ if (__pn_.empty())
+ return *this;
+
+ using PartKindPair = pair<string_view_t, PathPartKind>;
+ vector<PartKindPair> Parts;
+ // Guess as to how many elements the path has to avoid reallocating.
+ Parts.reserve(32);
+
+ // Track the total size of the parts as we collect them. This allows the
+ // resulting path to reserve the correct amount of memory.
+ size_t NewPathSize = 0;
+ auto AddPart = [&](PathPartKind K, string_view_t P) {
+ NewPathSize += P.size();
+ Parts.emplace_back(P, K);
+ };
+ auto LastPartKind = [&]() {
+ if (Parts.empty())
+ return PK_None;
+ return Parts.back().second;
+ };
+
+ bool MaybeNeedTrailingSep = false;
+ // Build a stack containing the remaining elements of the path, popping off
+ // elements which occur before a '..' entry.
+ for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {
+ auto Part = *PP;
+ PathPartKind Kind = ClassifyPathPart(Part);
+ switch (Kind) {
+ case PK_Filename:
+ case PK_RootSep: {
+ // Add all non-dot and non-dot-dot elements to the stack of elements.
+ AddPart(Kind, Part);
+ MaybeNeedTrailingSep = false;
+ break;
+ }
+ case PK_DotDot: {
+ // Only push a ".." element if there are no elements preceding the "..",
+ // or if the preceding element is itself "..".
+ auto LastKind = LastPartKind();
+ if (LastKind == PK_Filename) {
+ NewPathSize -= Parts.back().first.size();
+ Parts.pop_back();
+ } else if (LastKind != PK_RootSep)
+ AddPart(PK_DotDot, PATHSTR(".."));
+ MaybeNeedTrailingSep = LastKind == PK_Filename;
+ break;
+ }
+ case PK_Dot:
+ case PK_TrailingSep: {
+ MaybeNeedTrailingSep = true;
+ break;
+ }
+ case PK_None:
+ __libcpp_unreachable();
+ }
+ }
+ // [fs.path.generic]p6.8: If the path is empty, add a dot.
+ if (Parts.empty())
+ return PATHSTR(".");
+
+ // [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
+ // trailing directory-separator.
+ bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;
+
+ path Result;
+ Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);
+ for (auto& PK : Parts)
+ Result /= PK.first;
+
+ if (NeedTrailingSep)
+ Result /= PATHSTR("");
+
+ Result.make_preferred();
+ return Result;
+}
+
+static int DetermineLexicalElementCount(PathParser PP) {
+ int Count = 0;
+ for (; PP; ++PP) {
+ auto Elem = *PP;
+ if (Elem == PATHSTR(".."))
+ --Count;
+ else if (Elem != PATHSTR(".") && Elem != PATHSTR(""))
+ ++Count;
+ }
+ return Count;
+}
+
+path path::lexically_relative(const path& base) const {
+ { // perform root-name/root-directory mismatch checks
+ auto PP = PathParser::CreateBegin(__pn_);
+ auto PPBase = PathParser::CreateBegin(base.__pn_);
+ auto CheckIterMismatchAtBase = [&]() {
+ return PP.State != PPBase.State &&
+ (PP.inRootPath() || PPBase.inRootPath());
+ };
+ if (PP.inRootName() && PPBase.inRootName()) {
+ if (*PP != *PPBase)
+ return {};
+ } else if (CheckIterMismatchAtBase())
+ return {};
+
+ if (PP.inRootPath())
+ ++PP;
+ if (PPBase.inRootPath())
+ ++PPBase;
+ if (CheckIterMismatchAtBase())
+ return {};
+ }
+
+ // Find the first mismatching element
+ auto PP = PathParser::CreateBegin(__pn_);
+ auto PPBase = PathParser::CreateBegin(base.__pn_);
+ while (PP && PPBase && PP.State == PPBase.State && *PP == *PPBase) {
+ ++PP;
+ ++PPBase;
+ }
+
+ // If there is no mismatch, return ".".
+ if (!PP && !PPBase)
+ return ".";
+
+ // Otherwise, determine the number of elements, 'n', which are not dot or
+ // dot-dot minus the number of dot-dot elements.
+ int ElemCount = DetermineLexicalElementCount(PPBase);
+ if (ElemCount < 0)
+ return {};
+
+ // if n == 0 and (a == end() || a->empty()), returns path("."); otherwise
+ if (ElemCount == 0 && (PP.atEnd() || *PP == PATHSTR("")))
+ return PATHSTR(".");
+
+ // return a path constructed with 'n' dot-dot elements, followed by the
+ // elements of '*this' after the mismatch.
+ path Result;
+ // FIXME: Reserve enough room in Result that it won't have to re-allocate.
+ while (ElemCount--)
+ Result /= PATHSTR("..");
+ for (; PP; ++PP)
+ Result /= *PP;
+ return Result;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// path.comparisons
+static int CompareRootName(PathParser *LHS, PathParser *RHS) {
+ if (!LHS->inRootName() && !RHS->inRootName())
+ return 0;
+
+ auto GetRootName = [](PathParser *Parser) -> string_view_t {
+ return Parser->inRootName() ? **Parser : PATHSTR("");
+ };
+ int res = GetRootName(LHS).compare(GetRootName(RHS));
+ ConsumeRootName(LHS);
+ ConsumeRootName(RHS);
+ return res;
+}
+
+static int CompareRootDir(PathParser *LHS, PathParser *RHS) {
+ if (!LHS->inRootDir() && RHS->inRootDir())
+ return -1;
+ else if (LHS->inRootDir() && !RHS->inRootDir())
+ return 1;
+ else {
+ ConsumeRootDir(LHS);
+ ConsumeRootDir(RHS);
+ return 0;
+ }
+}
+
+static int CompareRelative(PathParser *LHSPtr, PathParser *RHSPtr) {
+ auto &LHS = *LHSPtr;
+ auto &RHS = *RHSPtr;
+
+ int res;
+ while (LHS && RHS) {
+ if ((res = (*LHS).compare(*RHS)) != 0)
+ return res;
+ ++LHS;
+ ++RHS;
+ }
+ return 0;
+}
+
+static int CompareEndState(PathParser *LHS, PathParser *RHS) {
+ if (LHS->atEnd() && !RHS->atEnd())
+ return -1;
+ else if (!LHS->atEnd() && RHS->atEnd())
+ return 1;
+ return 0;
+}
+
+int path::__compare(string_view_t __s) const {
+ auto LHS = PathParser::CreateBegin(__pn_);
+ auto RHS = PathParser::CreateBegin(__s);
+ int res;
+
+ if ((res = CompareRootName(&LHS, &RHS)) != 0)
+ return res;
+
+ if ((res = CompareRootDir(&LHS, &RHS)) != 0)
+ return res;
+
+ if ((res = CompareRelative(&LHS, &RHS)) != 0)
+ return res;
+
+ return CompareEndState(&LHS, &RHS);
+}
+
+////////////////////////////////////////////////////////////////////////////
+// path.nonmembers
+size_t hash_value(const path& __p) noexcept {
+ auto PP = PathParser::CreateBegin(__p.native());
+ size_t hash_value = 0;
+ hash<string_view_t> hasher;
+ while (PP) {
+ hash_value = __hash_combine(hash_value, hasher(*PP));
+ ++PP;
+ }
+ return hash_value;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// path.itr
+path::iterator path::begin() const {
+ auto PP = PathParser::CreateBegin(__pn_);
+ iterator it;
+ it.__path_ptr_ = this;
+ it.__state_ = static_cast<path::iterator::_ParserState>(PP.State);
+ it.__entry_ = PP.RawEntry;
+ it.__stashed_elem_.__assign_view(*PP);
+ return it;
+}
+
+path::iterator path::end() const {
+ iterator it{};
+ it.__state_ = path::iterator::_AtEnd;
+ it.__path_ptr_ = this;
+ return it;
+}
+
+path::iterator& path::iterator::__increment() {
+ PathParser PP(__path_ptr_->native(), __entry_, __state_);
+ ++PP;
+ __state_ = static_cast<_ParserState>(PP.State);
+ __entry_ = PP.RawEntry;
+ __stashed_elem_.__assign_view(*PP);
+ return *this;
+}
+
+path::iterator& path::iterator::__decrement() {
+ PathParser PP(__path_ptr_->native(), __entry_, __state_);
+ --PP;
+ __state_ = static_cast<_ParserState>(PP.State);
+ __entry_ = PP.RawEntry;
+ __stashed_elem_.__assign_view(*PP);
+ return *this;
+}
+
+#if defined(_LIBCPP_WIN32API)
+////////////////////////////////////////////////////////////////////////////
+// Windows path conversions
+size_t __wide_to_char(const wstring &str, char *out, size_t outlen) {
+ if (str.empty())
+ return 0;
+ ErrorHandler<size_t> err("__wide_to_char", nullptr);
+ UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+ BOOL used_default = FALSE;
+ int ret = WideCharToMultiByte(codepage, 0, str.data(), str.size(), out,
+ outlen, nullptr, &used_default);
+ if (ret <= 0 || used_default)
+ return err.report(errc::illegal_byte_sequence);
+ return ret;
+}
+
+size_t __char_to_wide(const string &str, wchar_t *out, size_t outlen) {
+ if (str.empty())
+ return 0;
+ ErrorHandler<size_t> err("__char_to_wide", nullptr);
+ UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+ int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(),
+ str.size(), out, outlen);
+ if (ret <= 0)
+ return err.report(errc::illegal_byte_sequence);
+ return ret;
+}
+#endif
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/path_parser.h b/contrib/libs/cxxsupp/libcxx/src/filesystem/path_parser.h
new file mode 100644
index 0000000000..630391fe6b
--- /dev/null
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/path_parser.h
@@ -0,0 +1,372 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 PATH_PARSER_H
+#define PATH_PARSER_H
+
+#include <__config>
+#include <__utility/unreachable.h>
+#include <cstddef>
+#include <filesystem>
+#include <utility>
+
+#include "format_string.h"
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+inline bool isSeparator(path::value_type C) {
+ if (C == '/')
+ return true;
+#if defined(_LIBCPP_WIN32API)
+ if (C == '\\')
+ return true;
+#endif
+ return false;
+}
+
+inline bool isDriveLetter(path::value_type C) {
+ return (C >= 'a' && C <= 'z') || (C >= 'A' && C <= 'Z');
+}
+
+namespace parser {
+
+using string_view_t = path::__string_view;
+using string_view_pair = pair<string_view_t, string_view_t>;
+using PosPtr = path::value_type const*;
+
+struct PathParser {
+ enum ParserState : unsigned char {
+ // Zero is a special sentinel value used by default constructed iterators.
+ PS_BeforeBegin = path::iterator::_BeforeBegin,
+ PS_InRootName = path::iterator::_InRootName,
+ PS_InRootDir = path::iterator::_InRootDir,
+ PS_InFilenames = path::iterator::_InFilenames,
+ PS_InTrailingSep = path::iterator::_InTrailingSep,
+ PS_AtEnd = path::iterator::_AtEnd
+ };
+
+ const string_view_t Path;
+ string_view_t RawEntry;
+ ParserState State;
+
+private:
+ PathParser(string_view_t P, ParserState State) noexcept : Path(P),
+ State(State) {}
+
+public:
+ PathParser(string_view_t P, string_view_t E, unsigned char S)
+ : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
+ // S cannot be '0' or PS_BeforeBegin.
+ }
+
+ static PathParser CreateBegin(string_view_t P) noexcept {
+ PathParser PP(P, PS_BeforeBegin);
+ PP.increment();
+ return PP;
+ }
+
+ static PathParser CreateEnd(string_view_t P) noexcept {
+ PathParser PP(P, PS_AtEnd);
+ return PP;
+ }
+
+ PosPtr peek() const noexcept {
+ auto TkEnd = getNextTokenStartPos();
+ auto End = getAfterBack();
+ return TkEnd == End ? nullptr : TkEnd;
+ }
+
+ void increment() noexcept {
+ const PosPtr End = getAfterBack();
+ const PosPtr Start = getNextTokenStartPos();
+ if (Start == End)
+ return makeState(PS_AtEnd);
+
+ switch (State) {
+ case PS_BeforeBegin: {
+ PosPtr TkEnd = consumeRootName(Start, End);
+ if (TkEnd)
+ return makeState(PS_InRootName, Start, TkEnd);
+ }
+ _LIBCPP_FALLTHROUGH();
+ case PS_InRootName: {
+ PosPtr TkEnd = consumeAllSeparators(Start, End);
+ if (TkEnd)
+ return makeState(PS_InRootDir, Start, TkEnd);
+ else
+ return makeState(PS_InFilenames, Start, consumeName(Start, End));
+ }
+ case PS_InRootDir:
+ return makeState(PS_InFilenames, Start, consumeName(Start, End));
+
+ case PS_InFilenames: {
+ PosPtr SepEnd = consumeAllSeparators(Start, End);
+ if (SepEnd != End) {
+ PosPtr TkEnd = consumeName(SepEnd, End);
+ if (TkEnd)
+ return makeState(PS_InFilenames, SepEnd, TkEnd);
+ }
+ return makeState(PS_InTrailingSep, Start, SepEnd);
+ }
+
+ case PS_InTrailingSep:
+ return makeState(PS_AtEnd);
+
+ case PS_AtEnd:
+ __libcpp_unreachable();
+ }
+ }
+
+ void decrement() noexcept {
+ const PosPtr REnd = getBeforeFront();
+ const PosPtr RStart = getCurrentTokenStartPos() - 1;
+ if (RStart == REnd) // we're decrementing the begin
+ return makeState(PS_BeforeBegin);
+
+ switch (State) {
+ case PS_AtEnd: {
+ // Try to consume a trailing separator or root directory first.
+ if (PosPtr SepEnd = consumeAllSeparators(RStart, REnd)) {
+ if (SepEnd == REnd)
+ return makeState(PS_InRootDir, Path.data(), RStart + 1);
+ PosPtr TkStart = consumeRootName(SepEnd, REnd);
+ if (TkStart == REnd)
+ return makeState(PS_InRootDir, RStart, RStart + 1);
+ return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1);
+ } else {
+ PosPtr TkStart = consumeRootName(RStart, REnd);
+ if (TkStart == REnd)
+ return makeState(PS_InRootName, TkStart + 1, RStart + 1);
+ TkStart = consumeName(RStart, REnd);
+ return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
+ }
+ }
+ case PS_InTrailingSep:
+ return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1,
+ RStart + 1);
+ case PS_InFilenames: {
+ PosPtr SepEnd = consumeAllSeparators(RStart, REnd);
+ if (SepEnd == REnd)
+ return makeState(PS_InRootDir, Path.data(), RStart + 1);
+ PosPtr TkStart = consumeRootName(SepEnd ? SepEnd : RStart, REnd);
+ if (TkStart == REnd) {
+ if (SepEnd)
+ return makeState(PS_InRootDir, SepEnd + 1, RStart + 1);
+ return makeState(PS_InRootName, TkStart + 1, RStart + 1);
+ }
+ TkStart = consumeName(SepEnd, REnd);
+ return makeState(PS_InFilenames, TkStart + 1, SepEnd + 1);
+ }
+ case PS_InRootDir:
+ return makeState(PS_InRootName, Path.data(), RStart + 1);
+ case PS_InRootName:
+ case PS_BeforeBegin:
+ __libcpp_unreachable();
+ }
+ }
+
+ /// \brief Return a view with the "preferred representation" of the current
+ /// element. For example trailing separators are represented as a '.'
+ string_view_t operator*() const noexcept {
+ switch (State) {
+ case PS_BeforeBegin:
+ case PS_AtEnd:
+ return PATHSTR("");
+ case PS_InRootDir:
+ if (RawEntry[0] == '\\')
+ return PATHSTR("\\");
+ else
+ return PATHSTR("/");
+ case PS_InTrailingSep:
+ return PATHSTR("");
+ case PS_InRootName:
+ case PS_InFilenames:
+ return RawEntry;
+ }
+ __libcpp_unreachable();
+ }
+
+ explicit operator bool() const noexcept {
+ return State != PS_BeforeBegin && State != PS_AtEnd;
+ }
+
+ PathParser& operator++() noexcept {
+ increment();
+ return *this;
+ }
+
+ PathParser& operator--() noexcept {
+ decrement();
+ return *this;
+ }
+
+ bool atEnd() const noexcept {
+ return State == PS_AtEnd;
+ }
+
+ bool inRootDir() const noexcept {
+ return State == PS_InRootDir;
+ }
+
+ bool inRootName() const noexcept {
+ return State == PS_InRootName;
+ }
+
+ bool inRootPath() const noexcept {
+ return inRootName() || inRootDir();
+ }
+
+private:
+ void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
+ State = NewState;
+ RawEntry = string_view_t(Start, End - Start);
+ }
+ void makeState(ParserState NewState) noexcept {
+ State = NewState;
+ RawEntry = {};
+ }
+
+ PosPtr getAfterBack() const noexcept { return Path.data() + Path.size(); }
+
+ PosPtr getBeforeFront() const noexcept { return Path.data() - 1; }
+
+ /// \brief Return a pointer to the first character after the currently
+ /// lexed element.
+ PosPtr getNextTokenStartPos() const noexcept {
+ switch (State) {
+ case PS_BeforeBegin:
+ return Path.data();
+ case PS_InRootName:
+ case PS_InRootDir:
+ case PS_InFilenames:
+ return &RawEntry.back() + 1;
+ case PS_InTrailingSep:
+ case PS_AtEnd:
+ return getAfterBack();
+ }
+ __libcpp_unreachable();
+ }
+
+ /// \brief Return a pointer to the first character in the currently lexed
+ /// element.
+ PosPtr getCurrentTokenStartPos() const noexcept {
+ switch (State) {
+ case PS_BeforeBegin:
+ case PS_InRootName:
+ return &Path.front();
+ case PS_InRootDir:
+ case PS_InFilenames:
+ case PS_InTrailingSep:
+ return &RawEntry.front();
+ case PS_AtEnd:
+ return &Path.back() + 1;
+ }
+ __libcpp_unreachable();
+ }
+
+ // Consume all consecutive separators.
+ PosPtr consumeAllSeparators(PosPtr P, PosPtr End) const noexcept {
+ if (P == nullptr || P == End || !isSeparator(*P))
+ return nullptr;
+ const int Inc = P < End ? 1 : -1;
+ P += Inc;
+ while (P != End && isSeparator(*P))
+ P += Inc;
+ return P;
+ }
+
+ // Consume exactly N separators, or return nullptr.
+ PosPtr consumeNSeparators(PosPtr P, PosPtr End, int N) const noexcept {
+ PosPtr Ret = consumeAllSeparators(P, End);
+ if (Ret == nullptr)
+ return nullptr;
+ if (P < End) {
+ if (Ret == P + N)
+ return Ret;
+ } else {
+ if (Ret == P - N)
+ return Ret;
+ }
+ return nullptr;
+ }
+
+ PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
+ PosPtr Start = P;
+ if (P == nullptr || P == End || isSeparator(*P))
+ return nullptr;
+ const int Inc = P < End ? 1 : -1;
+ P += Inc;
+ while (P != End && !isSeparator(*P))
+ P += Inc;
+ if (P == End && Inc < 0) {
+ // Iterating backwards and consumed all the rest of the input.
+ // Check if the start of the string would have been considered
+ // a root name.
+ PosPtr RootEnd = consumeRootName(End + 1, Start);
+ if (RootEnd)
+ return RootEnd - 1;
+ }
+ return P;
+ }
+
+ PosPtr consumeDriveLetter(PosPtr P, PosPtr End) const noexcept {
+ if (P == End)
+ return nullptr;
+ if (P < End) {
+ if (P + 1 == End || !isDriveLetter(P[0]) || P[1] != ':')
+ return nullptr;
+ return P + 2;
+ } else {
+ if (P - 1 == End || !isDriveLetter(P[-1]) || P[0] != ':')
+ return nullptr;
+ return P - 2;
+ }
+ }
+
+ PosPtr consumeNetworkRoot(PosPtr P, PosPtr End) const noexcept {
+ if (P == End)
+ return nullptr;
+ if (P < End)
+ return consumeName(consumeNSeparators(P, End, 2), End);
+ else
+ return consumeNSeparators(consumeName(P, End), End, 2);
+ }
+
+#if defined(_LIBCPP_WIN32API)
+ PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
+ if (PosPtr Ret = consumeDriveLetter(P, End))
+ return Ret;
+ if (PosPtr Ret = consumeNetworkRoot(P, End))
+ return Ret;
+ return nullptr;
+ }
+#else
+ PosPtr consumeRootName(PosPtr /*P*/, PosPtr /*End*/) const noexcept {
+ return nullptr;
+ }
+#endif
+};
+
+inline string_view_pair separate_filename(string_view_t const& s) {
+ if (s == PATHSTR(".") || s == PATHSTR("..") || s.empty())
+ return string_view_pair{s, PATHSTR("")};
+ auto pos = s.find_last_of('.');
+ if (pos == string_view_t::npos || pos == 0)
+ return string_view_pair{s, string_view_t{}};
+ return string_view_pair{s.substr(0, pos), s.substr(pos)};
+}
+
+inline string_view_t createView(PosPtr S, PosPtr E) noexcept {
+ return {S, static_cast<size_t>(E - S) + 1};
+}
+
+} // namespace parser
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
+
+#endif // PATH_PARSER_H
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/posix_compat.h b/contrib/libs/cxxsupp/libcxx/src/filesystem/posix_compat.h
index 36116ec5a3..f11f41552e 100644
--- a/contrib/libs/cxxsupp/libcxx/src/filesystem/posix_compat.h
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/posix_compat.h
@@ -24,9 +24,11 @@
#define POSIX_COMPAT_H
#include <__assert>
+#include <__config>
#include <filesystem>
-#include "filesystem_common.h"
+#include "error.h"
+#include "time_utils.h"
#if defined(_LIBCPP_WIN32API)
# define WIN32_LEAN_AND_MEAN
@@ -35,10 +37,13 @@
# include <io.h>
# include <winioctl.h>
#else
+# include <fcntl.h>
# include <unistd.h>
# include <sys/stat.h>
# include <sys/statvfs.h>
+# include <sys/time.h>
#endif
+#include <stdlib.h>
#include <time.h>
#if defined(_LIBCPP_WIN32API)
@@ -74,7 +79,6 @@ struct LIBCPP_REPARSE_DATA_BUFFER {
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
namespace detail {
-namespace {
#if defined(_LIBCPP_WIN32API)
@@ -118,36 +122,7 @@ namespace {
#define O_NONBLOCK 0
-
-// There were 369 years and 89 leap days from the Windows epoch
-// (1601) to the Unix epoch (1970).
-#define FILE_TIME_OFFSET_SECS (uint64_t(369 * 365 + 89) * (24 * 60 * 60))
-
-TimeSpec filetime_to_timespec(LARGE_INTEGER li) {
- TimeSpec ret;
- ret.tv_sec = li.QuadPart / 10000000 - FILE_TIME_OFFSET_SECS;
- ret.tv_nsec = (li.QuadPart % 10000000) * 100;
- return ret;
-}
-
-TimeSpec filetime_to_timespec(FILETIME ft) {
- LARGE_INTEGER li;
- li.LowPart = ft.dwLowDateTime;
- li.HighPart = ft.dwHighDateTime;
- return filetime_to_timespec(li);
-}
-
-FILETIME timespec_to_filetime(TimeSpec ts) {
- LARGE_INTEGER li;
- li.QuadPart =
- ts.tv_nsec / 100 + (ts.tv_sec + FILE_TIME_OFFSET_SECS) * 10000000;
- FILETIME ft;
- ft.dwLowDateTime = li.LowPart;
- ft.dwHighDateTime = li.HighPart;
- return ft;
-}
-
-int set_errno(int e = GetLastError()) {
+inline int set_errno(int e = GetLastError()) {
errno = static_cast<int>(__win_err_to_errc(e));
return -1;
}
@@ -170,7 +145,7 @@ private:
HANDLE h;
};
-int stat_handle(HANDLE h, StatT *buf) {
+inline int stat_handle(HANDLE h, StatT *buf) {
FILE_BASIC_INFO basic;
if (!GetFileInformationByHandleEx(h, FileBasicInfo, &basic, sizeof(basic)))
return set_errno();
@@ -208,7 +183,7 @@ int stat_handle(HANDLE h, StatT *buf) {
return 0;
}
-int stat_file(const wchar_t *path, StatT *buf, DWORD flags) {
+inline int stat_file(const wchar_t *path, StatT *buf, DWORD flags) {
WinHandle h(path, FILE_READ_ATTRIBUTES, flags);
if (!h)
return set_errno();
@@ -216,24 +191,26 @@ int stat_file(const wchar_t *path, StatT *buf, DWORD flags) {
return ret;
}
-int stat(const wchar_t *path, StatT *buf) { return stat_file(path, buf, 0); }
+inline int stat(const wchar_t *path, StatT *buf) { return stat_file(path, buf, 0); }
-int lstat(const wchar_t *path, StatT *buf) {
+inline int lstat(const wchar_t *path, StatT *buf) {
return stat_file(path, buf, FILE_FLAG_OPEN_REPARSE_POINT);
}
-int fstat(int fd, StatT *buf) {
+inline int fstat(int fd, StatT *buf) {
HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
return stat_handle(h, buf);
}
-int mkdir(const wchar_t *path, int permissions) {
+inline int mkdir(const wchar_t *path, int permissions) {
(void)permissions;
- return _wmkdir(path);
+ if (!CreateDirectoryW(path, nullptr))
+ return set_errno();
+ return 0;
}
-int symlink_file_dir(const wchar_t *oldname, const wchar_t *newname,
- bool is_dir) {
+inline int symlink_file_dir(const wchar_t *oldname, const wchar_t *newname,
+ bool is_dir) {
path dest(oldname);
dest.make_preferred();
oldname = dest.c_str();
@@ -249,21 +226,21 @@ int symlink_file_dir(const wchar_t *oldname, const wchar_t *newname,
return set_errno();
}
-int symlink_file(const wchar_t *oldname, const wchar_t *newname) {
+inline int symlink_file(const wchar_t *oldname, const wchar_t *newname) {
return symlink_file_dir(oldname, newname, false);
}
-int symlink_dir(const wchar_t *oldname, const wchar_t *newname) {
+inline int symlink_dir(const wchar_t *oldname, const wchar_t *newname) {
return symlink_file_dir(oldname, newname, true);
}
-int link(const wchar_t *oldname, const wchar_t *newname) {
+inline int link(const wchar_t *oldname, const wchar_t *newname) {
if (CreateHardLinkW(newname, oldname, nullptr))
return 0;
return set_errno();
}
-int remove(const wchar_t *path) {
+inline int remove(const wchar_t *path) {
detail::WinHandle h(path, DELETE, FILE_FLAG_OPEN_REPARSE_POINT);
if (!h)
return set_errno();
@@ -274,7 +251,7 @@ int remove(const wchar_t *path) {
return 0;
}
-int truncate_handle(HANDLE h, off_t length) {
+inline int truncate_handle(HANDLE h, off_t length) {
LARGE_INTEGER size_param;
size_param.QuadPart = length;
if (!SetFilePointerEx(h, size_param, 0, FILE_BEGIN))
@@ -284,19 +261,19 @@ int truncate_handle(HANDLE h, off_t length) {
return 0;
}
-int ftruncate(int fd, off_t length) {
+inline int ftruncate(int fd, off_t length) {
HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
return truncate_handle(h, length);
}
-int truncate(const wchar_t *path, off_t length) {
+inline int truncate(const wchar_t *path, off_t length) {
detail::WinHandle h(path, GENERIC_WRITE, 0);
if (!h)
return set_errno();
return truncate_handle(h, length);
}
-int rename(const wchar_t *from, const wchar_t *to) {
+inline int rename(const wchar_t *from, const wchar_t *to) {
if (!(MoveFileExW(from, to,
MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING |
MOVEFILE_WRITE_THROUGH)))
@@ -304,11 +281,11 @@ int rename(const wchar_t *from, const wchar_t *to) {
return 0;
}
-template <class... Args> int open(const wchar_t *filename, Args... args) {
- return _wopen(filename, args...);
+inline int chdir(const wchar_t* path) {
+ if (!SetCurrentDirectoryW(path))
+ return set_errno();
+ return 0;
}
-int close(int fd) { return _close(fd); }
-int chdir(const wchar_t *path) { return _wchdir(path); }
struct StatVFS {
uint64_t f_frsize;
@@ -317,7 +294,7 @@ struct StatVFS {
uint64_t f_bavail;
};
-int statvfs(const wchar_t *p, StatVFS *buf) {
+inline int statvfs(const wchar_t *p, StatVFS *buf) {
path dir = p;
while (true) {
error_code local_ec;
@@ -343,11 +320,29 @@ int statvfs(const wchar_t *p, StatVFS *buf) {
return 0;
}
-wchar_t *getcwd(wchar_t *buff, size_t size) { return _wgetcwd(buff, size); }
+inline wchar_t* getcwd([[maybe_unused]] wchar_t* in_buf, [[maybe_unused]] size_t in_size) {
+ // Only expected to be used with us allocating the buffer.
+ _LIBCPP_ASSERT(in_buf == nullptr, "Windows getcwd() assumes in_buf==nullptr");
+ _LIBCPP_ASSERT(in_size == 0, "Windows getcwd() assumes in_size==0");
+
+ size_t buff_size = MAX_PATH + 10;
+ std::unique_ptr<wchar_t, decltype(&::free)> buff(static_cast<wchar_t*>(malloc(buff_size * sizeof(wchar_t))), &::free);
+ DWORD retval = GetCurrentDirectoryW(buff_size, buff.get());
+ if (retval > buff_size) {
+ buff_size = retval;
+ buff.reset(static_cast<wchar_t*>(malloc(buff_size * sizeof(wchar_t))));
+ retval = GetCurrentDirectoryW(buff_size, buff.get());
+ }
+ if (!retval) {
+ set_errno();
+ return nullptr;
+ }
+ return buff.release();
+}
-wchar_t *realpath(const wchar_t *path, wchar_t *resolved_name) {
+inline wchar_t *realpath(const wchar_t *path, [[maybe_unused]] wchar_t *resolved_name) {
// Only expected to be used with us allocating the buffer.
- _LIBCPP_ASSERT(resolved_name == nullptr,
+ _LIBCPP_ASSERT_UNCATEGORIZED(resolved_name == nullptr,
"Windows realpath() assumes a null resolved_name");
WinHandle h(path, FILE_READ_ATTRIBUTES, 0);
@@ -386,7 +381,7 @@ wchar_t *realpath(const wchar_t *path, wchar_t *resolved_name) {
#define AT_SYMLINK_NOFOLLOW 1
using ModeT = int;
-int fchmod_handle(HANDLE h, int perms) {
+inline int fchmod_handle(HANDLE h, int perms) {
FILE_BASIC_INFO basic;
if (!GetFileInformationByHandleEx(h, FileBasicInfo, &basic, sizeof(basic)))
return set_errno();
@@ -400,7 +395,7 @@ int fchmod_handle(HANDLE h, int perms) {
return 0;
}
-int fchmodat(int fd, const wchar_t *path, int perms, int flag) {
+inline int fchmodat(int /*fd*/, const wchar_t *path, int perms, int flag) {
DWORD attributes = GetFileAttributesW(path);
if (attributes == INVALID_FILE_ATTRIBUTES)
return set_errno();
@@ -427,7 +422,7 @@ int fchmodat(int fd, const wchar_t *path, int perms, int flag) {
return 0;
}
-int fchmod(int fd, int perms) {
+inline int fchmod(int fd, int perms) {
HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
return fchmod_handle(h, perms);
}
@@ -435,7 +430,7 @@ int fchmod(int fd, int perms) {
#define MAX_SYMLINK_SIZE MAXIMUM_REPARSE_DATA_BUFFER_SIZE
using SSizeT = ::int64_t;
-SSizeT readlink(const wchar_t *path, wchar_t *ret_buf, size_t bufsize) {
+inline SSizeT readlink(const wchar_t *path, wchar_t *ret_buf, size_t bufsize) {
uint8_t buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
detail::WinHandle h(path, FILE_READ_ATTRIBUTES, FILE_FLAG_OPEN_REPARSE_POINT);
if (!h)
@@ -479,14 +474,13 @@ SSizeT readlink(const wchar_t *path, wchar_t *ret_buf, size_t bufsize) {
}
#else
-int symlink_file(const char *oldname, const char *newname) {
+inline int symlink_file(const char *oldname, const char *newname) {
return ::symlink(oldname, newname);
}
-int symlink_dir(const char *oldname, const char *newname) {
+inline int symlink_dir(const char *oldname, const char *newname) {
return ::symlink(oldname, newname);
}
using ::chdir;
-using ::close;
using ::fchmod;
#if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
using ::fchmodat;
@@ -497,7 +491,6 @@ using ::getcwd;
using ::link;
using ::lstat;
using ::mkdir;
-using ::open;
using ::readlink;
using ::realpath;
using ::remove;
@@ -514,7 +507,6 @@ using SSizeT = ::ssize_t;
#endif
-} // namespace
} // end namespace detail
_LIBCPP_END_NAMESPACE_FILESYSTEM
diff --git a/contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_common.h b/contrib/libs/cxxsupp/libcxx/src/filesystem/time_utils.h
index 0e71569d87..9177e1013d 100644
--- a/contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_common.h
+++ b/contrib/libs/cxxsupp/libcxx/src/filesystem/time_utils.h
@@ -6,54 +6,35 @@
//
//===----------------------------------------------------------------------===////
-#ifndef FILESYSTEM_COMMON_H
-#define FILESYSTEM_COMMON_H
+#ifndef FILESYSTEM_TIME_UTILS_H
+#define FILESYSTEM_TIME_UTILS_H
-#include <__assert>
#include <__config>
#include <array>
#include <chrono>
-#include <climits>
-#include <cstdarg>
-#include <ctime>
#include <filesystem>
+#include <limits>
#include <ratio>
#include <system_error>
+#include <type_traits>
#include <utility>
+#include "error.h"
+#include "format_string.h"
+
#if defined(_LIBCPP_WIN32API)
# define WIN32_LEAN_AND_MEAN
# define NOMINMAX
# include <windows.h>
#else
-# include <dirent.h> // for DIR & friends
-# include <fcntl.h> /* values for fchmodat */
+# include <fcntl.h>
# include <sys/stat.h>
-# include <sys/statvfs.h>
# include <sys/time.h> // for ::utimes as used in __last_write_time
-# include <unistd.h>
-#endif // defined(_LIBCPP_WIN32API)
-
-#include "../include/apple_availability.h"
-
-#if !defined(__APPLE__)
-// We can use the presence of UTIME_OMIT to detect platforms that provide
-// utimensat.
-#if defined(UTIME_OMIT)
-#define _LIBCPP_USE_UTIMENSAT
#endif
-#endif
-
-_LIBCPP_DIAGNOSTIC_PUSH
-_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wunused-function")
-_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-function")
-#if defined(_LIBCPP_WIN32API)
-# define PATHSTR(x) (L##x)
-# define PATH_CSTR_FMT "\"%ls\""
-#else
-# define PATHSTR(x) (x)
-# define PATH_CSTR_FMT "\"%s\""
+// We can use the presence of UTIME_OMIT to detect platforms that provide utimensat.
+#if defined(UTIME_OMIT)
+# define _LIBCPP_USE_UTIMENSAT
#endif
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
@@ -61,198 +42,11 @@ _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
namespace detail {
#if defined(_LIBCPP_WIN32API)
-// Non anonymous, to allow access from two translation units.
-errc __win_err_to_errc(int err);
-#endif
-
-namespace {
-
-static _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 0) string
-format_string_impl(const char* msg, va_list ap) {
- array<char, 256> buf;
-
- va_list apcopy;
- va_copy(apcopy, ap);
- int ret = ::vsnprintf(buf.data(), buf.size(), msg, apcopy);
- va_end(apcopy);
-
- string result;
- if (static_cast<size_t>(ret) < buf.size()) {
- result.assign(buf.data(), static_cast<size_t>(ret));
- } else {
- // 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_t 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, ap);
- _LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
- }
- return result;
-}
-
-static _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 1, 2) string
-format_string(const char* msg, ...) {
- string ret;
- va_list ap;
- va_start(ap, msg);
-#ifndef _LIBCPP_NO_EXCEPTIONS
- try {
-#endif // _LIBCPP_NO_EXCEPTIONS
- ret = format_string_impl(msg, ap);
-#ifndef _LIBCPP_NO_EXCEPTIONS
- } catch (...) {
- va_end(ap);
- throw;
- }
-#endif // _LIBCPP_NO_EXCEPTIONS
- va_end(ap);
- return ret;
-}
-
-error_code capture_errno() {
- _LIBCPP_ASSERT(errno != 0, "Expected errno to be non-zero");
- return error_code(errno, generic_category());
-}
-
-#if defined(_LIBCPP_WIN32API)
-error_code make_windows_error(int err) {
- return make_error_code(__win_err_to_errc(err));
-}
-#endif
-
-template <class T>
-T error_value();
-template <>
-_LIBCPP_CONSTEXPR_SINCE_CXX14 void error_value<void>() {}
-template <>
-bool error_value<bool>() {
- return false;
-}
-#if __SIZEOF_SIZE_T__ != __SIZEOF_LONG_LONG__
-template <>
-size_t error_value<size_t>() {
- return size_t(-1);
-}
-#endif
-template <>
-uintmax_t error_value<uintmax_t>() {
- return uintmax_t(-1);
-}
-template <>
-_LIBCPP_CONSTEXPR_SINCE_CXX14 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& ec) const {
- if (ec_) {
- *ec_ = ec;
- return error_value<T>();
- }
- string what = string("in ") + func_name_;
- switch (bool(p1_) + bool(p2_)) {
- case 0:
- __throw_filesystem_error(what, ec);
- case 1:
- __throw_filesystem_error(what, *p1_, ec);
- case 2:
- __throw_filesystem_error(what, *p1_, *p2_, ec);
- }
- __libcpp_unreachable();
- }
-
- _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 0)
- void report_impl(const error_code& ec, const char* msg, va_list ap) const {
- if (ec_) {
- *ec_ = ec;
- return;
- }
- string what =
- string("in ") + func_name_ + ": " + format_string_impl(msg, ap);
- switch (bool(p1_) + bool(p2_)) {
- case 0:
- __throw_filesystem_error(what, ec);
- case 1:
- __throw_filesystem_error(what, *p1_, ec);
- case 2:
- __throw_filesystem_error(what, *p1_, *p2_, ec);
- }
- __libcpp_unreachable();
- }
-
- _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
- T report(const error_code& ec, const char* msg, ...) const {
- va_list ap;
- va_start(ap, msg);
-#ifndef _LIBCPP_NO_EXCEPTIONS
- try {
-#endif // _LIBCPP_NO_EXCEPTIONS
- report_impl(ec, msg, ap);
-#ifndef _LIBCPP_NO_EXCEPTIONS
- } catch (...) {
- va_end(ap);
- throw;
- }
-#endif // _LIBCPP_NO_EXCEPTIONS
- va_end(ap);
- return error_value<T>();
- }
-
- T report(errc const& err) const {
- return report(make_error_code(err));
- }
-
- _LIBCPP_ATTRIBUTE_FORMAT(__printf__, 3, 4)
- T report(errc const& err, const char* msg, ...) const {
- va_list ap;
- va_start(ap, msg);
-#ifndef _LIBCPP_NO_EXCEPTIONS
- try {
-#endif // _LIBCPP_NO_EXCEPTIONS
- report_impl(make_error_code(err), msg, ap);
-#ifndef _LIBCPP_NO_EXCEPTIONS
- } catch (...) {
- va_end(ap);
- throw;
- }
-#endif // _LIBCPP_NO_EXCEPTIONS
- va_end(ap);
- return error_value<T>();
- }
-
-private:
- ErrorHandler(ErrorHandler const&) = delete;
- ErrorHandler& operator=(ErrorHandler const&) = delete;
-};
-
-using chrono::duration;
-using chrono::duration_cast;
-
-#if defined(_LIBCPP_WIN32API)
// Various C runtime versions (UCRT, or the legacy msvcrt.dll used by
// some mingw toolchains) provide different stat function implementations,
// with a number of limitations with respect to what we want from the
-// stat function. Instead provide our own (in the anonymous detail namespace
-// in posix_compat.h) which does exactly what we want, along with our own
-// stat structure and flag macros.
+// stat function. Instead provide our own which does exactly what we want,
+// along with our own stat structure and flag macros.
struct TimeSpec {
int64_t tv_sec;
@@ -276,12 +70,56 @@ struct StatT {
uintmax_t st_size;
};
+// There were 369 years and 89 leap days from the Windows epoch
+// (1601) to the Unix epoch (1970).
+#define FILE_TIME_OFFSET_SECS (uint64_t(369 * 365 + 89) * (24 * 60 * 60))
+
+inline TimeSpec filetime_to_timespec(LARGE_INTEGER li) {
+ TimeSpec ret;
+ ret.tv_sec = li.QuadPart / 10000000 - FILE_TIME_OFFSET_SECS;
+ ret.tv_nsec = (li.QuadPart % 10000000) * 100;
+ return ret;
+}
+
+inline TimeSpec filetime_to_timespec(FILETIME ft) {
+ LARGE_INTEGER li;
+ li.LowPart = ft.dwLowDateTime;
+ li.HighPart = ft.dwHighDateTime;
+ return filetime_to_timespec(li);
+}
+
+inline FILETIME timespec_to_filetime(TimeSpec ts) {
+ LARGE_INTEGER li;
+ li.QuadPart =
+ ts.tv_nsec / 100 + (ts.tv_sec + FILE_TIME_OFFSET_SECS) * 10000000;
+ FILETIME ft;
+ ft.dwLowDateTime = li.LowPart;
+ ft.dwHighDateTime = li.HighPart;
+ return ft;
+}
+
#else
using TimeSpec = struct timespec;
using TimeVal = struct timeval;
using StatT = struct stat;
+
+inline TimeVal make_timeval(TimeSpec const& ts) {
+ 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);
+ };
+ TimeVal TV = {};
+ TV.tv_sec = ts.tv_sec;
+ TV.tv_usec = Convert(ts.tv_nsec);
+ return TV;
+}
#endif
+using chrono::duration;
+using chrono::duration_cast;
+
template <class FileTimeT, class TimeT,
bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
struct time_util_base {
@@ -309,7 +147,7 @@ struct time_util_base {
.count();
private:
- static _LIBCPP_CONSTEXPR_SINCE_CXX14 fs_duration get_min_nsecs() {
+ static _LIBCPP_CONSTEXPR fs_duration get_min_nsecs() {
return duration_cast<fs_duration>(
fs_nanoseconds(min_nsec_timespec) -
duration_cast<fs_nanoseconds>(fs_seconds(1)));
@@ -329,7 +167,9 @@ private:
return max_seconds >= numeric_limits<TimeT>::max() &&
min_seconds <= numeric_limits<TimeT>::min();
}
+#if _LIBCPP_STD_VER >= 14
static_assert(check_range(), "the representable range is unacceptable small");
+#endif
};
template <class FileTimeT, class TimeT>
@@ -489,20 +329,9 @@ inline TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
inline TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
#endif
-#if !defined(_LIBCPP_WIN32API)
-inline TimeVal make_timeval(TimeSpec const& ts) {
- 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);
- };
- TimeVal TV = {};
- TV.tv_sec = ts.tv_sec;
- TV.tv_usec = Convert(ts.tv_nsec);
- return TV;
-}
+#ifndef _LIBCPP_HAS_NO_FILESYSTEM
+#if !defined(_LIBCPP_WIN32API)
inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
error_code& ec) {
TimeVal ConvertedTS[2] = {make_timeval(TS[0]), make_timeval(TS[1])};
@@ -514,8 +343,8 @@ inline bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS,
}
#if defined(_LIBCPP_USE_UTIMENSAT)
-bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
- error_code& ec) {
+inline 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;
@@ -524,8 +353,8 @@ bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS,
}
#endif
-bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
- error_code& ec) {
+inline 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
@@ -533,81 +362,24 @@ bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS,
#endif
}
-#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)
+#endif // !_LIBCPP_WIN32API
-template <class DirEntT>
-static file_type get_file_type(DirEntT*, long) {
- return file_type::none;
-}
+inline file_time_type __extract_last_write_time(const path& p, const StatT& st,
+ error_code* ec) {
+ using detail::fs_time;
+ ErrorHandler<file_time_type> err("last_write_time", ec, &p);
-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 // _LIBCPP_WIN32API
+ auto ts = detail::extract_mtime(st);
+ if (!fs_time::is_representable(ts))
+ return err.report(errc::value_too_large);
-static file_type get_file_type(const WIN32_FIND_DATAW& data) {
- if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
- data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
- return file_type::symlink;
- if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
- return file_type::directory;
- return file_type::regular;
-}
-static uintmax_t get_file_size(const WIN32_FIND_DATAW& data) {
- return (static_cast<uint64_t>(data.nFileSizeHigh) << 32) + data.nFileSizeLow;
-}
-static file_time_type get_write_time(const WIN32_FIND_DATAW& 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));
+ return fs_time::convert_from_timespec(ts);
}
-#endif // !_LIBCPP_WIN32API
+#endif // !_LIBCPP_HAS_NO_FILESYSTEM
-} // namespace
} // end namespace detail
_LIBCPP_END_NAMESPACE_FILESYSTEM
-_LIBCPP_DIAGNOSTIC_POP
-
-#endif // FILESYSTEM_COMMON_H
+#endif // FILESYSTEM_TIME_UTILS_H