diff mbox

[v3] Fix filesystem::create_directories() function

Message ID 20150923112655.GV2969@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely Sept. 23, 2015, 11:26 a.m. UTC
This function wasn't working properly (testing is useful!)

Tested x86_64-linux, powerpc64le-linux and x86_64-dragonfly4.1,
committed to trunk.
commit 9f9ee62dc3e3d5a1cc825298b93afedc2eaf0aeb
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Sep 22 23:43:59 2015 +0100

    Fix filesystem::create_directories() function
    
    	* src/filesystem/ops.cc (is_dot, is_dotdot): Define new helpers.
    	(create_directories): Fix error handling.
    	* testsuite/experimental/filesystem/operations/create_directories.cc:
    	New.
diff mbox

Patch

diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc
index b5c8eb9..5ff8120 100644
--- a/libstdc++-v3/src/filesystem/ops.cc
+++ b/libstdc++-v3/src/filesystem/ops.cc
@@ -85,6 +85,24 @@  fs::absolute(const path& p, const path& base)
 
 namespace
 {
+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  inline bool is_dot(wchar_t c) { return c == L'.'; }
+#else
+  inline bool is_dot(char c) { return c == '.'; }
+#endif
+
+  inline bool is_dot(const fs::path& path)
+  {
+    const auto& filename = path.native();
+    return filename.size() == 1 && is_dot(filename[0]);
+  }
+
+  inline bool is_dotdot(const fs::path& path)
+  {
+    const auto& filename = path.native();
+    return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
+  }
+
   struct free_as_in_malloc
   {
     void operator()(void* p) const { ::free(p); }
@@ -576,19 +594,36 @@  fs::create_directories(const path& p)
 bool
 fs::create_directories(const path& p, error_code& ec) noexcept
 {
+  if (p.empty())
+    {
+      ec = std::make_error_code(errc::invalid_argument);
+      return false;
+    }
   std::stack<path> missing;
   path pp = p;
-  ec.clear();
-  while (!p.empty() && !exists(pp, ec) && !ec.value())
+
+  while (!pp.empty() && status(pp, ec).type() == file_type::not_found)
     {
-      missing.push(pp);
-      pp = pp.parent_path();
+      ec.clear();
+      const auto& filename = pp.filename();
+      if (!is_dot(filename) && !is_dotdot(filename))
+	missing.push(pp);
+      pp.remove_filename();
     }
-  while (!missing.empty() && !ec.value())
+
+  if (ec || missing.empty())
+    return false;
+
+  do
     {
-      create_directory(missing.top(), ec);
+      const path& top = missing.top();
+      create_directory(top, ec);
+      if (ec && is_directory(top))
+	ec.clear();
       missing.pop();
     }
+  while (!missing.empty() && !ec);
+
   return missing.empty();
 }
 
diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc
new file mode 100644
index 0000000..b84d966
--- /dev/null
+++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc
@@ -0,0 +1,75 @@ 
+// 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;
+
+  // Test empty path.
+  bool b = fs::create_directories( "", ec );
+  VERIFY( ec );
+  VERIFY( !b );
+
+  // Test existing path.
+  b = fs::create_directories( fs::current_path(), ec );
+  VERIFY( !ec );
+  VERIFY( !b );
+
+  // Test non-existent path.
+  const auto p = __gnu_test::nonexistent_path();
+  b = fs::create_directories( p, ec );
+  VERIFY( !ec );
+  VERIFY( b );
+  VERIFY( is_directory(p) );
+
+  b = fs::create_directories( p/".", ec );
+  VERIFY( !ec );
+  VERIFY( !b );
+
+  b = fs::create_directories( p/"..", ec );
+  VERIFY( !ec );
+  VERIFY( !b );
+
+  b = fs::create_directories( p/"d1/d2/d3", ec );
+  VERIFY( !ec );
+  VERIFY( b );
+  VERIFY( is_directory(p/"d1/d2/d3") );
+
+  b = fs::create_directories( p/"./d4/../d5", ec );
+  VERIFY( !ec );
+  VERIFY( b );
+  VERIFY( is_directory(p/"./d4/../d5") );
+
+  remove_all(p, ec);
+}
+
+int
+main()
+{
+  test01();
+}