//  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

//  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

// define BOOST_FILESYSTEM_SOURCE so that <boost/system/config.hpp> knows
// the library is being built (possibly exporting rather than importing code)
#define BOOST_FILESYSTEM_SOURCE

#ifndef BOOST_SYSTEM_NO_DEPRECATED
# define BOOST_SYSTEM_NO_DEPRECATED
#endif

#include <boost/filesystem/config.hpp>
#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 <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(sep_pos, 1); } // 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()
  {
    m_pathname.erase(m_parent_path_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.size() - 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.size()
      && 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.size()
              && 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
  {
    std::pair<path::iterator, path::iterator> mm
      = detail::mismatch(begin(), end(), base.begin(), base.end());
    if (mm.first == begin() && mm.second == base.begin())
      return path();
    if (mm.first == end() && mm.second == base.end())
      return detail::dot_path();
    path tmp;
    for (; mm.second != base.end(); ++mm.second)
      tmp /= detail::dot_dot_path();
    for (; mm.first != end(); ++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());
        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

    return;
  }

}  // 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
