diff --git a/include/boost/filesystem/directory.hpp b/include/boost/filesystem/directory.hpp index 169c89d48..5ca1d03fe 100644 --- a/include/boost/filesystem/directory.hpp +++ b/include/boost/filesystem/directory.hpp @@ -99,6 +99,16 @@ void recursive_directory_iterator_increment(recursive_directory_iterator& it, sy BOOST_FILESYSTEM_DECL void recursive_directory_iterator_pop(recursive_directory_iterator& it, system::error_code* ec); +enum class directory_entry_update_mask : unsigned int +{ + none = 0u, + symlink_status = 1u, + status = 1u << 1u, + all = 3u +}; + +BOOST_BITMASK(directory_entry_update_mask) + } // namespace detail //--------------------------------------------------------------------------------------// @@ -199,7 +209,7 @@ class directory_entry directory_entry(boost::filesystem::path const& p, system::error_code& ec) : m_path(p) { - refresh_impl(&ec); + refresh_impl(update_mask::all, &ec); if (ec) m_path.clear(); } @@ -327,7 +337,7 @@ class directory_entry void assign(boost::filesystem::path const& p, system::error_code& ec) { m_path = p; - refresh_impl(&ec); + refresh_impl(update_mask::all, &ec); } /*! @@ -345,7 +355,7 @@ class directory_entry void assign(boost::filesystem::path&& p, system::error_code& ec) { m_path = static_cast< boost::filesystem::path&& >(p); - refresh_impl(&ec); + refresh_impl(update_mask::all, &ec); } #endif #if BOOST_FILESYSTEM_VERSION < 4 || defined(BOOST_FILESYSTEM_DOXYGEN) @@ -430,7 +440,7 @@ class directory_entry void replace_filename(boost::filesystem::path const& p, system::error_code& ec) { m_path.replace_filename(p); - refresh_impl(&ec); + refresh_impl(update_mask::all, &ec); } #endif #if BOOST_FILESYSTEM_VERSION < 4 || defined(BOOST_FILESYSTEM_DOXYGEN) @@ -491,17 +501,17 @@ class directory_entry * * \param ec Error code returned in case of failure. */ - void refresh(system::error_code& ec) noexcept { refresh_impl(&ec); } + void refresh(system::error_code& ec) noexcept { refresh_impl(update_mask::all, &ec); } /*! \overload */ - void refresh() { refresh_impl(); } + void refresh() { refresh_impl(update_mask::all); } /*! * \brief Returns the file status. * * \effects - * For the cached file status `m_status`, if `!status_known(m_status)`, calls `refresh(ec)`. Then returns - * `m_status`. + * For the cached file status `m_status`, if `!status_known(m_status)`, refreshes the cache to update it. + * Then returns `m_status`. * * \note The implementation does not query the filesystem after the file status has been cached. * Filesystem changes after the file status has been cached will not be reflected in the result. @@ -515,7 +525,7 @@ class directory_entry ec.clear(); if (!filesystem::status_known(m_status)) - refresh_impl(&ec); + refresh_impl(update_mask::all, &ec); return m_status; } @@ -523,7 +533,7 @@ class directory_entry file_status status() const { if (!filesystem::status_known(m_status)) - refresh_impl(); + refresh_impl(update_mask::all); return m_status; } @@ -531,8 +541,8 @@ class directory_entry * \brief Returns the symlink file status. * * \effects - * For the cached symlink file status `m_symlink_status`, if `!status_known(m_symlink_status)`, calls - * `refresh(ec)`. Then returns `m_symlink_status`. + * For the cached symlink file status `m_symlink_status`, if `!status_known(m_symlink_status)`, refreshes + * the cache to update it. Then returns `m_symlink_status`. * * \note The implementation does not query the filesystem after the symlink file status has been cached. * Filesystem changes after the symlink file status has been cached will not be reflected in the result. @@ -546,7 +556,7 @@ class directory_entry ec.clear(); if (!filesystem::status_known(m_symlink_status)) - refresh_impl(&ec); + refresh_impl(update_mask::symlink_status, &ec); return m_symlink_status; } @@ -554,7 +564,7 @@ class directory_entry file_status symlink_status() const { if (!filesystem::status_known(m_symlink_status)) - refresh_impl(); + refresh_impl(update_mask::symlink_status); return m_symlink_status; } @@ -577,7 +587,7 @@ class directory_entry ec.clear(); if (!filesystem::type_present(m_status)) - refresh_impl(&ec); + refresh_impl(update_mask::all, &ec); return m_status.type(); } @@ -585,7 +595,7 @@ class directory_entry filesystem::file_type file_type() const { if (!filesystem::type_present(m_status)) - refresh_impl(); + refresh_impl(update_mask::all); return m_status.type(); } @@ -608,7 +618,7 @@ class directory_entry ec.clear(); if (!filesystem::type_present(m_symlink_status)) - refresh_impl(&ec); + refresh_impl(update_mask::symlink_status, &ec); return m_symlink_status.type(); } @@ -616,7 +626,7 @@ class directory_entry filesystem::file_type symlink_file_type() const { if (!filesystem::type_present(m_symlink_status)) - refresh_impl(); + refresh_impl(update_mask::symlink_status); return m_symlink_status.type(); } @@ -942,7 +952,9 @@ class directory_entry #if !defined(BOOST_FILESYSTEM_DOXYGEN) private: - BOOST_FILESYSTEM_DECL void refresh_impl(system::error_code* ec = nullptr) const; + using update_mask = detail::directory_entry_update_mask; + + BOOST_FILESYSTEM_DECL void refresh_impl(update_mask mask, system::error_code* ec = nullptr) const; void assign_with_status(boost::filesystem::path&& p, file_status st, file_status symlink_st) { @@ -978,7 +990,7 @@ inline directory_entry::directory_entry(boost::filesystem::path const& p) : m_path(p) { #if BOOST_FILESYSTEM_VERSION >= 4 - refresh_impl(); + refresh_impl(update_mask::all); #endif } @@ -986,7 +998,7 @@ inline void directory_entry::assign(boost::filesystem::path&& p) { m_path = static_cast< boost::filesystem::path&& >(p); #if BOOST_FILESYSTEM_VERSION >= 4 - refresh_impl(); + refresh_impl(update_mask::all); #else m_status = file_status(); m_symlink_status = file_status(); @@ -997,7 +1009,7 @@ inline void directory_entry::assign(boost::filesystem::path const& p) { m_path = p; #if BOOST_FILESYSTEM_VERSION >= 4 - refresh_impl(); + refresh_impl(update_mask::all); #else m_status = file_status(); m_symlink_status = file_status(); @@ -1008,7 +1020,7 @@ inline void directory_entry::replace_filename(boost::filesystem::path const& p) { m_path.replace_filename(p); #if BOOST_FILESYSTEM_VERSION >= 4 - refresh_impl(); + refresh_impl(update_mask::all); #else m_status = file_status(); m_symlink_status = file_status(); diff --git a/src/directory.cpp b/src/directory.cpp index 31147b289..3f5830921 100644 --- a/src/directory.cpp +++ b/src/directory.cpp @@ -104,9 +104,14 @@ namespace filesystem { // // //--------------------------------------------------------------------------------------// -BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl(system::error_code* ec) const +BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl( + directory_entry::update_mask mask, + system::error_code* ec) const { - m_status = filesystem::file_status(); + const bool update_status = (mask & update_mask::status) != update_mask::none; + + if (update_status) + m_status = filesystem::file_status(); m_symlink_status = filesystem::file_status(); m_symlink_status = detail::symlink_status(m_path, ec); @@ -116,7 +121,7 @@ BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl(system::error_code* ec) // Also works if symlink_status fails - set m_status to status_error as well m_status = m_symlink_status; } - else + else if (update_status) { m_status = detail::status(m_path, ec); } diff --git a/test/operations_test.cpp b/test/operations_test.cpp index 2e9d9fe3b..bd4864190 100644 --- a/test/operations_test.cpp +++ b/test/operations_test.cpp @@ -934,7 +934,7 @@ void create_symlink_tests() BOOST_TEST(!fs::is_other(stat)); } - error_code ec = error_code(); + error_code ec; fs::create_symlink("doesnotexist", "", ec); BOOST_TEST(ec); } @@ -2403,6 +2403,191 @@ void symlink_is_empty_tests() BOOST_TEST_EQ(empty, true); } +// directory_entry_tests ----------------------------------------------------// + +void directory_entry_tests() +{ + cout << "directory_entry_tests..." << endl; + + fs::path reg_file(dir / "reg-file"); + fs::path nonexistent_file(dir / "nonexistent-file"); + fs::remove(reg_file); + fs::remove(nonexistent_file); + create_file(reg_file); + error_code ec; + + fs::directory_entry reg_entry(reg_file); + fs::directory_entry nonexistent_entry(nonexistent_file); + + BOOST_TEST(reg_entry.exists()); + BOOST_TEST(reg_entry.exists(ec)); + BOOST_TEST(!ec); + BOOST_TEST_EQ(reg_entry.status().type(), fs::regular_file); + BOOST_TEST_EQ(reg_entry.status(ec).type(), fs::regular_file); + BOOST_TEST(!ec); + BOOST_TEST_EQ(reg_entry.symlink_status().type(), fs::regular_file); + BOOST_TEST_EQ(reg_entry.symlink_status(ec).type(), fs::regular_file); + BOOST_TEST(!ec); + BOOST_TEST_EQ(reg_entry.file_type(), fs::regular_file); + BOOST_TEST_EQ(reg_entry.file_type(ec), fs::regular_file); + BOOST_TEST(!ec); + BOOST_TEST_EQ(reg_entry.symlink_file_type(), fs::regular_file); + BOOST_TEST_EQ(reg_entry.symlink_file_type(ec), fs::regular_file); + BOOST_TEST(!ec); + + reg_entry.refresh(ec); + BOOST_TEST(!ec); + + // Make sure status() and symlink_status() hold the expected types + // after a call to refresh, too + BOOST_TEST_EQ(reg_entry.symlink_status().type(), fs::regular_file); + BOOST_TEST_EQ(reg_entry.status().type(), fs::regular_file); + +#if BOOST_FILESYSTEM_VERSION >= 4 + // ctor overload with error_code + { + ec.clear(); + fs::directory_entry reg_entry2(reg_file, ec); + BOOST_TEST(!ec); + BOOST_TEST(reg_entry2.path() == reg_file); + } + + // assign overload with error_code + ec.clear(); + reg_entry.assign(reg_file, ec); + BOOST_TEST(!ec); + BOOST_TEST(reg_entry.path() == reg_file); +#endif + + // Missing file + BOOST_TEST_EQ(nonexistent_entry.status().type(), fs::file_not_found); + BOOST_TEST_EQ(nonexistent_entry.status(ec).type(), fs::file_not_found); + BOOST_TEST_EQ(nonexistent_entry.symlink_status().type(), fs::file_not_found); + BOOST_TEST_EQ(nonexistent_entry.symlink_status(ec).type(), fs::file_not_found); + BOOST_TEST_EQ(nonexistent_entry.file_type(), fs::file_not_found); + BOOST_TEST_EQ(nonexistent_entry.file_type(ec), fs::file_not_found); + BOOST_TEST_EQ(nonexistent_entry.symlink_file_type(), fs::file_not_found); + BOOST_TEST_EQ(nonexistent_entry.symlink_file_type(ec), fs::file_not_found); + BOOST_TEST(!nonexistent_entry.exists(ec)); + BOOST_TEST(!nonexistent_entry.exists()); + +#if BOOST_FILESYSTEM_VERSION >= 4 + // ctor overload with error_code + { + ec.clear(); + fs::directory_entry nonexistent_entry2(nonexistent_file, ec); + BOOST_TEST(ec); + BOOST_TEST(nonexistent_entry2.path().empty()); + } + + // assign overload with error_code + ec.clear(); + nonexistent_entry.assign(nonexistent_file, ec); + BOOST_TEST(ec); + BOOST_TEST_EQ(nonexistent_entry.path(), nonexistent_file); +#endif + + fs::remove(reg_file); +} + +// directory_entry_symlink_tests --------------------------------------------// + +void directory_entry_symlink_tests() +{ + cout << "directory_entry_symlink_tests..." << endl; + + fs::path reg_file(dir / "reg-file"); + fs::path valid_sym(dir / "valid-sym"); + fs::path dangling_sym(dir / "dangling-sym"); + fs::remove(reg_file); + fs::remove(valid_sym); + fs::remove(dangling_sym); + create_file(reg_file); + fs::create_symlink(reg_file, valid_sym); + fs::create_symlink("does not exist", dangling_sym); + error_code ec; + + fs::directory_entry sym_entry(valid_sym); + fs::directory_entry dsym_entry(dangling_sym); + + BOOST_TEST(sym_entry.exists()); + BOOST_TEST(sym_entry.exists(ec)); + BOOST_TEST(!ec); + BOOST_TEST(sym_entry.is_symlink()); + BOOST_TEST(sym_entry.is_symlink(ec)); + BOOST_TEST(!ec); + BOOST_TEST_EQ(sym_entry.status().type(), fs::regular_file); + BOOST_TEST_EQ(sym_entry.status(ec).type(), fs::regular_file); + BOOST_TEST(!ec); + BOOST_TEST_EQ(sym_entry.symlink_status().type(), fs::symlink_file); + BOOST_TEST_EQ(sym_entry.symlink_status(ec).type(), fs::symlink_file); + BOOST_TEST(!ec); + BOOST_TEST_EQ(sym_entry.file_type(), fs::regular_file); + BOOST_TEST_EQ(sym_entry.file_type(ec), fs::regular_file); + BOOST_TEST(!ec); + BOOST_TEST_EQ(sym_entry.symlink_file_type(), fs::symlink_file); + BOOST_TEST_EQ(sym_entry.symlink_file_type(ec), fs::symlink_file); + BOOST_TEST(!ec); + + BOOST_TEST(!dsym_entry.exists()); + BOOST_TEST(!dsym_entry.exists(ec)); + ec.clear(); + BOOST_TEST(dsym_entry.is_symlink()); + BOOST_TEST(dsym_entry.is_symlink(ec)); + BOOST_TEST(!ec); + BOOST_TEST_EQ(dsym_entry.status().type(), fs::file_not_found); + BOOST_TEST_EQ(dsym_entry.status(ec).type(), fs::file_not_found); + ec.clear(); + BOOST_TEST_EQ(dsym_entry.symlink_status().type(), fs::symlink_file); + BOOST_TEST_EQ(dsym_entry.symlink_status(ec).type(), fs::symlink_file); + BOOST_TEST(!ec); + BOOST_TEST_EQ(dsym_entry.file_type(), fs::file_not_found); + BOOST_TEST_EQ(dsym_entry.file_type(ec), fs::file_not_found); + ec.clear(); + BOOST_TEST_EQ(dsym_entry.symlink_file_type(), fs::symlink_file); + BOOST_TEST_EQ(dsym_entry.symlink_file_type(ec), fs::symlink_file); + BOOST_TEST(!ec); + + sym_entry.refresh(ec); + BOOST_TEST(!ec); + dsym_entry.refresh(ec); + BOOST_TEST(ec); + + // Make sure status() and symlink_status() hold the expected types + // after a call to refresh, too + BOOST_TEST_EQ(sym_entry.symlink_status().type(), fs::symlink_file); + BOOST_TEST_EQ(sym_entry.status().type(), fs::regular_file); + BOOST_TEST_EQ(dsym_entry.symlink_status().type(), fs::symlink_file); + BOOST_TEST_EQ(dsym_entry.status().type(), fs::file_not_found); + +#if BOOST_FILESYSTEM_VERSION >= 4 + // ctor overload with error_code + { + ec.clear(); + fs::directory_entry sym_entry2(valid_sym, ec); + BOOST_TEST(!ec); + BOOST_TEST(sym_entry2.path() == valid_sym); + + fs::directory_entry dsym_entry2(dangling_sym, ec); + BOOST_TEST(ec); + BOOST_TEST(dsym_entry2.path().empty()); + } + + ec.clear(); + sym_entry.assign(valid_sym, ec); + BOOST_TEST(!ec); + BOOST_TEST(sym_entry.path() == valid_sym); + + dsym_entry.assign(dangling_sym, ec); + BOOST_TEST(ec); + BOOST_TEST(dsym_entry.path() == dangling_sym); +#endif + + fs::remove(reg_file); + fs::remove(valid_sym); + fs::remove(dangling_sym); +} + // write_time_tests ----------------------------------------------------------------// void write_time_tests(const fs::path& dirx) @@ -3025,6 +3210,7 @@ int cpp_main(int argc, char* argv[]) weakly_canonical_basic_tests(); permissions_tests(); copy_file_tests(f1, d1); + directory_entry_tests(); if (create_symlink_ok) // only if symlinks supported { symlink_status_tests(); @@ -3033,6 +3219,7 @@ int cpp_main(int argc, char* argv[]) weakly_canonical_symlink_tests(); symlink_file_size_tests(); symlink_is_empty_tests(); + directory_entry_symlink_tests(); } iterator_status_tests(); // lots of cases by now, so a good time to test // dump_tree(dir);