From cce8c431c23ee801f5d31cd07bb793821321ee76 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sat, 4 May 2019 00:11:16 +0200 Subject: [PATCH 01/21] refs #12, multiple fixes for fs::recursive_directory_iterator concerning endless iterations, wrong pop and depth handling and better input_iterator_tag and copy semantics. --- include/ghc/filesystem.hpp | 109 ++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 55 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 3831eb2..1e12f12 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -725,9 +725,17 @@ public: void swap(recursive_directory_iterator& rhs); private: - directory_options _options; - bool _recursion_pending; - std::stack _dir_iter_stack; + struct recursive_directory_iterator_impl { + directory_options _options; + bool _recursion_pending; + std::stack _dir_iter_stack; + recursive_directory_iterator_impl(directory_options options, bool recursion_pending) + : _options(options) + , _recursion_pending(recursion_pending) + { + } + }; + std::shared_ptr _impl; }; // 30.10.14.2 directory_iterator non-member functions @@ -4756,51 +4764,42 @@ GHC_INLINE directory_iterator end(const directory_iterator&) noexcept // 30.10.14 class recursive_directory_iterator GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept - : _options(directory_options::none) - , _recursion_pending(true) + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) { - _dir_iter_stack.push(directory_iterator()); + _impl->_dir_iter_stack.push(directory_iterator()); } GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p) - : _options(directory_options::none) - , _recursion_pending(true) + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) { - _dir_iter_stack.push(directory_iterator(p)); + _impl->_dir_iter_stack.push(directory_iterator(p)); } GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options) - : _options(options) - , _recursion_pending(true) + : _impl(new recursive_directory_iterator_impl(options, true)) { - _dir_iter_stack.push(directory_iterator(p, options)); + _impl->_dir_iter_stack.push(directory_iterator(p, options)); } GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept - : _options(options) - , _recursion_pending(true) + : _impl(new recursive_directory_iterator_impl(options, true)) { - _dir_iter_stack.push(directory_iterator(p, options, ec)); + _impl->_dir_iter_stack.push(directory_iterator(p, options, ec)); } GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept - : _options(directory_options::none) - , _recursion_pending(true) + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) { - _dir_iter_stack.push(directory_iterator(p, ec)); + _impl->_dir_iter_stack.push(directory_iterator(p, ec)); } GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs) - : _options(rhs._options) - , _recursion_pending(rhs._recursion_pending) - , _dir_iter_stack(rhs._dir_iter_stack) + : _impl(rhs._impl) { } GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept - : _options(rhs._options) - , _recursion_pending(rhs._recursion_pending) - , _dir_iter_stack(std::move(rhs._dir_iter_stack)) + : _impl(std::move(rhs._impl)) { } @@ -4809,43 +4808,39 @@ GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {} // 30.10.14.1 observers GHC_INLINE directory_options recursive_directory_iterator::options() const { - return _options; + return _impl->_options; } GHC_INLINE int recursive_directory_iterator::depth() const { - return static_cast(_dir_iter_stack.empty() ? 0 : _dir_iter_stack.size() - 1); + return static_cast(_impl->_dir_iter_stack.size() - 1); } GHC_INLINE bool recursive_directory_iterator::recursion_pending() const { - return _recursion_pending; + return _impl->_recursion_pending; } GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const { - return *(_dir_iter_stack.top()); + return *(_impl->_dir_iter_stack.top()); } GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const { - return &(*(_dir_iter_stack.top())); + return &(*(_impl->_dir_iter_stack.top())); } // 30.10.14.1 modifiers recursive_directory_iterator& GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs) { - _options = rhs._options; - _recursion_pending = rhs._recursion_pending; - _dir_iter_stack = rhs._dir_iter_stack; + _impl = rhs._impl; return *this; } GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept { - _options = rhs._options; - _recursion_pending = rhs._recursion_pending; - _dir_iter_stack = std::move(rhs._dir_iter_stack); + _impl = std::move(rhs._impl); return *this; } @@ -4854,7 +4849,7 @@ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator+ std::error_code ec; increment(ec); if (ec) { - throw filesystem_error(detail::systemErrorText(ec.value()), _dir_iter_stack.empty() ? path() : _dir_iter_stack.top()->path(), ec); + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); } return *this; } @@ -4862,56 +4857,60 @@ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator+ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept { if (recursion_pending() && is_directory((*this)->status()) && (!is_symlink((*this)->symlink_status()) || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { - _dir_iter_stack.push(directory_iterator((*this)->path(), _options, ec)); + _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); } else { - _dir_iter_stack.top().increment(ec); + _impl->_dir_iter_stack.top().increment(ec); } - while (depth() && _dir_iter_stack.top() == directory_iterator()) { - _dir_iter_stack.pop(); - _dir_iter_stack.top().increment(ec); + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); } + _impl->_recursion_pending = true; return *this; } GHC_INLINE void recursive_directory_iterator::pop() +{ + std::error_code ec; + pop(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } +} + +GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) { if (depth() == 0) { *this = recursive_directory_iterator(); } else { - _dir_iter_stack.pop(); + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } } } -GHC_INLINE void recursive_directory_iterator::pop(std::error_code&) -{ - pop(); -} - GHC_INLINE void recursive_directory_iterator::disable_recursion_pending() { - _recursion_pending = false; + _impl->_recursion_pending = false; } // other members as required by 27.2.3, input iterators GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const { - return _dir_iter_stack.top() == rhs._dir_iter_stack.top(); + return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top(); } GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const { - return _dir_iter_stack.top() != rhs._dir_iter_stack.top(); + return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top(); } GHC_INLINE void recursive_directory_iterator::swap(recursive_directory_iterator& rhs) { - std::swap(_options, rhs._options); - bool rp = _recursion_pending; - _recursion_pending = rhs._recursion_pending; - rhs._recursion_pending = rp; - _dir_iter_stack.swap(rhs._dir_iter_stack); + std::swap(_impl, rhs._impl); } // 30.10.14.2 directory_iterator non-member functions From 547ecfb28f20958ea76bcd5ce12652b000852933 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sat, 4 May 2019 08:08:04 +0200 Subject: [PATCH 02/21] Test helper for file_time_type got additional error checking --- test/filesystem_test.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index faaa905..a1b3f8b 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -117,9 +117,15 @@ struct StringMaker static std::string convert(fs::file_time_type const& value) { std::time_t t = to_time_t(value); - std::tm ttm = *std::localtime(&t); - std::ostringstream os; - os << std::put_time(&ttm, "%Y-%m-%d %H:%M:%S"); + std::tm* ptm = std::localtime(&t); + if (ptm) { + std::tm ttm = *ptm; + std::ostringstream os; + os << std::put_time(&ttm, "%Y-%m-%d %H:%M:%S"); + } + else { + os << "(invalid-time)"; + } return os.str(); } }; From 5ea334899a84912d3632056659115067b6d3dfcd Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sat, 4 May 2019 08:47:41 +0200 Subject: [PATCH 03/21] refs #12, added more tests to recursive directory iterator, fixed additional issue with pop(). --- include/ghc/filesystem.hpp | 3 +- test/filesystem_test.cpp | 81 +++++++++++++++++++++++++++++--------- 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 1e12f12..b1d5bd9 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -4885,10 +4885,11 @@ GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) *this = recursive_directory_iterator(); } else { - while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + do { _impl->_dir_iter_stack.pop(); _impl->_dir_iter_stack.top().increment(ec); } + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); } } diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index faaa905..993e566 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -1205,29 +1206,73 @@ TEST_CASE("30.10.14 class recursive_directory_iterator", "[filesystem][recursive fs::recursive_directory_iterator rd5; rd5 = rd4; } -/* - if(1) { - fs::path d = ".."; - int maxDepth = 2; - fs::recursive_directory_iterator dit(d); - std::cout << d << std::endl; - for(auto & f : dit) { -#if 1 - if(dit.depth()<=maxDepth) { - std::cout << dit.depth() << ": " << f.path() << std::endl; + { + TemporaryDirectory t(TempOpt::change_path); + generateFile("a"); + fs::create_directory("d1"); + fs::create_directory("d1/d2"); + generateFile("d1/b"); + generateFile("d1/c"); + generateFile("d1/d2/d"); + generateFile("e"); + auto iter = fs::recursive_directory_iterator("."); + std::multimap result; + while(iter != fs::recursive_directory_iterator()) { + result.insert(std::make_pair(iter->path().string(), iter.depth())); + ++iter; + } + std::stringstream os; + for(auto p : result) { + os << "[" << p.first << "," << p.second << "],"; + } + CHECK(os.str() == "[./a,0],[./d1,0],[./d1/b,1],[./d1/c,1],[./d1/d2,1],[./d1/d2/d,2],[./e,0],"); + } + { + TemporaryDirectory t(TempOpt::change_path); + generateFile("a"); + fs::create_directory("d1"); + fs::create_directory("d1/d2"); + generateFile("d1/d2/b"); + generateFile("e"); + auto iter = fs::recursive_directory_iterator("."); + std::multimap result; + while(iter != fs::recursive_directory_iterator()) { + result.insert(std::make_pair(iter->path().string(), iter.depth())); + if(iter->path() == "./d1/d2") { + iter.disable_recursion_pending(); + } + ++iter; + } + std::stringstream os; + for(auto p : result) { + os << "[" << p.first << "," << p.second << "],"; + } + CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],"); + } + { + TemporaryDirectory t(TempOpt::change_path); + generateFile("a"); + fs::create_directory("d1"); + fs::create_directory("d1/d2"); + generateFile("d1/d2/b"); + generateFile("e"); + auto iter = fs::recursive_directory_iterator("."); + std::multimap result; + while(iter != fs::recursive_directory_iterator()) { + result.insert(std::make_pair(iter->path().string(), iter.depth())); + if(iter->path() == "./d1/d2") { + iter.pop(); } else { - dit.pop(); + ++iter; } -#else - std::cout << dit.depth() << ": " << f.path() << std::endl; - if(dit.depth()>maxDepth) { - dit.disable_recursion_pending(); - } -#endif } + std::stringstream os; + for(auto p : result) { + os << "[" << p.first << "," << p.second << "],"; + } + CHECK(os.str() == "[./a,0],[./d1,0],[./d1/d2,1],[./e,0],"); } - */ } TEST_CASE("30.10.15.1 absolute", "[filesystem][operations][fs.op.absolute]") From 2dd77916cfe63117ca01bc4c084e266b92909ec5 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sat, 4 May 2019 08:51:42 +0200 Subject: [PATCH 04/21] Updated readme --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 0e2034f..5ba6b78 100644 --- a/README.md +++ b/README.md @@ -438,6 +438,15 @@ to the expected behavior. ## Release Notes +### v1.1.2 (wip) + +* Bugfix for ([#11](https://github.com/gulrak/filesystem/issues/11)), + `fs::path::lexically_normal()` had some issues with `".."`-sequences. +* Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)), + `fs::recursive_directory_iterator` could run into endless loops, + the methods depth() and pop() had issues and the copy behaviour and + `input_iterator_tag` conformance was broken, added tests + ### [v1.1.0](https://github.com/gulrak/filesystem/releases/tag/v1.1.0) * Restructuring of the project directory. The header files are now using From e8ced30a84477e5111a85619095403e59f40fd6d Mon Sep 17 00:00:00 2001 From: gulrak Date: Sat, 4 May 2019 08:59:21 +0200 Subject: [PATCH 05/21] Fixed merge error leading to compile error. --- test/filesystem_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index 7341752..e864622 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -119,9 +119,9 @@ struct StringMaker { std::time_t t = to_time_t(value); std::tm* ptm = std::localtime(&t); + std::ostringstream os; if (ptm) { std::tm ttm = *ptm; - std::ostringstream os; os << std::put_time(&ttm, "%Y-%m-%d %H:%M:%S"); } else { From da215a8c9695e49a9f43316fa514435950e78087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sat, 4 May 2019 09:16:36 +0200 Subject: [PATCH 06/21] refs #12, fixed small issue with new recursive_directory_iterator tests on Windows due to not comparing generic format. --- test/filesystem_test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index e864622..9ba8039 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -1224,7 +1224,7 @@ TEST_CASE("30.10.14 class recursive_directory_iterator", "[filesystem][recursive auto iter = fs::recursive_directory_iterator("."); std::multimap result; while(iter != fs::recursive_directory_iterator()) { - result.insert(std::make_pair(iter->path().string(), iter.depth())); + result.insert(std::make_pair(iter->path().generic_string(), iter.depth())); ++iter; } std::stringstream os; @@ -1243,7 +1243,7 @@ TEST_CASE("30.10.14 class recursive_directory_iterator", "[filesystem][recursive auto iter = fs::recursive_directory_iterator("."); std::multimap result; while(iter != fs::recursive_directory_iterator()) { - result.insert(std::make_pair(iter->path().string(), iter.depth())); + result.insert(std::make_pair(iter->path().generic_string(), iter.depth())); if(iter->path() == "./d1/d2") { iter.disable_recursion_pending(); } @@ -1265,7 +1265,7 @@ TEST_CASE("30.10.14 class recursive_directory_iterator", "[filesystem][recursive auto iter = fs::recursive_directory_iterator("."); std::multimap result; while(iter != fs::recursive_directory_iterator()) { - result.insert(std::make_pair(iter->path().string(), iter.depth())); + result.insert(std::make_pair(iter->path().generic_string(), iter.depth())); if(iter->path() == "./d1/d2") { iter.pop(); } From dc37276a9af1e0d0f9d1f739423c753c73ea7ed3 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sun, 5 May 2019 08:51:53 +0200 Subject: [PATCH 07/21] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 8d9f245e379d373942c0f49be19dacc9293cdcf0 Merge: 3ceb4b0 857a355 Author: gulrak Date: Fri May 3 23:52:57 2019 +0200 Merge branch 'development-v1.1' of https://github.com/gulrak/filesystem into development-v1.1 commit 3ceb4b0e4fe90be46b1fdbdfb7e2b3f160cccf3c Author: gulrak Date: Fri May 3 23:51:48 2019 +0200 Less noisy interoperability testing on weakly_canonial() commit 38f82a90ef5f4ec1fa341ce3ab34f3836a6bd9ea Author: gulrak Date: Fri May 3 23:50:43 2019 +0200 Additional fix for recursive_directory_iterator commit 857a3558fb13e8fae76e29b5b91cb76b5e98ea20 Merge: 9da5784 b557b5b Author: Steffen Schuemann Date: Fri May 3 23:47:20 2019 +0200 Merge branch 'development-v1.1' of github.com:gulrak/filesystem into development-v1.1 # Conflicts: # test/filesystem_test.cpp commit 9da57846ac4de06c4b0521ab69ea42816fd6a029 Merge: 96e89ae 43e75c3 Author: Steffen Schuemann Date: Fri May 3 23:42:31 2019 +0200 Merge remote-tracking branch 'origin/master' into development-v1.1 commit 96e89ae7f9e983cb3d0977d9ba77f421a0c06b0f Merge: 9d71161 882c60b Author: Steffen Schuemann Date: Fri May 3 23:40:18 2019 +0200 Merge remote-tracking branch 'origin/master' into development-v1.1 commit b557b5b476c085183bd3da72cc9f511df3b52272 Merge: 263a2d2 9d71161 Author: Steffen Schümann Date: Fri May 3 22:44:25 2019 +0200 Merge branch 'development-v1.1' of https://github.com/gulrak/filesystem into development-v1.1 commit 263a2d2c5b9f25631513271a6e26a9cb66e8f9da Author: Steffen Schümann Date: Fri May 3 22:44:01 2019 +0200 Work on Windows tests. commit 9d71161323f22e994b51fda73ebc19af9e67d6e6 Author: Steffen Schuemann Date: Fri May 3 08:30:40 2019 +0200 Fix for issues with recursive_directory_iterator not behaving like an input iterator and regarding pop()/depth(). commit 2ac1352b4a37fdb247b56fb63c1e2c3f56761649 Author: gulrak Date: Thu May 2 09:12:42 2019 +0200 Refactored builds with std::fs to a CMake macro, added du example, added behaviour switch commit 624a6f63e5639504c2b554ba89985ef9c01c6fa9 Author: Steffen Schümann Date: Thu May 2 09:01:15 2019 +0200 Add path parameters to filesystem_error::what --- .gitignore | 1 + CMakeLists.txt | 9 ++++-- README.md | 15 ++++++++-- cmake/GhcHelper.cmake | 36 +++++++++++++++++++++++ examples/CMakeLists.txt | 33 +++------------------ examples/du.cpp | 57 ++++++++++++++++++++++++++++++++++++ include/ghc/filesystem.hpp | 59 +++++++++++++++++++++++++++++--------- test/CMakeLists.txt | 29 +------------------ test/filesystem_test.cpp | 49 ++++++++++++++++++++++++------- 9 files changed, 201 insertions(+), 87 deletions(-) create mode 100644 cmake/GhcHelper.cmake create mode 100644 examples/du.cpp diff --git a/.gitignore b/.gitignore index a0155cb..d787f76 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /build*/ +.vscode/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 597675c..39e3d85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,10 @@ target_compile_options(ghc_filesystem INTERFACE "$<$:/utf- get_directory_property(hasParent PARENT_DIRECTORY) if(NOT hasParent) - enable_testing() - add_subdirectory(test) - add_subdirectory(examples) + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/") + include(GhcHelper) + enable_testing() + add_subdirectory(test) + add_subdirectory(examples) endif() diff --git a/README.md b/README.md index 5ba6b78..bda1923 100644 --- a/README.md +++ b/README.md @@ -402,9 +402,8 @@ But this makes `fs::copy` with `fs::copy_options::create_symlinks` or `fs::copy_ just a more complicated syntax for the `fs::create_symlink` or `fs::create_hardlink` operation and I don't want to believe, that this was the intention of the original writing. As there is another issue related to copy, with a different take on the description, -I keep my version the way I read the description, as it is not contradicting the standard and useful. Let's see -what final solution the LWG comes up with in the end. - +*Note:* With v1.1.2 I decided to integrate a behavior switch for this and make the LWG #2682 +the default. ## Open Issues @@ -446,6 +445,16 @@ to the expected behavior. `fs::recursive_directory_iterator` could run into endless loops, the methods depth() and pop() had issues and the copy behaviour and `input_iterator_tag` conformance was broken, added tests +* Restructured some CMake code into a macro to ease the support for + C++17 std::filesystem builds of tests and examples for interoperability + checks. +* Some fixes on Windows tests to ease interoperability test runs. +* Reduced noise on `fs::weakly_canonical()` tests against `std::fs` +* Added simple `du` example showing the `recursive_directory_iterator` + used to add the sizes of files in a directory tree. +* Added error checking in `fs::file_time_type` test helpers +* `fs::copy()` now conforms LWG #2682, disallowing the use of + `copy_option::create_symlinks' to be used on directories ### [v1.1.0](https://github.com/gulrak/filesystem/releases/tag/v1.1.0) diff --git a/cmake/GhcHelper.cmake b/cmake/GhcHelper.cmake new file mode 100644 index 0000000..b095d2e --- /dev/null +++ b/cmake/GhcHelper.cmake @@ -0,0 +1,36 @@ +macro(AddExecutableWithStdFS targetName) + +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 7.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0)) + if(APPLE) + include_directories(/usr/local/opt/llvm/include) + link_directories(/usr/local/opt/llvm/lib) + endif() + add_executable(${targetName} ${ARGN}) + set_property(TARGET ${targetName} PROPERTY CXX_STANDARD 17) + if(APPLE) + target_link_libraries(${targetName} -lc++fs) + else() + target_compile_options(${targetName} PRIVATE "-stdlib=libc++") + target_link_libraries(${targetName} -stdlib=libc++ -lc++fs) + endif() + + target_link_libraries(${targetName} -lc++fs) + target_compile_definitions(${targetName} PRIVATE USE_STD_FS) +endif() + +if (CMAKE_COMPILER_IS_GNUCXX AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 8.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)) + add_executable(${targetName} ${ARGN}) + set_property(TARGET ${targetName} PROPERTY CXX_STANDARD 17) + target_link_libraries(${targetName} -lstdc++fs) + target_compile_definitions(${targetName} PRIVATE USE_STD_FS) +endif() + +if(CMAKE_CXX_COMPILER_ID MATCHES MSVC AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 19.15 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.15)) + add_executable(${targetName} ${ARGN}) + set_property(TARGET ${targetName} PROPERTY CXX_STANDARD 17) + set_property(TARGET ${targetName} PROPERTY CXX_STANDARD_REQUIRED ON) + target_compile_options(${targetName} PRIVATE "/Zc:__cplusplus") + target_compile_definitions(${targetName} PRIVATE USE_STD_FS _CRT_SECURE_NO_WARNINGS) +endif() + +endmacro() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 817200b..90a303f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,34 +4,9 @@ target_link_libraries(fs_dir ghc_filesystem) if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) target_compile_definitions(fs_dir PRIVATE _CRT_SECURE_NO_WARNINGS) endif() +AddExecutableWithStdFS(std_fs_dir dir.cpp) -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 7.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0)) - if(APPLE) - include_directories(/usr/local/opt/llvm/include) - link_directories(/usr/local/opt/llvm/lib) - endif() - add_executable(std_fs_dir dir.cpp) - set_property(TARGET std_fs_dir PROPERTY CXX_STANDARD 17) - if(APPLE) - target_link_libraries(std_fs_dir -lc++fs) - else() - target_compile_options(std_fs_dir PRIVATE "-stdlib=libc++") - target_link_libraries(std_fs_dir -stdlib=libc++ -lc++fs) - endif() +add_executable(fs_du du.cpp) +target_link_libraries(fs_du ghc_filesystem) +AddExecutableWithStdFS(std_fs_du du.cpp) - target_link_libraries(std_fs_dir -lc++fs) -endif() - -if (CMAKE_COMPILER_IS_GNUCXX AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 8.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)) - add_executable(std_fs_dir dir.cpp) - set_property(TARGET std_fs_dir PROPERTY CXX_STANDARD 17) - target_link_libraries(std_fs_dir -lstdc++fs) -endif() - -if(CMAKE_CXX_COMPILER_ID MATCHES MSVC AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 19.15 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.15)) - add_executable(std_fs_dir dir.cpp) - set_property(TARGET std_fs_dir PROPERTY CXX_STANDARD 17) - set_property(TARGET std_fs_dir PROPERTY CXX_STANDARD_REQUIRED ON) - target_compile_options(std_fs_dir PRIVATE "/Zc:__cplusplus") - target_compile_definitions(std_fs_dir PRIVATE _CRT_SECURE_NO_WARNINGS) -endif() diff --git a/examples/du.cpp b/examples/du.cpp new file mode 100644 index 0000000..88436d9 --- /dev/null +++ b/examples/du.cpp @@ -0,0 +1,57 @@ +#include +#include +#include + +#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include() +#include +namespace fs = std::filesystem; +#else +#include +namespace fs = ghc::filesystem; +#endif + +int main(int argc, char* argv[]) +{ +#ifdef GHC_FILESYSTEM_VERSION + fs::u8arguments u8guard(argc, argv); + if(!u8guard.valid()) { + std::cerr << "Invalid character encoding, UTF-8 based encoding needed." << std::endl; + std::exit(EXIT_FAILURE); + } +#endif + if(argc > 2) { + std::cerr << "USAGE: du " << std::endl; + exit(1); + } + fs::path dir{"."}; + if(argc == 2) { + dir = fs::u8path(argv[1]); + } + + uint64_t totalSize = 0; + int totalDirs = 0; + int totalFiles = 0; + int maxDepth = 0; + + try { + auto rdi = fs::recursive_directory_iterator(dir); + for(auto de : rdi) { + if(rdi.depth() > maxDepth) { + maxDepth = rdi.depth(); + } + if(de.is_regular_file()) { + totalSize += de.file_size(); + ++totalFiles; + } + else if(de.is_directory()) { + ++totalDirs; + } + } + } + catch(fs::filesystem_error fe) { + std::cerr << "Error: " << fe.what() << std::endl; + exit(1); + } + std::cout << totalSize << " bytes in " << totalFiles << " and " << totalDirs << " directories, maximum depth: " << maxDepth << std::endl; + return 0; +} diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index b1d5bd9..3657df7 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -92,6 +92,7 @@ #ifdef GHC_OS_WINDOWS #include +// additional includes #include #include #include @@ -142,9 +143,20 @@ #include #endif // GHC_EXPAND_IMPL -// configure LWG conformance (see README.md) +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories +// configure LWG conformance () +#define LWG_2682_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular +// file with that name, it is superceded by P1164R1, so only activate if really needed // #define LWG_2935_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) #define LWG_2937_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) #define GHC_FILESYSTEM_VERSION 10101L @@ -1014,6 +1026,7 @@ enum class portable_error { not_supported, not_implemented, invalid_argument, + is_a_directory, }; GHC_FS_API std::error_code make_error_code(portable_error err); } // namespace detail @@ -1038,6 +1051,8 @@ GHC_INLINE std::error_code make_error_code(portable_error err) return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category()); case portable_error::invalid_argument: return std::error_code(ERROR_INVALID_PARAMETER, std::system_category()); + case portable_error::is_a_directory: + return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); } #else switch (err) { @@ -1053,6 +1068,8 @@ GHC_INLINE std::error_code make_error_code(portable_error err) return std::error_code(ENOSYS, std::system_category()); case portable_error::invalid_argument: return std::error_code(EINVAL, std::system_category()); + case portable_error::is_a_directory: + return std::error_code(EISDIR, std::system_category()); } #endif return std::error_code(); @@ -1648,9 +1665,9 @@ GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::er GHC_INLINE bool is_not_found_error(std::error_code& ec) { #ifdef GHC_OS_WINDOWS - return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND; + return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME; #else - return ec.value() == ENOENT; + return ec.value() == ENOENT || ec.value() == ENOTDIR; #endif } @@ -1672,7 +1689,7 @@ GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uin fs.type(file_type::symlink); } } - if (ec.value() == ERROR_FILE_NOT_FOUND) { + if (detail::is_not_found_error(ec)) { return file_status(file_type::not_found); } return ec ? file_status(file_type::none) : fs; @@ -1687,9 +1704,8 @@ GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uin file_status f_s = detail::file_status_from_st_mode(fs.st_mode); return f_s; } - auto error = errno; - ec = std::error_code(error, std::system_category()); - if (error == ENOENT) { + ec = std::error_code(errno, std::system_category()); + if (detail::is_not_found_error(ec)) { return file_status(file_type::not_found, perms::unknown); } return file_status(file_type::none); @@ -1757,9 +1773,8 @@ GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status return fs; } else { - auto error = errno; ec = std::error_code(errno, std::system_category()); - if (error == ENOENT) { + if (detail::is_not_found_error(ec)) { return file_status(file_type::not_found, perms::unknown); } return file_status(file_type::none); @@ -1800,6 +1815,7 @@ GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) #endif } + //----------------------------------------------------------------------------- // 30.10.8.4.1 constructors and destructor @@ -2741,6 +2757,9 @@ GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const , _ec(ec) , _p1(p1) { + if (!_p1.empty()) { + _what_arg += ": '" + _p1.u8string() + "'"; + } } GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec) @@ -2750,6 +2769,12 @@ GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const , _p1(p1) , _p2(p2) { + if (!_p1.empty()) { + _what_arg += ": '" + _p1.u8string() + "'"; + } + if (!_p2.empty()) { + _what_arg += ", '" + _p2.u8string() + "'"; + } } GHC_INLINE const path& filesystem_error::path1() const noexcept @@ -2868,7 +2893,7 @@ GHC_INLINE path canonical(const path& p, std::error_code& ec) result = result.parent_path(); continue; } - else if ((result/pe).string().length() <= root.string().length()) { + else if ((result / pe).string().length() <= root.string().length()) { result /= pe; continue; } @@ -2958,10 +2983,10 @@ GHC_INLINE void copy(const path& from, const path& to, copy_options options, std } else if (is_regular_file(fs_from)) { if ((options & copy_options::directories_only) == copy_options::none) { - if ((options & copy_options::create_symlinks) == copy_options::create_symlinks) { + if ((options & copy_options::create_symlinks) != copy_options::none) { create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec); } - else if ((options & copy_options::create_hard_links) == copy_options::create_hard_links) { + else if ((options & copy_options::create_hard_links) != copy_options::none) { create_hard_link(from, to, ec); } else if (is_directory(fs_to)) { @@ -2972,7 +2997,12 @@ GHC_INLINE void copy(const path& from, const path& to, copy_options options, std } } } - else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) == copy_options::recursive)) { +#ifdef LWG_2682_BEHAVIOUR + else if(is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { + ec = detail::make_error_code(detail::portable_error::is_a_directory); + } +#endif + else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) { if (!exists(fs_to)) { create_directory(to, from, ec); if (ec) { @@ -4077,7 +4107,8 @@ GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept bool scan = true; for (auto pe : p) { if (scan) { - if (exists(result / pe, ec)) { + std::error_code tec; + if (exists(result / pe, tec)) { result /= pe; } else { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 96268dd..bdc182c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -18,34 +18,7 @@ if(CMAKE_GENERATOR STREQUAL Xcode) target_link_libraries(filesystem_test_cov PUBLIC ghc_filesystem PRIVATE --coverage) endif() ParseAndAddCatchTests(filesystem_test filesystem_test) - -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 7.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0)) - if(APPLE) - include_directories(/usr/local/opt/llvm/include) - link_directories(/usr/local/opt/llvm/lib) - endif() - add_executable(std_filesystem_test filesystem_test.cpp catch.hpp) - set_property(TARGET std_filesystem_test PROPERTY CXX_STANDARD 17) - target_compile_definitions(std_filesystem_test PRIVATE USE_STD_FS) - if(APPLE) - target_link_libraries(std_filesystem_test -lc++fs) - else() - target_compile_options(std_filesystem_test PRIVATE "-stdlib=libc++") - target_link_libraries(std_filesystem_test -stdlib=libc++ -lc++fs) - endif() -endif() -if (CMAKE_COMPILER_IS_GNUCXX AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 8.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)) - add_executable(std_filesystem_test filesystem_test.cpp catch.hpp) - set_property(TARGET std_filesystem_test PROPERTY CXX_STANDARD 17) - target_compile_definitions(std_filesystem_test PRIVATE USE_STD_FS) - target_link_libraries(std_filesystem_test -lstdc++fs) -endif() -if(CMAKE_CXX_COMPILER_ID MATCHES MSVC AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 19.15 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 19.15)) - add_executable(std_filesystem_test filesystem_test.cpp catch.hpp) - set_property(TARGET std_filesystem_test PROPERTY CXX_STANDARD 17) - target_compile_options(std_filesystem_test PRIVATE "/Zc:__cplusplus") - target_compile_definitions(std_filesystem_test PRIVATE USE_STD_FS _CRT_SECURE_NO_WARNINGS) -endif() +AddExecutableWithStdFS(std_filesystem_test filesystem_test.cpp catch.hpp) add_executable(multifile_test multi1.cpp multi2.cpp catch.hpp) target_link_libraries(multifile_test ghc_filesystem) diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index 9ba8039..847a745 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -88,8 +88,19 @@ using fstream = ghc::filesystem::fstream; #endif #include "catch.hpp" -//#define TEST_LWG_2935_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Behaviour Switches (should match the config in ghc/filesystem.hpp): +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories +#define TEST_LWG_2682_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular +// file with that name, it is superceded by P1164R1, so only activate if really needed +// #define TEST_LWG_2935_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) #define TEST_LWG_2937_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - template std::time_t to_time_t(TP tp) @@ -474,13 +485,14 @@ TEST_CASE("30.10.8.4.6 path native format observers", "[filesystem][path][fs.pat { #ifdef GHC_OS_WINDOWS #ifdef IS_WCHAR_PATH - CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type(L"ä\\€")); + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type(L"\u00E4\\\u20AC")); + // CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("ä\\€")); // MSVCs returns local DBCS encoding #else CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").native() == fs::path::string_type("\xc3\xa4\\\xe2\x82\xac")); + CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("\xc3\xa4\\\xe2\x82\xac")); CHECK(!::strcmp(fs::u8path("\xc3\xa4\\\xe2\x82\xac").c_str(), "\xc3\xa4\\\xe2\x82\xac")); CHECK((std::string)fs::u8path("\xc3\xa4\\\xe2\x82\xac") == std::string("\xc3\xa4\\\xe2\x82\xac")); #endif - CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").string() == std::string("\xc3\xa4\\\xe2\x82\xac")); CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").wstring() == std::wstring(L"\u00E4\\\u20AC")); CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u8string() == std::string("\xc3\xa4\\\xe2\x82\xac")); CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").u16string() == std::u16string(u"\u00E4\\\u20AC")); @@ -502,7 +514,9 @@ TEST_CASE("30.10.8.4.6 path native format observers", "[filesystem][path][fs.pat TEST_CASE("30.10.8.4.7 path generic format observers", "[filesystem][path][fs.path.generic.obs]") { #ifdef GHC_OS_WINDOWS +#ifndef IS_WCHAR_PATH CHECK(fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string() == std::string("\xc3\xa4/\xe2\x82\xac")); +#endif #ifndef USE_STD_FS auto t = fs::u8path("\xc3\xa4\\\xe2\x82\xac").generic_string, TestAllocator>(); CHECK(t.c_str() == std::string("\xc3\xa4/\xe2\x82\xac")); @@ -1361,6 +1375,9 @@ TEST_CASE("30.10.15.3 copy", "[filesystem][operations][fs.op.copy]") generateFile("dir1/file2"); fs::create_directory("dir1/dir2"); generateFile("dir1/dir2/file3"); +#ifdef TEST_LWG_2682_BEHAVIOUR + REQUIRE_THROWS_AS(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive), fs::filesystem_error); +#else REQUIRE_NOTHROW(fs::copy("dir1", "dir3", fs::copy_options::create_symlinks | fs::copy_options::recursive)); CHECK(!ec); CHECK(fs::exists("dir3/file1")); @@ -1369,6 +1386,7 @@ TEST_CASE("30.10.15.3 copy", "[filesystem][operations][fs.op.copy]") CHECK(fs::is_symlink("dir3/file2")); CHECK(fs::exists("dir3/dir2/file3")); CHECK(fs::is_symlink("dir3/dir2/file3")); +#endif } { TemporaryDirectory t(TempOpt::change_path); @@ -1471,7 +1489,7 @@ TEST_CASE("30.10.15.6 create_directories", "[filesystem][operations][fs.op.creat CHECK(!fs::is_directory(p)); CHECK(!fs::create_directories(p, ec)); #else - INFO("This test expects conformance predating LWG #2935 result. (As suggested by WG21 P1164R0, implemented by GCC with issue #86910.)"); + INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)"); p = t.path() / "testfile"; generateFile(p); CHECK(fs::is_regular_file(p)); @@ -1515,7 +1533,7 @@ TEST_CASE("30.10.15.7 create_directory", "[filesystem][operations][fs.op.create_ CHECK(!fs::is_directory(p)); CHECK(!fs::create_directories(p, ec)); #else - INFO("This test expects conformance predating LWG #2935 result. (As suggested by WG21 P1164R0, implemented by GCC with issue #86910.)"); + INFO("This test expects conformance with P1164R1. (implemented by GCC with issue #86910.)"); p = t.path() / "testfile"; generateFile(p); CHECK(fs::is_regular_file(p)); @@ -1618,7 +1636,7 @@ TEST_CASE("30.10.15.12 equivalent", "[filesystem][operations][fs.op.equivalent]" INFO("This test expects LWG #2937 result conformance."); std::error_code ec; bool result = false; - CHECK_THROWS_AS(fs::equivalent("foo", "foo3"), fs::filesystem_error); + REQUIRE_THROWS_AS(fs::equivalent("foo", "foo3"), fs::filesystem_error); CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec)); CHECK(!result); CHECK(ec); @@ -1636,7 +1654,7 @@ TEST_CASE("30.10.15.12 equivalent", "[filesystem][operations][fs.op.equivalent]" INFO("This test expects conformance predating LWG #2937 result."); std::error_code ec; bool result = false; - CHECK_NOTHROW(result = fs::equivalent("foo", "foo3")); + REQUIRE_NOTHROW(result = fs::equivalent("foo", "foo3")); CHECK(!result); CHECK_NOTHROW(result = fs::equivalent("foo", "foo3", ec)); CHECK(!result); @@ -2356,9 +2374,18 @@ TEST_CASE("30.10.15.38 temporary_directory_path", "[filesystem][operations][fs.o TEST_CASE("30.10.15.39 weakly_canonical", "[filesystem][operations][fs.op.weakly_canonical]") { - CHECK(fs::weakly_canonical("foo/bar") == "foo/bar"); - CHECK(fs::weakly_canonical("foo/./bar") == "foo/bar"); - CHECK(fs::weakly_canonical("foo/../bar") == "bar"); + INFO("This might fail on std::implementations that return fs::current_path() for fs::canonical(\"\")"); + CHECK(fs::weakly_canonical("") == "."); + if(fs::weakly_canonical("") == ".") { + CHECK(fs::weakly_canonical("foo/bar") == "foo/bar"); + CHECK(fs::weakly_canonical("foo/./bar") == "foo/bar"); + CHECK(fs::weakly_canonical("foo/../bar") == "bar"); + } + else { + CHECK(fs::weakly_canonical("foo/bar") == fs::current_path() / "foo/bar"); + CHECK(fs::weakly_canonical("foo/./bar") == fs::current_path() / "foo/bar"); + CHECK(fs::weakly_canonical("foo/../bar") == fs::current_path() / "bar"); + } { TemporaryDirectory t(TempOpt::change_path); @@ -2369,8 +2396,10 @@ TEST_CASE("30.10.15.39 weakly_canonical", "[filesystem][operations][fs.op.weakly CHECK(fs::weakly_canonical(dir) == dir); CHECK(fs::weakly_canonical(rel) == dir); CHECK(fs::weakly_canonical(dir / "f0") == dir / "f0"); + CHECK(fs::weakly_canonical(dir / "f0/") == dir / "f0/"); CHECK(fs::weakly_canonical(dir / "f1") == dir / "f1"); CHECK(fs::weakly_canonical(rel / "f0") == dir / "f0"); + CHECK(fs::weakly_canonical(rel / "f0/") == dir / "f0/"); CHECK(fs::weakly_canonical(rel / "f1") == dir / "f1"); CHECK(fs::weakly_canonical(rel / "./f0") == dir / "f0"); CHECK(fs::weakly_canonical(rel / "./f1") == dir / "f1"); From 280a8590f98bfddbeaef3798b20512e028d99d31 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sun, 5 May 2019 09:28:03 +0200 Subject: [PATCH 08/21] Version bump, bugfix release v1.1.2 --- README.md | 10 ++++++---- include/ghc/filesystem.hpp | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bda1923..a7d251a 100644 --- a/README.md +++ b/README.md @@ -291,18 +291,20 @@ between this and the standard C++17 API. ### LWG Defects This implementation has switchable behavior for the LWG defects +[#2682](https://wg21.cmeerw.net/lwg/issue2682), [#2935](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2935) and [#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937). The currently selected behavior is following +[#2682](https://wg21.cmeerw.net/lwg/issue2682), [#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937) but not following [#2935](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2935), as I feel it is a bug to report no error on a `create_directory()` or `create_directories()` where a regular file of the same name prohibits the creation of a directory and forces the user of those functions to double-check via `fs::is_directory` if it really worked. - -Update: The more intuitive approach to directory creation of treating a file with that name as an +The more intuitive approach to directory creation of treating a file with that name as an error is also advocated by the newer paper -[WG21 P1164R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1164r0.pdf) +[WG21 P1164R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1164r0.pdf), the revison +P1161R1 was agreed upon on Kona 2019 meeting [see merge](https://github.com/cplusplus/draft/issues/2703) and GCC by now switched to following its proposal ([GCC #86910](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86910)). @@ -437,7 +439,7 @@ to the expected behavior. ## Release Notes -### v1.1.2 (wip) +### [v1.1.2](https://github.com/gulrak/filesystem/releases/tag/v1.1.2) * Bugfix for ([#11](https://github.com/gulrak/filesystem/issues/11)), `fs::path::lexically_normal()` had some issues with `".."`-sequences. diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 3657df7..39c1361 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -159,7 +159,7 @@ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) -#define GHC_FILESYSTEM_VERSION 10101L +#define GHC_FILESYSTEM_VERSION 10102L namespace ghc { namespace filesystem { From de1a631db9fe6d568e0df3848e13f5d1bfeb3317 Mon Sep 17 00:00:00 2001 From: fenglc Date: Tue, 7 May 2019 19:18:17 +0800 Subject: [PATCH 09/21] cmake 3.7.2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39e3d85..648c6e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.9) +cmake_minimum_required(VERSION 3.7.2) project(ghcfilesystem) if(NOT DEFINED CMAKE_CXX_STANDARD) From cc476ac1fd576f8815d9f3bb60cc82e9f3f1fd93 Mon Sep 17 00:00:00 2001 From: gulrak Date: Tue, 7 May 2019 19:49:01 +0200 Subject: [PATCH 10/21] refs #13, rework of POSIX directory_iterator handling, performance optimization --- examples/du.cpp | 2 +- include/ghc/filesystem.hpp | 99 ++++++++++++++++++-------------------- 2 files changed, 48 insertions(+), 53 deletions(-) diff --git a/examples/du.cpp b/examples/du.cpp index 88436d9..95b9580 100644 --- a/examples/du.cpp +++ b/examples/du.cpp @@ -52,6 +52,6 @@ int main(int argc, char* argv[]) std::cerr << "Error: " << fe.what() << std::endl; exit(1); } - std::cout << totalSize << " bytes in " << totalFiles << " and " << totalDirs << " directories, maximum depth: " << maxDepth << std::endl; + std::cout << totalSize << " bytes in " << totalFiles << " files and " << totalDirs << " directories, maximum depth: " << maxDepth << std::endl; return 0; } diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 39c1361..4429a14 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -342,6 +342,8 @@ public: iterator end() const; private: + friend class directory_iterator; + void append_name(const char* name); static constexpr value_type generic_separator = '/'; template class input_iterator_range @@ -1909,7 +1911,8 @@ inline path& path::assign(const Source& source) template <> inline path& path::assign(const path& source) { - return assign(source._path); + _path = source._path; + return *this; } template @@ -1959,6 +1962,19 @@ GHC_INLINE path& path::operator/=(const path& p) return *this; } +GHC_INLINE void path::append_name(const char* name) +{ + if(_path.empty()) { + this->operator/=(path(name)); + } + else { + if(_path.back() != path::generic_separator) { + _path.push_back(path::generic_separator); + } + _path += name; + } +} + #endif // GHC_EXPAND_IMPL template @@ -4537,7 +4553,8 @@ public: if (_dirHandle != INVALID_HANDLE_VALUE) { do { if (FindNextFileW(_dirHandle, &_findData)) { - _current = _base / std::wstring(_findData.cFileName); + _current = _base; + _current.append_name(detail::toUtf8(_findData.cFileName).c_str()); copyToDirEntry(ec); } else { @@ -4584,32 +4601,17 @@ public: // POSIX implementation class directory_iterator::impl { - size_t directory_entry_buffer_size(DIR* d) - { - size_t result = std::max(sizeof(::dirent), sizeof(::dirent) - sizeof(::dirent::d_name) + NAME_MAX) + 1; - if (d) { - long rc = ::fpathconf(dirfd(d), _PC_NAME_MAX); - if (rc > long(result)) { - result = static_cast(rc); - } - } - return result; - } - + public: impl(const path& path, directory_options options) : _base(path) , _options(options) - , _dir((path.empty() ? nullptr : ::opendir(path.native().c_str())), - [](DIR* d) { - if (d) { - ::closedir(d); - } - }) - , _bufferSize(directory_entry_buffer_size(_dir.get())) - , _buffer(new char[_bufferSize]) - , _entry(reinterpret_cast<::dirent*>(&_buffer[0])) + , _dir(nullptr) + , _entry(nullptr) { + if(!path.empty()) { + _dir = ::opendir(path.native().c_str()); + } if (!path.empty()) { if (!_dir) { auto error = errno; @@ -4624,40 +4626,30 @@ public: } } impl(const impl& other) = delete; - int i_readdir_r(DIR* dir, struct dirent* entry, struct dirent** result) + ~impl() { -#if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 24)) - errno = 0; - auto de = readdir(dir); - if (de) { - *entry = *de; - *result = entry; - return 0; + if(_dir) { + ::closedir(_dir); } - return errno; -#else - return ::readdir_r(dir, entry, result); -#endif } void increment(std::error_code& ec) { if (_dir) { do { - dirent* result = 0; - if (0 == i_readdir_r(_dir.get(), _entry, &result)) { - if (result) { - _current = _base / path(_entry->d_name); - _dir_entry = directory_entry(_current, ec); - } - else { - _dir.reset(); - _current = path(); - break; - } + errno = 0; + _entry = readdir(_dir); + if (_entry) { + _current = _base; + _current.append_name(_entry->d_name); + _dir_entry = directory_entry(_current, ec); } else { + ::closedir(_dir); + _dir = nullptr; _current = path(); - ec = std::error_code(errno, std::system_category()); + if(errno) { + ec = std::error_code(errno, std::system_category()); + } break; } } while (std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0); @@ -4666,9 +4658,7 @@ public: path _base; directory_options _options; path _current; - std::shared_ptr _dir; - size_t _bufferSize; - std::unique_ptr _buffer; + DIR* _dir; struct ::dirent* _entry; directory_entry _dir_entry; std::error_code _ec; @@ -4893,9 +4883,14 @@ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment else { _impl->_dir_iter_stack.top().increment(ec); } - while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + if(!ec) { + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } + } + else if(!_impl->_dir_iter_stack.empty()) { _impl->_dir_iter_stack.pop(); - _impl->_dir_iter_stack.top().increment(ec); } _impl->_recursion_pending = true; return *this; From 04b6fa278b576bd9638ebd4844513673307dbfeb Mon Sep 17 00:00:00 2001 From: gulrak Date: Tue, 7 May 2019 19:52:38 +0200 Subject: [PATCH 11/21] wip version bump --- include/ghc/filesystem.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 4429a14..b41725e 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -159,7 +159,7 @@ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) -#define GHC_FILESYSTEM_VERSION 10102L +#define GHC_FILESYSTEM_VERSION 10103L namespace ghc { namespace filesystem { From bc1abfa01e3e6f037328e3424a9e1af5a48bbc9b Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Tue, 7 May 2019 20:33:56 +0200 Subject: [PATCH 12/21] Fix file_time_type handling when building against MSVC C++17 std::filesystem, additional test fixes. --- test/filesystem_test.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index 847a745..a485a03 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -105,9 +105,18 @@ using fstream = ghc::filesystem::fstream; template std::time_t to_time_t(TP tp) { - // Based on trick from: Nico Josuttis, C++17 - The Complete Guide - std::chrono::system_clock::duration dt = std::chrono::duration_cast(tp - TP::clock::now()); - return std::chrono::system_clock::to_time_t(std::chrono::system_clock::now() + dt); + using namespace std::chrono; + auto sctp = time_point_cast(tp - TP::clock::now() + system_clock::now()); + return system_clock::to_time_t(sctp); +} + +template +TP from_time_t(std::time_t t) +{ + using namespace std::chrono; + auto sctp = system_clock::from_time_t(t); + auto tp = time_point_cast(sctp - system_clock::now() + TP::clock::now()); + return tp; } namespace Catch { @@ -2072,7 +2081,7 @@ static fs::file_time_type timeFromString(const std::string& str) throw std::exception(); } #ifdef IS_WCHAR_PATH - return fs::file_time_type::min(); + return from_time_t(std::mktime(&tm)); #else return fs::file_time_type::clock::from_time_t(std::mktime(&tm)); #endif @@ -2414,7 +2423,7 @@ TEST_CASE("Windows: Long filename support", "[filesystem][path][fs.path.win.long { TemporaryDirectory t(TempOpt::change_path); char c = 'A'; - fs::path dir = fs::current_path(); + fs::path dir = "\\\\?\\" + fs::current_path().u8string(); for (; c <= 'Z'; ++c) { std::string part = std::string(16, c); dir /= part; From 87ae60ed8354d4d2ca524e14b57feb2a9e80d31e Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Tue, 7 May 2019 21:50:10 +0200 Subject: [PATCH 13/21] Added Xcode 10.2 to Travis-CI config. --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f3597fb..41d04e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,11 @@ matrix: - os: osx env: MATRIX_EVAL="CC=clang && CXX=clang++ && GENERATOR=Xcode" osx_image: xcode9.2 - + + - os: osx + env: MATRIX_EVAL="CC=clang && CXX=clang++ && GENERATOR=Xcode" + osx_image: xcode10.2 + install: - eval "${MATRIX_EVAL}" - | From 275a1b714cd9fdfac72e060148f669753569c28c Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Tue, 7 May 2019 22:02:38 +0200 Subject: [PATCH 14/21] Clearer non inverted logic for primitive case insensitive compare, updated readme. --- README.md | 3 ++- include/ghc/filesystem.hpp | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a7d251a..9430358 100644 --- a/README.md +++ b/README.md @@ -403,7 +403,8 @@ and part of the justification for the proposed solution is "we did it so for alm But this makes `fs::copy` with `fs::copy_options::create_symlinks` or `fs::copy_options::create_hard_links` just a more complicated syntax for the `fs::create_symlink` or `fs::create_hardlink` operation and I don't want to believe, that this was the intention of the original writing. -As there is another issue related to copy, with a different take on the description, +As there is another issue related to copy, with a different take on the description. + *Note:* With v1.1.2 I decided to integrate a behavior switch for this and make the LWG #2682 the default. diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index b41725e..1f32160 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -1379,20 +1379,20 @@ inline path::path(InputIterator first, InputIterator last, format fmt) namespace detail { -GHC_INLINE bool compare_no_case(const char* str1, const char* str2) +GHC_INLINE bool equals_simple_insensitive(const char* str1, const char* str2) { #ifdef GHC_OS_WINDOWS # ifdef __GNUC__ while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) { if (*str1++ == 0) - return false; + return true; } - return true; + return false; # else - return ::_stricmp(str1, str2); + return 0 == ::_stricmp(str1, str2); # endif #else - return ::strcasecmp(str1, str2); + return 0 == ::strcasecmp(str1, str2); #endif } @@ -1650,7 +1650,7 @@ GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::er prms = prms | perms::owner_write | perms::group_write | perms::others_write; } std::string ext = p.extension().generic_string(); - if (!compare_no_case(ext.c_str(), ".exe") || !compare_no_case(ext.c_str(), ".cmd") || !compare_no_case(ext.c_str(), ".bat") || !compare_no_case(ext.c_str(), ".com")) { + if (equals_simple_insensitive(ext.c_str(), ".exe") || equals_simple_insensitive(ext.c_str(), ".cmd") || equals_simple_insensitive(ext.c_str(), ".bat") || equals_simple_insensitive(ext.c_str(), ".com")) { prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec; } if (sz) { @@ -1810,7 +1810,7 @@ GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) #if defined(__ANDROID__) && __ANDROID_API__ < 26 _isvalid = true; #else - if (!detail::compare_no_case(::nl_langinfo(CODESET), "UTF-8")) { + if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) { _isvalid = true; } #endif From 163d401f99c1a4417034ffd5cb6a1912fcffb39b Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Tue, 7 May 2019 22:25:41 +0200 Subject: [PATCH 15/21] Added missing include for the fwd/impl use case. --- include/ghc/filesystem.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 1f32160..72f8a5f 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -129,6 +129,7 @@ #include #include #include +#include #include #include #include From fe3d15f449bf51f69db1194db6ce60bf0d83170e Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Tue, 7 May 2019 22:31:24 +0200 Subject: [PATCH 16/21] Version bump, bugfix release v1.1.4 --- README.md | 10 ++++++++++ include/ghc/filesystem.hpp | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9430358..97f424a 100644 --- a/README.md +++ b/README.md @@ -440,6 +440,16 @@ to the expected behavior. ## Release Notes +###[v1.1.4](https://github.com/gulrak/filesystem/releases/tag/v1.1.4) + +* Additional Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)), + error in old unified `readdir/readdir_r` code of `fs::directory_iterator`; + as `readdir_r` is now depricated, I decided to drop it and the resulting + code is much easier, shorter and due to more refactoring faster +* Fix for crashing unit tests against MSVC C++17 std::filesystem +* Travis-CI now additionally test with Xcode 10.2 on macOS +* Some minor refactorings + ### [v1.1.2](https://github.com/gulrak/filesystem/releases/tag/v1.1.2) * Bugfix for ([#11](https://github.com/gulrak/filesystem/issues/11)), diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index 72f8a5f..b97f7fe 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -160,7 +160,7 @@ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) -#define GHC_FILESYSTEM_VERSION 10103L +#define GHC_FILESYSTEM_VERSION 10104L namespace ghc { namespace filesystem { From 9ea5a8773b291add335980cf1f4e45c108722298 Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Tue, 7 May 2019 23:06:11 +0200 Subject: [PATCH 17/21] Updated readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97f424a..e575bc9 100644 --- a/README.md +++ b/README.md @@ -440,7 +440,7 @@ to the expected behavior. ## Release Notes -###[v1.1.4](https://github.com/gulrak/filesystem/releases/tag/v1.1.4) +### [v1.1.4](https://github.com/gulrak/filesystem/releases/tag/v1.1.4) * Additional Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)), error in old unified `readdir/readdir_r` code of `fs::directory_iterator`; From 71a50bb34aa7fbd652b12e213b034c2690ffd368 Mon Sep 17 00:00:00 2001 From: fenglc89 Date: Wed, 8 May 2019 17:40:56 +0800 Subject: [PATCH 18/21] cmake install --- CMakeLists.txt | 16 ++++++++++++++++ cmake/config.cmake.in | 5 +++++ 2 files changed, 21 insertions(+) create mode 100644 cmake/config.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 648c6e8..d538b50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,3 +32,19 @@ if(NOT hasParent) add_subdirectory(test) add_subdirectory(examples) endif() + +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + +set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}) +set(PACKAGECONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) + +configure_package_config_file(cmake/config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + INSTALL_DESTINATION ${PACKAGECONFIG_INSTALL_DIR} + PATH_VARS INCLUDE_INSTALL_DIR) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + DESTINATION ${PACKAGECONFIG_INSTALL_DIR}) diff --git a/cmake/config.cmake.in b/cmake/config.cmake.in new file mode 100644 index 0000000..3d25f9f --- /dev/null +++ b/cmake/config.cmake.in @@ -0,0 +1,5 @@ +@PACKAGE_INIT@ + +set_and_check(ghcfilesystem_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@") + +check_required_components(ghcfilesystem) From 7e975a8f7f974750569d0445af5fb49a692d00fb Mon Sep 17 00:00:00 2001 From: Steffen Schuemann Date: Sat, 11 May 2019 15:33:08 +0200 Subject: [PATCH 19/21] Updated readme, more tests, some typos in comments. --- README.md | 7 ++++++- include/ghc/fs_std.hpp | 2 +- test/filesystem_test.cpp | 17 ++++++++++++++++- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e575bc9..5c16741 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![Supported Platforms](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows-blue.svg) [![Build Status](https://travis-ci.org/gulrak/filesystem.svg?branch=master)](https://travis-ci.org/gulrak/filesystem) [![Build status](https://ci.appveyor.com/api/projects/status/t07wp3k2cddo0hpo/branch/master?svg=true)](https://ci.appveyor.com/project/gulrak/filesystem) -![Latest Release Tag](https://img.shields.io/github/tag/gulrak/filesystem.svg) +[![Latest Release Tag](https://img.shields.io/github/tag/gulrak/filesystem.svg)](https://github.com/gulrak/filesystem/tree/v1.1.4) # Filesystem @@ -97,6 +97,11 @@ in the standard, and there might be issues in these implementations too. ## Usage +### Downloads + +The latest release version is [v1.1.4](https://github.com/gulrak/filesystem/tree/v1.1.4) and +source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.1.4). + ### Using it as Single-File-Header As `ghc::filesystem` is at first a header-only library, it should be enough to copy the header diff --git a/include/ghc/fs_std.hpp b/include/ghc/fs_std.hpp index 2b500fb..9b54a0d 100644 --- a/include/ghc/fs_std.hpp +++ b/include/ghc/fs_std.hpp @@ -33,7 +33,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //--------------------------------------------------------------------------------------- -// fs_std.hpp - The dynamic switching header for that includes std::filesystem if detected +// fs_std.hpp - The dynamic switching header that includes std::filesystem if detected // or ghc::filesystem if not, and makes the resulting API available in the // namespace fs. //--------------------------------------------------------------------------------------- diff --git a/test/filesystem_test.cpp b/test/filesystem_test.cpp index a485a03..f00dc8f 100644 --- a/test/filesystem_test.cpp +++ b/test/filesystem_test.cpp @@ -295,7 +295,7 @@ bool operator!=(TestAllocator const& x, TestAllocator const& y) noexcept return !(x == y); } -TEST_CASE("Temporary Directory", "[temp dir]") +TEST_CASE("Temporary Directory", "[fs.test.tempdir]") { fs::path tempPath; { @@ -307,6 +307,21 @@ TEST_CASE("Temporary Directory", "[temp dir]") REQUIRE(!fs::exists(tempPath)); } +#ifdef GHC_FILESYSTEM_VERSION +TEST_CASE("fs::detail::fromUf88", "[filesystem][fs.detail.utf8]") +{ + CHECK(fs::detail::fromUtf8("foobar").length() == 6); + CHECK(fs::detail::fromUtf8("foobar") == L"foobar"); + CHECK(fs::detail::fromUtf8(u8"föobar").length() == 6); + CHECK(fs::detail::fromUtf8(u8"föobar") == L"föobar"); + + CHECK(fs::detail::toUtf8(std::wstring(L"foobar")).length() == 6); + CHECK(fs::detail::toUtf8(std::wstring(L"foobar")) == "foobar"); + CHECK(fs::detail::toUtf8(std::wstring(L"föobar")).length() == 7); + CHECK(fs::detail::toUtf8(std::wstring(L"föobar")) == u8"föobar"); +} +#endif + #ifndef GHC_OS_WINDOWS TEST_CASE("30.10.8.1 path(\"//host\").has_root_name()", "[filesystem][path][fs.path.generic]") { From 16a5fbb86e2e0bf72cb0c23ffe05f084da9a045b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sat, 11 May 2019 15:49:26 +0200 Subject: [PATCH 20/21] Small changes for MingW compiles concerning unsupported error code and clarified test in utf8 decoder. --- include/ghc/filesystem.hpp | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index b97f7fe..f7d4b40 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -740,13 +740,14 @@ public: void swap(recursive_directory_iterator& rhs); private: - struct recursive_directory_iterator_impl { + struct recursive_directory_iterator_impl + { directory_options _options; bool _recursion_pending; std::stack _dir_iter_stack; recursive_directory_iterator_impl(directory_options options, bool recursion_pending) - : _options(options) - , _recursion_pending(recursion_pending) + : _options(options) + , _recursion_pending(recursion_pending) { } }; @@ -1055,7 +1056,11 @@ GHC_INLINE std::error_code make_error_code(portable_error err) case portable_error::invalid_argument: return std::error_code(ERROR_INVALID_PARAMETER, std::system_category()); case portable_error::is_a_directory: - return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); +#ifdef ERROR_DIRECTORY_NOT_SUPPORTED + return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); +#else + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); +#endif } #else switch (err) { @@ -1211,7 +1216,7 @@ inline StringType fromUtf8(const std::string& utf8String, const typename StringT unsigned utf8_state = S_STRT; std::uint32_t codepoint = 0; while (iter < utf8String.end()) { - if (!(utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint))) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, (uint8_t)*iter++, codepoint)) == S_STRT) { if (sizeof(typename StringType::value_type) == 4) { result += codepoint; } @@ -1818,7 +1823,6 @@ GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) #endif } - //----------------------------------------------------------------------------- // 30.10.8.4.1 constructors and destructor @@ -1965,11 +1969,11 @@ GHC_INLINE path& path::operator/=(const path& p) GHC_INLINE void path::append_name(const char* name) { - if(_path.empty()) { + if (_path.empty()) { this->operator/=(path(name)); } else { - if(_path.back() != path::generic_separator) { + if (_path.back() != path::generic_separator) { _path.push_back(path::generic_separator); } _path += name; @@ -2428,7 +2432,7 @@ GHC_INLINE path path::lexically_normal() const continue; } else if (*(--dest.end()) != "..") { - if(dest._path.back() == generic_separator) { + if (dest._path.back() == generic_separator) { dest._path.pop_back(); } dest.remove_filename(); @@ -3015,7 +3019,7 @@ GHC_INLINE void copy(const path& from, const path& to, copy_options options, std } } #ifdef LWG_2682_BEHAVIOUR - else if(is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { + else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { ec = detail::make_error_code(detail::portable_error::is_a_directory); } #endif @@ -4602,7 +4606,6 @@ public: // POSIX implementation class directory_iterator::impl { - public: impl(const path& path, directory_options options) : _base(path) @@ -4610,7 +4613,7 @@ public: , _dir(nullptr) , _entry(nullptr) { - if(!path.empty()) { + if (!path.empty()) { _dir = ::opendir(path.native().c_str()); } if (!path.empty()) { @@ -4629,7 +4632,7 @@ public: impl(const impl& other) = delete; ~impl() { - if(_dir) { + if (_dir) { ::closedir(_dir); } } @@ -4648,7 +4651,7 @@ public: ::closedir(_dir); _dir = nullptr; _current = path(); - if(errno) { + if (errno) { ec = std::error_code(errno, std::system_category()); } break; @@ -4884,13 +4887,13 @@ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment else { _impl->_dir_iter_stack.top().increment(ec); } - if(!ec) { + if (!ec) { while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { _impl->_dir_iter_stack.pop(); _impl->_dir_iter_stack.top().increment(ec); } } - else if(!_impl->_dir_iter_stack.empty()) { + else if (!_impl->_dir_iter_stack.empty()) { _impl->_dir_iter_stack.pop(); } _impl->_recursion_pending = true; @@ -4915,8 +4918,7 @@ GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) do { _impl->_dir_iter_stack.pop(); _impl->_dir_iter_stack.top().increment(ec); - } - while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); + } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); } } From 38b9192d3a2229aea8c23787413c7b7405e27240 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Sch=C3=BCmann?= Date: Sat, 11 May 2019 15:51:17 +0200 Subject: [PATCH 21/21] Version bump to wip version. --- include/ghc/filesystem.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ghc/filesystem.hpp b/include/ghc/filesystem.hpp index f7d4b40..c9562d9 100644 --- a/include/ghc/filesystem.hpp +++ b/include/ghc/filesystem.hpp @@ -160,7 +160,7 @@ //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) -#define GHC_FILESYSTEM_VERSION 10104L +#define GHC_FILESYSTEM_VERSION 10105L namespace ghc { namespace filesystem {