]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libstdc++-v3/src/filesystem/ops.cc
PR libstdc++/78870 support std::filesystem on Windows
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / ops.cc
index 4a9e265d1d641baefbf614356e3338846f322ae1..40cadbf627014a9a42adfb1dbdec7bd263496394 100644 (file)
 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
 # include <utime.h> // utime
 #endif
+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+# include <windows.h>
+#endif
 
 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
   namespace experimental { namespace filesystem {
 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
 #include "ops-common.h"
 
-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
-# undef utime
-# define utime _wutime
-# undef chmod
-# define chmod _wchmod
-#endif
-
 namespace fs = std::experimental::filesystem;
+namespace posix = std::filesystem::__gnu_posix;
 
 fs::path
 fs::absolute(const path& p, const path& base)
@@ -109,7 +106,7 @@ namespace
     void operator()(void* p) const { ::free(p); }
   };
 
-  using char_ptr = std::unique_ptr<char[], free_as_in_malloc>;
+  using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
 }
 
 fs::path
@@ -122,7 +119,8 @@ fs::canonical(const path& p, const path& base, error_code& ec)
   char_ptr buf{ nullptr };
 # if _XOPEN_VERSION < 700
   // Not safe to call realpath(path, NULL)
-  buf.reset( (char*)::malloc(PATH_MAX) );
+  using char_type = fs::path::value_type;
+  buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
 # endif
   if (char* rp = ::realpath(pa.c_str(), buf.get()))
     {
@@ -241,12 +239,13 @@ namespace
   using std::filesystem::is_set;
 
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
-  typedef struct ::stat stat_type;
+  using posix::stat_type;
 
   using std::filesystem::is_not_found_errno;
   using std::filesystem::file_time;
   using std::filesystem::do_copy_file;
 #endif // _GLIBCXX_HAVE_SYS_STAT_H
+
 } // namespace
 
 void
