Implement filesystem::canonical() without realpath
authorJonathan Wakely <jwakely@redhat.com>
Wed, 16 Sep 2015 22:50:28 +0000 (23:50 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 16 Sep 2015 22:50:28 +0000 (23:50 +0100)
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

13 files changed:
libstdc++-v3/ChangeLog
libstdc++-v3/acinclude.m4
libstdc++-v3/config.h.in
libstdc++-v3/configure
libstdc++-v3/src/filesystem/ops.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/absolute.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/canonical.cc [new file with mode: 0644]
libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/current_path.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/file_size.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/status.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc

index d425f49519622754f70ed95b065d04bd1d8734df..b8c11843632c54d5ce4df6f6de4bc0bffcc5283f 100644 (file)
@@ -1,3 +1,25 @@
+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
index 64c9b7e32cf9901d4f61e72a09cc29bd43db31e7..c133c25407b535cbb6eeda9cfb59b7e16a177de1 100644 (file)
@@ -3947,13 +3947,24 @@ dnl
   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
index cc7a21e820c7cc6c7f1e3a03f6517427122ccd65..58eef93f5ec093c226f7ef51227af40189c9333d 100644 (file)
    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. */
index 6d35f30a18ad2bbbc770dc63b1fb726667769954..dea0a2d3506f4a05d908277db8bb0970b9154e08 100755 (executable)
@@ -79178,11 +79178,22 @@ else
       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;
 }
@@ -79199,11 +79210,22 @@ else
 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;
 }
index cefb927c6a507722f5f6ef092467bee6e60e1217..b5c8eb9e11184b96b6f83b47f84dcb140f3fe9a1 100644 (file)
@@ -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<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
index 14625b5bc2c7e2bb0c4b271818b9e8cc600ca254..f7507f5772698481829122662ae80163f29e81b8 100644 (file)
@@ -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 (file)
index 0000000..d752feb
--- /dev/null
@@ -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
+// <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();
+}
index 2410c802f23ccc2abf0bea839d603af341299458..35d49f0f7f7b037cc39086e0e82bf93f610a8cbf 100644 (file)
@@ -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 );
index c242ac0e3996be737d55b0a4895dc6d5d8535be7..81ade73ab5690fd185523e29b95dbbe5f2f582d8 100644 (file)
@@ -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);
index 0f1e5aaf203cfbe8d1817507643b6e9a6737a3a9..dba4a6f5a55b0ba2a607696de11f78dab4982993 100644 (file)
 
 #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) );
 }
 
index 04fa7bb614dd39fb3091fdb8b780378f584d1a8f..7603064d46a7bc75a235307f9e90f1620dc4c4f6 100644 (file)
@@ -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;
index 2c54494be29ff38a835bc511523d34902b0ad86e..0f1730d82fcff4ce5e480f626de3f13b9cffb180 100644 (file)
@@ -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;
index 2aacd1c38c3f75e2f0ad645dc6c5dddc40ab7ae6..bd9b6adf3e0ba79034b4099a9310db4845dabe76 100644 (file)
@@ -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))