diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 2311e934503..0f1089c6d2b 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,19 @@ +2015-05-15 Jonathan Wakely + + * 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. + 2015-05-14 Nathan Myers Jonathan Wakely diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc index f24cc19f1ef..661685aa04a 100644 --- a/libstdc++-v3/src/filesystem/ops.cc +++ b/libstdc++-v3/src/filesystem/ops.cc @@ -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(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(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(-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 } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc new file mode 100644 index 00000000000..0f1e5aaf203 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc @@ -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 +// . + +// { dg-options "-std=gnu++11 -lstdc++fs" } +// { dg-require-filesystem-ts "" } + +#include +#include + +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(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/file_size.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/file_size.cc new file mode 100644 index 00000000000..04fa7bb614d --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/file_size.cc @@ -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 +// . + +// { dg-options "-std=gnu++11 -lstdc++fs" } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +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(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/status.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/status.cc new file mode 100644 index 00000000000..2c54494be29 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/status.cc @@ -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 +// . + +// { dg-options "-std=gnu++11 -lstdc++fs" } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +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(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc new file mode 100644 index 00000000000..2aacd1c38c3 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc @@ -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 +// . + +// { dg-options "-std=gnu++11 -lstdc++fs" } +// { dg-require-filesystem-ts "" } + +#include +#include +#include +#include + +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(); +} diff --git a/libstdc++-v3/testsuite/util/testsuite_fs.h b/libstdc++-v3/testsuite/util/testsuite_fs.h index 0fb3a459d93..f404a7a7ff8 100644 --- a/libstdc++-v3/testsuite/util/testsuite_fs.h +++ b/libstdc++-v3/testsuite/util/testsuite_fs.h @@ -25,6 +25,11 @@ #include #include #include +#include +#if defined(_GNU_SOURCE) || _XOPEN_SOURCE >= 500 || _POSIX_C_SOURCE >= 200112L +# include +# include +#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