[libc++] Split sources for <filesystem>
authorLouis Dionne <ldionne.2@gmail.com>
Mon, 12 Jun 2023 17:43:55 +0000 (10:43 -0700)
committerLouis Dionne <ldionne.2@gmail.com>
Mon, 19 Jun 2023 13:07:05 +0000 (09:07 -0400)
The operations.cpp file contained the implementation of a ton of
functionality unrelated to just the filesystem operations, and
filesystem_common.h contained a lot of unrelated functionality as well.

Splitting this up into more files will make it possible in the future
to support parts of <filesystem> (e.g. path) on systems where there is
no notion of a filesystem.

Differential Revision: https://reviews.llvm.org/D152377

16 files changed:
libcxx/src/CMakeLists.txt
libcxx/src/filesystem/directory_entry.cpp [new file with mode: 0644]
libcxx/src/filesystem/directory_iterator.cpp
libcxx/src/filesystem/error.h [new file with mode: 0644]
libcxx/src/filesystem/file_descriptor.h [new file with mode: 0644]
libcxx/src/filesystem/filesystem_clock.cpp [new file with mode: 0644]
libcxx/src/filesystem/filesystem_error.cpp [new file with mode: 0644]
libcxx/src/filesystem/format_string.h [new file with mode: 0644]
libcxx/src/filesystem/operations.cpp
libcxx/src/filesystem/path.cpp [new file with mode: 0644]
libcxx/src/filesystem/path_parser.h [new file with mode: 0644]
libcxx/src/filesystem/posix_compat.h
libcxx/src/filesystem/time_utils.h [moved from libcxx/src/filesystem/filesystem_common.h with 55% similarity]
libcxx/test/libcxx/input.output/filesystems/class.directory_entry/directory_entry.mods/last_write_time.pass.cpp
libcxx/test/libcxx/input.output/filesystems/convert_file_time.pass.cpp
libcxx/utils/data/ignore_format.txt

index 358a61e..dede386 100644 (file)
@@ -110,10 +110,16 @@ endif()
 
 if (LIBCXX_ENABLE_FILESYSTEM)
   list(APPEND LIBCXX_SOURCES
-    filesystem/filesystem_common.h
-    filesystem/operations.cpp
+    filesystem/directory_entry.cpp
     filesystem/directory_iterator.cpp
+    filesystem/file_descriptor.h
+    filesystem/filesystem_clock.cpp
+    filesystem/filesystem_error.cpp
+    filesystem/operations.cpp
+    filesystem/path_parser.h
+    filesystem/path.cpp
     filesystem/posix_compat.h
+    filesystem/time_utils.h
     )
   # Filesystem uses __int128_t, which requires a definition of __muloi4 when
   # compiled with UBSAN. This definition is not provided by libgcc_s, but is
diff --git a/libcxx/src/filesystem/directory_entry.cpp b/libcxx/src/filesystem/directory_entry.cpp
new file mode 100644 (file)
index 0000000..4910d39
--- /dev/null
@@ -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
index 8d069cc..4a7e01b 100644 (file)
 #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
 
