From 30362998612b17c3e016d0fc8f0b3b2ead666927 Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Wed, 16 Sep 2015 23:50:28 +0100 Subject: [PATCH] Implement filesystem::canonical() without realpath PR libstdc++/67173 * acinclude.m4 (GLIBCXX_CHECK_FILESYSTEM_DEPS): Check _XOPEN_VERSION and PATH_MAX for _GLIBCXX_USE_REALPATH. * config.h.in: Regenerate. * configure: Regenerate. * src/filesystem/ops.cc: (canonical) [!_GLIBCXX_USE_REALPATH]: Add alternative implementation. * testsuite/experimental/filesystem/operations/canonical.cc: New. * testsuite/experimental/filesystem/operations/exists.cc: Add more tests. * testsuite/experimental/filesystem/operations/absolute.cc: Add test variables. * testsuite/experimental/filesystem/operations/copy.cc: Likewise. * testsuite/experimental/filesystem/operations/current_path.cc: Likewise. * testsuite/experimental/filesystem/operations/file_size.cc: Likewise. * testsuite/experimental/filesystem/operations/status.cc: Likewise. * testsuite/experimental/filesystem/operations/temp_directory_path.cc: Likewise. From-SVN: r227836 --- libstdc++-v3/ChangeLog | 22 +++++ libstdc++-v3/acinclude.m4 | 17 +++- libstdc++-v3/config.h.in | 2 +- libstdc++-v3/configure | 30 +++++- libstdc++-v3/src/filesystem/ops.cc | 99 ++++++++++++++++--- .../filesystem/operations/absolute.cc | 4 + .../filesystem/operations/canonical.cc | 77 +++++++++++++++ .../filesystem/operations/copy.cc | 4 + .../filesystem/operations/current_path.cc | 4 + .../filesystem/operations/exists.cc | 17 ++-- .../filesystem/operations/file_size.cc | 4 + .../filesystem/operations/status.cc | 4 + .../operations/temp_directory_path.cc | 4 + 13 files changed, 262 insertions(+), 26 deletions(-) create mode 100644 libstdc++-v3/testsuite/experimental/filesystem/operations/canonical.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index d425f495196..b8c11843632 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,25 @@ +2015-09-16 Jonathan Wakely + + PR libstdc++/67173 + * acinclude.m4 (GLIBCXX_CHECK_FILESYSTEM_DEPS): Check _XOPEN_VERSION + and PATH_MAX for _GLIBCXX_USE_REALPATH. + * config.h.in: Regenerate. + * configure: Regenerate. + * src/filesystem/ops.cc: (canonical) [!_GLIBCXX_USE_REALPATH]: Add + alternative implementation. + * testsuite/experimental/filesystem/operations/canonical.cc: New. + * testsuite/experimental/filesystem/operations/exists.cc: Add more + tests. + * testsuite/experimental/filesystem/operations/absolute.cc: Add test + variables. + * testsuite/experimental/filesystem/operations/copy.cc: Likewise. + * testsuite/experimental/filesystem/operations/current_path.cc: + Likewise. + * testsuite/experimental/filesystem/operations/file_size.cc: Likewise. + * testsuite/experimental/filesystem/operations/status.cc: Likewise. + * testsuite/experimental/filesystem/operations/temp_directory_path.cc: + Likewise. + 2015-09-11 Jonathan Wakely PR libstdc++/67173 diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index 64c9b7e32cf..c133c25407b 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -3947,13 +3947,24 @@ dnl AC_MSG_CHECKING([for realpath]) AC_CACHE_VAL(glibcxx_cv_realpath, [dnl GCC_TRY_COMPILE_OR_LINK( - [#include ], - [char *tmp = realpath((const char*)NULL, (char*)NULL);], + [ + #include + #include + ], + [ + #if _XOPEN_VERSION < 500 + #error + #elif _XOPEN_VERSION >= 700 || defined(PATH_MAX) + char *tmp = realpath((const char*)NULL, (char*)NULL); + #else + #error + #endif + ], [glibcxx_cv_realpath=yes], [glibcxx_cv_realpath=no]) ]) if test $glibcxx_cv_realpath = yes; then - AC_DEFINE(_GLIBCXX_USE_REALPATH, 1, [Define if realpath is available in .]) + AC_DEFINE(_GLIBCXX_USE_REALPATH, 1, [Define if usable realpath is available in .]) fi AC_MSG_RESULT($glibcxx_cv_realpath) dnl diff --git a/libstdc++-v3/config.h.in b/libstdc++-v3/config.h.in index cc7a21e820c..58eef93f5ec 100644 --- a/libstdc++-v3/config.h.in +++ b/libstdc++-v3/config.h.in @@ -883,7 +883,7 @@ of TR1 (Chapter 5.1). */ #undef _GLIBCXX_USE_RANDOM_TR1 -/* Define if realpath is available in . */ +/* Define if usable realpath is available in . */ #undef _GLIBCXX_USE_REALPATH /* Defined if sched_yield is available. */ diff --git a/libstdc++-v3/configure b/libstdc++-v3/configure index 6d35f30a18a..dea0a2d3506 100755 --- a/libstdc++-v3/configure +++ b/libstdc++-v3/configure @@ -79178,11 +79178,22 @@ else if test x$gcc_no_link = xyes; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include + #include + int main () { -char *tmp = realpath((const char*)NULL, (char*)NULL); + + #if _XOPEN_VERSION < 500 + #error + #elif _XOPEN_VERSION >= 700 || defined(PATH_MAX) + char *tmp = realpath((const char*)NULL, (char*)NULL); + #else + #error + #endif + ; return 0; } @@ -79199,11 +79210,22 @@ else fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ -#include + + #include + #include + int main () { -char *tmp = realpath((const char*)NULL, (char*)NULL); + + #if _XOPEN_VERSION < 500 + #error + #elif _XOPEN_VERSION >= 700 || defined(PATH_MAX) + char *tmp = realpath((const char*)NULL, (char*)NULL); + #else + #error + #endif + ; return 0; } diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc index cefb927c6a5..b5c8eb9e111 100644 --- a/libstdc++-v3/src/filesystem/ops.cc +++ b/libstdc++-v3/src/filesystem/ops.cc @@ -96,23 +96,98 @@ namespace fs::path fs::canonical(const path& p, const path& base, error_code& ec) { - path can; + const path pa = absolute(p, base); + path result; #ifdef _GLIBCXX_USE_REALPATH - char* buffer = nullptr; -#if defined(__SunOS_5_10) && defined(PATH_MAX) - buffer = (char*)::malloc(PATH_MAX); -#endif - if (char_ptr rp = char_ptr{::realpath(absolute(p, base).c_str(), buffer)}) + char_ptr buf{ nullptr }; +# if _XOPEN_VERSION < 700 + // Not safe to call realpath(path, NULL) + buf.reset( (char*)::malloc(PATH_MAX) ); +# endif + if (char* rp = ::realpath(pa.c_str(), buf.get())) { - can.assign(rp.get()); + if (buf == nullptr) + buf.reset(rp); + result.assign(rp); ec.clear(); + return result; + } + if (errno != ENAMETOOLONG) + { + ec.assign(errno, std::generic_category()); + return result; } - else - ec.assign(errno, std::generic_category()); -#else - ec = std::make_error_code(std::errc::not_supported); #endif - return can; + + auto fail = [&ec, &result](int e) mutable { + if (!ec.value()) + ec.assign(e, std::generic_category()); + result.clear(); + }; + + if (!exists(pa, ec)) + { + fail(ENOENT); + return result; + } + // else we can assume no unresolvable symlink loops + + result = pa.root_path(); + + deque cmpts; + for (auto& f : pa.relative_path()) + cmpts.push_back(f); + + while (!cmpts.empty()) + { + path f = std::move(cmpts.front()); + cmpts.pop_front(); + + if (f.compare(".") == 0) + { + if (!is_directory(result, ec)) + { + fail(ENOTDIR); + break; + } + } + else if (f.compare("..") == 0) + { + auto parent = result.parent_path(); + if (parent.empty()) + result = pa.root_path(); + else + result.swap(parent); + } + else + { + result /= f; + + if (is_symlink(result, ec)) + { + path link = read_symlink(result, ec); + if (!ec.value()) + { + if (link.is_absolute()) + { + result = link.root_path(); + link = link.relative_path(); + } + else + result.remove_filename(); + + cmpts.insert(cmpts.begin(), link.begin(), link.end()); + } + } + + if (ec.value() || !exists(result, ec)) + { + fail(ENOENT); + break; + } + } + } + return result; } fs::path diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/absolute.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/absolute.cc index 14625b5bc2c..f7507f57726 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/absolute.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/absolute.cc @@ -29,6 +29,8 @@ using std::experimental::filesystem::path; void test01() { + bool test __attribute__((unused)) = false; + for (const path& p : __gnu_test::test_paths) VERIFY( absolute(p).is_absolute() ); } @@ -36,6 +38,8 @@ test01() void test02() { + bool test __attribute__((unused)) = false; + path p1("/"); VERIFY( absolute(p1) == p1 ); VERIFY( absolute(p1, "/bar") == p1 ); diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/canonical.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/canonical.cc new file mode 100644 index 00000000000..d752febf44a --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/canonical.cc @@ -0,0 +1,77 @@ +// 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() +{ + bool test __attribute__((unused)) = false; + + std::error_code ec; + auto p = __gnu_test::nonexistent_path(); + canonical( p, ec ); + VERIFY( ec ); + + p = fs::current_path(); + canonical( p, ec ); + VERIFY( !ec ); + + p = "/"; + p = canonical( p, ec ); + VERIFY( p == "/" ); + VERIFY( !ec ); + + p = "/."; + p = canonical( p, ec ); + VERIFY( p == "/" ); + VERIFY( !ec ); + + p = "/.."; + p = canonical( p, ec ); + VERIFY( p == "/" ); + VERIFY( !ec ); + + p = "/../.././."; + p = canonical( p, ec ); + VERIFY( p == "/" ); + VERIFY( !ec ); + + p = "/dev/stdin"; + if (exists(p)) + { + auto p2 = canonical(p); + if (is_symlink(p)) + VERIFY( p != p2 ); + else + VERIFY( p == p2 ); + VERIFY( canonical(p2) == p2 ); + } +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc index 2410c802f23..35d49f0f7f7 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc @@ -29,6 +29,8 @@ using std::experimental::filesystem::path; void test01() { + bool test __attribute__((unused)) = false; + for (const path& p : __gnu_test::test_paths) VERIFY( absolute(p).is_absolute() ); } @@ -36,6 +38,8 @@ test01() void test02() { + bool test __attribute__((unused)) = false; + path p1("/"); VERIFY( absolute(p1) == p1 ); VERIFY( absolute(p1, "/bar") == p1 ); diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/current_path.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/current_path.cc index c242ac0e399..81ade73ab56 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/current_path.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/current_path.cc @@ -29,6 +29,8 @@ namespace fs = std::experimental::filesystem; void test01() { + bool test __attribute__((unused)) = false; + fs::path dot("."); fs::path cwd = fs::current_path(); std::error_code ec; @@ -39,6 +41,8 @@ test01() void test02() { + bool test __attribute__((unused)) = false; + auto oldwd = fs::current_path(); auto tmpdir = fs::temp_directory_path(); current_path(tmpdir); diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc index 0f1e5aaf203..dba4a6f5a55 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc @@ -20,32 +20,37 @@ #include #include +#include using std::experimental::filesystem::path; void test01() { + bool test __attribute__((unused)) = false; + VERIFY( exists(path{"/"}) ); VERIFY( exists(path{"/."}) ); VERIFY( exists(path{"."}) ); + VERIFY( exists(path{".."}) ); + VERIFY( exists(std::experimental::filesystem::current_path()) ); } void test02() { - path rel{"xXxXx"}; - while (exists(rel)) - rel /= "x"; + bool test __attribute__((unused)) = false; + + path rel = __gnu_test::nonexistent_path(); VERIFY( !exists(rel) ); } void test03() { - path abs{"/xXxXx"}; - while (exists(abs)) - abs /= "x"; + bool test __attribute__((unused)) = false; + + path abs = absolute(__gnu_test::nonexistent_path()); VERIFY( !exists(abs) ); } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/file_size.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/file_size.cc index 04fa7bb614d..7603064d46a 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/file_size.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/file_size.cc @@ -27,6 +27,8 @@ namespace fs = std::experimental::filesystem; void test01() { + bool test __attribute__((unused)) = false; + std::error_code ec; size_t size = fs::file_size(".", ec); VERIFY( ec == std::errc::is_a_directory ); @@ -45,6 +47,8 @@ test01() void test02() { + bool test __attribute__((unused)) = false; + fs::path p = __gnu_test::nonexistent_path(); std::error_code ec; diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/status.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/status.cc index 2c54494be29..0f1730d82fc 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/status.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/status.cc @@ -27,6 +27,8 @@ namespace fs = std::experimental::filesystem; void test01() { + bool test __attribute__((unused)) = false; + std::error_code ec; fs::file_status st1 = fs::status(".", ec); VERIFY( !ec ); @@ -39,6 +41,8 @@ test01() void test02() { + bool test __attribute__((unused)) = false; + fs::path p = __gnu_test::nonexistent_path(); std::error_code ec; diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc index 2aacd1c38c3..bd9b6adf3e0 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc @@ -37,6 +37,8 @@ namespace fs = std::experimental::filesystem; void test01() { + bool test __attribute__((unused)) = false; + clean_env(); if (!fs::exists("/tmp")) @@ -53,6 +55,8 @@ test01() void test02() { + bool test __attribute__((unused)) = false; + clean_env(); if (::setenv("TMPDIR", __gnu_test::nonexistent_path().string().c_str(), 1)) -- 2.30.2