@@ -263,15 +262,15 @@ fs::copy(const path& from, const path& to, copy_options options,
   // _GLIBCXX_RESOLVE_LIB_DEFECTS
   // 2681. filesystem::copy() cannot copy symlinks
   if (use_lstat || copy_symlinks
-      ? ::lstat(from.c_str(), &from_st)
-      : ::stat(from.c_str(), &from_st))
+      ? posix::lstat(from.c_str(), &from_st)
+      : posix::stat(from.c_str(), &from_st))
     {
       ec.assign(errno, std::generic_category());
       return;
     }
   if (use_lstat
-      ? ::lstat(to.c_str(), &to_st)
-      : ::stat(to.c_str(), &to_st))
+      ? posix::lstat(to.c_str(), &to_st)
+      : posix::stat(to.c_str(), &to_st))
     {
       if (!is_not_found_errno(errno))
        {
@@ -459,8 +458,8 @@ namespace
   {
     bool created = false;
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
-    ::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
-    if (::mkdir(p.c_str(), mode))
+    posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
+    if (posix::mkdir(p.c_str(), mode))
       {
        const int err = errno;
        if (err != EEXIST || !is_directory(p, ec))
@@ -513,7 +512,7 @@ fs::create_directory(const path& p, const path& attributes,
 {
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
   stat_type st;
-  if (::stat(attributes.c_str(), &st))
+  if (posix::stat(attributes.c_str(), &st))
     {
       ec.assign(errno, std::generic_category());
       return false;
@@ -562,11 +561,16 @@ void
 fs::create_hard_link(const path& to, const path& new_hard_link,
                     error_code& ec) noexcept
 {
-#ifdef _GLIBCXX_HAVE_UNISTD_H
+#ifdef _GLIBCXX_HAVE_LINK
   if (::link(to.c_str(), new_hard_link.c_str()))
     ec.assign(errno, std::generic_category());
   else
     ec.clear();
+#elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
+    ec.clear();
+  else
+    ec.assign((int)GetLastError(), generic_category());
 #else
   ec = std::make_error_code(std::errc::not_supported);
 #endif
@@ -586,7 +590,7 @@ void
 fs::create_symlink(const path& to, const path& new_symlink,
                   error_code& ec) noexcept
 {
-#ifdef _GLIBCXX_HAVE_UNISTD_H
+#ifdef _GLIBCXX_HAVE_SYMLINK
   if (::symlink(to.c_str(), new_symlink.c_str()))
     ec.assign(errno, std::generic_category());
   else
@@ -596,7 +600,6 @@ fs::create_symlink(const path& to, const path& new_symlink,
 #endif
 }
 
-
 fs::path
 fs::current_path()
 {
@@ -612,8 +615,8 @@ fs::current_path(error_code& ec)
 {
   path p;
 #ifdef _GLIBCXX_HAVE_UNISTD_H
-#ifdef __GLIBC__
-  if (char_ptr cwd = char_ptr{::getcwd(nullptr, 0)})
+#if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
     {
       p.assign(cwd.get());
       ec.clear();
@@ -621,6 +624,7 @@ fs::current_path(error_code& ec)
   else
     ec.assign(errno, std::generic_category());
 #else
+#ifdef _PC_PATH_MAX
   long path_max = pathconf(".", _PC_PATH_MAX);
   size_t size;
   if (path_max == -1)
@@ -629,9 +633,15 @@ fs::current_path(error_code& ec)
       size = 10240;
   else
       size = path_max;
+#elif defined(PATH_MAX)
+  size_t size = PATH_MAX;
+#else
+  size_t size = 1024;
+#endif
   for (char_ptr buf; p.empty(); size *= 2)
     {
-      buf.reset((char*)malloc(size));
+      using char_type = fs::path::value_type;
+      buf.reset((char_type*)malloc(size * sizeof(char_type)));
       if (buf)
        {
          if (getcwd(buf.get(), size))
@@ -671,7 +681,7 @@ void
 fs::current_path(const path& p, error_code& ec) noexcept
 {
 #ifdef _GLIBCXX_HAVE_UNISTD_H
-  if (::chdir(p.c_str()))
+  if (posix::chdir(p.c_str()))
     ec.assign(errno, std::generic_category());
   else
     ec.clear();
@@ -698,14 +708,14 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
   int err = 0;
   file_status s1, s2;
   stat_type st1, st2;
-  if (::stat(p1.c_str(), &st1) == 0)
+  if (posix::stat(p1.c_str(), &st1) == 0)
     s1 = make_file_status(st1);
   else if (is_not_found_errno(errno))
     s1.type(file_type::not_found);
   else
     err = errno;
 
-  if (::stat(p2.c_str(), &st2) == 0)
+  if (posix::stat(p2.c_str(), &st2) == 0)
     s2 = make_file_status(st2);
   else if (is_not_found_errno(errno))
     s2.type(file_type::not_found);
@@ -755,7 +765,7 @@ namespace
     {
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
       stat_type st;
-      if (::stat(p.c_str(), &st))
+      if (posix::stat(p.c_str(), &st))
        {
          ec.assign(errno, std::generic_category());
          return deflt;
@@ -805,7 +815,7 @@ fs::hard_link_count(const path& p)
 std::uintmax_t
 fs::hard_link_count(const path& p, error_code& ec) noexcept
 {
-  return do_stat(p, ec, std::mem_fn(&stat::st_nlink),
+  return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
                 static_cast<uintmax_t>(-1));
 }
 
@@ -881,11 +891,11 @@ fs::last_write_time(const path& p __attribute__((__unused__)),
   else
     ec.clear();
 #elif _GLIBCXX_HAVE_UTIME_H
-  ::utimbuf times;
+  posix::utimbuf times;
   times.modtime = s.count();
   times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
                         times.modtime);
-  if (::utime(p.c_str(), &times))
+  if (posix::utime(p.c_str(), &times))
     ec.assign(errno, std::generic_category());
   else
     ec.clear();
@@ -938,7 +948,7 @@ fs::permissions(const path& p, perms prms, error_code& ec) noexcept
 #else
   if (nofollow && is_symlink(st))
     ec = std::make_error_code(std::errc::operation_not_supported);
-  else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
+  else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
     err = errno;
 #endif
 
@@ -958,10 +968,10 @@ fs::read_symlink(const path& p)
   return tgt;
 }
 
-fs::path fs::read_symlink(const path& p, error_code& ec)
+fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
 {
   path result;
-#ifdef _GLIBCXX_HAVE_SYS_STAT_H
+#if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
   stat_type st;
   if (::lstat(p.c_str(), &st))
     {
@@ -1015,6 +1025,19 @@ fs::remove(const path& p)
 bool
 fs::remove(const path& p, error_code& ec) noexcept
 {
+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  if (exists(symlink_status(p, ec)))
+    {
+      if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
+         || DeleteFileW(p.c_str()))
+       {
+         ec.clear();
+         return true;
+       }
+      else if (!ec)
+       ec.assign((int)GetLastError(), generic_category());
+    }
+#else
   if (::remove(p.c_str()) == 0)
     {
       ec.clear();
@@ -1024,6 +1047,7 @@ fs::remove(const path& p, error_code& ec) noexcept
     ec.clear();
   else
     ec.assign(errno, std::generic_category());
+#endif
   return false;
 }
 
@@ -1077,7 +1101,7 @@ fs::rename(const path& from, const path& to)
 void
 fs::rename(const path& from, const path& to, error_code& ec) noexcept
 {
-  if (::rename(from.c_str(), to.c_str()))
+  if (posix::rename(from.c_str(), to.c_str()))
     ec.assign(errno, std::generic_category());
   else
     ec.clear();
@@ -1098,7 +1122,7 @@ fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
 #ifdef _GLIBCXX_HAVE_UNISTD_H
   if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
     ec.assign(EINVAL, std::generic_category());
-  else if (::truncate(p.c_str(), size))
+  else if (posix::truncate(p.c_str(), size))
     ec.assign(errno, std::generic_category());
   else
     ec.clear();
@@ -1126,23 +1150,14 @@ fs::space(const path& p, error_code& ec) noexcept
     static_cast<uintmax_t>(-1),
     static_cast<uintmax_t>(-1)
   };
-#ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
-  struct ::statvfs f;
-  if (::statvfs(p.c_str(), &f))
-      ec.assign(errno, std::generic_category());
-  else
-    {
-      uintmax_t fragment_size = f.f_frsize;
-      info = space_info{
-       f.f_blocks * fragment_size,
-       f.f_bfree * fragment_size,
-       f.f_bavail * fragment_size
-      };
-      ec.clear();
-    }
+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  path dir = absolute(p);
+  dir.remove_filename();
+  auto str = dir.c_str();
 #else
-  ec = std::make_error_code(std::errc::not_supported);
+  auto str = p.c_str();
 #endif
+  std::filesystem::do_space(str, info.capacity, info.free, info.available, ec);
   return info;
 }
 
@@ -1152,7 +1167,7 @@ fs::status(const fs::path& p, error_code& ec) noexcept
 {
   file_status status;
   stat_type st;
-  if (::stat(p.c_str(), &st))
+  if (posix::stat(p.c_str(), &st))
     {
       int err = errno;
       ec.assign(err, std::generic_category());
@@ -1176,7 +1191,7 @@ fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
 {
   file_status status;
   stat_type st;
-  if (::lstat(p.c_str(), &st))
+  if (posix::lstat(p.c_str(), &st))
     {
       int err = errno;
       ec.assign(err, std::generic_category());
@@ -1251,27 +1266,38 @@ fs::path fs::temp_directory_path()
 
 fs::path fs::temp_directory_path(error_code& ec)
 {
+  path p;
 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
-  ec = std::make_error_code(std::errc::not_supported);
-  return {}; // TODO
+  unsigned len = 1024;
+  std::wstring buf;
+  do
+    {
+      buf.resize(len);
+      len = GetTempPathW(buf.size(), buf.data());
+    } while (len > buf.size());
+
+  if (len == 0)
+    {
+      ec.assign((int)GetLastError(), std::system_category());
+      return p;
+    }
+  buf.resize(len);
+  p = std::move(buf);
 #else
   const char* tmpdir = nullptr;
   const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
   for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
     tmpdir = ::getenv(*e);
-  path p = tmpdir ? tmpdir : "/tmp";
+  p = tmpdir ? tmpdir : "/tmp";
   auto st = status(p, ec);
-  if (!ec)
+  if (ec)
+    p.clear();
+  else if (!is_directory(st))
     {
-      if (is_directory(st))
-       {
-         ec.clear();
-         return p;
-       }
-      else
-       ec = std::make_error_code(std::errc::not_a_directory);
+      p.clear();
+      ec = std::make_error_code(std::errc::not_a_directory);
     }
-  return {};
 #endif
+  return p;
 }