diff options
author | AlexSm <alex@ydb.tech> | 2023-12-27 23:31:58 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 23:31:58 +0100 |
commit | d67bfb4b4b7549081543e87a31bc6cb5c46ac973 (patch) | |
tree | 8674f2f1570877cb653e7ddcff37ba00288de15a /contrib/restricted/boost/filesystem/src | |
parent | 1f6bef05ed441c3aa2d565ac792b26cded704ac7 (diff) | |
download | ydb-d67bfb4b4b7549081543e87a31bc6cb5c46ac973.tar.gz |
Import libs 4 (#758)
Diffstat (limited to 'contrib/restricted/boost/filesystem/src')
6 files changed, 863 insertions, 839 deletions
diff --git a/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp b/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp index 72db677a09..a8d2a42732 100644 --- a/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp +++ b/contrib/restricted/boost/filesystem/src/codecvt_error_category.cpp @@ -15,7 +15,7 @@ #include <boost/config/warning_disable.hpp> #include <boost/filesystem/config.hpp> -#include <boost/filesystem/path_traits.hpp> +#include <boost/filesystem/detail/path_traits.hpp> #include <boost/system/error_category.hpp> #include <locale> #include <string> diff --git a/contrib/restricted/boost/filesystem/src/directory.cpp b/contrib/restricted/boost/filesystem/src/directory.cpp index 6a3e1dc731..f769f8fc18 100644 --- a/contrib/restricted/boost/filesystem/src/directory.cpp +++ b/contrib/restricted/boost/filesystem/src/directory.cpp @@ -74,15 +74,6 @@ #include <boost/filesystem/detail/header.hpp> // must be the last #include -// BOOST_FILESYSTEM_STATUS_CACHE enables file_status cache in -// dir_itr_increment. The config tests are placed here because some of the -// macros being tested come from dirent.h. -// -// TODO: find out what macros indicate dirent::d_type present in more libraries -#if defined(BOOST_WINDOWS_API) || defined(_DIRENT_HAVE_D_TYPE) // defined by GNU C library if d_type present -#define BOOST_FILESYSTEM_STATUS_CACHE -#endif - namespace fs = boost::filesystem; using boost::system::error_code; using boost::system::system_category; @@ -96,76 +87,34 @@ namespace filesystem { // // //--------------------------------------------------------------------------------------// -BOOST_FILESYSTEM_DECL -file_status directory_entry::get_status(system::error_code* ec) const +BOOST_FILESYSTEM_DECL void directory_entry::refresh_impl(system::error_code* ec) const { - if (!status_known(m_status)) + system::error_code local_ec; + m_symlink_status = detail::symlink_status(m_path, &local_ec); + + if (!filesystem::is_symlink(m_symlink_status)) { - // optimization: if the symlink status is known, and it isn't a symlink, - // then status and symlink_status are identical so just copy the - // symlink status to the regular status. - if (status_known(m_symlink_status) && !is_symlink(m_symlink_status)) - { - m_status = m_symlink_status; - if (ec) - ec->clear(); - } - else + // Also works if symlink_status fails - set m_status to status_error as well + m_status = m_symlink_status; + + if (BOOST_UNLIKELY(!!local_ec)) { - m_status = detail::status(m_path, ec); + if (!ec) + BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::directory_entry::refresh", m_path, local_ec)); + + *ec = local_ec; + return; } + + if (ec) + ec->clear(); } - else if (ec) + else { - ec->clear(); + m_status = detail::status(m_path, ec); } - - return m_status; } -BOOST_FILESYSTEM_DECL -file_status directory_entry::get_symlink_status(system::error_code* ec) const -{ - if (!status_known(m_symlink_status)) - m_symlink_status = detail::symlink_status(m_path, ec); - else if (ec) - ec->clear(); - - return m_symlink_status; -} - -// dispatch directory_entry supplied here rather than in -// <boost/filesystem/path_traits.hpp>, thus avoiding header circularity. -// test cases are in operations_unit_test.cpp - -namespace path_traits { - -BOOST_FILESYSTEM_DECL -void dispatch(directory_entry const& de, -#ifdef BOOST_WINDOWS_API - std::wstring& to, -#else - std::string& to, -#endif - codecvt_type const&) -{ - to = de.path().native(); -} - -BOOST_FILESYSTEM_DECL -void dispatch(directory_entry const& de, -#ifdef BOOST_WINDOWS_API - std::wstring& to -#else - std::string& to -#endif -) -{ - to = de.path().native(); -} - -} // namespace path_traits - //--------------------------------------------------------------------------------------// // // // directory_iterator // @@ -353,24 +302,43 @@ error_code dir_itr_increment(dir_itr_imp& imp, fs::path& filename, fs::file_stat filename = result->d_name; -#ifdef BOOST_FILESYSTEM_STATUS_CACHE +#if defined(BOOST_FILESYSTEM_HAS_DIRENT_D_TYPE) if (result->d_type == DT_UNKNOWN) // filesystem does not supply d_type value { sf = symlink_sf = fs::file_status(fs::status_error); } else // filesystem supplies d_type value { - if (result->d_type == DT_DIR) - sf = symlink_sf = fs::file_status(fs::directory_file); - else if (result->d_type == DT_REG) + if (result->d_type == DT_REG) sf = symlink_sf = fs::file_status(fs::regular_file); + else if (result->d_type == DT_DIR) + sf = symlink_sf = fs::file_status(fs::directory_file); else if (result->d_type == DT_LNK) { sf = fs::file_status(fs::status_error); symlink_sf = fs::file_status(fs::symlink_file); } else - sf = symlink_sf = fs::file_status(fs::status_error); + { + switch (result->d_type) + { + case DT_SOCK: + sf = symlink_sf = fs::file_status(fs::socket_file); + break; + case DT_FIFO: + sf = symlink_sf = fs::file_status(fs::fifo_file); + break; + case DT_BLK: + sf = symlink_sf = fs::file_status(fs::block_file); + break; + case DT_CHR: + sf = symlink_sf = fs::file_status(fs::character_file); + break; + default: + sf = symlink_sf = fs::file_status(fs::status_error); + break; + } + } } #else sf = symlink_sf = fs::file_status(fs::status_error); @@ -599,9 +567,12 @@ extra_data_format g_extra_data_format = file_directory_information_format; * \brief Extra buffer size for GetFileInformationByHandleEx-based or NtQueryDirectoryFile-based directory iterator. * * Must be large enough to accommodate at least one FILE_DIRECTORY_INFORMATION or *_DIR_INFO struct and one filename. - * NTFS, VFAT, exFAT support filenames up to 255 UTF-16/UCS-2 characters. ReFS supports filenames up to 32768 UTF-16 characters. + * NTFS, VFAT, exFAT and ReFS support filenames up to 255 UTF-16/UCS-2 characters. (For ReFS, there is no information + * on the on-disk format, and it is possible that it supports longer filenames, up to 32768 UTF-16/UCS-2 characters.) + * The buffer cannot be larger than 64k, because up to Windows 8.1, NtQueryDirectoryFile and GetFileInformationByHandleEx + * fail with ERROR_INVALID_PARAMETER when trying to retrieve the filenames from a network share. */ -BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = sizeof(file_id_extd_dir_info) + 65536u; +BOOST_CONSTEXPR_OR_CONST std::size_t dir_itr_extra_size = 65536u; inline system::error_code dir_itr_close(dir_itr_imp& imp) BOOST_NOEXCEPT { @@ -763,6 +734,28 @@ done: return error_code(); } +//! Returns \c true if the error code indicates that the OS or the filesystem does not support a particular directory info class +inline bool is_dir_info_class_not_supported(DWORD error) +{ + // Some mounted filesystems may not support FILE_ID_128 identifiers, which will cause + // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER, + // even though in general the operation is supported by the kernel. SMBv1 returns a special error + // code ERROR_INVALID_LEVEL in this case. + // Some other filesystems also don't implement other info classes and return ERROR_INVALID_PARAMETER + // (e.g. see https://github.com/boostorg/filesystem/issues/266), ERROR_GEN_FAILURE, ERROR_INVALID_FUNCTION + // or ERROR_INTERNAL_ERROR (https://github.com/boostorg/filesystem/issues/286). Treat these error codes + // as "non-permanent", even though ERROR_INVALID_PARAMETER is also returned if GetFileInformationByHandleEx + // in general does not support a certain info class. Worst case, we will make extra syscalls on directory + // iterator construction. + // Also note that Wine returns ERROR_CALL_NOT_IMPLEMENTED for unimplemented info classes, and + // up until 7.21 it didn't implement FileIdExtdDirectoryRestartInfo and FileFullDirectoryRestartInfo. + // (https://bugs.winehq.org/show_bug.cgi?id=53590) + return error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER || + error == ERROR_INVALID_LEVEL || error == ERROR_CALL_NOT_IMPLEMENTED || + error == ERROR_GEN_FAILURE || error == ERROR_INVALID_FUNCTION || + error == ERROR_INTERNAL_ERROR; +} + error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs::path const& dir, unsigned int opts, directory_iterator_params* params, fs::path& first_filename, fs::file_status& sf, fs::file_status& symlink_sf) { boost::intrusive_ptr< detail::dir_itr_imp > pimpl(new (dir_itr_extra_size) detail::dir_itr_imp()); @@ -828,9 +821,14 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: if (BOOST_UNLIKELY((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0u)) return make_error_code(system::errc::not_a_directory); - if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u) + if ((opts & static_cast< unsigned int >(directory_options::_detail_no_follow)) != 0u && (info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u) { - if ((info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0u && is_reparse_point_a_symlink_ioctl(h.handle)) + error_code ec; + const ULONG reparse_point_tag = detail::get_reparse_point_tag_ioctl(h.handle, dir, &ec); + if (BOOST_UNLIKELY(!!ec)) + return ec; + + if (detail::is_reparse_point_tag_a_symlink(reparse_point_tag)) return make_error_code(system::errc::too_many_symbolic_link_levels); } } @@ -845,14 +843,10 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: { DWORD error = ::GetLastError(); - if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER) + if (is_dir_info_class_not_supported(error)) { // Fall back to file_full_dir_info_format. - // Note that some mounted filesystems may not support FILE_ID_128 identifiers, which will cause - // GetFileInformationByHandleEx(FileIdExtdDirectoryRestartInfo) return ERROR_INVALID_PARAMETER, - // even though in general the operation is supported by the kernel. So don't downgrade to - // FileFullDirectoryRestartInfo permanently in this case - only for this particular iterator. - if (error == ERROR_NOT_SUPPORTED) + if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_full_dir_info_format); goto fallback_to_file_full_dir_info_format; } @@ -879,11 +873,12 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: { DWORD error = ::GetLastError(); - if (error == ERROR_NOT_SUPPORTED || error == ERROR_INVALID_PARAMETER) + if (is_dir_info_class_not_supported(error)) { // Fall back to file_id_both_dir_info - filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format); - goto fallback_to_file_id_both_dir_info; + if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) + filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_id_both_dir_info_format); + goto fallback_to_file_id_both_dir_info_format; } if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) @@ -902,12 +897,20 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: break; case file_id_both_dir_info_format: - fallback_to_file_id_both_dir_info: + fallback_to_file_id_both_dir_info_format: { if (!get_file_information_by_handle_ex(iterator_handle, file_id_both_directory_restart_info_class, extra_data, dir_itr_extra_size)) { DWORD error = ::GetLastError(); + if (is_dir_info_class_not_supported(error)) + { + // Fall back to file_directory_information + if (error == ERROR_NOT_SUPPORTED || error == ERROR_CALL_NOT_IMPLEMENTED) + filesystem::detail::atomic_store_relaxed(g_extra_data_format, file_directory_information_format); + goto fallback_to_file_directory_information_format; + } + if (error == ERROR_NO_MORE_FILES || error == ERROR_FILE_NOT_FOUND) goto done; @@ -924,6 +927,7 @@ error_code dir_itr_create(boost::intrusive_ptr< detail::dir_itr_imp >& imp, fs:: break; default: + fallback_to_file_directory_information_format: { NtQueryDirectoryFile_t* nt_query_directory_file = filesystem::detail::atomic_load_relaxed(boost::filesystem::detail::nt_query_directory_file_api); if (BOOST_UNLIKELY(!nt_query_directory_file)) @@ -1120,7 +1124,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_with_status + ( +#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; } @@ -1180,7 +1195,7 @@ void directory_iterator_increment(directory_iterator& it, system::error_code* ec && (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'))))) { - it.m_imp->dir_entry.replace_filename(filename, file_stat, symlink_file_stat); + it.m_imp->dir_entry.replace_filename_with_status(filename, file_stat, symlink_file_stat); return; } } @@ -1344,14 +1359,14 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail: return result; } - file_status symlink_stat; + file_type symlink_ft = status_error; // If we are not recursing into symlinks, we are going to have to know if the // stack top is a symlink, so get symlink_status and verify no error occurred. if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) == 0u || (imp->m_options & static_cast< unsigned int >(directory_options::skip_dangling_symlinks)) != 0u) { - symlink_stat = imp->m_stack.back()->symlink_status(ec); + symlink_ft = imp->m_stack.back()->symlink_file_type(ec); if (ec) return result; } @@ -1364,12 +1379,12 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail: // The predicate code has since been rewritten to pass error_code arguments, // per ticket #5653. - if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || !fs::is_symlink(symlink_stat)) + if ((imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink)) != 0u || symlink_ft != symlink_file) { - file_status stat = imp->m_stack.back()->status(ec); + file_type ft = imp->m_stack.back()->file_type(ec); if (BOOST_UNLIKELY(!!ec)) { - if (ec == make_error_condition(system::errc::no_such_file_or_directory) && fs::is_symlink(symlink_stat) && + if (ec == make_error_condition(system::errc::no_such_file_or_directory) && symlink_ft == symlink_file && (imp->m_options & static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) == static_cast< unsigned int >(directory_options::follow_directory_symlink | directory_options::skip_dangling_symlinks)) { // Skip dangling symlink and continue iteration on the current depth level @@ -1379,7 +1394,7 @@ inline push_directory_result recursive_directory_iterator_push_directory(detail: return result; } - if (!fs::is_directory(stat)) + if (ft != directory_file) return result; if (BOOST_UNLIKELY((imp->m_stack.size() - 1u) >= static_cast< std::size_t >((std::numeric_limits< int >::max)()))) diff --git a/contrib/restricted/boost/filesystem/src/operations.cpp b/contrib/restricted/boost/filesystem/src/operations.cpp index dd636e9063..c7808d5941 100644 --- a/contrib/restricted/boost/filesystem/src/operations.cpp +++ b/contrib/restricted/boost/filesystem/src/operations.cpp @@ -70,7 +70,7 @@ #include <unistd.h> #include <fcntl.h> -#if _POSIX_C_SOURCE < 200809L +#if !defined(BOOST_FILESYSTEM_HAS_POSIX_AT_APIS) #include <utime.h> #endif #include <limits.h> @@ -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 @@ -1511,50 +1511,36 @@ boost::winapi::NTSTATUS_ nt_create_file_handle_at(HANDLE& out, HANDLE basedir_ha #endif // !defined(UNDER_CE) -bool is_reparse_point_a_symlink_ioctl(HANDLE h) +ULONG get_reparse_point_tag_ioctl(HANDLE h, path const& p, error_code* ec) { - boost::scoped_ptr< reparse_data_buffer_with_storage > buf(new reparse_data_buffer_with_storage); + boost::scoped_ptr< reparse_data_buffer_with_storage > buf(new (std::nothrow) reparse_data_buffer_with_storage); + if (BOOST_UNLIKELY(!buf.get())) + { + if (!ec) + BOOST_FILESYSTEM_THROW(filesystem_error("Cannot allocate memory to query reparse point", p, make_error_code(system::errc::not_enough_memory))); + + *ec = make_error_code(system::errc::not_enough_memory); + return 0u; + } // Query the reparse data DWORD dwRetLen = 0u; BOOL result = ::DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, buf.get(), sizeof(*buf), &dwRetLen, NULL); if (BOOST_UNLIKELY(!result)) - return false; - - return is_reparse_point_tag_a_symlink(buf->rdb.ReparseTag); -} - -namespace { - -inline bool is_reparse_point_a_symlink(path const& p) -{ - handle_wrapper h(create_file_handle( - p, - FILE_READ_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)); - if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE)) - return false; - - GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api); - if (BOOST_LIKELY(get_file_information_by_handle_ex != NULL)) { - file_attribute_tag_info info; - BOOL result = get_file_information_by_handle_ex(h.handle, file_attribute_tag_info_class, &info, sizeof(info)); - if (BOOST_UNLIKELY(!result)) - return false; - - if ((info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0u) - return false; + DWORD err = ::GetLastError(); + if (!ec) + BOOST_FILESYSTEM_THROW(filesystem_error("Failed to query reparse point", p, error_code(err, system_category()))); - return is_reparse_point_tag_a_symlink(info.ReparseTag); + ec->assign(err, system_category()); + return 0u; } - return is_reparse_point_a_symlink_ioctl(h.handle); + return buf->rdb.ReparseTag; } +namespace { + inline std::size_t get_full_path_name(path const& src, std::size_t len, wchar_t* buf, wchar_t** p) { return static_cast< std::size_t >(::GetFullPathNameW(src.c_str(), static_cast< DWORD >(len), buf, p)); @@ -1590,6 +1576,7 @@ fs::file_status status_by_handle(HANDLE h, path const& p, error_code* ec) { fs::file_type ftype; DWORD attrs; + ULONG reparse_tag = 0u; GetFileInformationByHandleEx_t* get_file_information_by_handle_ex = filesystem::detail::atomic_load_relaxed(get_file_information_by_handle_ex_api); if (BOOST_LIKELY(get_file_information_by_handle_ex != NULL)) { @@ -1609,12 +1596,7 @@ fs::file_status status_by_handle(HANDLE h, path const& p, error_code* ec) } attrs = info.FileAttributes; - - if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) - { - ftype = is_reparse_point_tag_a_symlink(info.ReparseTag) ? fs::symlink_file : fs::reparse_file; - goto done; - } + reparse_tag = info.ReparseTag; } else { @@ -1626,25 +1608,48 @@ fs::file_status status_by_handle(HANDLE h, path const& p, error_code* ec) attrs = info.dwFileAttributes; - if (attrs & FILE_ATTRIBUTE_REPARSE_POINT) + if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u) { - ftype = is_reparse_point_a_symlink_ioctl(h) ? fs::symlink_file : fs::reparse_file; - goto done; + reparse_tag = get_reparse_point_tag_ioctl(h, p, ec); + if (ec) + { + if (BOOST_UNLIKELY(!!ec)) + return fs::file_status(fs::status_error); + } } } - ftype = (attrs & FILE_ATTRIBUTE_DIRECTORY) ? fs::directory_file : fs::regular_file; + if ((attrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0u) + { + if (reparse_tag == IO_REPARSE_TAG_DEDUP) + ftype = fs::regular_file; + else if (is_reparse_point_tag_a_symlink(reparse_tag)) + ftype = fs::symlink_file; + else + ftype = fs::reparse_file; + } + else if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0u) + { + ftype = fs::directory_file; + } + else + { + ftype = fs::regular_file; + } -done: return fs::file_status(ftype, make_permissions(p, attrs)); } //! symlink_status() implementation fs::file_status symlink_status_impl(path const& p, error_code* ec) { + // Normally, we only need FILE_READ_ATTRIBUTES access mode. But SMBv1 reports incorrect + // file attributes in GetFileInformationByHandleEx in this case (e.g. it reports FILE_ATTRIBUTE_NORMAL + // for a directory in a SMBv1 share), so we add FILE_READ_EA as a workaround. + // https://github.com/boostorg/filesystem/issues/282 handle_wrapper h(create_file_handle( p.c_str(), - FILE_READ_ATTRIBUTES, // dwDesiredAccess; attributes only + FILE_READ_ATTRIBUTES | FILE_READ_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, // lpSecurityAttributes OPEN_EXISTING, @@ -1688,7 +1693,7 @@ fs::file_status status_impl(path const& p, error_code* ec) // Resolve the symlink handle_wrapper h(create_file_handle( p.c_str(), - FILE_READ_ATTRIBUTES, // dwDesiredAccess; attributes only + FILE_READ_ATTRIBUTES | FILE_READ_EA, // see the comment in symlink_status_impl re. access mode FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, // lpSecurityAttributes OPEN_EXISTING, @@ -1775,7 +1780,7 @@ DWORD remove_nt6_by_handle(HANDLE handle, remove_impl_type impl) break; err = ::GetLastError(); - if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED)) + if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED || err == ERROR_CALL_NOT_IMPLEMENTED)) { // Downgrade to the older implementation impl = remove_disp_ex_flag_posix_semantics; @@ -1833,7 +1838,7 @@ DWORD remove_nt6_by_handle(HANDLE handle, remove_impl_type impl) break; } - else if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED)) + else if (BOOST_UNLIKELY(err == ERROR_INVALID_PARAMETER || err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED || err == ERROR_CALL_NOT_IMPLEMENTED)) { // Downgrade to the older implementation impl = remove_disp; @@ -1902,7 +1907,7 @@ inline bool remove_nt6_impl(path const& p, remove_impl_type impl, error_code* ec { handle_wrapper h(create_file_handle( p, - DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, + DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, @@ -1999,12 +2004,12 @@ 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_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_OPEN, - FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT + FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | FILE_OPEN_REPARSE_POINT ); if (!NT_SUCCESS(status)) @@ -2027,7 +2032,7 @@ uintmax_t remove_all_nt6_by_handle(HANDLE h, path const& p, error_code* ec) { hh.handle = create_file_handle( nested_path, - FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, @@ -2144,7 +2149,7 @@ inline uintmax_t remove_all_impl(path const& p, error_code* ec) { handle_wrapper h(create_file_handle( p, - FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE, + FILE_LIST_DIRECTORY | DELETE | FILE_READ_ATTRIBUTES | FILE_READ_EA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_EA | SYNCHRONIZE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, @@ -2385,12 +2390,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; } @@ -2437,14 +2442,14 @@ 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) - result.remove_filename(); + if (path_algorithms::compare_v4(result, root) != 0) + result.remove_filename_and_trailing_separators(); continue; } @@ -2459,7 +2464,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" @@ -2484,14 +2489,14 @@ path canonical(path const& p, path const& base, system::error_code* ec) path link(detail::read_symlink(result, ec)); if (ec && *ec) goto return_empty_path; - result.remove_filename(); + result.remove_filename_and_trailing_separators(); 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(); @@ -2499,15 +2504,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; } @@ -2603,10 +2608,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); @@ -2642,7 +2647,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); } @@ -2697,8 +2706,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; @@ -2953,7 +2966,14 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod // Create handle_wrappers here so that CloseHandle calls don't clobber error code returned by GetLastError handle_wrapper hw_from, hw_to; - hw_from.handle = create_file_handle(from.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS); + // See the comment in last_write_time regarding access rights used here for GetFileTime. + hw_from.handle = create_file_handle( + from.c_str(), + FILE_READ_ATTRIBUTES | FILE_READ_EA, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS); FILETIME lwt_from; if (hw_from.handle == INVALID_HANDLE_VALUE) @@ -2967,7 +2987,13 @@ bool copy_file(path const& from, path const& to, unsigned int options, error_cod if (!::GetFileTime(hw_from.handle, NULL, NULL, &lwt_from)) goto fail_last_error; - hw_to.handle = create_file_handle(to.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS); + hw_to.handle = create_file_handle( + to.c_str(), + FILE_READ_ATTRIBUTES | FILE_READ_EA, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS); if (hw_to.handle != INVALID_HANDLE_VALUE) { @@ -3088,9 +3114,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); @@ -3107,19 +3133,19 @@ bool create_directories(path const& p, system::error_code* ec) } } - --it; - parent.remove_filename(); + 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 = create_directory(parent, NULL, &local_ec); + created = detail::create_directory(parent, NULL, &local_ec); if (BOOST_UNLIKELY(!!local_ec)) { if (!ec) @@ -3749,9 +3775,10 @@ std::time_t creation_time(path const& p, system::error_code* ec) #else // defined(BOOST_POSIX_API) + // See the comment in last_write_time regarding access rights used here for GetFileTime. handle_wrapper hw(create_file_handle( p.c_str(), - FILE_READ_ATTRIBUTES, + FILE_READ_ATTRIBUTES | FILE_READ_EA, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, @@ -3765,7 +3792,6 @@ std::time_t creation_time(path const& p, system::error_code* ec) } FILETIME ct; - if (BOOST_UNLIKELY(!::GetFileTime(hw.handle, &ct, NULL, NULL))) goto fail; @@ -3807,9 +3833,12 @@ std::time_t last_write_time(path const& p, system::error_code* ec) #else // defined(BOOST_POSIX_API) + // GetFileTime is documented to require GENERIC_READ access right, but this causes problems if the file + // is opened by another process without FILE_SHARE_READ. In practice, FILE_READ_ATTRIBUTES works, and + // FILE_READ_EA is also added for good measure, in case if it matters for SMBv1. handle_wrapper hw(create_file_handle( p.c_str(), - FILE_READ_ATTRIBUTES, + FILE_READ_ATTRIBUTES | FILE_READ_EA, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, @@ -3823,7 +3852,6 @@ std::time_t last_write_time(path const& p, system::error_code* ec) } FILETIME lwt; - if (BOOST_UNLIKELY(!::GetFileTime(hw.handle, NULL, NULL, &lwt))) goto fail; @@ -4055,9 +4083,8 @@ path read_symlink(path const& p, system::error_code* ec) DWORD error; if (BOOST_UNLIKELY(h.handle == INVALID_HANDLE_VALUE)) { + return_last_error: error = ::GetLastError(); - - return_error: emit_error(error, p, ec, "boost::filesystem::read_symlink"); return symlink_path; } @@ -4065,10 +4092,7 @@ path read_symlink(path const& p, system::error_code* ec) boost::scoped_ptr< reparse_data_buffer_with_storage > buf(new reparse_data_buffer_with_storage); DWORD sz = 0u; if (BOOST_UNLIKELY(!::DeviceIoControl(h.handle, FSCTL_GET_REPARSE_POINT, NULL, 0, buf.get(), sizeof(*buf), &sz, NULL))) - { - error = ::GetLastError(); - goto return_error; - } + goto return_last_error; const wchar_t* buffer; std::size_t offset, len; @@ -4310,11 +4334,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) @@ -4324,7 +4345,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; @@ -4349,7 +4370,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; @@ -4395,7 +4416,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()) @@ -4432,9 +4458,9 @@ 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); + file_status head_status(detail::status_impl(head, &local_ec)); if (BOOST_UNLIKELY(head_status.type() == fs::status_error)) { if (!ec) @@ -4447,35 +4473,86 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) if (head_status.type() != fs::file_not_found) break; - head.remove_filename(); + head.remove_filename_and_trailing_separators(); } + if (head.empty()) + return path_algorithms::lexically_normal_v4(p); + + path const& dot_p = dot_path(); + path const& dot_dot_p = dot_dot_path(); + #else - // On Windows, filesystem APIs such as GetFileAttributesW perform lexical path normalization internally. - // As a result, a path like "c:\a\.." can be reported as present even if "c:\a" is not. This would break - // canonical, as symlink_status that it calls internally would report an error that the file at the intermediate - // path does not exist. To avoid this, scan the initial path in the forward direction. - // Also, operate on paths with preferred separators. This can be important on Windows since GetFileAttributesW, - // which is called in status() may return "file not found" for paths to network shares and mounted cloud - // storages that have forward slashes as separators. + // On Windows, filesystem APIs such as GetFileAttributesW and CreateFileW perform lexical path normalization + // internally. As a result, a path like "c:\a\.." can be reported as present even if "c:\a" is not. This would + // break canonical, as symlink_status that it calls internally would report an error that the file at the + // intermediate path does not exist. To avoid this, scan the initial path in the forward direction. + // Also, operate on paths with preferred separators. This can be important on Windows since GetFileAttributesW + // or CreateFileW, which is called in status() may return "file not found" for paths to network shares and + // mounted cloud storages that have forward slashes as separators. + // Also, avoid querying status of the root name such as \\?\c: as CreateFileW returns ERROR_INVALID_FUNCTION for + // such path. Querying the status of a root name such as c: is also not right as this path refers to the current + // directory on drive C:, which is not what we want to test for existence anyway. path::iterator itr(p.begin()); path head; - for (; itr != p_end; ++itr) + if (p.has_root_name()) { - path const& p_elem = *itr; - if (p_elem.size() == 1u && detail::is_directory_separator(p_elem.native()[0])) + BOOST_ASSERT(itr != p_end); + head = *itr; + path_algorithms::increment_v4(itr); + } + + if (p.has_root_directory()) + { + BOOST_ASSERT(itr != p_end); + // Convert generic separator returned by the iterator for the root directory to + // the preferred separator. + head += path::preferred_separator; + path_algorithms::increment_v4(itr); + } + + if (!head.empty()) + { + file_status head_status(detail::status_impl(head, &local_ec)); + if (BOOST_UNLIKELY(head_status.type() == fs::status_error)) { - // Convert generic separator returned by the iterator for the root directory to - // the preferred separator. - head += path::preferred_separator; + if (!ec) + BOOST_FILESYSTEM_THROW(filesystem_error("boost::filesystem::weakly_canonical", head, local_ec)); + + *ec = local_ec; + return path(); } - else + + if (head_status.type() == fs::file_not_found) { - head /= p_elem; + // If the root path does not exist then no path element exists + 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; 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 (path_algorithms::compare_v4(p_elem, dot_p) == 0) + continue; + + if (path_algorithms::compare_v4(p_elem, dot_dot_p) == 0) + { + if (head.has_relative_path()) + head.remove_filename_and_trailing_separators(); - file_status head_status = detail::status_impl(head, &local_ec); + continue; + } + + 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)) { if (!ec) @@ -4487,38 +4564,27 @@ path weakly_canonical(path const& p, path const& base, system::error_code* ec) if (head_status.type() == fs::file_not_found) { - head.remove_filename(); + head.remove_filename_and_trailing_separators(); break; } } + if (head.empty()) + return path_algorithms::lexically_normal_v4(p); + #endif - path const& dot_p = dot_path(); - path const& dot_dot_p = dot_dot_path(); 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; -#if defined(BOOST_WINDOWS_API) - if (tail_elem.size() == 1u && detail::is_directory_separator(tail_elem.native()[0])) - { - // Convert generic separator returned by the iterator for the root directory to - // the preferred separator. - tail += path::preferred_separator; - continue; - } -#endif - 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; } - if (head.empty()) - return p.lexically_normal(); - head = detail::canonical(head, base, &local_ec); if (BOOST_UNLIKELY(!!local_ec)) { @@ -4531,11 +4597,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/contrib/restricted/boost/filesystem/src/path.cpp b/contrib/restricted/boost/filesystem/src/path.cpp index 27b32e57c5..1d9748cac7 100644 --- a/contrib/restricted/boost/filesystem/src/path.cpp +++ b/contrib/restricted/boost/filesystem/src/path.cpp @@ -1,7 +1,7 @@ // filesystem path.cpp ------------------------------------------------------------- // // Copyright Beman Dawes 2008 -// Copyright Andrey Semashev 2021 +// Copyright Andrey Semashev 2021-2023 // Distributed under the Boost Software License, Version 1.0. // See http://www.boost.org/LICENSE_1_0.txt @@ -12,7 +12,7 @@ #include <boost/filesystem/config.hpp> #include <boost/filesystem/path.hpp> -#include <boost/filesystem/path_traits.hpp> // codecvt_error_category() +#include <boost/filesystem/detail/path_traits.hpp> // codecvt_error_category() #include <boost/scoped_array.hpp> #include <boost/system/error_category.hpp> // for BOOST_SYSTEM_HAS_CONSTEXPR #include <boost/assert.hpp> @@ -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,110 +150,247 @@ inline void first_element(string_type const& src, size_type& element_pos, size_t namespace boost { namespace filesystem { +namespace detail { -BOOST_FILESYSTEM_DECL void path::append_v3(path const& p) +// 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) { - if (!p.empty()) + for (; it1 != it1end && it2 != it2end && path_algorithms::compare_v4(*it1, *it2) == 0;) { - if (BOOST_LIKELY(this != &p)) - { - if (!detail::is_directory_separator(*p.m_pathname.begin())) - append_separator_if_needed(); - m_pathname += p.m_pathname; - } - else - { - // self-append - path rhs(p); - append_v3(rhs); - } + path_algorithms::increment_v4(it1); + path_algorithms::increment_v4(it2); } + return std::make_pair(it1, it2); } -BOOST_FILESYSTEM_DECL void path::append_v3(const value_type* begin, const value_type* end) +// normal --------------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v3(path const& p) { - if (begin != end) + 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); + +#if defined(BOOST_WINDOWS_API) + for (size_type i = 0; i < root_name_size; ++i) + { + if (normal.m_pathname[i] == path::separator) + normal.m_pathname[i] = path::preferred_separator; + } +#endif + + size_type root_path_size = root_name_size; + if (root_dir_pos < pathname_size) { - if (BOOST_LIKELY(begin < m_pathname.data() || begin >= (m_pathname.data() + m_pathname.size()))) + root_path_size = root_dir_pos + 1; + normal.m_pathname.push_back(path::preferred_separator); + } + + size_type i = root_path_size; + + // Skip redundant directory separators after the root directory + while (i < pathname_size && detail::is_directory_separator(pathname[i])) + ++i; + + if (i < pathname_size) + { + bool last_element_was_dot = false; + while (true) { - if (!detail::is_directory_separator(*begin)) - append_separator_if_needed(); - m_pathname.append(begin, end); + { + const size_type start_pos = i; + + // Find next separator + i += find_separator(pathname + i, pathname_size - i); + + const size_type size = i - start_pos; + + // Skip dot elements + if (size == 1u && pathname[start_pos] == path::dot) + { + last_element_was_dot = true; + goto skip_append; + } + + last_element_was_dot = false; + + // Process dot dot elements + 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] != path::dot || normal.m_pathname[pos + 1] != path::dot) + { + if (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1])) + --pos; + normal.m_pathname.erase(normal.m_pathname.begin() + pos , normal.m_pathname.end()); + goto skip_append; + } + } + + // Append the element + path_algorithms::append_separator_if_needed(normal); + normal.m_pathname.append(pathname + start_pos, size); + } + + skip_append: + if (i == pathname_size) + break; + + // Skip directory separators, including duplicates + while (i < pathname_size && detail::is_directory_separator(pathname[i])) + ++i; + + if (i == pathname_size) + { + // If a path ends with a separator, add a trailing dot element + goto append_trailing_dot; + } } - else + + if (normal.empty() || last_element_was_dot) { - // overlapping source - path rhs(begin, end); - append_v3(rhs); + append_trailing_dot: + path_algorithms::append_separator_if_needed(normal); + normal.m_pathname.push_back(path::dot); } } + + return normal; } -BOOST_FILESYSTEM_DECL void path::append_v4(path const& p) +BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v4(path const& p) { - if (!p.empty()) - { - if (BOOST_LIKELY(this != &p)) - { - const size_type that_size = p.m_pathname.size(); - size_type that_root_name_size = 0; - size_type that_root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), that_size, that_root_name_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); - // if (p.is_absolute()) - if - ( -#if defined(BOOST_WINDOWS_API) && !defined(UNDER_CE) - that_root_name_size > 0 && +#if defined(BOOST_WINDOWS_API) + for (size_type i = 0; i < root_name_size; ++i) + { + if (normal.m_pathname[i] == path::separator) + normal.m_pathname[i] = path::preferred_separator; + } #endif - that_root_dir_pos < that_size - ) + + size_type root_path_size = root_name_size; + if (root_dir_pos < pathname_size) + { + root_path_size = root_dir_pos + 1; + normal.m_pathname.push_back(path::preferred_separator); + } + + size_type i = root_path_size; + + // Skip redundant directory separators after the root directory + while (i < pathname_size && detail::is_directory_separator(pathname[i])) + ++i; + + if (i < pathname_size) + { + while (true) + { + bool last_element_was_dot = false; { - return_assign: - assign(p); - return; - } + const size_type start_pos = i; - size_type this_root_name_size = 0; - find_root_directory_start(m_pathname.c_str(), m_pathname.size(), this_root_name_size); + // Find next separator + i += find_separator(pathname + i, pathname_size - i); - if - ( - that_root_name_size > 0 && - (that_root_name_size != this_root_name_size || std::memcmp(m_pathname.c_str(), p.m_pathname.c_str(), this_root_name_size * sizeof(value_type)) != 0) - ) + const size_type size = i - start_pos; + + // Skip dot elements + 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] == 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] != path::dot || normal.m_pathname[pos + 1] != path::dot) + { + if (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1])) + --pos; + normal.m_pathname.erase(normal.m_pathname.begin() + pos, normal.m_pathname.end()); + goto skip_append; + } + } + + // Append the element + path_algorithms::append_separator_if_needed(normal); + normal.m_pathname.append(pathname + start_pos, size); + } + + skip_append: + if (i == pathname_size) { - goto return_assign; + // 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()) + path_algorithms::append_separator_if_needed(normal); + + break; } - if (that_root_dir_pos < that_size) + // Skip directory separators, including duplicates + while (i < pathname_size && detail::is_directory_separator(pathname[i])) + ++i; + + if (i == pathname_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()); + // If a path ends with a separator, add a trailing separator + if (!normal.empty() && !normal.filename_is_dot_dot()) + path_algorithms::append_separator_if_needed(normal); + break; } + } - const value_type* const that_path = p.m_pathname.c_str() + that_root_name_size; - if (!detail::is_directory_separator(*that_path)) - append_separator_if_needed(); - m_pathname.append(that_path, that_size - that_root_name_size); + // 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(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 { - // self-append - path rhs(p); - append_v4(rhs); + // overlapping source + string_type rhs(begin, end); + path_algorithms::append_v3(p, rhs.data(), rhs.data() + rhs.size()); } } - else if (has_filename_v4()) - { - m_pathname.push_back(preferred_separator); - } } -BOOST_FILESYSTEM_DECL void path::append_v4(const value_type* begin, const value_type* end) +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 < m_pathname.data() || begin >= (m_pathname.data() + m_pathname.size()))) + 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; @@ -270,17 +406,17 @@ BOOST_FILESYSTEM_DECL void path::append_v4(const value_type* begin, const value_ ) { return_assign: - assign(begin, end); + p.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); + 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(m_pathname.c_str(), begin, this_root_name_size * sizeof(value_type)) != 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; @@ -289,197 +425,219 @@ BOOST_FILESYSTEM_DECL void path::append_v4(const value_type* begin, const value_ 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()); + 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)) - append_separator_if_needed(); - m_pathname.append(that_path, end); + path_algorithms::append_separator_if_needed(p); + p.m_pathname.append(that_path, end); } else { // overlapping source - path rhs(begin, end); - append_v4(rhs); + string_type rhs(begin, end); + path_algorithms::append_v4(p, rhs.data(), rhs.data() + rhs.size()); } } - else if (has_filename_v4()) + else if (path_algorithms::has_filename_v4(p)) { - m_pathname.push_back(preferred_separator); + p.m_pathname.push_back(path::preferred_separator); } } -#ifdef BOOST_WINDOWS_API +// compare -------------------------------------------------------------------------// -BOOST_FILESYSTEM_DECL path path::generic_path() const +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 +) { - path tmp(*this); - std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/'); - return tmp; + 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; } -#endif // BOOST_WINDOWS_API +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::compare_v3(path const& p) const BOOST_NOEXCEPT +BOOST_FILESYSTEM_DECL int path_algorithms::compare_v3(path const& left, path const& right) { - return detail::lex_compare_v3(begin(), end(), p.begin(), p.end()); + return path_algorithms::lex_compare_v3(left.begin(), left.end(), right.begin(), right.end()); } -BOOST_FILESYSTEM_DECL int path::compare_v4(path const& p) const BOOST_NOEXCEPT +BOOST_FILESYSTEM_DECL int path_algorithms::compare_v4(path const& left, path const& right) { - return detail::lex_compare_v4(begin(), end(), p.begin(), p.end()); + return path_algorithms::lex_compare_v4(left.begin(), left.end(), right.begin(), right.end()); } -// append_separator_if_needed ----------------------------------------------------// +// append_separator_if_needed ------------------------------------------------------// -BOOST_FILESYSTEM_DECL path::string_type::size_type path::append_separator_if_needed() +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::append_separator_if_needed(path& p) { - if (!m_pathname.empty() && + if (!p.m_pathname.empty() && #ifdef BOOST_WINDOWS_API - *(m_pathname.end() - 1) != colon && + *(p.m_pathname.end() - 1) != colon && #endif - !detail::is_directory_separator(*(m_pathname.end() - 1))) + !detail::is_directory_separator(*(p.m_pathname.end() - 1))) { - string_type::size_type tmp(m_pathname.size()); - m_pathname += preferred_separator; + string_type::size_type tmp(p.m_pathname.size()); + p.m_pathname.push_back(path::preferred_separator); return tmp; } return 0; } -// erase_redundant_separator -----------------------------------------------------// +// erase_redundant_separator -------------------------------------------------------// -BOOST_FILESYSTEM_DECL void path::erase_redundant_separator(string_type::size_type sep_pos) +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 < m_pathname.size() // and something was appended - && (m_pathname[sep_pos + 1] == separator // and it was also separator + 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 - || m_pathname[sep_pos + 1] == preferred_separator // or preferred_separator + || p.m_pathname[sep_pos + 1] == path::preferred_separator // or preferred_separator #endif )) { - m_pathname.erase(m_pathname.begin() + sep_pos); // erase the added separator + p.m_pathname.erase(p.m_pathname.begin() + sep_pos); // erase the added separator } } // modifiers -----------------------------------------------------------------------// -#ifdef BOOST_WINDOWS_API -BOOST_FILESYSTEM_DECL path& path::make_preferred() +BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v3(path& p) { - std::replace(m_pathname.begin(), m_pathname.end(), L'/', L'\\'); - return *this; + p.remove_filename_and_trailing_separators(); } -#endif -BOOST_FILESYSTEM_DECL path& path::remove_filename() +BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v4(path& p) { - size_type end_pos = find_parent_path_size(); - m_pathname.erase(m_pathname.begin() + end_pos, m_pathname.end()); - return *this; + 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 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 void path::replace_extension_v3(path const& new_extension) +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 = m_pathname.size() - extension_v3().m_pathname.size(); - m_pathname.erase(m_pathname.begin() + ext_pos, m_pathname.end()); + 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] != dot) - m_pathname.push_back(dot); - m_pathname.append(new_extension.m_pathname); + 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::replace_extension_v4(path const& new_extension) +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 = m_pathname.size() - find_extension_v4_size(); - m_pathname.erase(m_pathname.begin() + ext_pos, m_pathname.end()); + 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] != dot) - m_pathname.push_back(dot); - m_pathname.append(new_extension.m_pathname); + 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::find_root_name_size() const +BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_name_size(path const& p) { size_type root_name_size = 0; - find_root_directory_start(m_pathname.c_str(), m_pathname.size(), root_name_size); + 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::find_root_path_size() const +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(m_pathname.c_str(), m_pathname.size(), root_name_size); + 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 < m_pathname.size()) + if (root_dir_pos < p.m_pathname.size()) size = root_dir_pos + 1; return size; } -BOOST_FILESYSTEM_DECL substring path::find_root_directory() const +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(m_pathname.c_str(), m_pathname.size(), root_name_size); - root_dir.size = static_cast< std::size_t >(root_dir.pos < m_pathname.size()); + 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 substring path::find_relative_path() const +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(m_pathname.c_str(), m_pathname.size(), root_name_size); + 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 < m_pathname.size()) + if (root_dir_pos < p.m_pathname.size()) { size = root_dir_pos + 1; - for (size_type n = m_pathname.size(); size < n; ++size) + for (size_type n = p.m_pathname.size(); size < n; ++size) { - if (!detail::is_directory_separator(m_pathname[size])) + if (!detail::is_directory_separator(p.m_pathname[size])) break; } } substring rel_path; rel_path.pos = size; - rel_path.size = m_pathname.size() - size; + rel_path.size = p.m_pathname.size() - size; return rel_path; } -BOOST_FILESYSTEM_DECL string_type::size_type path::find_parent_path_size() const +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_parent_path_size(path const& p) { - const size_type size = m_pathname.size(); + const size_type size = p.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 root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); - size_type filename_size = find_filename_size(m_pathname, root_name_size, size); + size_type filename_size = find_filename_size(p.m_pathname, root_name_size, size); size_type end_pos = size - filename_size; while (true) { @@ -493,7 +651,7 @@ BOOST_FILESYSTEM_DECL string_type::size_type path::find_parent_path_size() const --end_pos; - if (!detail::is_directory_separator(m_pathname[end_pos])) + if (!detail::is_directory_separator(p.m_pathname[end_pos])) { ++end_pos; break; @@ -510,13 +668,13 @@ BOOST_FILESYSTEM_DECL string_type::size_type path::find_parent_path_size() const return end_pos; } -BOOST_FILESYSTEM_DECL path path::filename_v3() const +BOOST_FILESYSTEM_DECL path path_algorithms::filename_v3(path const& p) { - const size_type size = m_pathname.size(); + const size_type size = p.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 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(m_pathname[size - 1]) && is_root_separator(m_pathname, root_dir_pos, size - 1)) + 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; @@ -530,77 +688,77 @@ BOOST_FILESYSTEM_DECL path path::filename_v3() const } else { - filename_size = find_filename_size(m_pathname, root_name_size, size); + 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(m_pathname[pos - 1]) && !is_root_separator(m_pathname, root_dir_pos, pos - 1)) + 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* p = m_pathname.c_str() + pos; - return path(p, p + filename_size); + const value_type* ptr = p.m_pathname.c_str() + pos; + return path(ptr, ptr + filename_size); } -BOOST_FILESYSTEM_DECL string_type::size_type path::find_filename_v4_size() const +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_filename_v4_size(path const& p) { - const size_type size = m_pathname.size(); + const size_type size = p.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); + 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::stem_v3() const +BOOST_FILESYSTEM_DECL path path_algorithms::stem_v3(path const& p) { - path name(filename_v3()); - if (name != detail::dot_path() && name != detail::dot_dot_path()) + 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(dot); + 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::stem_v4() const +BOOST_FILESYSTEM_DECL path path_algorithms::stem_v4(path const& p) { - path name(filename_v4()); - if (name != detail::dot_path() && name != detail::dot_dot_path()) + 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(dot); + 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::extension_v3() const +BOOST_FILESYSTEM_DECL path path_algorithms::extension_v3(path const& p) { - path name(filename_v3()); - if (name == detail::dot_path() || name == detail::dot_dot_path()) + 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(dot)); + 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 string_type::size_type path::find_extension_v4_size() const +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_extension_v4_size(path const& p) { - const size_type size = m_pathname.size(); + const size_type size = p.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); + 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 - !(m_pathname[filename_pos] == dot && - (filename_size == 1u || (filename_size == 2u && m_pathname[filename_pos + 1u] == dot))) + !(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 (m_pathname[ext_pos] == dot) + if (p.m_pathname[ext_pos] == path::dot) break; } @@ -611,21 +769,30 @@ BOOST_FILESYSTEM_DECL string_type::size_type path::find_extension_v4_size() cons return 0u; } -// lexical operations --------------------------------------------------------------// +} // namespace detail -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) +BOOST_FILESYSTEM_DECL path& path::remove_filename_and_trailing_separators() { - for (; it1 != it1end && it2 != it2end && *it1 == *it2;) - { - ++it1; - ++it2; - } - return std::make_pair(it1, it2); + 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; } -} // namespace detail + +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 { @@ -637,12 +804,12 @@ BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const return detail::dot_path(); std::ptrdiff_t n = 0; - for (; mm.second != base_e; ++mm.second) + for (; mm.second != base_e; detail::path_algorithms::increment_v4(mm.second)) { path const& p = *mm.second; - if (p == detail::dot_dot_path()) + if (detail::path_algorithms::compare_v4(p, detail::dot_dot_path()) == 0) --n; - else if (!p.empty() && p != detail::dot_path()) + else if (!p.empty() && detail::path_algorithms::compare_v4(p, detail::dot_path()) != 0) ++n; } if (n < 0) @@ -652,213 +819,29 @@ BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const path tmp; for (; n > 0; --n) - tmp /= detail::dot_dot_path(); - for (; mm.first != e; ++mm.first) - tmp /= *mm.first; + 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; } -// normal --------------------------------------------------------------------------// - -BOOST_FILESYSTEM_DECL path path::lexically_normal_v3() const -{ - const value_type* const pathname = m_pathname.c_str(); - const size_type pathname_size = 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); - #if defined(BOOST_WINDOWS_API) - for (size_type i = 0; i < root_name_size; ++i) - { - if (normal.m_pathname[i] == path::separator) - normal.m_pathname[i] = path::preferred_separator; - } -#endif - - size_type root_path_size = root_name_size; - if (root_dir_pos < pathname_size) - { - root_path_size = root_dir_pos + 1; - normal.m_pathname.push_back(preferred_separator); - } - - size_type i = root_path_size; - - // Skip redundant directory separators after the root directory - while (i < pathname_size && detail::is_directory_separator(pathname[i])) - ++i; - - if (i < pathname_size) - { - bool last_element_was_dot = false; - while (true) - { - { - const size_type start_pos = i; - - // Find next separator - i += find_separator(pathname + i, pathname_size - i); - - const size_type size = i - start_pos; - - // Skip dot elements - if (size == 1u && pathname[start_pos] == dot) - { - last_element_was_dot = true; - goto skip_append; - } - - 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) - { - // 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 (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1])) - --pos; - normal.m_pathname.erase(normal.m_pathname.begin() + pos , normal.m_pathname.end()); - goto skip_append; - } - } - - // Append the element - normal.append_separator_if_needed(); - normal.m_pathname.append(pathname + start_pos, size); - } - - skip_append: - if (i == pathname_size) - break; - - // Skip directory separators, including duplicates - while (i < pathname_size && detail::is_directory_separator(pathname[i])) - ++i; - if (i == pathname_size) - { - // If a path ends with a separator, add a trailing dot element - goto append_trailing_dot; - } - } - - if (normal.empty() || last_element_was_dot) - { - append_trailing_dot: - normal.append_separator_if_needed(); - normal.m_pathname.push_back(dot); - } - } - - return normal; +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::lexically_normal_v4() const +BOOST_FILESYSTEM_DECL path& path::make_preferred() { - const value_type* const pathname = m_pathname.c_str(); - const size_type pathname_size = 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); - -#if defined(BOOST_WINDOWS_API) - for (size_type i = 0; i < root_name_size; ++i) - { - if (normal.m_pathname[i] == path::separator) - normal.m_pathname[i] = path::preferred_separator; - } -#endif - - size_type root_path_size = root_name_size; - if (root_dir_pos < pathname_size) - { - root_path_size = root_dir_pos + 1; - normal.m_pathname.push_back(preferred_separator); - } - - size_type i = root_path_size; - - // Skip redundant directory separators after the root directory - while (i < pathname_size && detail::is_directory_separator(pathname[i])) - ++i; - - if (i < pathname_size) - { - while (true) - { - bool last_element_was_dot = false; - { - const size_type start_pos = i; - - // Find next separator - i += find_separator(pathname + i, pathname_size - i); - - const size_type size = i - start_pos; - - // Skip dot elements - if (size == 1u && pathname[start_pos] == 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) - { - // 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 (pos > root_path_size && detail::is_directory_separator(normal.m_pathname[pos - 1])) - --pos; - normal.m_pathname.erase(normal.m_pathname.begin() + pos, normal.m_pathname.end()); - goto skip_append; - } - } - - // Append the element - normal.append_separator_if_needed(); - normal.m_pathname.append(pathname + start_pos, size); - } - - skip_append: - if (i == pathname_size) - { - // 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(); - - break; - } - - // Skip directory separators, including duplicates - while (i < pathname_size && detail::is_directory_separator(pathname[i])) - ++i; - - if (i == pathname_size) - { - // If a path ends with a separator, add a trailing separator - if (!normal.empty() && !normal.filename_is_dot_dot()) - normal.append_separator_if_needed(); - 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); - } - - return normal; + std::replace(m_pathname.begin(), m_pathname.end(), L'/', L'\\'); + return *this; } +#endif // defined(BOOST_WINDOWS_API) + } // namespace filesystem } // namespace boost @@ -1001,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. @@ -1043,225 +1032,153 @@ 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) -{ - 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()); - first1.increment_v3(); - first2.increment_v3(); - } - if (first1 == last1 && first2 == last2) - return 0; - return first1 == last1 ? -1 : 1; -} - -BOOST_FILESYSTEM_DECL -int lex_compare_v4(path::iterator first1, path::iterator last1, path::iterator first2, path::iterator 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()); - ++first1; - ++first2; - } - if (first1 == last1 && first2 == last2) - return 0; - return first1 == last1 ? -1 : 1; -} - -} // namespace detail - -//--------------------------------------------------------------------------------------// -// // -// class path::iterator implementation // -// // -//--------------------------------------------------------------------------------------// - -BOOST_FILESYSTEM_DECL path::iterator path::begin() const +BOOST_FILESYSTEM_DECL void path_algorithms::increment_v3(path_detail::path_iterator& it) { - iterator itr; - itr.m_path_ptr = this; - - size_type element_size; - first_element(m_pathname, itr.m_pos, element_size); - - if (element_size > 0) - { - 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; -#endif - } - - return itr; -} - -BOOST_FILESYSTEM_DECL path::iterator path::end() const -{ - iterator itr; - itr.m_path_ptr = this; - itr.m_pos = m_pathname.size(); - 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()"); + 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 - m_pos += m_element.m_pathname.size(); + it.m_pos += it.m_element.m_pathname.size(); // if the end is reached, we are done - if (m_pos >= size) + if (it.m_pos >= size) { - BOOST_ASSERT_MSG(m_pos == size, "path::iterator increment after the referenced path was modified"); - m_element.clear(); // aids debugging + 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(m_path_ptr->m_pathname[m_pos])) + 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(m_path_ptr->m_pathname.c_str(), size, root_name_size); + 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 (m_pos == root_dir_pos && m_element.m_pathname.size() == root_name_size) + if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size) { - m_element.m_pathname = separator; // generic format; see docs + 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 (m_pos != size && detail::is_directory_separator(m_path_ptr->m_pathname[m_pos])) + while (it.m_pos != size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) { - ++m_pos; + ++it.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)) + if (it.m_pos == size && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) { - --m_pos; - m_element = detail::dot_path(); + --it.m_pos; + it.m_element = detail::dot_path(); return; } } // get m_element - size_type end_pos = m_path_ptr->m_pathname.find_first_of(separators, m_pos); + 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 = m_path_ptr->m_pathname.c_str(); - m_element.m_pathname.assign(p + m_pos, p + end_pos); + 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::iterator::increment_v4() +BOOST_FILESYSTEM_DECL void path_algorithms::increment_v4(path_detail::path_iterator& it) { - const size_type size = m_path_ptr->m_pathname.size(); - BOOST_ASSERT_MSG(m_pos <= size, "path::iterator increment past end()"); + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.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])) + 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])) { // The iterator was pointing to the last empty element of the path; set to end. - m_pos = size; + it.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(); + it.m_pos += it.m_element.m_pathname.size(); // if the end is reached, we are done - if (m_pos >= size) + if (it.m_pos >= size) { - BOOST_ASSERT_MSG(m_pos == size, "path::iterator increment after the referenced path was modified"); - m_element.clear(); // aids debugging + 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(m_path_ptr->m_pathname[m_pos])) + 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(m_path_ptr->m_pathname.c_str(), size, root_name_size); + 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 (m_pos == root_dir_pos && m_element.m_pathname.size() == root_name_size) + if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size) { - m_element.m_pathname = separator; // generic format; see docs + 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 (m_pos != size && detail::is_directory_separator(m_path_ptr->m_pathname[m_pos])) + while (it.m_pos != size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) { - ++m_pos; + ++it.m_pos; } // detect trailing separator - if (m_pos == size && - !is_root_separator(m_path_ptr->m_pathname, root_dir_pos, m_pos - 1)) + if (it.m_pos == size && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) { - --m_pos; - m_element.m_pathname.clear(); + --it.m_pos; + it.m_element.m_pathname.clear(); return; } } // get m_element - size_type end_pos = m_path_ptr->m_pathname.find_first_of(separators, m_pos); + 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 = m_path_ptr->m_pathname.c_str(); - m_element.m_pathname.assign(p + m_pos, p + end_pos); + 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::iterator::decrement_v3() +BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v3(path_detail::path_iterator& it) { - 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"); + 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(m_path_ptr->m_pathname.c_str(), size, root_name_size); + 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 && m_pos == root_dir_pos) + if (root_dir_pos < size && it.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); + 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 (m_pos == size && + if (it.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)) + 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)) { - --m_pos; - m_element = detail::dot_path(); + --it.m_pos; + it.m_element = detail::dot_path(); return; } // skip separators unless root directory - size_type end_pos = m_pos; + size_type end_pos = it.m_pos; while (end_pos > root_name_size) { --end_pos; @@ -1269,12 +1186,12 @@ BOOST_FILESYSTEM_DECL void path::iterator::decrement_v3() if (end_pos == root_dir_pos) { // Decremented to the root directory - m_pos = end_pos; - m_element.m_pathname = separator; // generic format; see docs + it.m_pos = end_pos; + it.m_element.m_pathname = path::separator; // generic format; see docs return; } - if (!detail::is_directory_separator(m_path_ptr->m_pathname[end_pos])) + if (!detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos])) { ++end_pos; break; @@ -1284,44 +1201,44 @@ BOOST_FILESYSTEM_DECL void path::iterator::decrement_v3() 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); + 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::iterator::decrement_v4() +BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v4(path_detail::path_iterator& it) { - 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"); + 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(m_path_ptr->m_pathname.c_str(), size, root_name_size); + 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 && m_pos == root_dir_pos) + if (root_dir_pos < size && it.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); + 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 (m_pos == size && + if (it.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)) + 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)) { - --m_pos; - m_element.m_pathname.clear(); + --it.m_pos; + it.m_element.m_pathname.clear(); return; } // skip separators unless root directory - size_type end_pos = m_pos; + size_type end_pos = it.m_pos; while (end_pos > root_name_size) { --end_pos; @@ -1329,12 +1246,12 @@ BOOST_FILESYSTEM_DECL void path::iterator::decrement_v4() if (end_pos == root_dir_pos) { // Decremented to the root directory - m_pos = end_pos; - m_element.m_pathname = separator; // generic format; see docs + it.m_pos = end_pos; + it.m_element.m_pathname = path::separator; // generic format; see docs return; } - if (!detail::is_directory_separator(m_path_ptr->m_pathname[end_pos])) + if (!detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos])) { ++end_pos; break; @@ -1344,10 +1261,42 @@ BOOST_FILESYSTEM_DECL void path::iterator::decrement_v4() 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); + 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 + +// path iterators ------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL path::iterator path::begin() const +{ + iterator itr; + itr.m_path_ptr = this; + + size_type element_size; + first_element(m_pathname, itr.m_pos, element_size); + + if (element_size > 0) + { + 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] == path::preferred_separator) + itr.m_element.m_pathname[0] = path::separator; +#endif + } + + return itr; +} + +BOOST_FILESYSTEM_DECL path::iterator path::end() const +{ + iterator itr; + itr.m_path_ptr = this; + itr.m_pos = m_pathname.size(); + return itr; } } // namespace filesystem diff --git a/contrib/restricted/boost/filesystem/src/path_traits.cpp b/contrib/restricted/boost/filesystem/src/path_traits.cpp index aa4b8815f7..baed387b4d 100644 --- a/contrib/restricted/boost/filesystem/src/path_traits.cpp +++ b/contrib/restricted/boost/filesystem/src/path_traits.cpp @@ -12,19 +12,19 @@ #include "platform_config.hpp" #include <boost/filesystem/config.hpp> -#include <boost/filesystem/path_traits.hpp> +#include <boost/filesystem/detail/path_traits.hpp> +#include <boost/filesystem/path.hpp> #include <boost/system/system_error.hpp> #include <boost/smart_ptr/scoped_array.hpp> #include <boost/assert.hpp> #include <string> #include <locale> // for codecvt_base::result -#include <cstring> // for strlen -#include <cwchar> // for wcslen +#include <cwchar> // for mbstate_t #include <cstddef> #include <boost/filesystem/detail/header.hpp> // must be the last #include -namespace pt = boost::filesystem::path_traits; +namespace pt = boost::filesystem::detail::path_traits; namespace fs = boost::filesystem; namespace bs = boost::system; @@ -51,12 +51,7 @@ BOOST_CONSTEXPR_OR_CONST std::size_t default_codecvt_buf_size = BOOST_FILESYSTEM // convert_aux const char* to wstring // //--------------------------------------------------------------------------------------// -void convert_aux( - const char* from, - const char* from_end, - wchar_t* to, wchar_t* to_end, - std::wstring& target, - pt::codecvt_type const& cvt) +void convert_aux(const char* from, const char* from_end, wchar_t* to, wchar_t* to_end, std::wstring& target, pt::codecvt_type const& cvt) { //std::cout << std::hex // << " from=" << std::size_t(from) @@ -83,12 +78,7 @@ void convert_aux( // convert_aux const wchar_t* to string // //--------------------------------------------------------------------------------------// -void convert_aux( - const wchar_t* from, - const wchar_t* from_end, - char* to, char* to_end, - std::string& target, - pt::codecvt_type const& cvt) +void convert_aux(const wchar_t* from, const wchar_t* from_end, char* to, char* to_end, std::string& target, pt::codecvt_type const& cvt) { //std::cout << std::hex // << " from=" << std::size_t(from) @@ -119,6 +109,7 @@ void convert_aux( namespace boost { namespace filesystem { +namespace detail { namespace path_traits { //--------------------------------------------------------------------------------------// @@ -126,32 +117,29 @@ namespace path_traits { //--------------------------------------------------------------------------------------// BOOST_FILESYSTEM_DECL -void convert(const char* from, - const char* from_end, // 0 for null terminated MBCS - std::wstring& to, codecvt_type const& cvt) +void convert(const char* from, const char* from_end, std::wstring& to, const codecvt_type* cvt) { - BOOST_ASSERT(from); - - if (!from_end) // null terminated - { - from_end = from + std::strlen(from); - } - if (from == from_end) return; + BOOST_ASSERT(from != NULL); + BOOST_ASSERT(from_end != NULL); + + if (!cvt) + cvt = &fs::path::codecvt(); + std::size_t buf_size = (from_end - from) * 3; // perhaps too large, but that's OK // dynamically allocate a buffer only if source is unusually large if (buf_size > default_codecvt_buf_size) { boost::scoped_array< wchar_t > buf(new wchar_t[buf_size]); - convert_aux(from, from_end, buf.get(), buf.get() + buf_size, to, cvt); + convert_aux(from, from_end, buf.get(), buf.get() + buf_size, to, *cvt); } else { wchar_t buf[default_codecvt_buf_size]; - convert_aux(from, from_end, buf, buf + default_codecvt_buf_size, to, cvt); + convert_aux(from, from_end, buf, buf + default_codecvt_buf_size, to, *cvt); } } @@ -160,20 +148,17 @@ void convert(const char* from, //--------------------------------------------------------------------------------------// BOOST_FILESYSTEM_DECL -void convert(const wchar_t* from, - const wchar_t* from_end, // 0 for null terminated MBCS - std::string& to, codecvt_type const& cvt) +void convert(const wchar_t* from, const wchar_t* from_end, std::string& to, const codecvt_type* cvt) { - BOOST_ASSERT(from); - - if (!from_end) // null terminated - { - from_end = from + std::wcslen(from); - } - if (from == from_end) return; + BOOST_ASSERT(from != NULL); + BOOST_ASSERT(from_end != NULL); + + if (!cvt) + cvt = &fs::path::codecvt(); + // The codecvt length functions may not be implemented, and I don't really // understand them either. Thus this code is just a guess; if it turns // out the buffer is too small then an error will be reported and the code @@ -185,16 +170,17 @@ void convert(const wchar_t* from, if (buf_size > default_codecvt_buf_size) { boost::scoped_array< char > buf(new char[buf_size]); - convert_aux(from, from_end, buf.get(), buf.get() + buf_size, to, cvt); + convert_aux(from, from_end, buf.get(), buf.get() + buf_size, to, *cvt); } else { char buf[default_codecvt_buf_size]; - convert_aux(from, from_end, buf, buf + default_codecvt_buf_size, to, cvt); + convert_aux(from, from_end, buf, buf + default_codecvt_buf_size, to, *cvt); } } } // namespace path_traits +} // namespace detail } // namespace filesystem } // namespace boost diff --git a/contrib/restricted/boost/filesystem/src/windows_tools.hpp b/contrib/restricted/boost/filesystem/src/windows_tools.hpp index 8a2de7f0c9..ca62bfbf7e 100644 --- a/contrib/restricted/boost/filesystem/src/windows_tools.hpp +++ b/contrib/restricted/boost/filesystem/src/windows_tools.hpp @@ -1,7 +1,8 @@ // windows_tools.hpp -----------------------------------------------------------------// -// Copyright 2002-2009, 2014 Beman Dawes // Copyright 2001 Dietmar Kuehl +// Copyright 2002-2009, 2014 Beman Dawes +// Copyright 2021-2022 Andrey Semashev // Distributed under the Boost Software License, Version 1.0. // See http://www.boost.org/LICENSE_1_0.txt @@ -23,6 +24,10 @@ #include <boost/filesystem/detail/header.hpp> // must be the last #include +#ifndef IO_REPARSE_TAG_DEDUP +#define IO_REPARSE_TAG_DEDUP (0x80000013L) +#endif + #ifndef IO_REPARSE_TAG_MOUNT_POINT #define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L) #endif @@ -57,14 +62,14 @@ 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; return prms; } -bool is_reparse_point_a_symlink_ioctl(HANDLE h); +ULONG get_reparse_point_tag_ioctl(HANDLE h, boost::filesystem::path const& p, boost::system::error_code* ec); inline bool is_reparse_point_tag_a_symlink(ULONG reparse_point_tag) { @@ -156,6 +161,9 @@ struct object_attributes #ifndef FILE_DIRECTORY_FILE #define FILE_DIRECTORY_FILE 0x00000001 #endif +#ifndef FILE_SYNCHRONOUS_IO_NONALERT +#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020 +#endif #ifndef FILE_OPEN_FOR_BACKUP_INTENT #define FILE_OPEN_FOR_BACKUP_INTENT 0x00004000 #endif |