diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b346979..a8d1a39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -264,7 +264,7 @@ jobs: compiler: clang++-14 cxxstd: "03-gnu,11-gnu,14-gnu,17-gnu,20-gnu,2b-gnu" cxxflags: -stdlib=libc++ - linkflags: -stdlib=libc++ + linkflags: "-stdlib=libc++ -lubsan" ubsan: 1 build_variant: debug os: ubuntu-22.04 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8edf9c0..0db21d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,31 @@ endif() add_library(boost_filesystem ${BOOST_FILESYSTEM_SOURCES}) add_library(Boost::filesystem ALIAS boost_filesystem) +get_target_property(BOOST_FILESYSTEM_TARGET_TYPE boost_filesystem TYPE) +if(BOOST_FILESYSTEM_TARGET_TYPE STREQUAL "SHARED_LIBRARY") + set(CMAKE_REQUIRED_LIBRARIES "-Wl,--no-undefined") + check_cxx_source_compiles("#include <${CMAKE_CURRENT_SOURCE_DIR}/config/has_linkflag_no_undefined.cpp>" BOOST_FILESYSTEM_HAS_LINKFLAG_NO_UNDEFINED) + unset(CMAKE_REQUIRED_LIBRARIES) + if(NOT BOOST_FILESYSTEM_HAS_LINKFLAG_NO_UNDEFINED) + set(CMAKE_REQUIRED_LIBRARIES "-Wl,-undefined,error") + check_cxx_source_compiles("#include <${CMAKE_CURRENT_SOURCE_DIR}/config/has_linkflag_no_undefined.cpp>" BOOST_FILESYSTEM_HAS_LINKFLAG_UNDEFINED_ERROR) + unset(CMAKE_REQUIRED_LIBRARIES) + endif() + if(BOOST_FILESYSTEM_HAS_LINKFLAG_NO_UNDEFINED) + if(NOT CMAKE_VERSION VERSION_LESS 3.13) + target_link_options(boost_filesystem PRIVATE "-Wl,--no-undefined") + else() + target_link_libraries(boost_filesystem PRIVATE "-Wl,--no-undefined") + endif() + elseif(BOOST_FILESYSTEM_HAS_LINKFLAG_UNDEFINED_ERROR) + if(NOT CMAKE_VERSION VERSION_LESS 3.13) + target_link_options(boost_filesystem PRIVATE "-Wl,-undefined,error") + else() + target_link_libraries(boost_filesystem PRIVATE "-Wl,-undefined,error") + endif() + endif() +endif() + target_include_directories(boost_filesystem PUBLIC include) target_include_directories(boost_filesystem PRIVATE src) diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 788b14e..13c540d 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -101,6 +101,27 @@ rule check-cxx20-atomic-ref ( properties * ) return $(result) ; } +# The rule checks if the linker supports requiring no unresolved symbols +rule check-linkflag-no-undefined ( properties * ) +{ + local result ; + + if shared in $(properties) + { + if [ configure.builds ../config//has_linkflag_no_undefined : $(properties) : "has -Wl,--no-undefined" ] + { + result = "-Wl,--no-undefined" ; + } + else if [ configure.builds ../config//has_linkflag_undefined_error : $(properties) : "has -Wl,-undefined,error" ] + { + result = "-Wl,-undefined,error" ; + } + } + + #ECHO Result: $(result) ; + return $(result) ; +} + project boost/filesystem : requirements hpux,gcc:_INCLUDE_STDC__SOURCE_199901 @@ -117,6 +138,8 @@ project boost/filesystem @check-statx @select-windows-crypto-api @check-cxx20-atomic-ref + # Make sure no undefined references are left from the library + @check-linkflag-no-undefined windows:_SCL_SECURE_NO_WARNINGS windows:_SCL_SECURE_NO_DEPRECATE windows:_CRT_SECURE_NO_WARNINGS @@ -131,6 +154,7 @@ project boost/filesystem # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105329 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105651 gcc-12:"-Wno-restrict" + : source-location ../src : usage-requirements # pass these requirement to dependents (i.e. users) shared:BOOST_FILESYSTEM_DYN_LINK=1 diff --git a/config/Jamfile.v2 b/config/Jamfile.v2 index b34d3aa..aae2af5 100644 --- a/config/Jamfile.v2 +++ b/config/Jamfile.v2 @@ -39,3 +39,8 @@ exe has_bcrypt : has_bcrypt.cpp : ../src bcrypt ; explicit has_bcrypt ; obj is_windows_ce : is_windows_ce.cpp ; explicit is_windows_ce ; + +lib has_linkflag_no_undefined : has_linkflag_no_undefined.cpp : shared "-Wl,--no-undefined" ; +explicit has_linkflag_no_undefined ; +lib has_linkflag_undefined_error : has_linkflag_no_undefined.cpp : shared "-Wl,-undefined,error" ; +explicit has_linkflag_undefined_error ; diff --git a/config/has_linkflag_no_undefined.cpp b/config/has_linkflag_no_undefined.cpp new file mode 100644 index 0000000..94960fc --- /dev/null +++ b/config/has_linkflag_no_undefined.cpp @@ -0,0 +1,18 @@ +// Copyright 2023 Andrey Semashev + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt + +// See library home page at http://www.boost.org/libs/filesystem + +#if defined(_MSC_VER) +// MSVC's link.exe does not support -Wl,... flags, but doesn't fail the linking. +// The linker may be used by different compilers, not only MSVC. +// Luckily, those compilers all pretend to be MSVC. +#error "MSVC and compatible compilers don't support -Wl,... flags" +#endif + +int main() +{ + return 0; +} diff --git a/doc/release_history.html b/doc/release_history.html index 0e84089..8285705 100644 --- a/doc/release_history.html +++ b/doc/release_history.html @@ -45,6 +45,7 @@
  • v4: path::remove_filename now presesrves the trailing directory separator. (#271)
  • Added path::remove_filename_and_trailing_separators
  • , which removes the filename and directory separators preceding it from the path. This behavior is similar to path::remove_filename in Filesystem v3, but is also usable in v4.
  • Added path::replace_filename, which replaces filename in a path.
  • +
  • Updated implementation of the library version selection to avoid ODR violations. (#279)
  • 1.81.0

    diff --git a/include/boost/filesystem/path.hpp b/include/boost/filesystem/path.hpp index 75a3db6..61278ef 100644 --- a/include/boost/filesystem/path.hpp +++ b/include/boost/filesystem/path.hpp @@ -43,6 +43,8 @@ namespace boost { namespace filesystem { +class path; + namespace path_detail { // intentionally don't use filesystem::detail to not bring internal Boost.Filesystem functions into ADL via path_constants template< typename Char, Char Separator, Char PreferredSeparator, Char Dot > @@ -67,15 +69,83 @@ BOOST_CONSTEXPR_OR_CONST typename path_constants< Char, Separator, PreferredSepa path_constants< Char, Separator, PreferredSeparator, Dot >::dot; #endif -// A struct that denotes a contiguous range of characters in a string. A lightweight alternative to string_view. -struct substring -{ - std::size_t pos; - std::size_t size; -}; +class path_iterator; +class path_reverse_iterator; } // namespace path_detail +namespace detail { + +struct path_algorithms +{ + // A struct that denotes a contiguous range of characters in a string. A lightweight alternative to string_view. + struct substring + { + std::size_t pos; + std::size_t size; + }; + + typedef path_traits::path_native_char_type value_type; + typedef std::basic_string< value_type > string_type; + + static bool has_filename_v3(path const& p); + static bool has_filename_v4(path const& p); + BOOST_FILESYSTEM_DECL static path filename_v3(path const& p); + static path filename_v4(path const& p); + + BOOST_FILESYSTEM_DECL static path stem_v3(path const& p); + BOOST_FILESYSTEM_DECL static path stem_v4(path const& p); + BOOST_FILESYSTEM_DECL static path extension_v3(path const& p); + static path extension_v4(path const& p); + + BOOST_FILESYSTEM_DECL static void remove_filename_v3(path& p); + BOOST_FILESYSTEM_DECL static void remove_filename_v4(path& p); + + BOOST_FILESYSTEM_DECL static void replace_extension_v3(path& p, path const& new_extension); + BOOST_FILESYSTEM_DECL static void replace_extension_v4(path& p, path const& new_extension); + + BOOST_FILESYSTEM_DECL static path lexically_normal_v3(path const& p); + BOOST_FILESYSTEM_DECL static path lexically_normal_v4(path const& p); + + BOOST_FILESYSTEM_DECL static int compare_v3(path const& left, path const& right); + BOOST_FILESYSTEM_DECL static int compare_v4(path const& left, path const& right); + + BOOST_FILESYSTEM_DECL static void append_v3(path& p, const value_type* b, const value_type* e); + BOOST_FILESYSTEM_DECL static void append_v4(path& p, const value_type* b, const value_type* e); + static void append_v4(path& left, path const& right); + + // Returns: If separator is to be appended, m_pathname.size() before append. Otherwise 0. + // Note: An append is never performed if size()==0, so a returned 0 is unambiguous. + BOOST_FILESYSTEM_DECL static string_type::size_type append_separator_if_needed(path& p); + BOOST_FILESYSTEM_DECL static void erase_redundant_separator(path& p, string_type::size_type sep_pos); + + BOOST_FILESYSTEM_DECL static string_type::size_type find_root_name_size(path const& p); + BOOST_FILESYSTEM_DECL static string_type::size_type find_root_path_size(path const& p); + BOOST_FILESYSTEM_DECL static substring find_root_directory(path const& p); + BOOST_FILESYSTEM_DECL static substring find_relative_path(path const& p); + BOOST_FILESYSTEM_DECL static string_type::size_type find_parent_path_size(path const& p); + BOOST_FILESYSTEM_DECL static string_type::size_type find_filename_v4_size(path const& p); + BOOST_FILESYSTEM_DECL static string_type::size_type find_extension_v4_size(path const& p); + + BOOST_FILESYSTEM_DECL static int lex_compare_v3 + ( + path_detail::path_iterator first1, path_detail::path_iterator const& last1, + path_detail::path_iterator first2, path_detail::path_iterator const& last2 + ); + BOOST_FILESYSTEM_DECL static int lex_compare_v4 + ( + path_detail::path_iterator first1, path_detail::path_iterator const& last1, + path_detail::path_iterator first2, path_detail::path_iterator const& last2 + ); + + BOOST_FILESYSTEM_DECL static void increment_v3(path_detail::path_iterator& it); + BOOST_FILESYSTEM_DECL static void increment_v4(path_detail::path_iterator& it); + BOOST_FILESYSTEM_DECL static void decrement_v3(path_detail::path_iterator& it); + BOOST_FILESYSTEM_DECL static void decrement_v4(path_detail::path_iterator& it); +}; + +} // namespace detail + //------------------------------------------------------------------------------------// // // // class path // @@ -91,12 +161,16 @@ class path : #endif > { + friend class path_detail::path_iterator; + friend class path_detail::path_reverse_iterator; + friend struct detail::path_algorithms; + public: // value_type is the character type used by the operating system API to // represent paths. - typedef path_constants_base::value_type value_type; - typedef std::basic_string< value_type > string_type; + typedef detail::path_algorithms::value_type value_type; + typedef detail::path_algorithms::string_type string_type; typedef detail::path_traits::codecvt_type codecvt_type; // ----- character encoding conversions ----- @@ -245,10 +319,9 @@ private: }; public: - class iterator; - friend class iterator; + typedef path_detail::path_iterator iterator; typedef iterator const_iterator; - class reverse_iterator; + typedef path_detail::path_reverse_iterator reverse_iterator; typedef reverse_iterator const_reverse_iterator; public: @@ -725,11 +798,7 @@ public: return append(source); } - BOOST_FORCEINLINE path& append(path const& p) - { - BOOST_FILESYSTEM_VERSIONED_SYM(append)(p.m_pathname.data(), p.m_pathname.data() + p.m_pathname.size()); - return *this; - } + path& append(path const& p); template< typename Source > BOOST_FORCEINLINE typename boost::enable_if_c< @@ -754,11 +823,7 @@ public: return *this; } - BOOST_FORCEINLINE path& append(path const& p, codecvt_type const&) - { - BOOST_FILESYSTEM_VERSIONED_SYM(append)(p.m_pathname.data(), p.m_pathname.data() + p.m_pathname.size()); - return *this; - } + path& append(path const& p, codecvt_type const&); template< typename Source > BOOST_FORCEINLINE typename boost::enable_if_c< @@ -783,11 +848,7 @@ public: return *this; } - BOOST_FORCEINLINE path& append(const value_type* begin, const value_type* end) - { - BOOST_FILESYSTEM_VERSIONED_SYM(append)(begin, end); - return *this; - } + path& append(const value_type* begin, const value_type* end); template< typename InputIterator > BOOST_FORCEINLINE typename boost::enable_if_c< @@ -803,11 +864,7 @@ public: return *this; } - BOOST_FORCEINLINE path& append(const value_type* begin, const value_type* end, codecvt_type const&) - { - BOOST_FILESYSTEM_VERSIONED_SYM(append)(begin, end); - return *this; - } + path& append(const value_type* begin, const value_type* end, codecvt_type const&); template< typename InputIterator > BOOST_FORCEINLINE typename boost::enable_if_c< @@ -835,19 +892,12 @@ public: #else // BOOST_WINDOWS_API BOOST_FILESYSTEM_DECL path& make_preferred(); // change slashes to backslashes #endif - BOOST_FORCEINLINE path& remove_filename() - { - BOOST_FILESYSTEM_VERSIONED_SYM(remove_filename)(); - return *this; - } + path& remove_filename(); BOOST_FILESYSTEM_DECL path& remove_filename_and_trailing_separators(); BOOST_FILESYSTEM_DECL path& remove_trailing_separator(); BOOST_FILESYSTEM_DECL path& replace_filename(path const& replacement); - BOOST_FORCEINLINE path& replace_extension(path const& new_extension = path()) - { - BOOST_FILESYSTEM_VERSIONED_SYM(replace_extension)(new_extension); - return *this; - } + path& replace_extension(path const& new_extension = path()); + void swap(path& rhs) BOOST_NOEXCEPT { m_pathname.swap(rhs.m_pathname); } // ----- observers ----- @@ -953,10 +1003,7 @@ public: // ----- compare ----- - BOOST_FORCEINLINE int compare(path const& p) const // generic, lexicographical - { - return BOOST_FILESYSTEM_VERSIONED_SYM(compare)(p); - } + int compare(path const& p) const; // generic, lexicographical template< typename Source > BOOST_FORCEINLINE typename boost::enable_if_c< @@ -1002,42 +1049,42 @@ public: // ----- decomposition ----- - path root_path() const { return path(m_pathname.c_str(), m_pathname.c_str() + find_root_path_size()); } + path root_path() const { return path(m_pathname.c_str(), m_pathname.c_str() + detail::path_algorithms::find_root_path_size(*this)); } // returns 0 or 1 element path even on POSIX, root_name() is non-empty() for network paths - path root_name() const { return path(m_pathname.c_str(), m_pathname.c_str() + find_root_name_size()); } + path root_name() const { return path(m_pathname.c_str(), m_pathname.c_str() + detail::path_algorithms::find_root_name_size(*this)); } // returns 0 or 1 element path path root_directory() const { - path_detail::substring root_dir = find_root_directory(); + detail::path_algorithms::substring root_dir = detail::path_algorithms::find_root_directory(*this); const value_type* p = m_pathname.c_str() + root_dir.pos; return path(p, p + root_dir.size); } path relative_path() const { - path_detail::substring rel_path = find_relative_path(); + detail::path_algorithms::substring rel_path = detail::path_algorithms::find_relative_path(*this); const value_type* p = m_pathname.c_str() + rel_path.pos; return path(p, p + rel_path.size); } - path parent_path() const { return path(m_pathname.c_str(), m_pathname.c_str() + find_parent_path_size()); } + path parent_path() const { return path(m_pathname.c_str(), m_pathname.c_str() + detail::path_algorithms::find_parent_path_size(*this)); } - BOOST_FORCEINLINE path filename() const { return BOOST_FILESYSTEM_VERSIONED_SYM(filename)(); } // returns 0 or 1 element path - BOOST_FORCEINLINE path stem() const { return BOOST_FILESYSTEM_VERSIONED_SYM(stem)(); } // returns 0 or 1 element path - BOOST_FORCEINLINE path extension() const { return BOOST_FILESYSTEM_VERSIONED_SYM(extension)(); } // returns 0 or 1 element path + path filename() const; // returns 0 or 1 element path + path stem() const; // returns 0 or 1 element path + path extension() const; // returns 0 or 1 element path // ----- query ----- bool empty() const BOOST_NOEXCEPT { return m_pathname.empty(); } bool filename_is_dot() const; bool filename_is_dot_dot() const; - bool has_root_path() const { return find_root_path_size() > 0; } - bool has_root_name() const { return find_root_name_size() > 0; } - bool has_root_directory() const { return find_root_directory().size > 0; } - bool has_relative_path() const { return find_relative_path().size > 0; } - bool has_parent_path() const { return find_parent_path_size() > 0; } - BOOST_FORCEINLINE bool has_filename() const { return BOOST_FILESYSTEM_VERSIONED_SYM(has_filename)(); } + bool has_root_path() const { return detail::path_algorithms::find_root_path_size(*this) > 0; } + bool has_root_name() const { return detail::path_algorithms::find_root_name_size(*this) > 0; } + bool has_root_directory() const { return detail::path_algorithms::find_root_directory(*this).size > 0; } + bool has_relative_path() const { return detail::path_algorithms::find_relative_path(*this).size > 0; } + bool has_parent_path() const { return detail::path_algorithms::find_parent_path_size(*this) > 0; } + bool has_filename() const; bool has_stem() const { return !stem().empty(); } bool has_extension() const { return !extension().empty(); } bool is_relative() const { return !is_absolute(); } @@ -1053,7 +1100,7 @@ public: // ----- lexical operations ----- - BOOST_FORCEINLINE path lexically_normal() const { return BOOST_FILESYSTEM_VERSIONED_SYM(lexically_normal)(); } + path lexically_normal() const; BOOST_FILESYSTEM_DECL path lexically_relative(path const& base) const; path lexically_proximate(path const& base) const; @@ -1110,56 +1157,6 @@ public: //--------------------------------------------------------------------------------------// // class path private members // //--------------------------------------------------------------------------------------// -private: - bool has_filename_v3() const { return !m_pathname.empty(); } - bool has_filename_v4() const { return find_filename_v4_size() > 0; } - BOOST_FILESYSTEM_DECL path filename_v3() const; - path filename_v4() const - { - string_type::size_type filename_size = find_filename_v4_size(); - string_type::size_type pos = m_pathname.size() - filename_size; - const value_type* p = m_pathname.c_str() + pos; - return path(p, p + filename_size); - } - BOOST_FILESYSTEM_DECL path stem_v3() const; - BOOST_FILESYSTEM_DECL path stem_v4() const; - BOOST_FILESYSTEM_DECL path extension_v3() const; - path extension_v4() const - { - string_type::size_type extension_size = find_extension_v4_size(); - string_type::size_type pos = m_pathname.size() - extension_size; - const value_type* p = m_pathname.c_str() + pos; - return path(p, p + extension_size); - } - - BOOST_FILESYSTEM_DECL void remove_filename_v3(); - BOOST_FILESYSTEM_DECL void remove_filename_v4(); - - BOOST_FILESYSTEM_DECL void replace_extension_v3(path const& new_extension); - BOOST_FILESYSTEM_DECL void replace_extension_v4(path const& new_extension); - - BOOST_FILESYSTEM_DECL path lexically_normal_v3() const; - BOOST_FILESYSTEM_DECL path lexically_normal_v4() const; - - BOOST_FILESYSTEM_DECL int compare_v3(path const& p) const; - BOOST_FILESYSTEM_DECL int compare_v4(path const& p) const; - - BOOST_FILESYSTEM_DECL void append_v3(const value_type* b, const value_type* e); - BOOST_FILESYSTEM_DECL void append_v4(const value_type* b, const value_type* e); - - // Returns: If separator is to be appended, m_pathname.size() before append. Otherwise 0. - // Note: An append is never performed if size()==0, so a returned 0 is unambiguous. - BOOST_FILESYSTEM_DECL string_type::size_type append_separator_if_needed(); - BOOST_FILESYSTEM_DECL void erase_redundant_separator(string_type::size_type sep_pos); - - BOOST_FILESYSTEM_DECL string_type::size_type find_root_name_size() const; - BOOST_FILESYSTEM_DECL string_type::size_type find_root_path_size() const; - BOOST_FILESYSTEM_DECL path_detail::substring find_root_directory() const; - BOOST_FILESYSTEM_DECL path_detail::substring find_relative_path() const; - BOOST_FILESYSTEM_DECL string_type::size_type find_parent_path_size() const; - BOOST_FILESYSTEM_DECL string_type::size_type find_filename_v4_size() const; - BOOST_FILESYSTEM_DECL string_type::size_type find_extension_v4_size() const; - private: /* * m_pathname has the type, encoding, and format required by the native @@ -1174,8 +1171,6 @@ private: }; namespace detail { -BOOST_FILESYSTEM_DECL int lex_compare_v3(path::iterator first1, path::iterator last1, path::iterator first2, path::iterator last2); -BOOST_FILESYSTEM_DECL int lex_compare_v4(path::iterator first1, path::iterator last1, path::iterator first2, path::iterator last2); BOOST_FILESYSTEM_DECL path const& dot_path(); BOOST_FILESYSTEM_DECL path const& dot_dot_path(); } // namespace detail @@ -1184,13 +1179,15 @@ BOOST_FILESYSTEM_DECL path const& dot_dot_path(); typedef path wpath; #endif +namespace path_detail { + //------------------------------------------------------------------------------------// // class path::iterator // //------------------------------------------------------------------------------------// -class path::iterator : +class path_iterator : public boost::iterator_facade< - path::iterator, + path_iterator, const path, boost::bidirectional_traversal_tag > @@ -1198,24 +1195,18 @@ class path::iterator : private: friend class boost::iterator_core_access; friend class boost::filesystem::path; - friend class boost::filesystem::path::reverse_iterator; - friend BOOST_FILESYSTEM_DECL int detail::lex_compare_v3(path::iterator first1, path::iterator last1, path::iterator first2, path::iterator last2); + friend class path_reverse_iterator; + friend struct boost::filesystem::detail::path_algorithms; path const& dereference() const { return m_element; } - bool equal(iterator const& rhs) const + bool equal(path_iterator const& rhs) const BOOST_NOEXCEPT { return m_path_ptr == rhs.m_path_ptr && m_pos == rhs.m_pos; } - BOOST_FORCEINLINE void increment() { BOOST_FILESYSTEM_VERSIONED_SYM(increment)(); } - BOOST_FORCEINLINE void decrement() { BOOST_FILESYSTEM_VERSIONED_SYM(decrement)(); } - -private: - BOOST_FILESYSTEM_DECL void increment_v3(); - BOOST_FILESYSTEM_DECL void increment_v4(); - BOOST_FILESYSTEM_DECL void decrement_v3(); - BOOST_FILESYSTEM_DECL void decrement_v4(); + void increment(); + void decrement(); private: // current element @@ -1227,22 +1218,22 @@ private: // position of the last separator in the path. // end() iterator is indicated by // m_pos == m_path_ptr->m_pathname.size() - string_type::size_type m_pos; + path::string_type::size_type m_pos; }; //------------------------------------------------------------------------------------// // class path::reverse_iterator // //------------------------------------------------------------------------------------// -class path::reverse_iterator : +class path_reverse_iterator : public boost::iterator_facade< - path::reverse_iterator, + path_reverse_iterator, const path, boost::bidirectional_traversal_tag > { public: - explicit reverse_iterator(iterator itr) : + explicit path_reverse_iterator(path_iterator itr) : m_itr(itr) { if (itr != itr.m_path_ptr->begin()) @@ -1254,14 +1245,14 @@ private: friend class boost::filesystem::path; path const& dereference() const { return m_element; } - bool equal(reverse_iterator const& rhs) const { return m_itr == rhs.m_itr; } + bool equal(path_reverse_iterator const& rhs) const BOOST_NOEXCEPT { return m_itr == rhs.m_itr; } void increment() { --m_itr; if (m_itr != m_itr.m_path_ptr->begin()) { - iterator tmp = m_itr; + path_iterator tmp = m_itr; m_element = *--tmp; } } @@ -1273,38 +1264,24 @@ private: } private: - iterator m_itr; + path_iterator m_itr; path m_element; }; +// std::lexicographical_compare would infinitely recurse because path iterators +// yield paths, so provide a path aware version +bool lexicographical_compare(path_iterator first1, path_iterator const& last1, path_iterator first2, path_iterator const& last2); + +} // namespace path_detail + +using path_detail::lexicographical_compare; + //------------------------------------------------------------------------------------// // // // non-member functions // // // //------------------------------------------------------------------------------------// -// std::lexicographical_compare would infinitely recurse because path iterators -// yield paths, so provide a path aware version -BOOST_FORCEINLINE bool lexicographical_compare(path::iterator first1, path::iterator last1, path::iterator first2, path::iterator last2) -{ - return BOOST_FILESYSTEM_VERSIONED_SYM(detail::lex_compare)(first1, last1, first2, last2) < 0; -} - -BOOST_FORCEINLINE path::compare_op::result_type path::compare_op::operator() (const value_type* source, const value_type* source_end, const codecvt_type*) const -{ - path src; - src.m_pathname.assign(source, source_end); - return m_self.compare(src); -} - -template< typename OtherChar > -BOOST_FORCEINLINE path::compare_op::result_type path::compare_op::operator() (const OtherChar* source, const OtherChar* source_end, const codecvt_type* cvt) const -{ - path src; - detail::path_traits::convert(source, source_end, src.m_pathname, cvt); - return m_self.compare(src); -} - BOOST_FORCEINLINE bool operator==(path const& lhs, path const& rhs) { return lhs.compare(rhs) == 0; @@ -1545,6 +1522,41 @@ inline bool is_element_separator(path::value_type c) BOOST_NOEXCEPT // class path miscellaneous function implementations // //------------------------------------------------------------------------------------// +namespace detail { + +inline bool path_algorithms::has_filename_v3(path const& p) +{ + return !p.m_pathname.empty(); +} + +inline bool path_algorithms::has_filename_v4(path const& p) +{ + return path_algorithms::find_filename_v4_size(p) > 0; +} + +inline path path_algorithms::filename_v4(path const& p) +{ + string_type::size_type filename_size = path_algorithms::find_filename_v4_size(p); + string_type::size_type pos = p.m_pathname.size() - filename_size; + const value_type* ptr = p.m_pathname.c_str() + pos; + return path(ptr, ptr + filename_size); +} + +inline path path_algorithms::extension_v4(path const& p) +{ + string_type::size_type extension_size = path_algorithms::find_extension_v4_size(p); + string_type::size_type pos = p.m_pathname.size() - extension_size; + const value_type* ptr = p.m_pathname.c_str() + pos; + return path(ptr, ptr + extension_size); +} + +inline void path_algorithms::append_v4(path& left, path const& right) +{ + path_algorithms::append_v4(left, right.m_pathname.c_str(), right.m_pathname.c_str() + right.m_pathname.size()); +} + +} // namespace detail + // Note: Because of the range constructor in C++23 std::string_view that involves a check for contiguous_range concept, // any non-template function call that requires a check whether the source argument (which may be fs::path) // is convertible to std::string_view must be made after fs::path::iterator is defined. This includes overload @@ -1552,6 +1564,21 @@ inline bool is_element_separator(path::value_type c) BOOST_NOEXCEPT // is not defined and defined, which causes compilation errors with gcc 11 and later. // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106808 +BOOST_FORCEINLINE path::compare_op::result_type path::compare_op::operator() (const value_type* source, const value_type* source_end, const codecvt_type*) const +{ + path src; + src.m_pathname.assign(source, source_end); + return m_self.compare(src); +} + +template< typename OtherChar > +BOOST_FORCEINLINE path::compare_op::result_type path::compare_op::operator() (const OtherChar* source, const OtherChar* source_end, const codecvt_type* cvt) const +{ + path src; + detail::path_traits::convert(source, source_end, src.m_pathname, cvt); + return m_self.compare(src); +} + inline path& path::operator=(path const& p) { return assign(p); @@ -1608,7 +1635,7 @@ inline bool path::filename_is_dot_dot() const #if !defined(BOOST_FILESYSTEM_NO_DEPRECATED) BOOST_FILESYSTEM_DETAIL_DEPRECATED("Use path::lexically_normal() instead") -inline path& path::normalize() +BOOST_FORCEINLINE path& path::normalize() { path tmp(lexically_normal()); m_pathname.swap(tmp.m_pathname); @@ -1617,6 +1644,100 @@ inline path& path::normalize() #endif // !defined(BOOST_FILESYSTEM_NO_DEPRECATED) +// The following functions are defined differently, depending on Boost.Filesystem version in use. +// To avoid ODR violation, these functions are not defined when the library itself is built. +// This makes sure they are not compiled when the library is built, and the only version there is +// is the one in user's code. Users are supposed to consistently use the same Boost.Filesystem version +// in all their translation units. +#if !defined(BOOST_FILESYSTEM_SOURCE) + +BOOST_FORCEINLINE path& path::append(path const& p) +{ + BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::append)(*this, p.m_pathname.data(), p.m_pathname.data() + p.m_pathname.size()); + return *this; +} + +BOOST_FORCEINLINE path& path::append(path const& p, codecvt_type const&) +{ + BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::append)(*this, p.m_pathname.data(), p.m_pathname.data() + p.m_pathname.size()); + return *this; +} + +BOOST_FORCEINLINE path& path::append(const value_type* begin, const value_type* end) +{ + BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::append)(*this, begin, end); + return *this; +} + +BOOST_FORCEINLINE path& path::append(const value_type* begin, const value_type* end, codecvt_type const&) +{ + BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::append)(*this, begin, end); + return *this; +} + +BOOST_FORCEINLINE path& path::remove_filename() +{ + BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::remove_filename)(*this); + return *this; +} + +BOOST_FORCEINLINE path& path::replace_extension(path const& new_extension) +{ + BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::replace_extension)(*this, new_extension); + return *this; +} + +BOOST_FORCEINLINE int path::compare(path const& p) const +{ + return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::compare)(*this, p); +} + +BOOST_FORCEINLINE path path::filename() const +{ + return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::filename)(*this); +} + +BOOST_FORCEINLINE path path::stem() const +{ + return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::stem)(*this); +} + +BOOST_FORCEINLINE path path::extension() const +{ + return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::extension)(*this); +} + +BOOST_FORCEINLINE bool path::has_filename() const +{ + return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::has_filename)(*this); +} + +BOOST_FORCEINLINE path path::lexically_normal() const +{ + return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::lexically_normal)(*this); +} + +namespace path_detail { + +BOOST_FORCEINLINE void path_iterator::increment() +{ + BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::increment)(*this); +} + +BOOST_FORCEINLINE void path_iterator::decrement() +{ + BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::decrement)(*this); +} + +BOOST_FORCEINLINE bool lexicographical_compare(path_iterator first1, path_iterator const& last1, path_iterator first2, path_iterator const& last2) +{ + return BOOST_FILESYSTEM_VERSIONED_SYM(detail::path_algorithms::lex_compare)(first1, last1, first2, last2) < 0; +} + +} // namespace path_detail + +#endif // !defined(BOOST_FILESYSTEM_SOURCE) + //--------------------------------------------------------------------------------------// // class path member template specializations // //--------------------------------------------------------------------------------------// diff --git a/src/directory.cpp b/src/directory.cpp index 7eeda26..7ec7d12 100644 --- a/src/directory.cpp +++ b/src/directory.cpp @@ -1109,7 +1109,18 @@ void directory_iterator_construct(directory_iterator& it, path const& p, unsigne && (filename_str[1] == static_cast< path::string_type::value_type >('\0') || (filename_str[1] == path::dot && filename_str[2] == static_cast< path::string_type::value_type >('\0'))))) { - imp->dir_entry.assign(p / filename, file_stat, symlink_file_stat); + path full_path(p); + path_algorithms::append_v4(full_path, filename); + imp->dir_entry.assign + ( +#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + static_cast< path&& >(full_path), +#else + full_path, +#endif + file_stat, + symlink_file_stat + ); it.m_imp.swap(imp); return; } diff --git a/src/operations.cpp b/src/operations.cpp index 5e5e934..e937ced 100644 --- a/src/operations.cpp +++ b/src/operations.cpp @@ -1116,7 +1116,7 @@ uintmax_t remove_all_impl count += fs::detail::remove_all_impl ( #if defined(BOOST_FILESYSTEM_HAS_FDOPENDIR_NOFOLLOW) && defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) - itr->path().filename(), + path_algorithms::filename_v4(itr->path()), #else itr->path(), #endif @@ -2007,7 +2007,7 @@ uintmax_t remove_all_nt6_by_handle(HANDLE h, path const& p, error_code* ec) ( hh.handle, h, - nested_path.filename(), + path_algorithms::filename_v4(nested_path), 0u, // FileAttributes FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, @@ -2393,12 +2393,12 @@ path absolute(path const& p, path const& base, system::error_code* ec) else { res.concat(abs_base.root_directory()); - res /= abs_base.relative_path(); + path_algorithms::append_v4(res, abs_base.relative_path()); } path p_relative_path(p.relative_path()); if (!p_relative_path.empty()) - res /= p_relative_path; + path_algorithms::append_v4(res, p_relative_path); return res; } @@ -2445,13 +2445,13 @@ path canonical(path const& p, path const& base, system::error_code* ec) path result; while (true) { - for (path::iterator itr(source.begin()), end(source.end()); itr != end; ++itr) + for (path::iterator itr(source.begin()), end(source.end()); itr != end; path_algorithms::increment_v4(itr)) { - if (*itr == dot_p) + if (path_algorithms::compare_v4(*itr, dot_p) == 0) continue; - if (*itr == dot_dot_p) + if (path_algorithms::compare_v4(*itr, dot_dot_p) == 0) { - if (result != root) + if (path_algorithms::compare_v4(result, root) != 0) result.remove_filename_and_trailing_separators(); continue; } @@ -2467,7 +2467,7 @@ path canonical(path const& p, path const& base, system::error_code* ec) continue; } - result /= *itr; + path_algorithms::append_v4(result, *itr); // If we don't have an absolute path yet then don't check symlink status. // This avoids checking "C:" which is "the current directory on drive C" @@ -2496,10 +2496,10 @@ path canonical(path const& p, path const& base, system::error_code* ec) if (link.is_absolute()) { - for (++itr; itr != end; ++itr) + for (path_algorithms::increment_v4(itr); itr != end; path_algorithms::increment_v4(itr)) { - if (*itr != dot_p) - link /= *itr; + if (path_algorithms::compare_v4(*itr, dot_p) != 0) + path_algorithms::append_v4(link, *itr); } source = link; root = source.root_path(); @@ -2507,15 +2507,15 @@ path canonical(path const& p, path const& base, system::error_code* ec) else // link is relative { link.remove_trailing_separator(); - if (link == dot_p) + if (path_algorithms::compare_v4(link, dot_p) == 0) continue; path new_source(result); - new_source /= link; - for (++itr; itr != end; ++itr) + path_algorithms::append_v4(new_source, link); + for (path_algorithms::increment_v4(itr); itr != end; path_algorithms::increment_v4(itr)) { - if (*itr != dot_p) - new_source /= *itr; + if (path_algorithms::compare_v4(*itr, dot_p) != 0) + path_algorithms::append_v4(new_source, *itr); } source = new_source; } @@ -2611,10 +2611,10 @@ void copy(path const& from, path const& to, unsigned int options, system::error_ relative_from = detail::relative(abs_from, abs_to, ec); if (ec && *ec) return; - if (relative_from != dot_path()) - relative_from /= from.filename(); + if (path_algorithms::compare_v4(relative_from, dot_path()) != 0) + path_algorithms::append_v4(relative_from, path_algorithms::filename_v4(from)); else - relative_from = from.filename(); + relative_from = path_algorithms::filename_v4(from); pfrom = &relative_from; } detail::create_symlink(*pfrom, to, ec); @@ -2650,7 +2650,11 @@ void copy(path const& from, path const& to, unsigned int options, system::error_ } if (is_directory(to_stat)) - detail::copy_file(from, to / from.filename(), options, ec); + { + path target(to); + path_algorithms::append_v4(target, path_algorithms::filename_v4(from)); + detail::copy_file(from, target, options, ec); + } else detail::copy_file(from, to, options, ec); } @@ -2705,8 +2709,12 @@ void copy(path const& from, path const& to, unsigned int options, system::error_ while (itr != end_dit) { path const& p = itr->path(); - // Set _detail_recursing flag so that we don't recurse more than for one level deeper into the directory if options are copy_options::none - detail::copy(p, to / p.filename(), options | static_cast< unsigned int >(copy_options::_detail_recursing), ec); + { + path target(to); + path_algorithms::append_v4(target, path_algorithms::filename_v4(p)); + // Set _detail_recursing flag so that we don't recurse more than for one level deeper into the directory if options are copy_options::none + detail::copy(p, target, options | static_cast< unsigned int >(copy_options::_detail_recursing), ec); + } if (ec && *ec) return; @@ -3096,9 +3104,9 @@ bool create_directories(path const& p, system::error_code* ec) error_code local_ec; // Find the initial part of the path that exists - for (path fname = parent.filename(); parent.has_relative_path(); fname = parent.filename()) + for (path fname = path_algorithms::filename_v4(parent); parent.has_relative_path(); fname = path_algorithms::filename_v4(parent)) { - if (!fname.empty() && fname != dot_p && fname != dot_dot_p) + if (!fname.empty() && path_algorithms::compare_v4(fname, dot_p) != 0 && path_algorithms::compare_v4(fname, dot_dot_p) != 0) { file_status existing_status = detail::status_impl(parent, &local_ec); @@ -3115,17 +3123,17 @@ bool create_directories(path const& p, system::error_code* ec) } } - --it; + path_algorithms::decrement_v4(it); parent.remove_filename_and_trailing_separators(); } // Create missing directories bool created = false; - for (; it != e; ++it) + for (; it != e; path_algorithms::increment_v4(it)) { path const& fname = *it; - parent /= fname; - if (!fname.empty() && fname != dot_p && fname != dot_dot_p) + path_algorithms::append_v4(parent, fname); + if (!fname.empty() && path_algorithms::compare_v4(fname, dot_p) != 0 && path_algorithms::compare_v4(fname, dot_dot_p) != 0) { created = detail::create_directory(parent, NULL, &local_ec); if (BOOST_UNLIKELY(!!local_ec)) @@ -4318,11 +4326,8 @@ path temp_directory_path(system::error_code* ec) #else // Windows #if !defined(UNDER_CE) - const wchar_t* tmp_env = L"TMP"; - const wchar_t* temp_env = L"TEMP"; - const wchar_t* localappdata_env = L"LOCALAPPDATA"; - const wchar_t* userprofile_env = L"USERPROFILE"; - const wchar_t* env_list[] = { tmp_env, temp_env, localappdata_env, userprofile_env }; + static const wchar_t* env_list[] = { L"TMP", L"TEMP", L"LOCALAPPDATA", L"USERPROFILE" }; + static const wchar_t temp_dir[] = L"Temp"; path p; for (unsigned int i = 0; i < sizeof(env_list) / sizeof(*env_list); ++i) @@ -4332,7 +4337,7 @@ path temp_directory_path(system::error_code* ec) { p = env; if (i >= 2) - p /= L"Temp"; + path_algorithms::append_v4(p, temp_dir, temp_dir + (sizeof(temp_dir) / sizeof(*temp_dir) - 1u)); error_code lcl_ec; if (exists(p, lcl_ec) && !lcl_ec && is_directory(p, lcl_ec) && !lcl_ec) break; @@ -4357,7 +4362,7 @@ path temp_directory_path(system::error_code* ec) goto getwindir_error; p = buf.get(); // do not depend on initial buf size, see ticket #10388 - p /= L"Temp"; + path_algorithms::append_v4(p, temp_dir, temp_dir + (sizeof(temp_dir) / sizeof(*temp_dir) - 1u)); } return p; @@ -4403,7 +4408,12 @@ path system_complete(path const& p, system::error_code* ec) { #ifdef BOOST_POSIX_API - return (p.empty() || p.is_absolute()) ? p : current_path() / p; + if (p.empty() || p.is_absolute()) + return p; + + path res(current_path()); + path_algorithms::append_v4(res, p); + return res; #else if (p.empty()) @@ -4440,7 +4450,7 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) path::iterator itr(p_end); path head(p); - for (; !head.empty(); --itr) + for (; !head.empty(); path_algorithms::decrement_v4(itr)) { file_status head_status(detail::status_impl(head, &local_ec)); if (BOOST_UNLIKELY(head_status.type() == fs::status_error)) @@ -4459,7 +4469,7 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) } if (head.empty()) - return p.lexically_normal(); + return path_algorithms::lexically_normal_v4(p); path const& dot_p = dot_path(); path const& dot_dot_p = dot_dot_path(); @@ -4482,7 +4492,7 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) { BOOST_ASSERT(itr != p_end); head = *itr; - ++itr; + path_algorithms::increment_v4(itr); } if (p.has_root_directory()) @@ -4491,7 +4501,7 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) // Convert generic separator returned by the iterator for the root directory to // the preferred separator. head += path::preferred_separator; - ++itr; + path_algorithms::increment_v4(itr); } if (!head.empty()) @@ -4509,22 +4519,22 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) if (head_status.type() == fs::file_not_found) { // If the root path does not exist then no path element exists - return p.lexically_normal(); + return path_algorithms::lexically_normal_v4(p); } } path const& dot_p = dot_path(); path const& dot_dot_p = dot_dot_path(); - for (; itr != p_end; ++itr) + for (; itr != p_end; path_algorithms::increment_v4(itr)) { path const& p_elem = *itr; // Avoid querying status of paths containing dot and dot-dot elements, as this will break // if the root name starts with "\\?\". - if (p_elem == dot_p) + if (path_algorithms::compare_v4(p_elem, dot_p) == 0) continue; - if (p_elem == dot_dot_p) + if (path_algorithms::compare_v4(p_elem, dot_dot_p) == 0) { if (head.has_relative_path()) head.remove_filename_and_trailing_separators(); @@ -4532,7 +4542,7 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) continue; } - head /= p_elem; + path_algorithms::append_v4(head, p_elem); file_status head_status(detail::status_impl(head, &local_ec)); if (BOOST_UNLIKELY(head_status.type() == fs::status_error)) @@ -4552,18 +4562,18 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) } if (head.empty()) - return p.lexically_normal(); + return path_algorithms::lexically_normal_v4(p); #endif path tail; bool tail_has_dots = false; - for (; itr != p_end; ++itr) + for (; itr != p_end; path_algorithms::increment_v4(itr)) { path const& tail_elem = *itr; - tail /= tail_elem; + path_algorithms::append_v4(tail, tail_elem); // for a later optimization, track if any dot or dot-dot elements are present - if (!tail_has_dots && (tail_elem == dot_p || tail_elem == dot_dot_p)) + if (!tail_has_dots && (path_algorithms::compare_v4(tail_elem, dot_p) == 0 || path_algorithms::compare_v4(tail_elem, dot_dot_p) == 0)) tail_has_dots = true; } @@ -4579,11 +4589,11 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) if (BOOST_LIKELY(!tail.empty())) { - head /= tail; + path_algorithms::append_v4(head, tail); // optimization: only normalize if tail had dot or dot-dot element if (tail_has_dots) - return head.lexically_normal(); + return path_algorithms::lexically_normal_v4(head); } return head; diff --git a/src/path.cpp b/src/path.cpp index 5d7b2ba..1d9748c 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -60,7 +60,6 @@ namespace { typedef path::value_type value_type; typedef path::string_type string_type; typedef string_type::size_type size_type; -using boost::filesystem::path_detail::substring; #ifdef BOOST_WINDOWS_API @@ -151,458 +150,26 @@ inline void first_element(string_type const& src, size_type& element_pos, size_t namespace boost { namespace filesystem { - -BOOST_FILESYSTEM_DECL void path::append_v3(const value_type* begin, const value_type* end) -{ - if (begin != end) - { - if (BOOST_LIKELY(begin < m_pathname.data() || begin >= (m_pathname.data() + m_pathname.size()))) - { - if (!detail::is_directory_separator(*begin)) - append_separator_if_needed(); - m_pathname.append(begin, end); - } - else - { - // overlapping source - string_type rhs(begin, end); - append_v3(rhs.data(), rhs.data() + rhs.size()); - } - } -} - -BOOST_FILESYSTEM_DECL void path::append_v4(const value_type* begin, const value_type* end) -{ - if (begin != end) - { - if (BOOST_LIKELY(begin < m_pathname.data() || begin >= (m_pathname.data() + m_pathname.size()))) - { - const size_type that_size = end - begin; - size_type that_root_name_size = 0; - size_type that_root_dir_pos = find_root_directory_start(begin, that_size, that_root_name_size); - - // if (p.is_absolute()) - if - ( -#if defined(BOOST_WINDOWS_API) && !defined(UNDER_CE) - that_root_name_size > 0 && -#endif - that_root_dir_pos < that_size - ) - { - return_assign: - assign(begin, end); - return; - } - - size_type this_root_name_size = 0; - find_root_directory_start(m_pathname.c_str(), m_pathname.size(), this_root_name_size); - - if - ( - that_root_name_size > 0 && - (that_root_name_size != this_root_name_size || std::memcmp(m_pathname.c_str(), begin, this_root_name_size * sizeof(value_type)) != 0) - ) - { - goto return_assign; - } - - if (that_root_dir_pos < that_size) - { - // Remove root directory (if any) and relative path to replace with those from p - m_pathname.erase(m_pathname.begin() + this_root_name_size, m_pathname.end()); - } - - const value_type* const that_path = begin + that_root_name_size; - if (!detail::is_directory_separator(*that_path)) - append_separator_if_needed(); - m_pathname.append(that_path, end); - } - else - { - // overlapping source - string_type rhs(begin, end); - append_v4(rhs.data(), rhs.data() + rhs.size()); - } - } - else if (has_filename_v4()) - { - m_pathname.push_back(preferred_separator); - } -} - -#ifdef BOOST_WINDOWS_API - -BOOST_FILESYSTEM_DECL path path::generic_path() const -{ - path tmp(*this); - std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/'); - return tmp; -} - -#endif // BOOST_WINDOWS_API - -BOOST_FILESYSTEM_DECL int path::compare_v3(path const& p) const -{ - return detail::lex_compare_v3(begin(), end(), p.begin(), p.end()); -} - -BOOST_FILESYSTEM_DECL int path::compare_v4(path const& p) const -{ - return detail::lex_compare_v4(begin(), end(), p.begin(), p.end()); -} - -// append_separator_if_needed ----------------------------------------------------// - -BOOST_FILESYSTEM_DECL path::string_type::size_type path::append_separator_if_needed() -{ - if (!m_pathname.empty() && -#ifdef BOOST_WINDOWS_API - *(m_pathname.end() - 1) != colon && -#endif - !detail::is_directory_separator(*(m_pathname.end() - 1))) - { - string_type::size_type tmp(m_pathname.size()); - m_pathname += preferred_separator; - return tmp; - } - return 0; -} - -// erase_redundant_separator -----------------------------------------------------// - -BOOST_FILESYSTEM_DECL void path::erase_redundant_separator(string_type::size_type sep_pos) -{ - if (sep_pos // a separator was added - && sep_pos < m_pathname.size() // and something was appended - && (m_pathname[sep_pos + 1] == separator // and it was also separator -#ifdef BOOST_WINDOWS_API - || m_pathname[sep_pos + 1] == preferred_separator // or preferred_separator -#endif - )) - { - m_pathname.erase(m_pathname.begin() + sep_pos); // erase the added separator - } -} - -// modifiers -----------------------------------------------------------------------// - -#ifdef BOOST_WINDOWS_API -BOOST_FILESYSTEM_DECL path& path::make_preferred() -{ - std::replace(m_pathname.begin(), m_pathname.end(), L'/', L'\\'); - return *this; -} -#endif - -BOOST_FILESYSTEM_DECL path& path::remove_filename_and_trailing_separators() -{ - size_type end_pos = find_parent_path_size(); - m_pathname.erase(m_pathname.begin() + end_pos, m_pathname.end()); - return *this; -} - -BOOST_FILESYSTEM_DECL void path::remove_filename_v3() -{ - remove_filename_and_trailing_separators(); -} - -BOOST_FILESYSTEM_DECL void path::remove_filename_v4() -{ - size_type filename_size = find_filename_v4_size(); - m_pathname.erase(m_pathname.begin() + (m_pathname.size() - filename_size), m_pathname.end()); -} - -BOOST_FILESYSTEM_DECL path& path::remove_trailing_separator() -{ - if (!m_pathname.empty() && detail::is_directory_separator(m_pathname[m_pathname.size() - 1])) - m_pathname.erase(m_pathname.end() - 1); - return *this; -} - -BOOST_FILESYSTEM_DECL path& path::replace_filename(path const& replacement) -{ - remove_filename_v4(); - append_v4(replacement.m_pathname.c_str(), replacement.m_pathname.c_str() + replacement.m_pathname.size()); - return *this; -} - -BOOST_FILESYSTEM_DECL void path::replace_extension_v3(path const& new_extension) -{ - // erase existing extension, including the dot, if any - size_type ext_pos = m_pathname.size() - extension_v3().m_pathname.size(); - m_pathname.erase(m_pathname.begin() + ext_pos, m_pathname.end()); - - if (!new_extension.empty()) - { - // append new_extension, adding the dot if necessary - if (new_extension.m_pathname[0] != dot) - m_pathname.push_back(dot); - m_pathname.append(new_extension.m_pathname); - } -} - -BOOST_FILESYSTEM_DECL void path::replace_extension_v4(path const& new_extension) -{ - // erase existing extension, including the dot, if any - size_type ext_pos = m_pathname.size() - find_extension_v4_size(); - m_pathname.erase(m_pathname.begin() + ext_pos, m_pathname.end()); - - if (!new_extension.empty()) - { - // append new_extension, adding the dot if necessary - if (new_extension.m_pathname[0] != dot) - m_pathname.push_back(dot); - m_pathname.append(new_extension.m_pathname); - } -} - -// decomposition -------------------------------------------------------------------// - -BOOST_FILESYSTEM_DECL size_type path::find_root_name_size() const -{ - size_type root_name_size = 0; - find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size); - return root_name_size; -} - -BOOST_FILESYSTEM_DECL size_type path::find_root_path_size() const -{ - size_type root_name_size = 0; - size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size); - - size_type size = root_name_size; - if (root_dir_pos < m_pathname.size()) - size = root_dir_pos + 1; - - return size; -} - -BOOST_FILESYSTEM_DECL substring path::find_root_directory() const -{ - substring root_dir; - size_type root_name_size = 0; - root_dir.pos = find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size); - root_dir.size = static_cast< std::size_t >(root_dir.pos < m_pathname.size()); - return root_dir; -} - -BOOST_FILESYSTEM_DECL substring path::find_relative_path() const -{ - size_type root_name_size = 0; - size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size); - - // Skip root name, root directory and any duplicate separators - size_type size = root_name_size; - if (root_dir_pos < m_pathname.size()) - { - size = root_dir_pos + 1; - - for (size_type n = m_pathname.size(); size < n; ++size) - { - if (!detail::is_directory_separator(m_pathname[size])) - break; - } - } - - substring rel_path; - rel_path.pos = size; - rel_path.size = m_pathname.size() - size; - - return rel_path; -} - -BOOST_FILESYSTEM_DECL string_type::size_type path::find_parent_path_size() const -{ - const size_type size = m_pathname.size(); - size_type root_name_size = 0; - size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), size, root_name_size); - - size_type filename_size = find_filename_size(m_pathname, root_name_size, size); - size_type end_pos = size - filename_size; - while (true) - { - if (end_pos <= root_name_size) - { - // Keep the root name as the parent path if there was a filename - if (filename_size == 0) - end_pos = 0u; - break; - } - - --end_pos; - - if (!detail::is_directory_separator(m_pathname[end_pos])) - { - ++end_pos; - break; - } - - if (end_pos == root_dir_pos) - { - // Keep the trailing root directory if there was a filename - end_pos += filename_size > 0; - break; - } - } - - return end_pos; -} - -BOOST_FILESYSTEM_DECL path path::filename_v3() const -{ - const size_type size = m_pathname.size(); - size_type root_name_size = 0; - size_type root_dir_pos = find_root_directory_start(m_pathname.c_str(), size, root_name_size); - size_type filename_size, pos; - if (root_dir_pos < size && detail::is_directory_separator(m_pathname[size - 1]) && is_root_separator(m_pathname, root_dir_pos, size - 1)) - { - // Return root directory - pos = root_dir_pos; - filename_size = 1u; - } - else if (root_name_size == size) - { - // Return root name - pos = 0u; - filename_size = root_name_size; - } - else - { - filename_size = find_filename_size(m_pathname, root_name_size, size); - pos = size - filename_size; - if (filename_size == 0u && pos > root_name_size && detail::is_directory_separator(m_pathname[pos - 1]) && !is_root_separator(m_pathname, root_dir_pos, pos - 1)) - return detail::dot_path(); - } - - const value_type* p = m_pathname.c_str() + pos; - return path(p, p + filename_size); -} - -BOOST_FILESYSTEM_DECL string_type::size_type path::find_filename_v4_size() const -{ - const size_type size = m_pathname.size(); - size_type root_name_size = 0; - find_root_directory_start(m_pathname.c_str(), size, root_name_size); - return find_filename_size(m_pathname, root_name_size, size); -} - -BOOST_FILESYSTEM_DECL path path::stem_v3() const -{ - path name(filename_v3()); - if (name != detail::dot_path() && name != detail::dot_dot_path()) - { - size_type pos = name.m_pathname.rfind(dot); - if (pos != string_type::npos) - name.m_pathname.erase(name.m_pathname.begin() + pos, name.m_pathname.end()); - } - return name; -} - -BOOST_FILESYSTEM_DECL path path::stem_v4() const -{ - path name(filename_v4()); - if (name != detail::dot_path() && name != detail::dot_dot_path()) - { - size_type pos = name.m_pathname.rfind(dot); - if (pos != 0 && pos != string_type::npos) - name.m_pathname.erase(name.m_pathname.begin() + pos, name.m_pathname.end()); - } - return name; -} - -BOOST_FILESYSTEM_DECL path path::extension_v3() const -{ - path name(filename_v3()); - if (name == detail::dot_path() || name == detail::dot_dot_path()) - return path(); - size_type pos(name.m_pathname.rfind(dot)); - return pos == string_type::npos ? path() : path(name.m_pathname.c_str() + pos); -} - -BOOST_FILESYSTEM_DECL string_type::size_type path::find_extension_v4_size() const -{ - const size_type size = m_pathname.size(); - size_type root_name_size = 0; - find_root_directory_start(m_pathname.c_str(), size, root_name_size); - size_type filename_size = find_filename_size(m_pathname, root_name_size, size); - size_type filename_pos = size - filename_size; - if - ( - filename_size > 0u && - // Check for "." and ".." filenames - !(m_pathname[filename_pos] == dot && - (filename_size == 1u || (filename_size == 2u && m_pathname[filename_pos + 1u] == dot))) - ) - { - size_type ext_pos = size; - while (ext_pos > filename_pos) - { - --ext_pos; - if (m_pathname[ext_pos] == dot) - break; - } - - if (ext_pos > filename_pos) - return size - ext_pos; - } - - return 0u; -} - -// lexical operations --------------------------------------------------------------// - namespace detail { + // C++14 provides a mismatch algorithm with four iterator arguments(), but earlier // standard libraries didn't, so provide this needed functionality. inline std::pair< path::iterator, path::iterator > mismatch(path::iterator it1, path::iterator it1end, path::iterator it2, path::iterator it2end) { - for (; it1 != it1end && it2 != it2end && *it1 == *it2;) + for (; it1 != it1end && it2 != it2end && path_algorithms::compare_v4(*it1, *it2) == 0;) { - ++it1; - ++it2; + path_algorithms::increment_v4(it1); + path_algorithms::increment_v4(it2); } return std::make_pair(it1, it2); } -} // namespace detail - -BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const -{ - path::iterator b = begin(), e = end(), base_b = base.begin(), base_e = base.end(); - std::pair< path::iterator, path::iterator > mm = detail::mismatch(b, e, base_b, base_e); - if (mm.first == b && mm.second == base_b) - return path(); - if (mm.first == e && mm.second == base_e) - return detail::dot_path(); - - std::ptrdiff_t n = 0; - for (; mm.second != base_e; ++mm.second) - { - path const& p = *mm.second; - if (p == detail::dot_dot_path()) - --n; - else if (!p.empty() && p != detail::dot_path()) - ++n; - } - if (n < 0) - return path(); - if (n == 0 && (mm.first == e || mm.first->empty())) - return detail::dot_path(); - - path tmp; - for (; n > 0; --n) - tmp /= detail::dot_dot_path(); - for (; mm.first != e; ++mm.first) - tmp /= *mm.first; - return tmp; -} // normal --------------------------------------------------------------------------// -BOOST_FILESYSTEM_DECL path path::lexically_normal_v3() const +BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v3(path const& p) { - const value_type* const pathname = m_pathname.c_str(); - const size_type pathname_size = m_pathname.size(); + const value_type* const pathname = p.m_pathname.c_str(); + const size_type pathname_size = p.m_pathname.size(); size_type root_name_size = 0; size_type root_dir_pos = find_root_directory_start(pathname, pathname_size, root_name_size); path normal(pathname, pathname + root_name_size); @@ -619,7 +186,7 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v3() const if (root_dir_pos < pathname_size) { root_path_size = root_dir_pos + 1; - normal.m_pathname.push_back(preferred_separator); + normal.m_pathname.push_back(path::preferred_separator); } size_type i = root_path_size; @@ -642,7 +209,7 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v3() const const size_type size = i - start_pos; // Skip dot elements - if (size == 1u && pathname[start_pos] == dot) + if (size == 1u && pathname[start_pos] == path::dot) { last_element_was_dot = true; goto skip_append; @@ -651,13 +218,13 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v3() const last_element_was_dot = false; // Process dot dot elements - if (size == 2u && pathname[start_pos] == dot && pathname[start_pos + 1] == dot && normal.m_pathname.size() > root_path_size) + if (size == 2u && pathname[start_pos] == path::dot && pathname[start_pos + 1] == path::dot && normal.m_pathname.size() > root_path_size) { // Don't remove previous dot dot elements const size_type normal_size = normal.m_pathname.size(); size_type filename_size = find_filename_size(normal.m_pathname, root_path_size, normal_size); size_type pos = normal_size - filename_size; - if (filename_size != 2u || normal.m_pathname[pos] != dot || normal.m_pathname[pos + 1] != dot) + if (filename_size != 2u || normal.m_pathname[pos] != path::dot || normal.m_pathname[pos + 1] != path::dot) { if (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1])) --pos; @@ -667,7 +234,7 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v3() const } // Append the element - normal.append_separator_if_needed(); + path_algorithms::append_separator_if_needed(normal); normal.m_pathname.append(pathname + start_pos, size); } @@ -689,18 +256,18 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v3() const if (normal.empty() || last_element_was_dot) { append_trailing_dot: - normal.append_separator_if_needed(); - normal.m_pathname.push_back(dot); + path_algorithms::append_separator_if_needed(normal); + normal.m_pathname.push_back(path::dot); } } return normal; } -BOOST_FILESYSTEM_DECL path path::lexically_normal_v4() const +BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v4(path const& p) { - const value_type* const pathname = m_pathname.c_str(); - const size_type pathname_size = m_pathname.size(); + const value_type* const pathname = p.m_pathname.c_str(); + const size_type pathname_size = p.m_pathname.size(); size_type root_name_size = 0; size_type root_dir_pos = find_root_directory_start(pathname, pathname_size, root_name_size); path normal(pathname, pathname + root_name_size); @@ -717,7 +284,7 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v4() const if (root_dir_pos < pathname_size) { root_path_size = root_dir_pos + 1; - normal.m_pathname.push_back(preferred_separator); + normal.m_pathname.push_back(path::preferred_separator); } size_type i = root_path_size; @@ -740,20 +307,20 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v4() const const size_type size = i - start_pos; // Skip dot elements - if (size == 1u && pathname[start_pos] == dot) + if (size == 1u && pathname[start_pos] == path::dot) { last_element_was_dot = true; goto skip_append; } // Process dot dot elements - if (size == 2u && pathname[start_pos] == dot && pathname[start_pos + 1] == dot && normal.m_pathname.size() > root_path_size) + if (size == 2u && pathname[start_pos] == path::dot && pathname[start_pos + 1] == path::dot && normal.m_pathname.size() > root_path_size) { // Don't remove previous dot dot elements const size_type normal_size = normal.m_pathname.size(); size_type filename_size = find_filename_size(normal.m_pathname, root_path_size, normal_size); size_type pos = normal_size - filename_size; - if (filename_size != 2u || normal.m_pathname[pos] != dot || normal.m_pathname[pos + 1] != dot) + if (filename_size != 2u || normal.m_pathname[pos] != path::dot || normal.m_pathname[pos + 1] != path::dot) { if (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1])) --pos; @@ -763,7 +330,7 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v4() const } // Append the element - normal.append_separator_if_needed(); + path_algorithms::append_separator_if_needed(normal); normal.m_pathname.append(pathname + start_pos, size); } @@ -772,7 +339,7 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v4() const { // If a path ends with a trailing dot after a directory element, add a trailing separator if (last_element_was_dot && !normal.empty() && !normal.filename_is_dot_dot()) - normal.append_separator_if_needed(); + path_algorithms::append_separator_if_needed(normal); break; } @@ -785,19 +352,496 @@ BOOST_FILESYSTEM_DECL path path::lexically_normal_v4() const { // If a path ends with a separator, add a trailing separator if (!normal.empty() && !normal.filename_is_dot_dot()) - normal.append_separator_if_needed(); + path_algorithms::append_separator_if_needed(normal); break; } } // If the original path was not empty and normalized ended up being empty, make it a dot if (normal.empty()) - normal.m_pathname.push_back(dot); + normal.m_pathname.push_back(path::dot); } return normal; } +// append --------------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL void path_algorithms::append_v3(path& p, const value_type* begin, const value_type* end) +{ + if (begin != end) + { + if (BOOST_LIKELY(begin < p.m_pathname.data() || begin >= (p.m_pathname.data() + p.m_pathname.size()))) + { + if (!detail::is_directory_separator(*begin)) + path_algorithms::append_separator_if_needed(p); + p.m_pathname.append(begin, end); + } + else + { + // overlapping source + string_type rhs(begin, end); + path_algorithms::append_v3(p, rhs.data(), rhs.data() + rhs.size()); + } + } +} + +BOOST_FILESYSTEM_DECL void path_algorithms::append_v4(path& p, const value_type* begin, const value_type* end) +{ + if (begin != end) + { + if (BOOST_LIKELY(begin < p.m_pathname.data() || begin >= (p.m_pathname.data() + p.m_pathname.size()))) + { + const size_type that_size = end - begin; + size_type that_root_name_size = 0; + size_type that_root_dir_pos = find_root_directory_start(begin, that_size, that_root_name_size); + + // if (p.is_absolute()) + if + ( +#if defined(BOOST_WINDOWS_API) && !defined(UNDER_CE) + that_root_name_size > 0 && +#endif + that_root_dir_pos < that_size + ) + { + return_assign: + p.assign(begin, end); + return; + } + + size_type this_root_name_size = 0; + find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), this_root_name_size); + + if + ( + that_root_name_size > 0 && + (that_root_name_size != this_root_name_size || std::memcmp(p.m_pathname.c_str(), begin, this_root_name_size * sizeof(value_type)) != 0) + ) + { + goto return_assign; + } + + if (that_root_dir_pos < that_size) + { + // Remove root directory (if any) and relative path to replace with those from p + p.m_pathname.erase(p.m_pathname.begin() + this_root_name_size, p.m_pathname.end()); + } + + const value_type* const that_path = begin + that_root_name_size; + if (!detail::is_directory_separator(*that_path)) + path_algorithms::append_separator_if_needed(p); + p.m_pathname.append(that_path, end); + } + else + { + // overlapping source + string_type rhs(begin, end); + path_algorithms::append_v4(p, rhs.data(), rhs.data() + rhs.size()); + } + } + else if (path_algorithms::has_filename_v4(p)) + { + p.m_pathname.push_back(path::preferred_separator); + } +} + +// compare -------------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL int path_algorithms::lex_compare_v3 +( + path_detail::path_iterator first1, path_detail::path_iterator const& last1, + path_detail::path_iterator first2, path_detail::path_iterator const& last2 +) +{ + for (; first1 != last1 && first2 != last2;) + { + if (first1->native() < first2->native()) + return -1; + if (first2->native() < first1->native()) + return 1; + BOOST_ASSERT(first2->native() == first1->native()); + path_algorithms::increment_v3(first1); + path_algorithms::increment_v3(first2); + } + if (first1 == last1 && first2 == last2) + return 0; + return first1 == last1 ? -1 : 1; +} + +BOOST_FILESYSTEM_DECL int path_algorithms::lex_compare_v4 +( + path_detail::path_iterator first1, path_detail::path_iterator const& last1, + path_detail::path_iterator first2, path_detail::path_iterator const& last2 +) +{ + for (; first1 != last1 && first2 != last2;) + { + if (first1->native() < first2->native()) + return -1; + if (first2->native() < first1->native()) + return 1; + BOOST_ASSERT(first2->native() == first1->native()); + path_algorithms::increment_v4(first1); + path_algorithms::increment_v4(first2); + } + if (first1 == last1 && first2 == last2) + return 0; + return first1 == last1 ? -1 : 1; +} + +BOOST_FILESYSTEM_DECL int path_algorithms::compare_v3(path const& left, path const& right) +{ + return path_algorithms::lex_compare_v3(left.begin(), left.end(), right.begin(), right.end()); +} + +BOOST_FILESYSTEM_DECL int path_algorithms::compare_v4(path const& left, path const& right) +{ + return path_algorithms::lex_compare_v4(left.begin(), left.end(), right.begin(), right.end()); +} + +// append_separator_if_needed ------------------------------------------------------// + +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::append_separator_if_needed(path& p) +{ + if (!p.m_pathname.empty() && +#ifdef BOOST_WINDOWS_API + *(p.m_pathname.end() - 1) != colon && +#endif + !detail::is_directory_separator(*(p.m_pathname.end() - 1))) + { + string_type::size_type tmp(p.m_pathname.size()); + p.m_pathname.push_back(path::preferred_separator); + return tmp; + } + return 0; +} + +// erase_redundant_separator -------------------------------------------------------// + +BOOST_FILESYSTEM_DECL void path_algorithms::erase_redundant_separator(path& p, string_type::size_type sep_pos) +{ + if (sep_pos // a separator was added + && sep_pos < p.m_pathname.size() // and something was appended + && (p.m_pathname[sep_pos + 1] == path::separator // and it was also separator +#ifdef BOOST_WINDOWS_API + || p.m_pathname[sep_pos + 1] == path::preferred_separator // or preferred_separator +#endif + )) + { + p.m_pathname.erase(p.m_pathname.begin() + sep_pos); // erase the added separator + } +} + +// modifiers -----------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v3(path& p) +{ + p.remove_filename_and_trailing_separators(); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v4(path& p) +{ + size_type filename_size = path_algorithms::find_filename_v4_size(p); + p.m_pathname.erase(p.m_pathname.begin() + (p.m_pathname.size() - filename_size), p.m_pathname.end()); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::replace_extension_v3(path& p, path const& new_extension) +{ + // erase existing extension, including the dot, if any + size_type ext_pos = p.m_pathname.size() - path_algorithms::extension_v3(p).m_pathname.size(); + p.m_pathname.erase(p.m_pathname.begin() + ext_pos, p.m_pathname.end()); + + if (!new_extension.empty()) + { + // append new_extension, adding the dot if necessary + if (new_extension.m_pathname[0] != path::dot) + p.m_pathname.push_back(path::dot); + p.m_pathname.append(new_extension.m_pathname); + } +} + +BOOST_FILESYSTEM_DECL void path_algorithms::replace_extension_v4(path& p, path const& new_extension) +{ + // erase existing extension, including the dot, if any + size_type ext_pos = p.m_pathname.size() - path_algorithms::find_extension_v4_size(p); + p.m_pathname.erase(p.m_pathname.begin() + ext_pos, p.m_pathname.end()); + + if (!new_extension.empty()) + { + // append new_extension, adding the dot if necessary + if (new_extension.m_pathname[0] != path::dot) + p.m_pathname.push_back(path::dot); + p.m_pathname.append(new_extension.m_pathname); + } +} + +// decomposition -------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_name_size(path const& p) +{ + size_type root_name_size = 0; + find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size); + return root_name_size; +} + +BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_path_size(path const& p) +{ + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size); + + size_type size = root_name_size; + if (root_dir_pos < p.m_pathname.size()) + size = root_dir_pos + 1; + + return size; +} + +BOOST_FILESYSTEM_DECL path_algorithms::substring path_algorithms::find_root_directory(path const& p) +{ + substring root_dir; + size_type root_name_size = 0; + root_dir.pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size); + root_dir.size = static_cast< std::size_t >(root_dir.pos < p.m_pathname.size()); + return root_dir; +} + +BOOST_FILESYSTEM_DECL path_algorithms::substring path_algorithms::find_relative_path(path const& p) +{ + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size); + + // Skip root name, root directory and any duplicate separators + size_type size = root_name_size; + if (root_dir_pos < p.m_pathname.size()) + { + size = root_dir_pos + 1; + + for (size_type n = p.m_pathname.size(); size < n; ++size) + { + if (!detail::is_directory_separator(p.m_pathname[size])) + break; + } + } + + substring rel_path; + rel_path.pos = size; + rel_path.size = p.m_pathname.size() - size; + + return rel_path; +} + +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_parent_path_size(path const& p) +{ + const size_type size = p.m_pathname.size(); + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); + + size_type filename_size = find_filename_size(p.m_pathname, root_name_size, size); + size_type end_pos = size - filename_size; + while (true) + { + if (end_pos <= root_name_size) + { + // Keep the root name as the parent path if there was a filename + if (filename_size == 0) + end_pos = 0u; + break; + } + + --end_pos; + + if (!detail::is_directory_separator(p.m_pathname[end_pos])) + { + ++end_pos; + break; + } + + if (end_pos == root_dir_pos) + { + // Keep the trailing root directory if there was a filename + end_pos += filename_size > 0; + break; + } + } + + return end_pos; +} + +BOOST_FILESYSTEM_DECL path path_algorithms::filename_v3(path const& p) +{ + const size_type size = p.m_pathname.size(); + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); + size_type filename_size, pos; + if (root_dir_pos < size && detail::is_directory_separator(p.m_pathname[size - 1]) && is_root_separator(p.m_pathname, root_dir_pos, size - 1)) + { + // Return root directory + pos = root_dir_pos; + filename_size = 1u; + } + else if (root_name_size == size) + { + // Return root name + pos = 0u; + filename_size = root_name_size; + } + else + { + filename_size = find_filename_size(p.m_pathname, root_name_size, size); + pos = size - filename_size; + if (filename_size == 0u && pos > root_name_size && detail::is_directory_separator(p.m_pathname[pos - 1]) && !is_root_separator(p.m_pathname, root_dir_pos, pos - 1)) + return detail::dot_path(); + } + + const value_type* ptr = p.m_pathname.c_str() + pos; + return path(ptr, ptr + filename_size); +} + +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_filename_v4_size(path const& p) +{ + const size_type size = p.m_pathname.size(); + size_type root_name_size = 0; + find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); + return find_filename_size(p.m_pathname, root_name_size, size); +} + +BOOST_FILESYSTEM_DECL path path_algorithms::stem_v3(path const& p) +{ + path name(path_algorithms::filename_v3(p)); + if (path_algorithms::compare_v4(name, detail::dot_path()) != 0 && path_algorithms::compare_v4(name, detail::dot_dot_path()) != 0) + { + size_type pos = name.m_pathname.rfind(path::dot); + if (pos != string_type::npos) + name.m_pathname.erase(name.m_pathname.begin() + pos, name.m_pathname.end()); + } + return name; +} + +BOOST_FILESYSTEM_DECL path path_algorithms::stem_v4(path const& p) +{ + path name(path_algorithms::filename_v4(p)); + if (path_algorithms::compare_v4(name, detail::dot_path()) != 0 && path_algorithms::compare_v4(name, detail::dot_dot_path()) != 0) + { + size_type pos = name.m_pathname.rfind(path::dot); + if (pos != 0 && pos != string_type::npos) + name.m_pathname.erase(name.m_pathname.begin() + pos, name.m_pathname.end()); + } + return name; +} + +BOOST_FILESYSTEM_DECL path path_algorithms::extension_v3(path const& p) +{ + path name(path_algorithms::filename_v3(p)); + if (path_algorithms::compare_v4(name, detail::dot_path()) == 0 || path_algorithms::compare_v4(name, detail::dot_dot_path()) == 0) + return path(); + size_type pos(name.m_pathname.rfind(path::dot)); + return pos == string_type::npos ? path() : path(name.m_pathname.c_str() + pos); +} + +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_extension_v4_size(path const& p) +{ + const size_type size = p.m_pathname.size(); + size_type root_name_size = 0; + find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); + size_type filename_size = find_filename_size(p.m_pathname, root_name_size, size); + size_type filename_pos = size - filename_size; + if + ( + filename_size > 0u && + // Check for "." and ".." filenames + !(p.m_pathname[filename_pos] == path::dot && + (filename_size == 1u || (filename_size == 2u && p.m_pathname[filename_pos + 1u] == path::dot))) + ) + { + size_type ext_pos = size; + while (ext_pos > filename_pos) + { + --ext_pos; + if (p.m_pathname[ext_pos] == path::dot) + break; + } + + if (ext_pos > filename_pos) + return size - ext_pos; + } + + return 0u; +} + +} // namespace detail + +BOOST_FILESYSTEM_DECL path& path::remove_filename_and_trailing_separators() +{ + size_type end_pos = detail::path_algorithms::find_parent_path_size(*this); + m_pathname.erase(m_pathname.begin() + end_pos, m_pathname.end()); + return *this; +} + +BOOST_FILESYSTEM_DECL path& path::remove_trailing_separator() +{ + if (!m_pathname.empty() && detail::is_directory_separator(m_pathname[m_pathname.size() - 1])) + m_pathname.erase(m_pathname.end() - 1); + return *this; +} + +BOOST_FILESYSTEM_DECL path& path::replace_filename(path const& replacement) +{ + detail::path_algorithms::remove_filename_v4(*this); + detail::path_algorithms::append_v4(*this, replacement.m_pathname.data(), replacement.m_pathname.data() + replacement.m_pathname.size()); + return *this; +} + +// lexical operations --------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const +{ + path::iterator b = begin(), e = end(), base_b = base.begin(), base_e = base.end(); + std::pair< path::iterator, path::iterator > mm = detail::mismatch(b, e, base_b, base_e); + if (mm.first == b && mm.second == base_b) + return path(); + if (mm.first == e && mm.second == base_e) + return detail::dot_path(); + + std::ptrdiff_t n = 0; + for (; mm.second != base_e; detail::path_algorithms::increment_v4(mm.second)) + { + path const& p = *mm.second; + if (detail::path_algorithms::compare_v4(p, detail::dot_dot_path()) == 0) + --n; + else if (!p.empty() && detail::path_algorithms::compare_v4(p, detail::dot_path()) != 0) + ++n; + } + if (n < 0) + return path(); + if (n == 0 && (mm.first == e || mm.first->empty())) + return detail::dot_path(); + + path tmp; + for (; n > 0; --n) + detail::path_algorithms::append_v4(tmp, detail::dot_dot_path()); + for (; mm.first != e; detail::path_algorithms::increment_v4(mm.first)) + detail::path_algorithms::append_v4(tmp, *mm.first); + return tmp; +} + +#if defined(BOOST_WINDOWS_API) + +BOOST_FILESYSTEM_DECL path path::generic_path() const +{ + path tmp(*this); + std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/'); + return tmp; +} + +BOOST_FILESYSTEM_DECL path& path::make_preferred() +{ + std::replace(m_pathname.begin(), m_pathname.end(), L'/', L'\\'); + return *this; +} + +#endif // defined(BOOST_WINDOWS_API) + } // namespace filesystem } // namespace boost @@ -940,7 +984,13 @@ find_next_separator: return pos; } -// first_element --------------------------------------------------------------------// +//--------------------------------------------------------------------------------------// +// // +// class path::iterator implementation // +// // +//--------------------------------------------------------------------------------------// + +// first_element ----------------------------------------------------------------------// // sets pos and len of first element, excluding extra separators // if src.empty(), sets pos,len, to 0,0. @@ -982,49 +1032,244 @@ namespace boost { namespace filesystem { namespace detail { -BOOST_FILESYSTEM_DECL -int lex_compare_v3(path::iterator first1, path::iterator last1, path::iterator first2, path::iterator last2) +BOOST_FILESYSTEM_DECL void path_algorithms::increment_v3(path_detail::path_iterator& it) { - for (; first1 != last1 && first2 != last2;) + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.m_pos < size, "path::iterator increment past end()"); + + // increment to position past current element; if current element is implicit dot, + // this will cause m_pos to represent the end iterator + it.m_pos += it.m_element.m_pathname.size(); + + // if the end is reached, we are done + if (it.m_pos >= size) { - if (first1->native() < first2->native()) - return -1; - if (first2->native() < first1->native()) - return 1; - BOOST_ASSERT(first2->native() == first1->native()); - first1.increment_v3(); - first2.increment_v3(); + BOOST_ASSERT_MSG(it.m_pos == size, "path::iterator increment after the referenced path was modified"); + it.m_element.clear(); // aids debugging + return; } - if (first1 == last1 && first2 == last2) - return 0; - return first1 == last1 ? -1 : 1; + + // process separator (Windows drive spec is only case not a separator) + if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size); + + // detect root directory and set iterator value to the separator if it is + if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size) + { + it.m_element.m_pathname = path::separator; // generic format; see docs + return; + } + + // skip separators until m_pos points to the start of the next element + while (it.m_pos != size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + ++it.m_pos; + } + + // detect trailing separator, and treat it as ".", per POSIX spec + if (it.m_pos == size && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) + { + --it.m_pos; + it.m_element = detail::dot_path(); + return; + } + } + + // get m_element + size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos); + if (end_pos == string_type::npos) + end_pos = size; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos); } -BOOST_FILESYSTEM_DECL -int lex_compare_v4(path::iterator first1, path::iterator last1, path::iterator first2, path::iterator last2) +BOOST_FILESYSTEM_DECL void path_algorithms::increment_v4(path_detail::path_iterator& it) { - for (; first1 != last1 && first2 != last2;) + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator increment past end()"); + + if (it.m_element.m_pathname.empty() && (it.m_pos + 1) == size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) { - if (first1->native() < first2->native()) - return -1; - if (first2->native() < first1->native()) - return 1; - BOOST_ASSERT(first2->native() == first1->native()); - ++first1; - ++first2; + // The iterator was pointing to the last empty element of the path; set to end. + it.m_pos = size; + return; } - if (first1 == last1 && first2 == last2) - return 0; - return first1 == last1 ? -1 : 1; + + // increment to position past current element; if current element is implicit dot, + // this will cause m_pos to represent the end iterator + it.m_pos += it.m_element.m_pathname.size(); + + // if the end is reached, we are done + if (it.m_pos >= size) + { + BOOST_ASSERT_MSG(it.m_pos == size, "path::iterator increment after the referenced path was modified"); + it.m_element.clear(); // aids debugging + return; + } + + // process separator (Windows drive spec is only case not a separator) + if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size); + + // detect root directory and set iterator value to the separator if it is + if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size) + { + it.m_element.m_pathname = path::separator; // generic format; see docs + return; + } + + // skip separators until m_pos points to the start of the next element + while (it.m_pos != size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + ++it.m_pos; + } + + // detect trailing separator + if (it.m_pos == size && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) + { + --it.m_pos; + it.m_element.m_pathname.clear(); + return; + } + } + + // get m_element + size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos); + if (end_pos == string_type::npos) + end_pos = size; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v3(path_detail::path_iterator& it) +{ + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.m_pos > 0, "path::iterator decrement past begin()"); + BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator decrement after the referenced path was modified"); + + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size); + + if (root_dir_pos < size && it.m_pos == root_dir_pos) + { + // Was pointing at root directory, decrement to root name + set_to_root_name: + it.m_pos = 0u; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p, p + root_name_size); + return; + } + + // if at end and there was a trailing non-root '/', return "." + if (it.m_pos == size && + size > 1 && + detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos - 1]) && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) + { + --it.m_pos; + it.m_element = detail::dot_path(); + return; + } + + // skip separators unless root directory + size_type end_pos = it.m_pos; + while (end_pos > root_name_size) + { + --end_pos; + + if (end_pos == root_dir_pos) + { + // Decremented to the root directory + it.m_pos = end_pos; + it.m_element.m_pathname = path::separator; // generic format; see docs + return; + } + + if (!detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos])) + { + ++end_pos; + break; + } + } + + if (end_pos <= root_name_size) + goto set_to_root_name; + + size_type filename_size = find_filename_size(it.m_path_ptr->m_pathname, root_name_size, end_pos); + it.m_pos = end_pos - filename_size; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v4(path_detail::path_iterator& it) +{ + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.m_pos > 0, "path::iterator decrement past begin()"); + BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator decrement after the referenced path was modified"); + + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size); + + if (root_dir_pos < size && it.m_pos == root_dir_pos) + { + // Was pointing at root directory, decrement to root name + set_to_root_name: + it.m_pos = 0u; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p, p + root_name_size); + return; + } + + // if at end and there was a trailing '/', return "" + if (it.m_pos == size && + size > 1 && + detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos - 1]) && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) + { + --it.m_pos; + it.m_element.m_pathname.clear(); + return; + } + + // skip separators unless root directory + size_type end_pos = it.m_pos; + while (end_pos > root_name_size) + { + --end_pos; + + if (end_pos == root_dir_pos) + { + // Decremented to the root directory + it.m_pos = end_pos; + it.m_element.m_pathname = path::separator; // generic format; see docs + return; + } + + if (!detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos])) + { + ++end_pos; + break; + } + } + + if (end_pos <= root_name_size) + goto set_to_root_name; + + size_type filename_size = find_filename_size(it.m_path_ptr->m_pathname, root_name_size, end_pos); + it.m_pos = end_pos - filename_size; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos); } } // namespace detail -//--------------------------------------------------------------------------------------// -// // -// class path::iterator implementation // -// // -//--------------------------------------------------------------------------------------// +// path iterators ------------------------------------------------------------------// BOOST_FILESYSTEM_DECL path::iterator path::begin() const { @@ -1038,8 +1283,8 @@ BOOST_FILESYSTEM_DECL path::iterator path::begin() const { itr.m_element = m_pathname.substr(itr.m_pos, element_size); #ifdef BOOST_WINDOWS_API - if (itr.m_element.m_pathname.size() == 1u && itr.m_element.m_pathname[0] == preferred_separator) - itr.m_element.m_pathname[0] = separator; + if (itr.m_element.m_pathname.size() == 1u && itr.m_element.m_pathname[0] == path::preferred_separator) + itr.m_element.m_pathname[0] = path::separator; #endif } @@ -1054,241 +1299,6 @@ BOOST_FILESYSTEM_DECL path::iterator path::end() const return itr; } -BOOST_FILESYSTEM_DECL void path::iterator::increment_v3() -{ - const size_type size = m_path_ptr->m_pathname.size(); - BOOST_ASSERT_MSG(m_pos < size, "path::iterator increment past end()"); - - // increment to position past current element; if current element is implicit dot, - // this will cause m_pos to represent the end iterator - m_pos += m_element.m_pathname.size(); - - // if the end is reached, we are done - if (m_pos >= size) - { - BOOST_ASSERT_MSG(m_pos == size, "path::iterator increment after the referenced path was modified"); - m_element.clear(); // aids debugging - return; - } - - // process separator (Windows drive spec is only case not a separator) - if (detail::is_directory_separator(m_path_ptr->m_pathname[m_pos])) - { - size_type root_name_size = 0; - size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname.c_str(), size, root_name_size); - - // detect root directory and set iterator value to the separator if it is - if (m_pos == root_dir_pos && m_element.m_pathname.size() == root_name_size) - { - m_element.m_pathname = separator; // generic format; see docs - return; - } - - // skip separators until m_pos points to the start of the next element - while (m_pos != size && detail::is_directory_separator(m_path_ptr->m_pathname[m_pos])) - { - ++m_pos; - } - - // detect trailing separator, and treat it as ".", per POSIX spec - if (m_pos == size && - !is_root_separator(m_path_ptr->m_pathname, root_dir_pos, m_pos - 1)) - { - --m_pos; - m_element = detail::dot_path(); - return; - } - } - - // get m_element - size_type end_pos = m_path_ptr->m_pathname.find_first_of(separators, m_pos); - if (end_pos == string_type::npos) - end_pos = size; - const path::value_type* p = m_path_ptr->m_pathname.c_str(); - m_element.m_pathname.assign(p + m_pos, p + end_pos); -} - -BOOST_FILESYSTEM_DECL void path::iterator::increment_v4() -{ - const size_type size = m_path_ptr->m_pathname.size(); - BOOST_ASSERT_MSG(m_pos <= size, "path::iterator increment past end()"); - - if (m_element.m_pathname.empty() && (m_pos + 1) == size && detail::is_directory_separator(m_path_ptr->m_pathname[m_pos])) - { - // The iterator was pointing to the last empty element of the path; set to end. - m_pos = size; - return; - } - - // increment to position past current element; if current element is implicit dot, - // this will cause m_pos to represent the end iterator - m_pos += m_element.m_pathname.size(); - - // if the end is reached, we are done - if (m_pos >= size) - { - BOOST_ASSERT_MSG(m_pos == size, "path::iterator increment after the referenced path was modified"); - m_element.clear(); // aids debugging - return; - } - - // process separator (Windows drive spec is only case not a separator) - if (detail::is_directory_separator(m_path_ptr->m_pathname[m_pos])) - { - size_type root_name_size = 0; - size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname.c_str(), size, root_name_size); - - // detect root directory and set iterator value to the separator if it is - if (m_pos == root_dir_pos && m_element.m_pathname.size() == root_name_size) - { - m_element.m_pathname = separator; // generic format; see docs - return; - } - - // skip separators until m_pos points to the start of the next element - while (m_pos != size && detail::is_directory_separator(m_path_ptr->m_pathname[m_pos])) - { - ++m_pos; - } - - // detect trailing separator - if (m_pos == size && - !is_root_separator(m_path_ptr->m_pathname, root_dir_pos, m_pos - 1)) - { - --m_pos; - m_element.m_pathname.clear(); - return; - } - } - - // get m_element - size_type end_pos = m_path_ptr->m_pathname.find_first_of(separators, m_pos); - if (end_pos == string_type::npos) - end_pos = size; - const path::value_type* p = m_path_ptr->m_pathname.c_str(); - m_element.m_pathname.assign(p + m_pos, p + end_pos); -} - -BOOST_FILESYSTEM_DECL void path::iterator::decrement_v3() -{ - const size_type size = m_path_ptr->m_pathname.size(); - BOOST_ASSERT_MSG(m_pos > 0, "path::iterator decrement past begin()"); - BOOST_ASSERT_MSG(m_pos <= size, "path::iterator decrement after the referenced path was modified"); - - size_type root_name_size = 0; - size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname.c_str(), size, root_name_size); - - if (root_dir_pos < size && m_pos == root_dir_pos) - { - // Was pointing at root directory, decrement to root name - set_to_root_name: - m_pos = 0u; - const path::value_type* p = m_path_ptr->m_pathname.c_str(); - m_element.m_pathname.assign(p, p + root_name_size); - return; - } - - // if at end and there was a trailing non-root '/', return "." - if (m_pos == size && - size > 1 && - detail::is_directory_separator(m_path_ptr->m_pathname[m_pos - 1]) && - !is_root_separator(m_path_ptr->m_pathname, root_dir_pos, m_pos - 1)) - { - --m_pos; - m_element = detail::dot_path(); - return; - } - - // skip separators unless root directory - size_type end_pos = m_pos; - while (end_pos > root_name_size) - { - --end_pos; - - if (end_pos == root_dir_pos) - { - // Decremented to the root directory - m_pos = end_pos; - m_element.m_pathname = separator; // generic format; see docs - return; - } - - if (!detail::is_directory_separator(m_path_ptr->m_pathname[end_pos])) - { - ++end_pos; - break; - } - } - - if (end_pos <= root_name_size) - goto set_to_root_name; - - size_type filename_size = find_filename_size(m_path_ptr->m_pathname, root_name_size, end_pos); - m_pos = end_pos - filename_size; - const path::value_type* p = m_path_ptr->m_pathname.c_str(); - m_element.m_pathname.assign(p + m_pos, p + end_pos); -} - -BOOST_FILESYSTEM_DECL void path::iterator::decrement_v4() -{ - const size_type size = m_path_ptr->m_pathname.size(); - BOOST_ASSERT_MSG(m_pos > 0, "path::iterator decrement past begin()"); - BOOST_ASSERT_MSG(m_pos <= size, "path::iterator decrement after the referenced path was modified"); - - size_type root_name_size = 0; - size_type root_dir_pos = find_root_directory_start(m_path_ptr->m_pathname.c_str(), size, root_name_size); - - if (root_dir_pos < size && m_pos == root_dir_pos) - { - // Was pointing at root directory, decrement to root name - set_to_root_name: - m_pos = 0u; - const path::value_type* p = m_path_ptr->m_pathname.c_str(); - m_element.m_pathname.assign(p, p + root_name_size); - return; - } - - // if at end and there was a trailing '/', return "" - if (m_pos == size && - size > 1 && - detail::is_directory_separator(m_path_ptr->m_pathname[m_pos - 1]) && - !is_root_separator(m_path_ptr->m_pathname, root_dir_pos, m_pos - 1)) - { - --m_pos; - m_element.m_pathname.clear(); - return; - } - - // skip separators unless root directory - size_type end_pos = m_pos; - while (end_pos > root_name_size) - { - --end_pos; - - if (end_pos == root_dir_pos) - { - // Decremented to the root directory - m_pos = end_pos; - m_element.m_pathname = separator; // generic format; see docs - return; - } - - if (!detail::is_directory_separator(m_path_ptr->m_pathname[end_pos])) - { - ++end_pos; - break; - } - } - - if (end_pos <= root_name_size) - goto set_to_root_name; - - size_type filename_size = find_filename_size(m_path_ptr->m_pathname, root_name_size, end_pos); - m_pos = end_pos - filename_size; - const path::value_type* p = m_path_ptr->m_pathname.c_str(); - m_element.m_pathname.assign(p + m_pos, p + end_pos); -} - } // namespace filesystem } // namespace boost diff --git a/src/windows_tools.hpp b/src/windows_tools.hpp index 0b1d198..122d5cf 100644 --- a/src/windows_tools.hpp +++ b/src/windows_tools.hpp @@ -62,7 +62,7 @@ inline boost::filesystem::perms make_permissions(boost::filesystem::path const& boost::filesystem::perms prms = boost::filesystem::owner_read | boost::filesystem::group_read | boost::filesystem::others_read; if ((attr & FILE_ATTRIBUTE_READONLY) == 0u) prms |= boost::filesystem::owner_write | boost::filesystem::group_write | boost::filesystem::others_write; - boost::filesystem::path ext = p.extension(); + boost::filesystem::path ext = detail::path_algorithms::extension_v4(p); wchar_t const* q = ext.c_str(); if (equal_extension(q, L".exe", L".EXE") || equal_extension(q, L".com", L".COM") || equal_extension(q, L".bat", L".BAT") || equal_extension(q, L".cmd", L".CMD")) prms |= boost::filesystem::owner_exe | boost::filesystem::group_exe | boost::filesystem::others_exe;