blob: b4d650926ea08528bba3793761426b864752a517 [file] [log] [blame]
// 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