+2015-09-16 Jonathan Wakely <jwakely@redhat.com>
+
+ 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 <jwakely@redhat.com>
PR libstdc++/67173
AC_MSG_CHECKING([for realpath])
AC_CACHE_VAL(glibcxx_cv_realpath, [dnl
GCC_TRY_COMPILE_OR_LINK(
- [#include <stdlib.h>],
- [char *tmp = realpath((const char*)NULL, (char*)NULL);],
+ [
+ #include <stdlib.h>
+ #include <unistd.h>
+ ],
+ [
+ #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 <stdlib.h>.])
+ AC_DEFINE(_GLIBCXX_USE_REALPATH, 1, [Define if usable realpath is available in <stdlib.h>.])
fi
AC_MSG_RESULT($glibcxx_cv_realpath)
dnl
of TR1 (Chapter 5.1). */
#undef _GLIBCXX_USE_RANDOM_TR1
-/* Define if realpath is available in <stdlib.h>. */
+/* Define if usable realpath is available in <stdlib.h>. */
#undef _GLIBCXX_USE_REALPATH
/* Defined if sched_yield is available. */
if test x$gcc_no_link = xyes; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stdlib.h>
+
+ #include <stdlib.h>
+ #include <unistd.h>
+
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;
}
fi
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stdlib.h>
+
+ #include <stdlib.h>
+ #include <unistd.h>
+
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;
}
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<path> 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
void
test01()
{
+ bool test __attribute__((unused)) = false;
+
for (const path& p : __gnu_test::test_paths)
VERIFY( absolute(p).is_absolute() );
}
void
test02()
{
+ bool test __attribute__((unused)) = false;
+
path p1("/");
VERIFY( absolute(p1) == p1 );
VERIFY( absolute(p1, "/bar") == p1 );
--- /dev/null
+// 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()
+{
+ 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();
+}
void
test01()
{
+ bool test __attribute__((unused)) = false;
+
for (const path& p : __gnu_test::test_paths)
VERIFY( absolute(p).is_absolute() );
}
void
test02()
{
+ bool test __attribute__((unused)) = false;
+
path p1("/");
VERIFY( absolute(p1) == p1 );
VERIFY( absolute(p1, "/bar") == p1 );
void
test01()
{
+ bool test __attribute__((unused)) = false;
+
fs::path dot(".");
fs::path cwd = fs::current_path();
std::error_code ec;
void
test02()
{
+ bool test __attribute__((unused)) = false;
+
auto oldwd = fs::current_path();
auto tmpdir = fs::temp_directory_path();
current_path(tmpdir);
#include <experimental/filesystem>
#include <testsuite_hooks.h>
+#include <testsuite_fs.h>
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) );
}
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 );
void
test02()
{
+ bool test __attribute__((unused)) = false;
+
fs::path p = __gnu_test::nonexistent_path();
std::error_code ec;
void
test01()
{
+ bool test __attribute__((unused)) = false;
+
std::error_code ec;
fs::file_status st1 = fs::status(".", ec);
VERIFY( !ec );
void
test02()
{
+ bool test __attribute__((unused)) = false;
+
fs::path p = __gnu_test::nonexistent_path();
std::error_code ec;
void
test01()
{
+ bool test __attribute__((unused)) = false;
+
clean_env();
if (!fs::exists("/tmp"))
void
test02()
{
+ bool test __attribute__((unused)) = false;
+
clean_env();
if (::setenv("TMPDIR", __gnu_test::nonexistent_path().string().c_str(), 1))