diff --git a/libcxx/src/filesystem/error.h b/libcxx/src/filesystem/error.h
new file mode 100644 (file)
index 0000000..7269600
--- /dev/null
@@ -0,0 +1,246 @@
+//===----------------------------------------------------------------------===////
+//
+// 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
+
+// TODO: Check whether these functions actually need internal linkage, or if they can be made normal header functions
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-template")
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+namespace detail {
+namespace {
+
+#if defined(_LIBCPP_WIN32API)
+
+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
+
+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_ + ": " + 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 anonymous namespace
+} // end namespace detail
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
+
+#endif // FILESYSTEM_ERROR_H
diff --git a/libcxx/src/filesystem/file_descriptor.h b/libcxx/src/filesystem/file_descriptor.h
new file mode 100644 (file)
index 0000000..e952042
--- /dev/null
@@ -0,0 +1,283 @@
+//===----------------------------------------------------------------------===////
+//
+// 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"
+
+#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)
+
+// TODO: Check whether these functions actually need internal linkage, or if they can be made normal header functions
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-template")
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+namespace detail {
+namespace {
+
+#if !defined(_LIBCPP_WIN32API)
+
+#if defined(DT_BLK)
+template <class DirEntT, class = decltype(DirEntT::d_type)>
+static file_type get_file_type(DirEntT* ent, int) {
+  switch (ent->d_type) {
+  case DT_BLK:
+    return file_type::block;
+  case DT_CHR:
+    return file_type::character;
+  case DT_DIR:
+    return file_type::directory;
+  case DT_FIFO:
+    return file_type::fifo;
+  case DT_LNK:
+    return file_type::symlink;
+  case DT_REG:
+    return file_type::regular;
+  case DT_SOCK:
+    return file_type::socket;
+  // Unlike in lstat, hitting "unknown" here simply means that the underlying
+  // filesystem doesn't support d_type. Report is as 'none' so we correctly
+  // set the cache to empty.
+  case DT_UNKNOWN:
+    break;
+  }
+  return file_type::none;
+}
+#endif // defined(DT_BLK)
+
+template <class DirEntT>
+static file_type get_file_type(DirEntT*, long) {
+  return file_type::none;
+}
+
+static pair<string_view, file_type> posix_readdir(DIR* dir_stream,
+                                                  error_code& ec) {
+  struct dirent* dir_entry_ptr = nullptr;
+  errno = 0; // zero errno in order to detect errors
+  ec.clear();
+  if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) {
+    if (errno)
+      ec = capture_errno();
+    return {};
+  } else {
+    return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)};
+  }
+}
+
+#else // _LIBCPP_WIN32API
+
+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));
+}
+
+#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;
+    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 descriptor = -1) : name(*p), fd(descriptor) {}
+};
+
+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
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
+
+_LIBCPP_DIAGNOSTIC_POP
+
+#endif // FILESYSTEM_FILE_DESCRIPTOR_H
diff --git a/libcxx/src/filesystem/filesystem_clock.cpp b/libcxx/src/filesystem/filesystem_clock.cpp
new file mode 100644 (file)
index 0000000..c6b3c89
--- /dev/null
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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>
+
+#include "posix_compat.h" // TimeSpec
+
+#if defined(_LIBCPP_WIN32API)
+# define WIN32_LEAN_AND_MEAN
+# define NOMINMAX
+# include <windows.h>
+#endif
+
+#if !defined(CLOCK_REALTIME) && !defined(_LIBCPP_WIN32API)
+# include <sys/time.h> // for gettimeofday and timeval
+#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(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
+}
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
diff --git a/libcxx/src/filesystem/filesystem_error.cpp b/libcxx/src/filesystem/filesystem_error.cpp
new file mode 100644 (file)
index 0000000..5faed3b
--- /dev/null
@@ -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/libcxx/src/filesystem/format_string.h b/libcxx/src/filesystem/format_string.h
new file mode 100644 (file)
index 0000000..d95d9bd
--- /dev/null
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===////
+//
+// 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
+
+// TODO: Check whether these functions actually need internal linkage, or if they can be made normal header functions
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-template")
+
+_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
+
+namespace detail {
+namespace {
+
+_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(static_cast<size_t>(ret) == (size_with_null - 1), "TODO");
+  }
+  return result;
+}
+
+_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 anonymous namespace
+} // end namespace detail
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
+
+_LIBCPP_DIAGNOSTIC_POP
+
+#endif // FILESYSTEM_FORMAT_STRING_H
index af31fe0..f6c3ff1 100644 (file)
@@ -7,6 +7,7 @@
 //===----------------------------------------------------------------------===//
 
 #include <__assert>
+#include <__config>
 #include <__utility/unreachable.h>
 #include <array>
 #include <climits>
 #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
 # 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);
-  }
-
-  PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
-#if defined(_LIBCPP_WIN32API)
-    if (PosPtr Ret = consumeDriveLetter(P, End))
-      return Ret;
-    if (PosPtr Ret = consumeNetworkRoot(P, End))
-      return Ret;
-#endif
-    return nullptr;
-  }
-};
-
-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;
@@ -642,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();
@@ -1193,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);
@@ -1214,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) {
@@ -1609,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/libcxx/src/filesystem/path.cpp b/libcxx/src/filesystem/path.cpp
new file mode 100644 (file)
index 0000000..82f1ba7
--- /dev/null
@@ -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/libcxx/src/filesystem/path_parser.h b/libcxx/src/filesystem/path_parser.h
new file mode 100644 (file)
index 0000000..4cc75a5
--- /dev/null
@@ -0,0 +1,379 @@
+//===----------------------------------------------------------------------===//
+//
+// 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"
+
+// TODO: Check whether these functions actually need internal linkage, or if they can be made normal header functions
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-template")
+
+_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);
+  }
+
+  PosPtr consumeRootName(PosPtr P, PosPtr End) const noexcept {
+#if defined(_LIBCPP_WIN32API)
+    if (PosPtr Ret = consumeDriveLetter(P, End))
+      return Ret;
+    if (PosPtr Ret = consumeNetworkRoot(P, End))
+      return Ret;
+#endif
+    return nullptr;
+  }
+};
+
+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
+
+_LIBCPP_END_NAMESPACE_FILESYSTEM
+
+_LIBCPP_DIAGNOSTIC_POP
+
+#endif // PATH_PARSER_H
index 36116ec..bddead7 100644 (file)
 #define POSIX_COMPAT_H
 
 #include <__assert>
