diff options
author | hiddenpath <hiddenpath@yandex-team.com> | 2024-02-21 23:16:42 +0300 |
---|---|---|
committer | hiddenpath <hiddenpath@yandex-team.com> | 2024-02-21 23:33:25 +0300 |
commit | 9052eb5cc304b8da8885fc4e3364ebddc16945f3 (patch) | |
tree | 3c252f6161dd0745c7732d74c9304c000645ab47 /contrib/libs/cxxsupp/libcxx/src/filesystem | |
parent | f5eb715f103692e7c7536e13bef3f281fd78e5e7 (diff) | |
download | ydb-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.cpp | 74 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/directory_iterator.cpp | 18 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/error.h | 238 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/file_descriptor.h | 298 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_clock.cpp | 64 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/filesystem_error.cpp | 37 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/format_string.h | 77 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/operations.cpp | 1166 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/path.cpp | 460 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/path_parser.h | 372 | ||||
-rw-r--r-- | contrib/libs/cxxsupp/libcxx/src/filesystem/posix_compat.h | 124 | ||||
-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 |