| // filesystem path.cpp ------------------------------------------------------------- // |
| |
| // Copyright Beman Dawes 2008 |
| |
| // 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" |
| |
| // Old standard library configurations, particularly MingGW, don't support wide strings. |
| // Report this with an explicit error message. |
| #include <boost/config.hpp> |
| # if defined( BOOST_NO_STD_WSTRING ) |
| # error Configuration not supported: Boost.Filesystem V3 and later requires std::wstring support |
| # endif |
| |
| #include <boost/filesystem/path.hpp> |
| #include <boost/filesystem/operations.hpp> // for filesystem_error |
| #include <boost/scoped_array.hpp> |
| #include <boost/system/error_code.hpp> |
| #include <boost/assert.hpp> |
| #include <algorithm> |
| #include <iterator> |
| #include <utility> |
| #include <cstddef> |
| #include <cstring> |
| #include <cassert> |
| |
| #ifdef BOOST_WINDOWS_API |
| # include "windows_file_codecvt.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 |
| |
| namespace fs = boost::filesystem; |
| |
| using boost::filesystem::path; |
| |
| using std::string; |
| using std::wstring; |
| |
| using boost::system::error_code; |
| |
| //--------------------------------------------------------------------------------------// |
| // // |
| // 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* const separators = L"/\\"; |
| const wchar_t* separator_string = L"/"; |
| const wchar_t* preferred_separator_string = L"\\"; |
| const wchar_t colon = L':'; |
| const wchar_t questionmark = L'?'; |
| |
| inline bool is_letter(wchar_t c) |
| { |
| return (c >= L'a' && c <=L'z') || (c >= L'A' && c <=L'Z'); |
| } |
| |
| # else |
| |
| const char* const separators = "/"; |
| const char* separator_string = "/"; |
| const char* preferred_separator_string = "/"; |
| |
| # endif |
| |
| bool is_root_separator(const string_type& str, size_type pos); |
| // pos is position of the separator |
| |
| size_type filename_pos(const string_type& str, |
| size_type end_pos); // end_pos is past-the-end position |
| // Returns: 0 if str itself is filename (or empty) |
| |
| size_type root_directory_start(const string_type& path, size_type size); |
| // Returns: npos if no root_directory found |
| |
| void first_element( |
| const string_type& src, |
| size_type& element_pos, |
| size_type& element_size, |
| # if !BOOST_WORKAROUND(BOOST_MSVC, <= 1310) // VC++ 7.1 |
| size_type size = string_type::npos |
| # else |
| size_type size = -1 |
| # endif |
| ); |
| |
| } // unnamed namespace |
| |
| //--------------------------------------------------------------------------------------// |
| // // |
| // class path implementation // |
| // // |
| //--------------------------------------------------------------------------------------// |
| |
| namespace boost |
| { |
| namespace filesystem |
| { |
| |
| BOOST_FILESYSTEM_DECL path& path::operator/=(const path& p) |
| { |
| if (p.empty()) |
| return *this; |
| if (this == &p) // self-append |
| { |
| path rhs(p); |
| if (!detail::is_directory_separator(rhs.m_pathname[0])) |
| m_append_separator_if_needed(); |
| m_pathname += rhs.m_pathname; |
| } |
| else |
| { |
| if (!detail::is_directory_separator(*p.m_pathname.begin())) |
| m_append_separator_if_needed(); |
| m_pathname += p.m_pathname; |
| } |
| return *this; |
| } |
| |
| BOOST_FILESYSTEM_DECL path& path::operator/=(const value_type* ptr) |
| { |
| if (!*ptr) |
| return *this; |
| if (ptr >= m_pathname.data() |
| && ptr < m_pathname.data() + m_pathname.size()) // overlapping source |
| { |
| path rhs(ptr); |
| if (!detail::is_directory_separator(rhs.m_pathname[0])) |
| m_append_separator_if_needed(); |
| m_pathname += rhs.m_pathname; |
| } |
| else |
| { |
| if (!detail::is_directory_separator(*ptr)) |
| m_append_separator_if_needed(); |
| m_pathname += ptr; |
| } |
| return *this; |
| } |
| |
| # ifdef BOOST_WINDOWS_API |
| |
| BOOST_FILESYSTEM_DECL path path::generic_path() const |
| { |
| path tmp(*this); |
| std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/'); |
| return tmp; |
| } |
| |
| # endif // BOOST_WINDOWS_API |
| |
| BOOST_FILESYSTEM_DECL int path::compare(const path& p) const BOOST_NOEXCEPT |
| { |
| return detail::lex_compare(begin(), end(), p.begin(), p.end()); |
| } |
| |
| // m_append_separator_if_needed ----------------------------------------------------// |
| |
| BOOST_FILESYSTEM_DECL path::string_type::size_type path::m_append_separator_if_needed() |
| { |
| if (!m_pathname.empty() && |
| # ifdef BOOST_WINDOWS_API |
| *(m_pathname.end()-1) != colon && |
| # endif |
| !detail::is_directory_separator(*(m_pathname.end()-1))) |
| { |
| string_type::size_type tmp(m_pathname.size()); |
| m_pathname += preferred_separator; |
| return tmp; |
| } |
| return 0; |
| } |
| |
| // m_erase_redundant_separator -----------------------------------------------------// |
| |
| BOOST_FILESYSTEM_DECL void path::m_erase_redundant_separator(string_type::size_type sep_pos) |
| { |
| if (sep_pos // a separator was added |
| && sep_pos < m_pathname.size() // and something was appended |
| && (m_pathname[sep_pos+1] == separator // and it was also separator |
| # ifdef BOOST_WINDOWS_API |
| || m_pathname[sep_pos+1] == preferred_separator // or preferred_separator |
| # endif |
| ) |
| ) |
| { |
| m_pathname.erase(m_pathname.begin() + sep_pos); // erase the added separator |
| } |
| } |
| |
| // modifiers -----------------------------------------------------------------------// |
| |
| # ifdef BOOST_WINDOWS_API |
| BOOST_FILESYSTEM_DECL path& path::make_preferred() |
| { |
| std::replace(m_pathname.begin(), m_pathname.end(), L'/', L'\\'); |
| return *this; |
| } |
| # endif |
| |
| BOOST_FILESYSTEM_DECL path& path::remove_filename() |
| { |
| size_type end_pos(m_parent_path_end()); |
| if (end_pos == string_type::npos) |
| end_pos = 0u; |
| 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_extension(const path& new_extension) |
| { |
| // erase existing extension, including the dot, if any |
| m_pathname.erase(m_pathname.size()-extension().m_pathname.size()); |
| |
| 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); |
| } |
| |
| return *this; |
| } |
| |
| // decomposition -------------------------------------------------------------------// |
| |
| BOOST_FILESYSTEM_DECL path path::root_path() const |
| { |
| path temp(root_name()); |
| if (!root_directory().empty()) temp.m_pathname += root_directory().c_str(); |
| return temp; |
| } |
| |
| BOOST_FILESYSTEM_DECL path path::root_name() const |
| { |
| iterator itr(begin()); |
| |
| return (itr.m_pos != m_pathname.size() |
| && ( |
| (itr.m_element.m_pathname.size() > 1 |
| && detail::is_directory_separator(itr.m_element.m_pathname[0]) |
| && detail::is_directory_separator(itr.m_element.m_pathname[1])) |
| # ifdef BOOST_WINDOWS_API |
| || itr.m_element.m_pathname[itr.m_element.m_pathname.size()-1] == colon |
| # endif |
| )) |
| ? itr.m_element |
| : path(); |
| } |
| |
| BOOST_FILESYSTEM_DECL path path::root_directory() const |
| { |
| size_type pos(root_directory_start(m_pathname, m_pathname.size())); |
| |
| return pos == string_type::npos |
| ? path() |
| : path(m_pathname.c_str() + pos, m_pathname.c_str() + pos + 1); |
| } |
| |
| BOOST_FILESYSTEM_DECL path path::relative_path() const |
| { |
| iterator itr(begin()); |
| |
| for (; itr.m_pos != m_pathname.size() |
| && (detail::is_directory_separator(itr.m_element.m_pathname[0]) |
| # ifdef BOOST_WINDOWS_API |
| || itr.m_element.m_pathname[itr.m_element.m_pathname.size()-1] == colon |
| # endif |
| ); ++itr) {} |
| |
| return path(m_pathname.c_str() + itr.m_pos); |
| } |
| |
| BOOST_FILESYSTEM_DECL string_type::size_type path::m_parent_path_end() const |
| { |
| size_type end_pos(filename_pos(m_pathname, m_pathname.size())); |
| |
| bool filename_was_separator = !m_pathname.empty() |
| && detail::is_directory_separator(m_pathname[end_pos]); |
| |
| // skip separators unless root directory |
| size_type root_dir_pos(root_directory_start(m_pathname, end_pos)); |
| for (; |
| end_pos > 0 |
| && (end_pos-1) != root_dir_pos |
| && detail::is_directory_separator(m_pathname[end_pos-1]) |
| ; |
| --end_pos) {} |
| |
| return (end_pos == 1 && root_dir_pos == 0 && filename_was_separator) |
| ? string_type::npos |
| : end_pos; |
| } |
| |
| BOOST_FILESYSTEM_DECL path path::parent_path() const |
| { |
| size_type end_pos(m_parent_path_end()); |
| return end_pos == string_type::npos |
| ? path() |
| : path(m_pathname.c_str(), m_pathname.c_str() + end_pos); |
| } |
| |
| BOOST_FILESYSTEM_DECL path path::filename() const |
| { |
| size_type pos(filename_pos(m_pathname, m_pathname.size())); |
| return (!m_pathname.empty() |
| && pos |
| && detail::is_directory_separator(m_pathname[pos]) |
| && !is_root_separator(m_pathname, pos)) |
| ? detail::dot_path() |
| : path(m_pathname.c_str() + pos); |
| } |
| |
| BOOST_FILESYSTEM_DECL path path::stem() const |
| { |
| path name(filename()); |
| if (name == detail::dot_path() || name == detail::dot_dot_path()) return name; |
| size_type pos(name.m_pathname.rfind(dot)); |
| return pos == string_type::npos |
| ? name |
| : path(name.m_pathname.c_str(), name.m_pathname.c_str() + pos); |
| } |
| |
| BOOST_FILESYSTEM_DECL path path::extension() const |
| { |
| path name(filename()); |
| if (name == detail::dot_path() || name == detail::dot_dot_path()) return path(); |
| size_type pos(name.m_pathname.rfind(dot)); |
| return pos == string_type::npos |
| ? path() |
| : path(name.m_pathname.c_str() + pos); |
| } |
| |
| // lexical operations --------------------------------------------------------------// |
| |
| namespace detail |
| { |
| // C++14 provides a mismatch algorithm with four iterator arguments(), but earlier |
| // standard libraries didn't, so provide this needed functionality. |
| inline |
| std::pair<path::iterator, path::iterator> mismatch(path::iterator it1, |
| path::iterator it1end, path::iterator it2, path::iterator it2end) |
| { |
| for (; it1 != it1end && it2 != it2end && *it1 == *it2;) |
| { |
| ++it1; |
| ++it2; |
| } |
| return std::make_pair(it1, it2); |
| } |
| } |
| |
| BOOST_FILESYSTEM_DECL path path::lexically_relative(const path& base) const |
| { |
| path::iterator b = begin(), e = end(), base_b = base.begin(), base_e = base.end(); |
| std::pair<path::iterator, path::iterator> mm = detail::mismatch(b, e, base_b, base_e); |
| if (mm.first == b && mm.second == base_b) |
| return path(); |
| if (mm.first == e && mm.second == base_e) |
| return detail::dot_path(); |
| |
| std::ptrdiff_t n = 0; |
| for (; mm.second != base_e; ++mm.second) |
| { |
| path const& p = *mm.second; |
| if (p == detail::dot_dot_path()) |
| --n; |
| else if (!p.empty() && p != detail::dot_path()) |
| ++n; |
| } |
| if (n < 0) |
| return path(); |
| if (n == 0 && (mm.first == e || mm.first->empty())) |
| return detail::dot_path(); |
| |
| path tmp; |
| for (; n > 0; --n) |
| tmp /= detail::dot_dot_path(); |
| for (; mm.first != e; ++mm.first) |
| tmp /= *mm.first; |
| return tmp; |
| } |
| |
| // normal --------------------------------------------------------------------------// |
| |
| BOOST_FILESYSTEM_DECL path path::lexically_normal() const |
| { |
| if (m_pathname.empty()) |
| return *this; |
| |
| path temp; |
| iterator start(begin()); |
| iterator last(end()); |
| iterator stop(last--); |
| for (iterator itr(start); itr != stop; ++itr) |
| { |
| // ignore "." except at start and last |
| if (itr->native().size() == 1 |
| && (itr->native())[0] == dot |
| && itr != start |
| && itr != last) continue; |
| |
| // ignore a name and following ".." |
| if (!temp.empty() |
| && itr->native().size() == 2 |
| && (itr->native())[0] == dot |
| && (itr->native())[1] == dot) // dot dot |
| { |
| string_type lf(temp.filename().native()); |
| string_type::size_type lf_size = lf.size(); |
| if (lf_size > 0 |
| && (lf_size != 1 |
| || (lf[0] != dot |
| && lf[0] != separator)) |
| && (lf_size != 2 |
| || (lf[0] != dot |
| && lf[1] != dot |
| # ifdef BOOST_WINDOWS_API |
| && lf[1] != colon |
| # endif |
| ) |
| ) |
| ) |
| { |
| temp.remove_filename(); |
| //// if not root directory, must also remove "/" if any |
| //if (temp.native().size() > 0 |
| // && temp.native()[temp.native().size()-1] |
| // == separator) |
| //{ |
| // string_type::size_type rds( |
| // root_directory_start(temp.native(), temp.native().size())); |
| // if (rds == string_type::npos |
| // || rds != temp.native().size()-1) |
| // { |
| // temp.m_pathname.erase(temp.native().size()-1); |
| // } |
| //} |
| |
| iterator next(itr); |
| if (temp.empty() && ++next != stop |
| && next == last && *last == detail::dot_path()) |
| { |
| temp /= detail::dot_path(); |
| } |
| continue; |
| } |
| } |
| |
| temp /= *itr; |
| } |
| |
| if (temp.empty()) |
| temp /= detail::dot_path(); |
| return temp; |
| } |
| |
| } // namespace filesystem |
| } // namespace boost |
| |
| //--------------------------------------------------------------------------------------// |
| // // |
| // class path helpers implementation // |
| // // |
| //--------------------------------------------------------------------------------------// |
| |
| namespace |
| { |
| |
| // is_root_separator ---------------------------------------------------------------// |
| |
| bool is_root_separator(const string_type & str, size_type pos) |
| // pos is position of the separator |
| { |
| BOOST_ASSERT_MSG(!str.empty() && fs::detail::is_directory_separator(str[pos]), |
| "precondition violation"); |
| |
| // subsequent logic expects pos to be for leftmost slash of a set |
| while (pos > 0 && fs::detail::is_directory_separator(str[pos-1])) |
| --pos; |
| |
| // "/" [...] |
| if (pos == 0) |
| return true; |
| |
| # ifdef BOOST_WINDOWS_API |
| // "c:/" [...] |
| if (pos == 2 && is_letter(str[0]) && str[1] == colon) |
| return true; |
| # endif |
| |
| // "//" name "/" |
| if (pos < 3 || !fs::detail::is_directory_separator(str[0]) |
| || !fs::detail::is_directory_separator(str[1])) |
| return false; |
| |
| return str.find_first_of(separators, 2) == pos; |
| } |
| |
| // filename_pos --------------------------------------------------------------------// |
| |
| size_type filename_pos(const string_type & str, |
| size_type end_pos) // end_pos is past-the-end position |
| // return 0 if str itself is filename (or empty) |
| { |
| // case: "//" |
| if (end_pos == 2 |
| && fs::detail::is_directory_separator(str[0]) |
| && fs::detail::is_directory_separator(str[1])) return 0; |
| |
| // case: ends in "/" |
| if (end_pos && fs::detail::is_directory_separator(str[end_pos-1])) |
| return end_pos-1; |
| |
| // set pos to start of last element |
| size_type pos(str.find_last_of(separators, end_pos-1)); |
| |
| # ifdef BOOST_WINDOWS_API |
| if (pos == string_type::npos && end_pos > 1) |
| pos = str.find_last_of(colon, end_pos-2); |
| # endif |
| |
| return (pos == string_type::npos // path itself must be a filename (or empty) |
| || (pos == 1 && fs::detail::is_directory_separator(str[0]))) // or net |
| ? 0 // so filename is entire string |
| : pos + 1; // or starts after delimiter |
| } |
| |
| // root_directory_start ------------------------------------------------------------// |
| |
| size_type root_directory_start(const string_type & path, size_type size) |
| // return npos if no root_directory found |
| { |
| |
| # ifdef BOOST_WINDOWS_API |
| // case "c:/" |
| if (size > 2 |
| && path[1] == colon |
| && fs::detail::is_directory_separator(path[2])) return 2; |
| # endif |
| |
| // case "//" |
| if (size == 2 |
| && fs::detail::is_directory_separator(path[0]) |
| && fs::detail::is_directory_separator(path[1])) return string_type::npos; |
| |
| # ifdef BOOST_WINDOWS_API |
| // case "\\?\" |
| if (size > 4 |
| && fs::detail::is_directory_separator(path[0]) |
| && fs::detail::is_directory_separator(path[1]) |
| && path[2] == questionmark |
| && fs::detail::is_directory_separator(path[3])) |
| { |
| string_type::size_type pos(path.find_first_of(separators, 4)); |
| return pos < size ? pos : string_type::npos; |
| } |
| # endif |
| |
| // case "//net {/}" |
| if (size > 3 |
| && fs::detail::is_directory_separator(path[0]) |
| && fs::detail::is_directory_separator(path[1]) |
| && !fs::detail::is_directory_separator(path[2])) |
| { |
| string_type::size_type pos(path.find_first_of(separators, 2)); |
| return pos < size ? pos : string_type::npos; |
| } |
| |
| // case "/" |
| if (size > 0 && fs::detail::is_directory_separator(path[0])) return 0; |
| |
| return string_type::npos; |
| } |
| |
| // first_element --------------------------------------------------------------------// |
| // sets pos and len of first element, excluding extra separators |
| // if src.empty(), sets pos,len, to 0,0. |
| |
| void first_element( |
| const string_type & src, |
| size_type & element_pos, |
| size_type & element_size, |
| size_type size |
| ) |
| { |
| if (size == string_type::npos) size = src.size(); |
| element_pos = 0; |
| element_size = 0; |
| if (src.empty()) return; |
| |
| string_type::size_type cur(0); |
| |
| // deal with // [network] |
| if (size >= 2 && fs::detail::is_directory_separator(src[0]) |
| && fs::detail::is_directory_separator(src[1]) |
| && (size == 2 |
| || !fs::detail::is_directory_separator(src[2]))) |
| { |
| cur += 2; |
| element_size += 2; |
| } |
| |
| // leading (not non-network) separator |
| else if (fs::detail::is_directory_separator(src[0])) |
| { |
| ++element_size; |
| // bypass extra leading separators |
| while (cur+1 < size |
| && fs::detail::is_directory_separator(src[cur+1])) |
| { |
| ++cur; |
| ++element_pos; |
| } |
| return; |
| } |
| |
| // at this point, we have either a plain name, a network name, |
| // or (on Windows only) a device name |
| |
| // find the end |
| while (cur < size |
| # ifdef BOOST_WINDOWS_API |
| && src[cur] != colon |
| # endif |
| && !fs::detail::is_directory_separator(src[cur])) |
| { |
| ++cur; |
| ++element_size; |
| } |
| |
| # ifdef BOOST_WINDOWS_API |
| if (cur == size) return; |
| // include device delimiter |
| if (src[cur] == colon) |
| { ++element_size; } |
| # endif |
| } |
| |
| } // unnamed namespace |
| |
| |
| namespace boost |
| { |
| namespace filesystem |
| { |
| namespace detail |
| { |
| BOOST_FILESYSTEM_DECL |
| int lex_compare(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; |
| } |
| |
| BOOST_FILESYSTEM_DECL |
| const path& dot_path() |
| { |
| # ifdef BOOST_WINDOWS_API |
| static const fs::path dot_pth(L"."); |
| # else |
| static const fs::path dot_pth("."); |
| # endif |
| return dot_pth; |
| } |
| |
| BOOST_FILESYSTEM_DECL |
| const path& dot_dot_path() |
| { |
| # ifdef BOOST_WINDOWS_API |
| static const fs::path dot_dot(L".."); |
| # else |
| static const fs::path dot_dot(".."); |
| # endif |
| return dot_dot; |
| } |
| } |
| |
| //--------------------------------------------------------------------------------------// |
| // // |
| // class path::iterator implementation // |
| // // |
| //--------------------------------------------------------------------------------------// |
| |
| 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); |
| itr.m_element = m_pathname.substr(itr.m_pos, element_size); |
| if (itr.m_element.m_pathname == preferred_separator_string) |
| itr.m_element.m_pathname = separator_string; // needed for Windows, harmless on POSIX |
| 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::m_path_iterator_increment(path::iterator & it) |
| { |
| BOOST_ASSERT_MSG(it.m_pos < it.m_path_ptr->m_pathname.size(), |
| "path::basic_iterator increment past end()"); |
| |
| // increment to position past current element; if current element is implicit dot, |
| // this will cause it.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 == it.m_path_ptr->m_pathname.size()) |
| { |
| it.m_element.clear(); // aids debugging, may release unneeded memory |
| return; |
| } |
| |
| // both POSIX and Windows treat paths that begin with exactly two separators specially |
| bool was_net(it.m_element.m_pathname.size() > 2 |
| && detail::is_directory_separator(it.m_element.m_pathname[0]) |
| && detail::is_directory_separator(it.m_element.m_pathname[1]) |
| && !detail::is_directory_separator(it.m_element.m_pathname[2])); |
| |
| // 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])) |
| { |
| // detect root directory |
| if (was_net |
| # ifdef BOOST_WINDOWS_API |
| // case "c:/" |
| || it.m_element.m_pathname[it.m_element.m_pathname.size()-1] == colon |
| # endif |
| ) |
| { |
| it.m_element.m_pathname = separator; // generic format; see docs |
| return; |
| } |
| |
| // skip separators until it.m_pos points to the start of the next element |
| while (it.m_pos != it.m_path_ptr->m_pathname.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 == it.m_path_ptr->m_pathname.size() |
| && !is_root_separator(it.m_path_ptr->m_pathname, 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 = it.m_path_ptr->m_pathname.size(); |
| it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos); |
| } |
| |
| BOOST_FILESYSTEM_DECL void path::m_path_iterator_decrement(path::iterator & it) |
| { |
| BOOST_ASSERT_MSG(it.m_pos, "path::iterator decrement past begin()"); |
| |
| size_type end_pos(it.m_pos); |
| |
| // if at end and there was a trailing non-root '/', return "." |
| if (it.m_pos == it.m_path_ptr->m_pathname.size() |
| && it.m_path_ptr->m_pathname.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, it.m_pos-1) |
| ) |
| { |
| --it.m_pos; |
| it.m_element = detail::dot_path(); |
| return; |
| } |
| |
| size_type root_dir_pos(root_directory_start(it.m_path_ptr->m_pathname, end_pos)); |
| |
| // skip separators unless root directory |
| for ( |
| ; |
| end_pos > 0 |
| && (end_pos-1) != root_dir_pos |
| && detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos-1]) |
| ; |
| --end_pos) {} |
| |
| it.m_pos = filename_pos(it.m_path_ptr->m_pathname, end_pos); |
| it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos); |
| if (it.m_element.m_pathname == preferred_separator_string) // needed for Windows, harmless on POSIX |
| it.m_element.m_pathname = separator_string; // generic format; see docs |
| } |
| |
| } // 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 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& path_locale() |
| // std::locale("") construction, needed on non-Apple POSIX systems, can throw |
| // (if environmental variables LC_MESSAGES or LANG are wrong, for example), so |
| // path_locale() provides lazy initialization via a local static to ensure that any |
| // exceptions occur after main() starts and so can be caught. Furthermore, |
| // path_locale() is only called if path::codecvt() or path::imbue() are themselves |
| // actually called, ensuring that an exception will only be thrown if std::locale("") |
| // is really needed. |
| { |
| // [locale] paragraph 6: Once a facet reference is obtained from a locale object by |
| // calling use_facet<>, that reference remains usable, and the results from member |
| // functions of it may be cached and re-used, as long as some locale object refers |
| // to that facet. |
| static std::locale loc(default_locale()); |
| #ifdef BOOST_FILESYSTEM_DEBUG |
| std::cout << "***** path_locale() called" << std::endl; |
| #endif |
| return loc; |
| } |
| } // unnamed namespace |
| |
| //--------------------------------------------------------------------------------------// |
| // path::codecvt() and path::imbue() implementation // |
| //--------------------------------------------------------------------------------------// |
| |
| namespace boost |
| { |
| namespace filesystem |
| { |
| // See comments above |
| |
| BOOST_FILESYSTEM_DECL const path::codecvt_type& path::codecvt() |
| { |
| #ifdef BOOST_FILESYSTEM_DEBUG |
| std::cout << "***** path::codecvt() called" << std::endl; |
| #endif |
| BOOST_ASSERT_MSG(&path_locale(), "boost::filesystem::path locale initialization error"); |
| |
| return std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t> >(path_locale()); |
| } |
| |
| BOOST_FILESYSTEM_DECL std::locale path::imbue(const std::locale& loc) |
| { |
| #ifdef BOOST_FILESYSTEM_DEBUG |
| std::cout << "***** path::imbue() called" << std::endl; |
| #endif |
| std::locale temp(path_locale()); |
| path_locale() = loc; |
| return temp; |
| } |
| |
| } // namespace filesystem |
| } // namespace boost |