+#include <__config>
 #include <filesystem>
 
-#include "filesystem_common.h"
+#include "error.h"
 
 #if defined(_LIBCPP_WIN32API)
 # define WIN32_LEAN_AND_MEAN
 # include <io.h>
 # include <winioctl.h>
 #else
+# include <fcntl.h>
 # include <unistd.h>
 # include <sys/stat.h>
 # include <sys/statvfs.h>
 #endif
+#include <stdlib.h>
 #include <time.h>
 
 #if defined(_LIBCPP_WIN32API)
@@ -71,12 +74,53 @@ struct LIBCPP_REPARSE_DATA_BUFFER {
 };
 #endif
 
+// TODO: Check whether these functions actually need internal linkage, or if they can be made normal header functions
+_LIBCPP_DIAGNOSTIC_PUSH
+_LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-function")
+_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-template")
+
 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
 
 namespace detail {
 namespace {
 
 #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 which does exactly what we want,
+// along with our own stat structure and flag macros.
+
+struct TimeSpec {
+  int64_t tv_sec;
+  int64_t tv_nsec;
+};
+struct StatT {
+  unsigned st_mode;
+  TimeSpec st_atim;
+  TimeSpec st_mtim;
+  uint64_t st_dev; // FILE_ID_INFO::VolumeSerialNumber
+  struct FileIdStruct {
+    unsigned char id[16]; // FILE_ID_INFO::FileId
+    bool operator==(const FileIdStruct &other) const {
+      for (int i = 0; i < 16; i++)
+        if (id[i] != other.id[i])
+          return false;
+      return true;
+    }
+  } st_ino;
+  uint32_t st_nlink;
+  uintmax_t st_size;
+};
+
+#else
+using TimeSpec = struct timespec;
+using TimeVal = struct timeval;
+using StatT = struct stat;
+#endif
+
+#if defined(_LIBCPP_WIN32API)
 
 // Various C runtime header sets provide more or less of these. As we
 // provide our own implementation, undef all potential defines from the
@@ -400,7 +444,7 @@ int fchmod_handle(HANDLE h, int perms) {
   return 0;
 }
 
-int fchmodat(int fd, const wchar_t *path, int perms, int flag) {
+int fchmodat(int /*fd*/, const wchar_t *path, int perms, int flag) {
   DWORD attributes = GetFileAttributesW(path);
   if (attributes == INVALID_FILE_ATTRIBUTES)
     return set_errno();
@@ -519,4 +563,6 @@ using SSizeT = ::ssize_t;
 
 _LIBCPP_END_NAMESPACE_FILESYSTEM
 
+_LIBCPP_DIAGNOSTIC_POP
+
 #endif // POSIX_COMPAT_H
similarity index 55%
rename from libcxx/src/filesystem/filesystem_common.h
rename to libcxx/src/filesystem/time_utils.h
index a1756cf..2e299b8 100644 (file)
@@ -6,34 +6,30 @@
 //
 //===----------------------------------------------------------------------===////
 
-#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 <cerrno>
 #include <chrono>
-#include <climits>
-#include <cstdarg>
-#include <ctime>
 #include <filesystem>
+#include <limits>
 #include <ratio>
-#include <utility>
 #include <system_error>
+#include <type_traits>
+#include <utility>
+
+#include "error.h"
+#include "format_string.h"
+#include "posix_compat.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 <sys/time.h> // for ::utimes as used in __last_write_time
-# include <unistd.h>
-#endif // defined(_LIBCPP_WIN32API)
+#endif
 
 // We can use the presence of UTIME_OMIT to detect platforms that provide utimensat.
 #if defined(UTIME_OMIT)
@@ -46,240 +42,14 @@ _LIBCPP_GCC_DIAGNOSTIC_IGNORED("-Wunused-function")
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-function")
 _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wunused-template")
 
-#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 {
-
-#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_HAS_NO_EXCEPTIONS
-  try {
-#endif // _LIBCPP_HAS_NO_EXCEPTIONS
-    ret = format_string_impl(msg, ap);
-#ifndef _LIBCPP_HAS_NO_EXCEPTIONS
-  } catch (...) {
-    va_end(ap);
-    throw;
-  }
-#endif // _LIBCPP_HAS_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_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;
-};
-
 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.
-
-struct TimeSpec {
-  int64_t tv_sec;
-  int64_t tv_nsec;
-};
-struct StatT {
-  unsigned st_mode;
-  TimeSpec st_atim;
-  TimeSpec st_mtim;
-  uint64_t st_dev; // FILE_ID_INFO::VolumeSerialNumber
-  struct FileIdStruct {
-    unsigned char id[16]; // FILE_ID_INFO::FileId
-    bool operator==(const FileIdStruct &other) const {
-      for (int i = 0; i < 16; i++)
-        if (id[i] != other.id[i])
-          return false;
-      return true;
-    }
-  } st_ino;
-  uint32_t st_nlink;
-  uintmax_t st_size;
-};
-
-#else
-using TimeSpec = struct timespec;
-using TimeVal = struct timeval;
-using StatT = struct stat;
-#endif
-
 template <class FileTimeT, class TimeT,
           bool IsFloat = is_floating_point<typename FileTimeT::rep>::value>
 struct time_util_base {
@@ -307,7 +77,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)));
@@ -327,7 +97,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>
@@ -531,75 +303,19 @@ 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)
-
-template <class DirEntT>
-static file_type get_file_type(DirEntT*, long) {
-  return file_type::none;
-}
+#endif // !_LIBCPP_WIN32API
 
