diff options
author | snaury <snaury@yandex-team.com> | 2024-10-16 12:16:48 +0300 |
---|---|---|
committer | snaury <snaury@yandex-team.com> | 2024-10-16 12:32:13 +0300 |
commit | e0fb25470a47f0c243091ed28bf54a186f732f6a (patch) | |
tree | e85dfe628401f4f21749ab95b9d711242e3b49cd /contrib/restricted/boost/filesystem/src/path.cpp | |
parent | b3b4a0b9681eb0981f9958a426c95a53f79169a7 (diff) | |
download | ydb-e0fb25470a47f0c243091ed28bf54a186f732f6a.tar.gz |
ydblib: add jinja2cpp
commit_hash:f3563041f6f6f7443e75fc99acd2c967d0debb04
Diffstat (limited to 'contrib/restricted/boost/filesystem/src/path.cpp')
-rw-r--r-- | contrib/restricted/boost/filesystem/src/path.cpp | 1719 |
1 files changed, 1719 insertions, 0 deletions
diff --git a/contrib/restricted/boost/filesystem/src/path.cpp b/contrib/restricted/boost/filesystem/src/path.cpp new file mode 100644 index 0000000000..82b2c7d520 --- /dev/null +++ b/contrib/restricted/boost/filesystem/src/path.cpp @@ -0,0 +1,1719 @@ +// filesystem path.cpp ------------------------------------------------------------- // + +// Copyright Beman Dawes 2008 +// Copyright Andrey Semashev 2021-2024 + +// Distributed under the Boost Software License, Version 1.0. +// See http://www.boost.org/LICENSE_1_0.txt + +// Library home page: http://www.boost.org/libs/filesystem + +#include "platform_config.hpp" + +#include <boost/filesystem/config.hpp> +#include <boost/filesystem/path.hpp> +#include <boost/filesystem/detail/path_traits.hpp> // codecvt_error_category() +#include <boost/system/error_category.hpp> // for BOOST_SYSTEM_HAS_CONSTEXPR +#include <boost/assert.hpp> +#include <algorithm> +#include <iterator> +#include <utility> +#include <string> +#include <cstddef> +#include <cstring> +#include <cstdlib> // std::atexit + +#ifdef BOOST_WINDOWS_API +#include "windows_file_codecvt.hpp" +#include "windows_tools.hpp" +#include <windows.h> +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) +#include <boost/filesystem/detail/utf8_codecvt_facet.hpp> +#endif + +#ifdef BOOST_FILESYSTEM_DEBUG +#include <iostream> +#include <iomanip> +#endif + +#include "atomic_tools.hpp" +#include "private_config.hpp" + +#include <boost/filesystem/detail/header.hpp> // must be the last #include + +namespace fs = boost::filesystem; + +using boost::filesystem::path; + +//--------------------------------------------------------------------------------------// +// // +// class path helpers // +// // +//--------------------------------------------------------------------------------------// + +namespace { +//------------------------------------------------------------------------------------// +// miscellaneous class path helpers // +//------------------------------------------------------------------------------------// + +typedef path::value_type value_type; +typedef path::string_type string_type; +typedef string_type::size_type size_type; + +#ifdef BOOST_WINDOWS_API + +const wchar_t dot_path_literal[] = L"."; +const wchar_t dot_dot_path_literal[] = L".."; +const wchar_t separators[] = L"/\\"; +using boost::filesystem::detail::colon; +using boost::filesystem::detail::questionmark; + +inline bool is_alnum(wchar_t c) +{ + return boost::filesystem::detail::is_letter(c) || (c >= L'0' && c <= L'9'); +} + +inline bool is_device_name_char(wchar_t c) +{ + // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html + // Device names are: + // + // - PRN + // - AUX + // - NUL + // - CON + // - LPT[1-9] + // - COM[1-9] + // - CONIN$ + // - CONOUT$ + return is_alnum(c) || c == L'$'; +} + +//! Returns position of the first directory separator in the \a size initial characters of \a p, or \a size if not found +inline size_type find_separator(const wchar_t* p, size_type size) noexcept +{ + size_type pos = 0u; + for (; pos < size; ++pos) + { + const wchar_t c = p[pos]; + if (boost::filesystem::detail::is_directory_separator(c)) + break; + } + return pos; +} + +#else // BOOST_WINDOWS_API + +const char dot_path_literal[] = "."; +const char dot_dot_path_literal[] = ".."; +const char separators[] = "/"; + +//! Returns position of the first directory separator in the \a size initial characters of \a p, or \a size if not found +inline size_type find_separator(const char* p, size_type size) noexcept +{ + const char* sep = static_cast< const char* >(std::memchr(p, '/', size)); + size_type pos = size; + if (BOOST_LIKELY(!!sep)) + pos = sep - p; + return pos; +} + +#endif // BOOST_WINDOWS_API + +// pos is position of the separator +bool is_root_separator(string_type const& str, size_type root_dir_pos, size_type pos); + +// Returns: Size of the filename element that ends at end_pos (which is past-the-end position). 0 if no filename found. +size_type find_filename_size(string_type const& str, size_type root_name_size, size_type end_pos); + +// Returns: starting position of root directory or size if not found. Sets root_name_size to length +// of the root name if the characters before the returned position (if any) are considered a root name. +size_type find_root_directory_start(const value_type* path, size_type size, size_type& root_name_size); + +// Finds position and size of the first element of the path +void first_element(string_type const& src, size_type& element_pos, size_type& element_size, size_type size); + +// Finds position and size of the first element of the path +inline void first_element(string_type const& src, size_type& element_pos, size_type& element_size) +{ + first_element(src, element_pos, element_size, src.size()); +} + +} // unnamed namespace + +//--------------------------------------------------------------------------------------// +// // +// class path implementation // +// // +//--------------------------------------------------------------------------------------// + +namespace boost { +namespace filesystem { +namespace detail { + +// C++14 provides a mismatch algorithm with four iterator arguments(), but earlier +// standard libraries didn't, so provide this needed functionality. +inline std::pair< path::iterator, path::iterator > mismatch(path::iterator it1, path::iterator it1end, path::iterator it2, path::iterator it2end) +{ + for (; it1 != it1end && it2 != it2end && path_algorithms::compare_v4(*it1, *it2) == 0;) + { + path_algorithms::increment_v4(it1); + path_algorithms::increment_v4(it2); + } + return std::make_pair(it1, it2); +} + +// normal --------------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v3(path const& p) +{ + 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) + { + 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) + { + { + 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; + } + } + + if (normal.empty() || last_element_was_dot) + { + append_trailing_dot: + path_algorithms::append_separator_if_needed(normal); + normal.m_pathname.push_back(path::dot); + } + } + + return normal; +} + +BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v4(path const& p) +{ + 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); + + 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; + { + 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; + } + + // 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) + { + // 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; + } + + // 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()) + path_algorithms::append_separator_if_needed(normal); + break; + } + } + + // If the original path was not empty and normalized ended up being empty, make it a dot + if (normal.empty()) + normal.m_pathname.push_back(path::dot); + } + + return normal; +} + +// generic_path ---------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL path path_algorithms::generic_path_v3(path const& p) +{ + path tmp; + const size_type pathname_size = p.m_pathname.size(); + tmp.m_pathname.reserve(pathname_size); + + const value_type* const pathname = p.m_pathname.c_str(); + + // Don't remove duplicate slashes from the root name + size_type root_name_size = 0u; + size_type root_dir_pos = find_root_directory_start(pathname, pathname_size, root_name_size); + if (root_name_size > 0u) + { + tmp.m_pathname.append(pathname, root_name_size); +#if defined(BOOST_WINDOWS_API) + std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/'); +#endif + } + + size_type pos = root_name_size; + if (root_dir_pos < pathname_size) + { + tmp.m_pathname.push_back(path::separator); + pos = root_dir_pos + 1u; + } + + while (pos < pathname_size) + { + size_type element_size = find_separator(pathname + pos, pathname_size - pos); + if (element_size > 0u) + { + tmp.m_pathname.append(pathname + pos, element_size); + + pos += element_size; + if (pos >= pathname_size) + break; + + tmp.m_pathname.push_back(path::separator); + } + + ++pos; + } + + return tmp; +} + +BOOST_FILESYSTEM_DECL path path_algorithms::generic_path_v4(path const& p) +{ + path tmp; + const size_type pathname_size = p.m_pathname.size(); + tmp.m_pathname.reserve(pathname_size); + + const value_type* const pathname = p.m_pathname.c_str(); + + // Treat root name specially as it may contain backslashes, duplicate ones too, + // in case of UNC paths and Windows-specific prefixes. + size_type root_name_size = 0u; + size_type root_dir_pos = find_root_directory_start(pathname, pathname_size, root_name_size); + if (root_name_size > 0u) + tmp.m_pathname.append(pathname, root_name_size); + + size_type pos = root_name_size; + if (root_dir_pos < pathname_size) + { + tmp.m_pathname.push_back(path::separator); + pos = root_dir_pos + 1u; + } + + while (pos < pathname_size) + { + size_type element_size = find_separator(pathname + pos, pathname_size - pos); + if (element_size > 0u) + { + tmp.m_pathname.append(pathname + pos, element_size); + + pos += element_size; + if (pos >= pathname_size) + break; + + tmp.m_pathname.push_back(path::separator); + } + + ++pos; + } + + return tmp; +} + +#if defined(BOOST_WINDOWS_API) + +// make_preferred -------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL void path_algorithms::make_preferred_v3(path& p) +{ + std::replace(p.m_pathname.begin(), p.m_pathname.end(), L'/', L'\\'); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::make_preferred_v4(path& p) +{ + const size_type pathname_size = p.m_pathname.size(); + if (pathname_size > 0u) + { + value_type* const pathname = &p.m_pathname[0]; + + // Avoid converting slashes in the root name + size_type root_name_size = 0u; + find_root_directory_start(pathname, pathname_size, root_name_size); + + std::replace(pathname + root_name_size, pathname + pathname_size, L'/', L'\\'); + } +} + +#endif // defined(BOOST_WINDOWS_API) + +// append --------------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL void path_algorithms::append_v3(path& p, const value_type* begin, const value_type* end) +{ + if (begin != end) + { + if (BOOST_LIKELY(begin < p.m_pathname.data() || begin >= (p.m_pathname.data() + p.m_pathname.size()))) + { + if (!detail::is_directory_separator(*begin)) + path_algorithms::append_separator_if_needed(p); + p.m_pathname.append(begin, end); + } + else + { + // overlapping source + string_type rhs(begin, end); + path_algorithms::append_v3(p, rhs.data(), rhs.data() + rhs.size()); + } + } +} + +BOOST_FILESYSTEM_DECL void path_algorithms::append_v4(path& p, const value_type* begin, const value_type* end) +{ + if (begin != end) + { + if (BOOST_LIKELY(begin < p.m_pathname.data() || begin >= (p.m_pathname.data() + p.m_pathname.size()))) + { + const size_type that_size = end - begin; + size_type that_root_name_size = 0; + size_type that_root_dir_pos = find_root_directory_start(begin, that_size, that_root_name_size); + + // if (p.is_absolute()) + if + ( +#if defined(BOOST_WINDOWS_API) + that_root_name_size > 0 && +#endif + that_root_dir_pos < that_size + ) + { + return_assign: + p.assign(begin, end); + return; + } + + size_type this_root_name_size = 0; + find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), this_root_name_size); + + if + ( + that_root_name_size > 0 && + (that_root_name_size != this_root_name_size || std::memcmp(p.m_pathname.c_str(), begin, this_root_name_size * sizeof(value_type)) != 0) + ) + { + goto return_assign; + } + + if (that_root_dir_pos < that_size) + { + // Remove root directory (if any) and relative path to replace with those from p + p.m_pathname.erase(p.m_pathname.begin() + this_root_name_size, p.m_pathname.end()); + } + + const value_type* const that_path = begin + that_root_name_size; + if (!detail::is_directory_separator(*that_path)) + path_algorithms::append_separator_if_needed(p); + p.m_pathname.append(that_path, end); + } + else + { + // overlapping source + string_type rhs(begin, end); + path_algorithms::append_v4(p, rhs.data(), rhs.data() + rhs.size()); + } + } + else if (path_algorithms::has_filename_v4(p)) + { + p.m_pathname.push_back(path::preferred_separator); + } +} + +// compare -------------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL int path_algorithms::lex_compare_v3 +( + path_detail::path_iterator first1, path_detail::path_iterator const& last1, + path_detail::path_iterator first2, path_detail::path_iterator const& last2 +) +{ + for (; first1 != last1 && first2 != last2;) + { + if (first1->native() < first2->native()) + return -1; + if (first2->native() < first1->native()) + return 1; + BOOST_ASSERT(first2->native() == first1->native()); + path_algorithms::increment_v3(first1); + path_algorithms::increment_v3(first2); + } + if (first1 == last1 && first2 == last2) + return 0; + return first1 == last1 ? -1 : 1; +} + +BOOST_FILESYSTEM_DECL int path_algorithms::lex_compare_v4 +( + path_detail::path_iterator first1, path_detail::path_iterator const& last1, + path_detail::path_iterator first2, path_detail::path_iterator const& last2 +) +{ + for (; first1 != last1 && first2 != last2;) + { + if (first1->native() < first2->native()) + return -1; + if (first2->native() < first1->native()) + return 1; + BOOST_ASSERT(first2->native() == first1->native()); + path_algorithms::increment_v4(first1); + path_algorithms::increment_v4(first2); + } + if (first1 == last1 && first2 == last2) + return 0; + return first1 == last1 ? -1 : 1; +} + +BOOST_FILESYSTEM_DECL int path_algorithms::compare_v3(path const& left, path const& right) +{ + return path_algorithms::lex_compare_v3(left.begin(), left.end(), right.begin(), right.end()); +} + +BOOST_FILESYSTEM_DECL int path_algorithms::compare_v4(path const& left, path const& right) +{ + return path_algorithms::lex_compare_v4(left.begin(), left.end(), right.begin(), right.end()); +} + +// append_separator_if_needed ------------------------------------------------------// + +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::append_separator_if_needed(path& p) +{ + if (!p.m_pathname.empty() && +#ifdef BOOST_WINDOWS_API + *(p.m_pathname.end() - 1) != colon && +#endif + !detail::is_directory_separator(*(p.m_pathname.end() - 1))) + { + string_type::size_type tmp(p.m_pathname.size()); + p.m_pathname.push_back(path::preferred_separator); + return tmp; + } + return 0; +} + +// erase_redundant_separator -------------------------------------------------------// + +BOOST_FILESYSTEM_DECL void path_algorithms::erase_redundant_separator(path& p, string_type::size_type sep_pos) +{ + if (sep_pos // a separator was added + && sep_pos < p.m_pathname.size() // and something was appended + && (p.m_pathname[sep_pos + 1] == path::separator // and it was also separator +#ifdef BOOST_WINDOWS_API + || p.m_pathname[sep_pos + 1] == path::preferred_separator // or preferred_separator +#endif + )) + { + p.m_pathname.erase(p.m_pathname.begin() + sep_pos); // erase the added separator + } +} + +// modifiers -----------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v3(path& p) +{ + p.remove_filename_and_trailing_separators(); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v4(path& p) +{ + size_type filename_size = path_algorithms::find_filename_v4_size(p); + p.m_pathname.erase(p.m_pathname.begin() + (p.m_pathname.size() - filename_size), p.m_pathname.end()); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::replace_extension_v3(path& p, path const& new_extension) +{ + // erase existing extension, including the dot, if any + size_type ext_pos = p.m_pathname.size() - path_algorithms::extension_v3(p).m_pathname.size(); + p.m_pathname.erase(p.m_pathname.begin() + ext_pos, p.m_pathname.end()); + + if (!new_extension.empty()) + { + // append new_extension, adding the dot if necessary + if (new_extension.m_pathname[0] != path::dot) + p.m_pathname.push_back(path::dot); + p.m_pathname.append(new_extension.m_pathname); + } +} + +BOOST_FILESYSTEM_DECL void path_algorithms::replace_extension_v4(path& p, path const& new_extension) +{ + // erase existing extension, including the dot, if any + size_type ext_pos = p.m_pathname.size() - path_algorithms::find_extension_v4_size(p); + p.m_pathname.erase(p.m_pathname.begin() + ext_pos, p.m_pathname.end()); + + if (!new_extension.empty()) + { + // append new_extension, adding the dot if necessary + if (new_extension.m_pathname[0] != path::dot) + p.m_pathname.push_back(path::dot); + p.m_pathname.append(new_extension.m_pathname); + } +} + +// decomposition -------------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_name_size(path const& p) +{ + size_type root_name_size = 0; + find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size); + return root_name_size; +} + +BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_path_size(path const& p) +{ + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size); + + size_type size = root_name_size; + if (root_dir_pos < p.m_pathname.size()) + size = root_dir_pos + 1; + + return size; +} + +BOOST_FILESYSTEM_DECL path_algorithms::substring path_algorithms::find_root_directory(path const& p) +{ + substring root_dir; + size_type root_name_size = 0; + root_dir.pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size); + root_dir.size = static_cast< std::size_t >(root_dir.pos < p.m_pathname.size()); + return root_dir; +} + +BOOST_FILESYSTEM_DECL path_algorithms::substring path_algorithms::find_relative_path(path const& p) +{ + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), p.m_pathname.size(), root_name_size); + + // Skip root name, root directory and any duplicate separators + size_type size = root_name_size; + if (root_dir_pos < p.m_pathname.size()) + { + size = root_dir_pos + 1; + + for (size_type n = p.m_pathname.size(); size < n; ++size) + { + if (!detail::is_directory_separator(p.m_pathname[size])) + break; + } + } + + substring rel_path; + rel_path.pos = size; + rel_path.size = p.m_pathname.size() - size; + + return rel_path; +} + +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_parent_path_size(path const& p) +{ + const size_type size = p.m_pathname.size(); + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); + + size_type filename_size = find_filename_size(p.m_pathname, root_name_size, size); + size_type end_pos = size - filename_size; + while (true) + { + if (end_pos <= root_name_size) + { + // Keep the root name as the parent path if there was a filename + if (filename_size == 0) + end_pos = 0u; + break; + } + + --end_pos; + + if (!detail::is_directory_separator(p.m_pathname[end_pos])) + { + ++end_pos; + break; + } + + if (end_pos == root_dir_pos) + { + // Keep the trailing root directory if there was a filename + end_pos += filename_size > 0; + break; + } + } + + return end_pos; +} + +BOOST_FILESYSTEM_DECL path path_algorithms::filename_v3(path const& p) +{ + const size_type size = p.m_pathname.size(); + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); + size_type filename_size, pos; + if (root_dir_pos < size && detail::is_directory_separator(p.m_pathname[size - 1]) && is_root_separator(p.m_pathname, root_dir_pos, size - 1)) + { + // Return root directory + pos = root_dir_pos; + filename_size = 1u; + } + else if (root_name_size == size) + { + // Return root name + pos = 0u; + filename_size = root_name_size; + } + else + { + filename_size = find_filename_size(p.m_pathname, root_name_size, size); + pos = size - filename_size; + if (filename_size == 0u && pos > root_name_size && detail::is_directory_separator(p.m_pathname[pos - 1]) && !is_root_separator(p.m_pathname, root_dir_pos, pos - 1)) + return detail::dot_path(); + } + + const value_type* ptr = p.m_pathname.c_str() + pos; + return path(ptr, ptr + filename_size); +} + +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_filename_v4_size(path const& p) +{ + const size_type size = p.m_pathname.size(); + size_type root_name_size = 0; + find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); + return find_filename_size(p.m_pathname, root_name_size, size); +} + +BOOST_FILESYSTEM_DECL path path_algorithms::stem_v3(path const& p) +{ + path name(path_algorithms::filename_v3(p)); + if (path_algorithms::compare_v4(name, detail::dot_path()) != 0 && path_algorithms::compare_v4(name, detail::dot_dot_path()) != 0) + { + size_type pos = name.m_pathname.rfind(path::dot); + if (pos != string_type::npos) + name.m_pathname.erase(name.m_pathname.begin() + pos, name.m_pathname.end()); + } + return name; +} + +BOOST_FILESYSTEM_DECL path path_algorithms::stem_v4(path const& p) +{ + path name(path_algorithms::filename_v4(p)); + if (path_algorithms::compare_v4(name, detail::dot_path()) != 0 && path_algorithms::compare_v4(name, detail::dot_dot_path()) != 0) + { + size_type pos = name.m_pathname.rfind(path::dot); + if (pos != 0 && pos != string_type::npos) + name.m_pathname.erase(name.m_pathname.begin() + pos, name.m_pathname.end()); + } + return name; +} + +BOOST_FILESYSTEM_DECL path path_algorithms::extension_v3(path const& p) +{ + path name(path_algorithms::filename_v3(p)); + if (path_algorithms::compare_v4(name, detail::dot_path()) == 0 || path_algorithms::compare_v4(name, detail::dot_dot_path()) == 0) + return path(); + size_type pos(name.m_pathname.rfind(path::dot)); + return pos == string_type::npos ? path() : path(name.m_pathname.c_str() + pos); +} + +BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_extension_v4_size(path const& p) +{ + const size_type size = p.m_pathname.size(); + size_type root_name_size = 0; + find_root_directory_start(p.m_pathname.c_str(), size, root_name_size); + size_type filename_size = find_filename_size(p.m_pathname, root_name_size, size); + size_type filename_pos = size - filename_size; + if + ( + filename_size > 0u && + // Check for "." and ".." filenames + !(p.m_pathname[filename_pos] == path::dot && + (filename_size == 1u || (filename_size == 2u && p.m_pathname[filename_pos + 1u] == path::dot))) + ) + { + size_type ext_pos = size; + while (ext_pos > filename_pos) + { + --ext_pos; + if (p.m_pathname[ext_pos] == path::dot) + break; + } + + if (ext_pos > filename_pos) + return size - ext_pos; + } + + return 0u; +} + +} // namespace detail + +BOOST_FILESYSTEM_DECL path& path::remove_filename_and_trailing_separators() +{ + size_type end_pos = detail::path_algorithms::find_parent_path_size(*this); + m_pathname.erase(m_pathname.begin() + end_pos, m_pathname.end()); + return *this; +} + +BOOST_FILESYSTEM_DECL path& path::remove_trailing_separator() +{ + if (!m_pathname.empty() && detail::is_directory_separator(m_pathname[m_pathname.size() - 1])) + m_pathname.erase(m_pathname.end() - 1); + return *this; +} + +BOOST_FILESYSTEM_DECL path& path::replace_filename(path const& replacement) +{ + detail::path_algorithms::remove_filename_v4(*this); + detail::path_algorithms::append_v4(*this, replacement.m_pathname.data(), replacement.m_pathname.data() + replacement.m_pathname.size()); + return *this; +} + +// lexical operations --------------------------------------------------------------// + +BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const +{ + path::iterator b = begin(), e = end(), base_b = base.begin(), base_e = base.end(); + std::pair< path::iterator, path::iterator > mm = detail::mismatch(b, e, base_b, base_e); + if (mm.first == b && mm.second == base_b) + return path(); + if (mm.first == e && mm.second == base_e) + return detail::dot_path(); + + std::ptrdiff_t n = 0; + for (; mm.second != base_e; detail::path_algorithms::increment_v4(mm.second)) + { + path const& p = *mm.second; + if (detail::path_algorithms::compare_v4(p, detail::dot_dot_path()) == 0) + --n; + else if (!p.empty() && detail::path_algorithms::compare_v4(p, detail::dot_path()) != 0) + ++n; + } + if (n < 0) + return path(); + if (n == 0 && (mm.first == e || mm.first->empty())) + return detail::dot_path(); + + path tmp; + for (; n > 0; --n) + detail::path_algorithms::append_v4(tmp, detail::dot_dot_path()); + for (; mm.first != e; detail::path_algorithms::increment_v4(mm.first)) + detail::path_algorithms::append_v4(tmp, *mm.first); + return tmp; +} + +} // namespace filesystem +} // namespace boost + +//--------------------------------------------------------------------------------------// +// // +// class path helpers implementation // +// // +//--------------------------------------------------------------------------------------// + +namespace { + +// is_root_separator ---------------------------------------------------------------// + +// pos is position of the separator +inline bool is_root_separator(string_type const& str, size_type root_dir_pos, size_type pos) +{ + BOOST_ASSERT_MSG(pos < str.size() && fs::detail::is_directory_separator(str[pos]), "precondition violation"); + + // root_dir_pos points at the leftmost separator, we need to skip any duplicate separators right of root dir + while (pos > root_dir_pos && fs::detail::is_directory_separator(str[pos - 1])) + --pos; + + return pos == root_dir_pos; +} + +// find_filename_size --------------------------------------------------------------// + +// Returns: Size of the filename element that ends at end_pos (which is past-the-end position). 0 if no filename found. +inline size_type find_filename_size(string_type const& str, size_type root_name_size, size_type end_pos) +{ + size_type pos = end_pos; + while (pos > root_name_size) + { + --pos; + + if (fs::detail::is_directory_separator(str[pos])) + { + ++pos; // filename starts past the separator + break; + } + } + + return end_pos - pos; +} + +// find_root_directory_start -------------------------------------------------------// + +// Returns: starting position of root directory or size if not found +size_type find_root_directory_start(const value_type* path, size_type size, size_type& root_name_size) +{ + root_name_size = 0; + if (size == 0) + return 0; + + bool parsing_root_name = false; + size_type pos = 0; + + // case "//", possibly followed by more characters + if (fs::detail::is_directory_separator(path[0])) + { + if (size >= 2 && fs::detail::is_directory_separator(path[1])) + { + if (size == 2) + { + // The whole path is just a pair of separators + root_name_size = 2; + return 2; + } +#ifdef BOOST_WINDOWS_API + // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file + // cases "\\?\" and "\\.\" + else if (size >= 4 && (path[2] == questionmark || path[2] == fs::path::dot) && fs::detail::is_directory_separator(path[3])) + { + parsing_root_name = true; + pos += 4; + } +#endif + else if (fs::detail::is_directory_separator(path[2])) + { + // The path starts with three directory separators, which is interpreted as a root directory followed by redundant separators + return 0; + } + else + { + // case "//net {/}" + parsing_root_name = true; + pos += 2; + goto find_next_separator; + } + } +#ifdef BOOST_WINDOWS_API + // https://stackoverflow.com/questions/23041983/path-prefixes-and + // case "\??\" (NT path prefix) + else if (size >= 4 && path[1] == questionmark && path[2] == questionmark && fs::detail::is_directory_separator(path[3])) + { + parsing_root_name = true; + pos += 4; + } +#endif + else + { + // The path starts with a separator, possibly followed by a non-separator character + return 0; + } + } + +#ifdef BOOST_WINDOWS_API + // case "c:" or "prn:" + // Note: There is ambiguity in a "c:x" path interpretation. It could either mean a file "x" located at the current directory for drive C:, + // or an alternative stream "x" of a file "c". Windows API resolve this as the former, and so do we. + if ((size - pos) >= 2 && fs::detail::is_letter(path[pos])) + { + size_type i = pos + 1; + for (; i < size; ++i) + { + if (!is_device_name_char(path[i])) + break; + } + + if (i < size && path[i] == colon) + { + pos = i + 1; + root_name_size = pos; + parsing_root_name = false; + + if (pos < size && fs::detail::is_directory_separator(path[pos])) + return pos; + } + } +#endif + + if (!parsing_root_name) + return size; + +find_next_separator: + pos += find_separator(path + pos, size - pos); + if (parsing_root_name) + root_name_size = pos; + + return pos; +} + +//--------------------------------------------------------------------------------------// +// // +// 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. +void first_element(string_type const& src, size_type& element_pos, size_type& element_size, size_type size) +{ + element_pos = 0; + element_size = 0; + if (src.empty()) + return; + + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(src.c_str(), size, root_name_size); + + // First element is the root name, if there is one + if (root_name_size > 0) + { + element_size = root_name_size; + return; + } + + // Otherwise, the root directory + if (root_dir_pos < size) + { + element_pos = root_dir_pos; + element_size = 1u; + return; + } + + // Otherwise, the first filename or directory name in a relative path + size_type end_pos = src.find_first_of(separators); + if (end_pos == string_type::npos) + end_pos = src.size(); + element_size = end_pos; +} + +} // unnamed namespace + +namespace boost { +namespace filesystem { +namespace detail { + +BOOST_FILESYSTEM_DECL void path_algorithms::increment_v3(path_detail::path_iterator& it) +{ + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.m_pos < size, "path::iterator increment past end()"); + + // increment to position past current element; if current element is implicit dot, + // this will cause m_pos to represent the end iterator + it.m_pos += it.m_element.m_pathname.size(); + + // if the end is reached, we are done + if (it.m_pos >= size) + { + BOOST_ASSERT_MSG(it.m_pos == size, "path::iterator increment after the referenced path was modified"); + it.m_element.clear(); // aids debugging + return; + } + + // process separator (Windows drive spec is only case not a separator) + if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size); + + // detect root directory and set iterator value to the separator if it is + if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size) + { + it.m_element.m_pathname = path::separator; // generic format; see docs + return; + } + + // skip separators until m_pos points to the start of the next element + while (it.m_pos != size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + ++it.m_pos; + } + + // detect trailing separator, and treat it as ".", per POSIX spec + if (it.m_pos == size && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) + { + --it.m_pos; + it.m_element = detail::dot_path(); + return; + } + } + + // get m_element + size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos); + if (end_pos == string_type::npos) + end_pos = size; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::increment_v4(path_detail::path_iterator& it) +{ + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator increment past end()"); + + if (it.m_element.m_pathname.empty() && (it.m_pos + 1) == size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + // The iterator was pointing to the last empty element of the path; set to end. + 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 + it.m_pos += it.m_element.m_pathname.size(); + + // if the end is reached, we are done + if (it.m_pos >= size) + { + BOOST_ASSERT_MSG(it.m_pos == size, "path::iterator increment after the referenced path was modified"); + it.m_element.clear(); // aids debugging + return; + } + + // process separator (Windows drive spec is only case not a separator) + if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size); + + // detect root directory and set iterator value to the separator if it is + if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size) + { + it.m_element.m_pathname = path::separator; // generic format; see docs + return; + } + + // skip separators until m_pos points to the start of the next element + while (it.m_pos != size && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos])) + { + ++it.m_pos; + } + + // detect trailing separator + if (it.m_pos == size && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) + { + --it.m_pos; + it.m_element.m_pathname.clear(); + return; + } + } + + // get m_element + size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos); + if (end_pos == string_type::npos) + end_pos = size; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v3(path_detail::path_iterator& it) +{ + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.m_pos > 0, "path::iterator decrement past begin()"); + BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator decrement after the referenced path was modified"); + + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size); + + if (root_dir_pos < size && it.m_pos == root_dir_pos) + { + // Was pointing at root directory, decrement to root name + set_to_root_name: + it.m_pos = 0u; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p, p + root_name_size); + return; + } + + // if at end and there was a trailing non-root '/', return "." + if (it.m_pos == size && + size > 1 && + detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos - 1]) && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) + { + --it.m_pos; + it.m_element = detail::dot_path(); + return; + } + + // skip separators unless root directory + size_type end_pos = it.m_pos; + while (end_pos > root_name_size) + { + --end_pos; + + if (end_pos == root_dir_pos) + { + // Decremented to the root directory + it.m_pos = end_pos; + it.m_element.m_pathname = path::separator; // generic format; see docs + return; + } + + if (!detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos])) + { + ++end_pos; + break; + } + } + + if (end_pos <= root_name_size) + goto set_to_root_name; + + size_type filename_size = find_filename_size(it.m_path_ptr->m_pathname, root_name_size, end_pos); + it.m_pos = end_pos - filename_size; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos); +} + +BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v4(path_detail::path_iterator& it) +{ + const size_type size = it.m_path_ptr->m_pathname.size(); + BOOST_ASSERT_MSG(it.m_pos > 0, "path::iterator decrement past begin()"); + BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator decrement after the referenced path was modified"); + + size_type root_name_size = 0; + size_type root_dir_pos = find_root_directory_start(it.m_path_ptr->m_pathname.c_str(), size, root_name_size); + + if (root_dir_pos < size && it.m_pos == root_dir_pos) + { + // Was pointing at root directory, decrement to root name + set_to_root_name: + it.m_pos = 0u; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p, p + root_name_size); + return; + } + + // if at end and there was a trailing '/', return "" + if (it.m_pos == size && + size > 1 && + detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos - 1]) && + !is_root_separator(it.m_path_ptr->m_pathname, root_dir_pos, it.m_pos - 1)) + { + --it.m_pos; + it.m_element.m_pathname.clear(); + return; + } + + // skip separators unless root directory + size_type end_pos = it.m_pos; + while (end_pos > root_name_size) + { + --end_pos; + + if (end_pos == root_dir_pos) + { + // Decremented to the root directory + it.m_pos = end_pos; + it.m_element.m_pathname = path::separator; // generic format; see docs + return; + } + + if (!detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos])) + { + ++end_pos; + break; + } + } + + if (end_pos <= root_name_size) + goto set_to_root_name; + + size_type filename_size = find_filename_size(it.m_path_ptr->m_pathname, root_name_size, end_pos); + it.m_pos = end_pos - filename_size; + const path::value_type* p = it.m_path_ptr->m_pathname.c_str(); + it.m_element.m_pathname.assign(p + it.m_pos, p + end_pos); +} + +} // namespace detail + +// 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 +} // namespace boost + +namespace { + +//------------------------------------------------------------------------------------// +// locale helpers // +//------------------------------------------------------------------------------------// + +// Prior versions of these locale and codecvt implementations tried to take advantage +// of static initialization where possible, kept a local copy of the current codecvt +// facet (to avoid codecvt() having to call use_facet()), and was not multi-threading +// safe (again for efficiency). +// +// This was error prone, and required different implementation techniques depending +// on the compiler and also whether static or dynamic linking was used. Furthermore, +// users could not easily provide their multi-threading safe wrappers because the +// path interface requires the implementation itself to call codecvt() to obtain the +// default facet, and the initialization of the static within path_locale() could race. +// +// The code below is portable to all platforms, is much simpler, and hopefully will be +// much more robust. Timing tests (on Windows, using a Visual C++ release build) +// indicated the current code is roughly 9% slower than the previous code, and that +// seems a small price to pay for better code that is easier to use. + +std::locale default_locale() +{ +#if defined(BOOST_WINDOWS_API) + std::locale global_loc = std::locale(); + return std::locale(global_loc, new boost::filesystem::detail::windows_file_codecvt()); +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__) + // "All BSD system functions expect their string parameters to be in UTF-8 encoding + // and nothing else." See + // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPInternational/Articles/FileEncodings.html + // + // "The kernel will reject any filename that is not a valid UTF-8 string, and it will + // even be normalized (to Unicode NFD) before stored on disk, at least when using HFS. + // The right way to deal with it would be to always convert the filename to UTF-8 + // before trying to open/create a file." See + // http://lists.apple.com/archives/unix-porting/2007/Sep/msg00023.html + // + // "How a file name looks at the API level depends on the API. Current Carbon APIs + // handle file names as an array of UTF-16 characters; POSIX ones handle them as an + // array of UTF-8, which is why UTF-8 works well in Terminal. How it's stored on disk + // depends on the disk format; HFS+ uses UTF-16, but that's not important in most + // cases." See + // http://lists.apple.com/archives/applescript-users/2002/Sep/msg00319.html + // + // Many thanks to Peter Dimov for digging out the above references! + + std::locale global_loc = std::locale(); + return std::locale(global_loc, new boost::filesystem::detail::utf8_codecvt_facet()); +#else // Other POSIX + // ISO C calls std::locale("") "the locale-specific native environment", and this + // locale is the default for many POSIX-based operating systems such as Linux. + return std::locale(""); +#endif +} + +std::locale* g_path_locale = nullptr; + +void schedule_path_locale_cleanup() noexcept; + +// std::locale("") construction, needed on non-Apple POSIX systems, can throw +// (if environmental variables LC_MESSAGES or LANG are wrong, for example), so +// get_path_locale() provides lazy initialization to ensure that any +// exceptions occur after main() starts and so can be caught. Furthermore, +// g_path_locale is only initialized if path::codecvt() or path::imbue() are themselves +// actually called, ensuring that an exception will only be thrown if std::locale("") +// is really needed. +inline std::locale& get_path_locale() +{ +#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED) + atomic_ns::atomic_ref< std::locale* > a(g_path_locale); + std::locale* p = a.load(atomic_ns::memory_order_acquire); + if (BOOST_UNLIKELY(!p)) + { + std::locale* new_p = new std::locale(default_locale()); + if (a.compare_exchange_strong(p, new_p, atomic_ns::memory_order_acq_rel, atomic_ns::memory_order_acquire)) + { + p = new_p; + schedule_path_locale_cleanup(); + } + else + { + delete new_p; + } + } + return *p; +#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED) + std::locale* p = g_path_locale; + if (BOOST_UNLIKELY(!p)) + { + g_path_locale = p = new std::locale(default_locale()); + schedule_path_locale_cleanup(); + } + return *p; +#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED) +} + +inline std::locale* replace_path_locale(std::locale const& loc) +{ + std::locale* new_p = new std::locale(loc); +#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED) + std::locale* p = atomic_ns::atomic_ref< std::locale* >(g_path_locale).exchange(new_p, atomic_ns::memory_order_acq_rel); +#else + std::locale* p = g_path_locale; + g_path_locale = new_p; +#endif + if (!p) + schedule_path_locale_cleanup(); + return p; +} + +#if defined(_MSC_VER) + +const boost::filesystem::path* g_dot_path = nullptr; +const boost::filesystem::path* g_dot_dot_path = nullptr; + +inline void schedule_path_locale_cleanup() noexcept +{ +} + +inline boost::filesystem::path const& get_dot_path() +{ +#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED) + atomic_ns::atomic_ref< const boost::filesystem::path* > a(g_dot_path); + const boost::filesystem::path* p = a.load(atomic_ns::memory_order_acquire); + if (BOOST_UNLIKELY(!p)) + { + const boost::filesystem::path* new_p = new boost::filesystem::path(dot_path_literal); + if (a.compare_exchange_strong(p, new_p, atomic_ns::memory_order_acq_rel, atomic_ns::memory_order_acquire)) + p = new_p; + else + delete new_p; + } + return *p; +#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED) + const boost::filesystem::path* p = g_dot_path; + if (BOOST_UNLIKELY(!p)) + g_dot_path = p = new boost::filesystem::path(dot_path_literal); + return *p; +#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED) +} + +inline boost::filesystem::path const& get_dot_dot_path() +{ +#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED) + atomic_ns::atomic_ref< const boost::filesystem::path* > a(g_dot_dot_path); + const boost::filesystem::path* p = a.load(atomic_ns::memory_order_acquire); + if (BOOST_UNLIKELY(!p)) + { + const boost::filesystem::path* new_p = new boost::filesystem::path(dot_dot_path_literal); + if (a.compare_exchange_strong(p, new_p, atomic_ns::memory_order_acq_rel, atomic_ns::memory_order_acquire)) + p = new_p; + else + delete new_p; + } + return *p; +#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED) + const boost::filesystem::path* p = g_dot_dot_path; + if (BOOST_UNLIKELY(!p)) + g_dot_dot_path = p = new boost::filesystem::path(dot_dot_path_literal); + return *p; +#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED) +} + +void __cdecl destroy_path_globals() +{ + delete g_dot_dot_path; + g_dot_dot_path = nullptr; + delete g_dot_path; + g_dot_path = nullptr; + delete g_path_locale; + g_path_locale = nullptr; +} + +BOOST_FILESYSTEM_INIT_FUNC init_path_globals() +{ +#if !defined(BOOST_SYSTEM_HAS_CONSTEXPR) + // codecvt_error_category needs to be called early to dynamic-initialize the error category instance + boost::filesystem::codecvt_error_category(); +#endif + std::atexit(&destroy_path_globals); + return BOOST_FILESYSTEM_INITRETSUCCESS_V; +} + +#if _MSC_VER >= 1400 + +#pragma section(".CRT$XCM", long, read) +__declspec(allocate(".CRT$XCM")) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN +extern const init_func_ptr_t p_init_path_globals = &init_path_globals; + +#else // _MSC_VER >= 1400 + +#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 +#pragma data_seg(push, old_seg) +#endif +#pragma data_seg(".CRT$XCM") +BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN +extern const init_func_ptr_t p_init_path_globals = &init_path_globals; +#pragma data_seg() +#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0 +#pragma data_seg(pop, old_seg) +#endif + +#endif // _MSC_VER >= 1400 + +#if defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN) +//! Makes sure the global initializer pointers are referenced and not removed by linker +struct globals_retainer +{ + const init_func_ptr_t* volatile m_p_init_path_globals; + + globals_retainer() { m_p_init_path_globals = &p_init_path_globals; } +}; +BOOST_ATTRIBUTE_UNUSED +static const globals_retainer g_globals_retainer; +#endif // defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN) + +#else // defined(_MSC_VER) + +struct path_locale_deleter +{ + ~path_locale_deleter() + { + delete g_path_locale; + g_path_locale = nullptr; + } +}; + +#if defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY) + +BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED +const path_locale_deleter g_path_locale_deleter = {}; +BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) +const boost::filesystem::path g_dot_path(dot_path_literal); +BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) +const boost::filesystem::path g_dot_dot_path(dot_dot_path_literal); + +inline void schedule_path_locale_cleanup() noexcept +{ +} + +inline boost::filesystem::path const& get_dot_path() +{ + return g_dot_path; +} + +inline boost::filesystem::path const& get_dot_dot_path() +{ + return g_dot_dot_path; +} + +#else // defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY) + +inline void schedule_path_locale_cleanup() noexcept +{ + BOOST_ATTRIBUTE_UNUSED static const path_locale_deleter g_path_locale_deleter; +} + +inline boost::filesystem::path const& get_dot_path() +{ + static const boost::filesystem::path g_dot_path(dot_path_literal); + return g_dot_path; +} + +inline boost::filesystem::path const& get_dot_dot_path() +{ + static const boost::filesystem::path g_dot_dot_path(dot_dot_path_literal); + return g_dot_dot_path; +} + +#endif // defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY) + +#endif // defined(_MSC_VER) + +} // unnamed namespace + +//--------------------------------------------------------------------------------------// +// path::codecvt() and path::imbue() implementation // +//--------------------------------------------------------------------------------------// + +namespace boost { +namespace filesystem { + +BOOST_FILESYSTEM_DECL path::codecvt_type const& path::codecvt() +{ +#ifdef BOOST_FILESYSTEM_DEBUG + std::cout << "***** path::codecvt() called" << std::endl; +#endif + return std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(get_path_locale()); +} + +BOOST_FILESYSTEM_DECL std::locale path::imbue(std::locale const& loc) +{ +#ifdef BOOST_FILESYSTEM_DEBUG + std::cout << "***** path::imbue() called" << std::endl; +#endif + std::locale* p = replace_path_locale(loc); + if (BOOST_LIKELY(p != nullptr)) + { + // Note: copying/moving std::locale does not throw + std::locale temp(std::move(*p)); + delete p; + return temp; + } + + return default_locale(); +} + +namespace detail { + +BOOST_FILESYSTEM_DECL path const& dot_path() +{ + return get_dot_path(); +} + +BOOST_FILESYSTEM_DECL path const& dot_dot_path() +{ + return get_dot_dot_path(); +} + +} // namespace detail +} // namespace filesystem +} // namespace boost + +#include <boost/filesystem/detail/footer.hpp> |