commit 7bb46da1e629300e84641a59303d32ecbee7047c
Author: Jonathan Wakely <jwakely@redhat.com>
Date: Fri May 15 13:06:09 2015 +0100
* src/filesystem/ops.cc (stat_type): Define alias for struct stat and
use throughout the file.
(make_file_type): New function.
(file_size(const path&, error_code&)): Report an error for anything
that isn't a regular file.
(status(const path&), symlink_status(const path&)): Do not throw for
file_type::not_found.
(temp_directory_path()): Check additional environment variables.
* testsuite/experimental/filesystem/operations/exists.cc: New.
* testsuite/experimental/filesystem/operations/file_size.cc: New.
* testsuite/experimental/filesystem/operations/status.cc: New.
* testsuite/experimental/filesystem/operations/temp_directory_path.cc:
New.
@@ -150,33 +150,39 @@ namespace
#ifdef _GLIBCXX_HAVE_SYS_STAT_H
namespace
{
- fs::file_status
- make_file_status(const struct ::stat& st)
+ typedef struct ::stat stat_type;
+
+ inline fs::file_type
+ make_file_type(const stat_type& st)
{
- using fs::file_status;
using fs::file_type;
- using fs::perms;
- file_type ft;
- perms perm = static_cast<perms>(st.st_mode) & perms::mask;
#ifdef _GLIBCXX_HAVE_S_ISREG
if (S_ISREG(st.st_mode))
- ft = file_type::regular;
+ return file_type::regular;
else if (S_ISDIR(st.st_mode))
- ft = file_type::directory;
+ return file_type::directory;
else if (S_ISCHR(st.st_mode))
- ft = file_type::character;
+ return file_type::character;
else if (S_ISBLK(st.st_mode))
- ft = file_type::block;
+ return file_type::block;
else if (S_ISFIFO(st.st_mode))
- ft = file_type::fifo;
+ return file_type::fifo;
else if (S_ISLNK(st.st_mode))
- ft = file_type::symlink;
+ return file_type::symlink;
else if (S_ISSOCK(st.st_mode))
- ft = file_type::socket;
- else
+ return file_type::socket;
#endif
- ft = file_type::unknown;
- return file_status{ft, perm};
+ return file_type::unknown;
+
+ }
+
+ inline fs::file_status
+ make_file_status(const stat_type& st)
+ {
+ return fs::file_status{
+ make_file_type(st),
+ static_cast<fs::perms>(st.st_mode) & fs::perms::mask
+ };
}
inline bool
@@ -186,7 +192,7 @@ namespace
}
inline fs::file_time_type
- file_time(const struct ::stat& st)
+ file_time(const stat_type& st)
{
using namespace std::chrono;
return fs::file_time_type{
@@ -201,10 +207,10 @@ namespace
bool
do_copy_file(const fs::path& from, const fs::path& to,
fs::copy_options option,
- struct ::stat* from_st, struct ::stat* to_st,
+ stat_type* from_st, stat_type* to_st,
std::error_code& ec) noexcept
{
- struct ::stat st1, st2;
+ stat_type st1, st2;
fs::file_status t, f;
if (to_st == nullptr)
@@ -342,7 +348,7 @@ fs::copy(const path& from, const path& to, copy_options options,
bool use_lstat = create_symlinks || skip_symlinks;
file_status f, t;
- struct ::stat from_st, to_st;
+ stat_type from_st, to_st;
if (use_lstat
? ::lstat(from.c_str(), &from_st)
: ::stat(from.c_str(), &from_st))
@@ -564,7 +570,7 @@ fs::create_directory(const path& p, const path& attributes,
error_code& ec) noexcept
{
#ifdef _GLIBCXX_HAVE_SYS_STAT_H
- struct ::stat st;
+ stat_type st;
if (::stat(attributes.c_str(), &st))
{
ec.assign(errno, std::generic_category());
@@ -747,7 +753,7 @@ bool
fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
{
#ifdef _GLIBCXX_HAVE_SYS_STAT_H
- struct ::stat st1, st2;
+ stat_type st1, st2;
if (::stat(p1.c_str(), &st1) == 0 && ::stat(p2.c_str(), &st2) == 0)
{
file_status s1 = make_file_status(st1);
@@ -789,7 +795,7 @@ namespace
do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
{
#ifdef _GLIBCXX_HAVE_SYS_STAT_H
- struct ::stat st;
+ stat_type st;
if (::stat(p.c_str(), &st))
{
ec.assign(errno, std::generic_category());
@@ -807,8 +813,24 @@ namespace
std::uintmax_t
fs::file_size(const path& p, error_code& ec) noexcept
{
- return do_stat(p, ec, std::mem_fn(&stat::st_size),
- static_cast<uintmax_t>(-1));
+ struct S
+ {
+ S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
+ S() : type(file_type::not_found) { }
+ file_type type;
+ size_t size;
+ };
+ auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
+ if (s.type == file_type::regular)
+ return s.size;
+ if (!ec)
+ {
+ if (s.type == file_type::directory)
+ ec = std::make_error_code(std::errc::is_a_directory);
+ else
+ ec = std::make_error_code(std::errc::not_supported);
+ }
+ return -1;
}
std::uintmax_t
@@ -938,7 +960,7 @@ fs::read_symlink(const path& p)
fs::path fs::read_symlink(const path& p, error_code& ec)
{
#ifdef _GLIBCXX_HAVE_SYS_STAT_H
- struct ::stat st;
+ stat_type st;
if (::lstat(p.c_str(), &st))
{
ec.assign(errno, std::generic_category());
@@ -1094,13 +1116,13 @@ fs::file_status
fs::status(const fs::path& p, std::error_code& ec) noexcept
{
file_status status;
- struct ::stat st;
+ stat_type st;
if (::stat(p.c_str(), &st))
{
int err = errno;
ec.assign(err, std::generic_category());
if (is_not_found_errno(err))
- status = file_status{file_type::not_found};
+ status.type(file_type::not_found);
}
else
{
@@ -1114,13 +1136,13 @@ fs::file_status
fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
{
file_status status;
- struct ::stat st;
+ stat_type st;
if (::lstat(p.c_str(), &st))
{
int err = errno;
ec.assign(err, std::generic_category());
if (is_not_found_errno(err))
- status = file_status{file_type::not_found};
+ status.type(file_type::not_found);
}
else
{
@@ -1135,20 +1157,20 @@ fs::file_status
fs::status(const fs::path& p)
{
std::error_code ec;
- auto s = status(p, ec);
- if (ec.value())
+ auto result = status(p, ec);
+ if (result.type() == file_type::none)
_GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
- return s;
+ return result;
}
fs::file_status
fs::symlink_status(const fs::path& p)
{
std::error_code ec;
- auto s = symlink_status(p, ec);
- if (ec.value())
- _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", ec));
- return s;
+ auto result = symlink_status(p, ec);
+ if (result.type() == file_type::none)
+ _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
+ return result;
}
fs::path
@@ -1194,11 +1216,18 @@ fs::path fs::temp_directory_path(error_code& ec)
ec = std::make_error_code(std::errc::not_supported);
return {}; // TODO
#else
- const char* tmpdir = ::getenv("TMPDIR");
- if (!tmpdir)
- tmpdir = "/tmp";
- ec.clear();
- return tmpdir;
+ 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";
+ if (exists(p) && is_directory(p))
+ {
+ ec.clear();
+ return p;
+ }
+ ec = std::make_error_code(std::errc::not_a_directory);
+ return {};
#endif
}
new file mode 100644
@@ -0,0 +1,58 @@
+// Copyright (C) 2015 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++11 -lstdc++fs" }
+// { dg-require-filesystem-ts "" }
+
+#include <experimental/filesystem>
+#include <testsuite_hooks.h>
+
+using std::experimental::filesystem::path;
+
+void
+test01()
+{
+ VERIFY( exists(path{"/"}) );
+ VERIFY( exists(path{"/."}) );
+ VERIFY( exists(path{"."}) );
+}
+
+void
+test02()
+{
+ path rel{"xXxXx"};
+ while (exists(rel))
+ rel /= "x";
+ VERIFY( !exists(rel) );
+}
+
+void
+test03()
+{
+ path abs{"/xXxXx"};
+ while (exists(abs))
+ abs /= "x";
+ VERIFY( !exists(abs) );
+}
+
+int
+main()
+{
+ test01();
+ test02();
+ test03();
+}
new file mode 100644
@@ -0,0 +1,70 @@
+// Copyright (C) 2015 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++11 -lstdc++fs" }
+// { dg-require-filesystem-ts "" }
+
+#include <experimental/filesystem>
+#include <testsuite_hooks.h>
+#include <testsuite_fs.h>
+
+namespace fs = std::experimental::filesystem;
+
+void
+test01()
+{
+ std::error_code ec;
+ size_t size = fs::file_size(".", ec);
+ VERIFY( ec == std::errc::is_a_directory );
+ VERIFY( size == -1 );
+
+ try {
+ size = fs::file_size(".");
+ ec.clear();
+ } catch (const fs::filesystem_error& e) {
+ ec = e.code();
+ }
+ VERIFY( ec == std::errc::is_a_directory );
+ VERIFY( size == -1 );
+}
+
+void
+test02()
+{
+ fs::path p = __gnu_test::nonexistent_path();
+
+ std::error_code ec;
+ size_t size = fs::file_size(p, ec);
+ VERIFY( ec );
+ VERIFY( size == -1 );
+
+ try {
+ size = fs::file_size(p);
+ ec.clear();
+ } catch (const fs::filesystem_error& e) {
+ ec = e.code();
+ }
+ VERIFY( ec );
+ VERIFY( size == -1 );
+}
+
+int
+main()
+{
+ test01();
+ test02();
+}
new file mode 100644
@@ -0,0 +1,58 @@
+// Copyright (C) 2015 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++11 -lstdc++fs" }
+// { dg-require-filesystem-ts "" }
+
+#include <experimental/filesystem>
+#include <testsuite_hooks.h>
+#include <testsuite_fs.h>
+
+namespace fs = std::experimental::filesystem;
+
+void
+test01()
+{
+ std::error_code ec;
+ fs::file_status st1 = fs::status(".", ec);
+ VERIFY( !ec );
+ VERIFY( st1.type() == fs::file_type::directory );
+
+ fs::file_status st2 = fs::status(".");
+ VERIFY( st2.type() == fs::file_type::directory );
+}
+
+void
+test02()
+{
+ fs::path p = __gnu_test::nonexistent_path();
+
+ std::error_code ec;
+ fs::file_status st1 = fs::status(p, ec);
+ VERIFY( ec );
+ VERIFY( st1.type() == fs::file_type::not_found );
+
+ fs::file_status st2 = fs::status(p);
+ VERIFY( st2.type() == fs::file_type::not_found );
+}
+
+int
+main()
+{
+ test01();
+ test02();
+}
new file mode 100644
@@ -0,0 +1,80 @@
+// Copyright (C) 2015 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3. If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++11 -lstdc++fs" }
+// { dg-require-filesystem-ts "" }
+
+#include <experimental/filesystem>
+#include <stdlib.h>
+#include <testsuite_hooks.h>
+#include <testsuite_fs.h>
+
+void
+clean_env()
+{
+ ::unsetenv("TMPDIR");
+ ::unsetenv("TMP");
+ ::unsetenv("TEMPDIR");
+ ::unsetenv("TEMP");
+}
+
+namespace fs = std::experimental::filesystem;
+
+void
+test01()
+{
+ clean_env();
+
+ if (!fs::exists("/tmp"))
+ return; // just give up
+
+ std::error_code ec;
+ fs::path p1 = fs::temp_directory_path(ec);
+ VERIFY( exists(p1) );
+
+ fs::path p2 = fs::temp_directory_path();
+ VERIFY( p1 == p2 );
+}
+
+void
+test02()
+{
+ clean_env();
+
+ if (::setenv("TMPDIR", __gnu_test::nonexistent_path().string().c_str(), 1))
+ return; // just give up
+
+ std::error_code ec;
+ fs::path p = fs::temp_directory_path(ec);
+ VERIFY( ec );
+
+ std::error_code ec2;
+ try {
+ p = fs::temp_directory_path();
+ } catch (const fs::filesystem_error& e) {
+ ec2 = e.code();
+ }
+ VERIFY( ec2 == ec );
+}
+
+
+int
+main()
+{
+ test01();
+ test02();
+}
@@ -25,6 +25,11 @@
#include <experimental/filesystem>
#include <iostream>
#include <string>
+#include <cstdio>
+#if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
+# include <stdlib.h>
+# include <unistd.h>
+#endif
namespace __gnu_test
{
@@ -63,5 +68,31 @@ namespace __gnu_test
"a", "a/b", "a/b/", "a/b/c", "a/b/c.d", "a/b/..", "a/b/c.", "a/b/.c"
};
+ // This is NOT supposed to be a secure way to get a unique name!
+ // We just need a path that doesn't exist for testing purposes.
+ std::experimental::filesystem::path
+ nonexistent_path()
+ {
+ std::experimental::filesystem::path p;
+#if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L
+ char tmp[] = "test.XXXXXX";
+ int fd = ::mkstemp(tmp);
+ if (fd == -1)
+ throw std::experimental::filesystem::filesystem_error("mkstemp failed",
+ std::error_code(errno, std::generic_category()));
+ ::unlink(tmp);
+ ::close(fd);
+ p = tmp;
+#else
+ char* tmp = tempnam(".", "test.");
+ if (!tmp)
+ throw std::experimental::filesystem::filesystem_error("tempnam failed",
+ std::error_code(errno, std::generic_category()));
+ p = tmp;
+ ::free(tmp);
+#endif
+ return p;
+ }
+
} // namespace __gnu_test
#endif