-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)};
-  }
-}
+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);
 
-#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;
+  return fs_time::convert_from_timespec(ts);
 }
-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));
-}
-
-#endif // !_LIBCPP_WIN32API
 
 } // namespace
 } // end namespace detail
@@ -608,4 +324,4 @@ _LIBCPP_END_NAMESPACE_FILESYSTEM
 
 _LIBCPP_DIAGNOSTIC_POP
 
-#endif // FILESYSTEM_COMMON_H
+#endif // FILESYSTEM_TIME_UTILS_H
index 4c6ac40..e581963 100644 (file)
@@ -7,10 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 // UNSUPPORTED: c++03
-// ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../../../src/filesystem
+// ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../../../src
 
-// This test relies on calling functions from the libcxx internal header
-// filesystem_common.h; the Windows implementation uses different
+// This test relies on calling functions from the libcxx internal headers
+// of <filesystem>; the Windows implementation uses different
 // internals and doesn't provide the same set_file_times function as for
 // other platforms.
 // UNSUPPORTED: windows
@@ -30,7 +30,7 @@
 #include "test_macros.h"
 #include "filesystem_test_helper.h"
 
-#include "filesystem_common.h"
+#include "filesystem/time_utils.h"
 
 using namespace fs::detail;
 
index b86282c..2d057cb 100644 (file)
@@ -12,7 +12,7 @@
 
 // typedef TrivialClock file_time_type;
 
-// ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../src/filesystem -Wno-macro-redefined
+// ADDITIONAL_COMPILE_FLAGS: -I %S/../../../../src -Wno-macro-redefined
 
 #include <cassert>
 #include <chrono>
@@ -22,7 +22,7 @@
 #include <string>
 #include <type_traits>
 
-#include "filesystem_common.h"
+#include "filesystem/time_utils.h"
 
 #ifndef __SIZEOF_INT128__
 #define TEST_HAS_NO_INT128_T
index 2281cc2..dfe30a2 100644 (file)
@@ -790,11 +790,18 @@ libcxx/src/condition_variable_destructor.cpp
 libcxx/src/debug.cpp
 libcxx/src/exception.cpp
 libcxx/src/experimental/memory_resource.cpp
+libcxx/src/filesystem/directory_entry.cpp
 libcxx/src/filesystem/directory_iterator.cpp
-libcxx/src/filesystem/filesystem_common.h
+libcxx/src/filesystem/error.h
+libcxx/src/filesystem/file_descriptor.h
+libcxx/src/filesystem/filesystem_clock.cpp
+libcxx/src/filesystem/filesystem_error.cpp
 libcxx/src/filesystem/int128_builtins.cpp
 libcxx/src/filesystem/operations.cpp
+libcxx/src/filesystem/path.cpp
+libcxx/src/filesystem/path_parser.h
 libcxx/src/filesystem/posix_compat.h
+libcxx/src/filesystem/time_utils.h
 libcxx/src/functional.cpp
 libcxx/src/future.cpp
 libcxx/src/hash.cpp