Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / filesystem / src / path.cpp
1 //  filesystem path.cpp  -------------------------------------------------------------  //
2
3 //  Copyright Beman Dawes 2008
4
5 //  Distributed under the Boost Software License, Version 1.0.
6 //  See http://www.boost.org/LICENSE_1_0.txt
7
8 //  Library home page: http://www.boost.org/libs/filesystem
9
10 // Include Boost.Predef first so that windows.h is guaranteed to be not included
11 #include <boost/predef/os/windows.h>
12 #if BOOST_OS_WINDOWS
13 #include <boost/winapi/config.hpp>
14 #endif
15
16 //  Old standard library configurations, particularly MingGW, don't support wide strings.
17 //  Report this with an explicit error message.
18 #include <boost/config.hpp>
19 # if defined( BOOST_NO_STD_WSTRING )
20 #   error Configuration not supported: Boost.Filesystem V3 and later requires std::wstring support
21 # endif
22
23 #ifndef BOOST_SYSTEM_NO_DEPRECATED
24 # define BOOST_SYSTEM_NO_DEPRECATED
25 #endif
26
27 #include <boost/filesystem/config.hpp>
28 #include <boost/filesystem/path.hpp>
29 #include <boost/filesystem/operations.hpp>  // for filesystem_error
30 #include <boost/scoped_array.hpp>
31 #include <boost/system/error_code.hpp>
32 #include <boost/assert.hpp>
33 #include <algorithm>
34 #include <iterator>
35 #include <utility>
36 #include <cstddef>
37 #include <cstring>
38 #include <cassert>
39
40 #ifdef BOOST_WINDOWS_API
41 # include "windows_file_codecvt.hpp"
42 # include <windows.h>
43 #elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) \
44  || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
45 # include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
46 #endif
47
48 #ifdef BOOST_FILESYSTEM_DEBUG
49 # include <iostream>
50 # include <iomanip>
51 #endif
52
53 namespace fs = boost::filesystem;
54
55 using boost::filesystem::path;
56
57 using std::string;
58 using std::wstring;
59
60 using boost::system::error_code;
61
62 //--------------------------------------------------------------------------------------//
63 //                                                                                      //
64 //                                class path helpers                                    //
65 //                                                                                      //
66 //--------------------------------------------------------------------------------------//
67
68 namespace
69 {
70   //------------------------------------------------------------------------------------//
71   //                        miscellaneous class path helpers                            //
72   //------------------------------------------------------------------------------------//
73
74   typedef path::value_type        value_type;
75   typedef path::string_type       string_type;
76   typedef string_type::size_type  size_type;
77
78 # ifdef BOOST_WINDOWS_API
79
80   const wchar_t* const separators = L"/\\";
81   const wchar_t* separator_string = L"/";
82   const wchar_t* preferred_separator_string = L"\\";
83   const wchar_t colon = L':';
84   const wchar_t questionmark = L'?';
85
86   inline bool is_letter(wchar_t c)
87   {
88     return (c >= L'a' && c <=L'z') || (c >= L'A' && c <=L'Z');
89   }
90
91 # else
92
93   const char* const separators = "/";
94   const char* separator_string = "/";
95   const char* preferred_separator_string = "/";
96
97 # endif
98
99   bool is_root_separator(const string_type& str, size_type pos);
100     // pos is position of the separator
101
102   size_type filename_pos(const string_type& str,
103                           size_type end_pos); // end_pos is past-the-end position
104   //  Returns: 0 if str itself is filename (or empty)
105
106   size_type root_directory_start(const string_type& path, size_type size);
107   //  Returns:  npos if no root_directory found
108
109   void first_element(
110       const string_type& src,
111       size_type& element_pos,
112       size_type& element_size,
113 #     if !BOOST_WORKAROUND(BOOST_MSVC, <= 1310) // VC++ 7.1
114       size_type size = string_type::npos
115 #     else
116       size_type size = -1
117 #     endif
118     );
119
120 }  // unnamed namespace
121
122 //--------------------------------------------------------------------------------------//
123 //                                                                                      //
124 //                            class path implementation                                 //
125 //                                                                                      //
126 //--------------------------------------------------------------------------------------//
127
128 namespace boost
129 {
130 namespace filesystem
131 {
132
133   BOOST_FILESYSTEM_DECL path& path::operator/=(const path& p)
134   {
135     if (p.empty())
136       return *this;
137     if (this == &p)  // self-append
138     {
139       path rhs(p);
140       if (!detail::is_directory_separator(rhs.m_pathname[0]))
141         m_append_separator_if_needed();
142       m_pathname += rhs.m_pathname;
143     }
144     else
145     {
146       if (!detail::is_directory_separator(*p.m_pathname.begin()))
147         m_append_separator_if_needed();
148       m_pathname += p.m_pathname;
149     }
150     return *this;
151   }
152
153   BOOST_FILESYSTEM_DECL path& path::operator/=(const value_type* ptr)
154   {
155     if (!*ptr)
156       return *this;
157     if (ptr >= m_pathname.data()
158       && ptr < m_pathname.data() + m_pathname.size())  // overlapping source
159     {
160       path rhs(ptr);
161       if (!detail::is_directory_separator(rhs.m_pathname[0]))
162         m_append_separator_if_needed();
163       m_pathname += rhs.m_pathname;
164     }
165     else
166     {
167       if (!detail::is_directory_separator(*ptr))
168         m_append_separator_if_needed();
169       m_pathname += ptr;
170     }
171     return *this;
172   }
173
174 # ifdef BOOST_WINDOWS_API
175
176   BOOST_FILESYSTEM_DECL path path::generic_path() const
177   {
178     path tmp(*this);
179     std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/');
180     return tmp;
181   }
182
183 # endif  // BOOST_WINDOWS_API
184
185   BOOST_FILESYSTEM_DECL int path::compare(const path& p) const BOOST_NOEXCEPT
186   {
187     return detail::lex_compare(begin(), end(), p.begin(), p.end());
188   }
189
190   //  m_append_separator_if_needed  ----------------------------------------------------//
191
192   BOOST_FILESYSTEM_DECL path::string_type::size_type path::m_append_separator_if_needed()
193   {
194     if (!m_pathname.empty() &&
195 #     ifdef BOOST_WINDOWS_API
196       *(m_pathname.end()-1) != colon &&
197 #     endif
198       !detail::is_directory_separator(*(m_pathname.end()-1)))
199     {
200       string_type::size_type tmp(m_pathname.size());
201       m_pathname += preferred_separator;
202       return tmp;
203     }
204     return 0;
205   }
206
207   //  m_erase_redundant_separator  -----------------------------------------------------//
208
209   BOOST_FILESYSTEM_DECL void path::m_erase_redundant_separator(string_type::size_type sep_pos)
210   {
211     if (sep_pos                         // a separator was added
212       && sep_pos < m_pathname.size()         // and something was appended
213       && (m_pathname[sep_pos+1] == separator // and it was also separator
214 #      ifdef BOOST_WINDOWS_API
215        || m_pathname[sep_pos+1] == preferred_separator  // or preferred_separator
216 #      endif
217 )) { m_pathname.erase(sep_pos, 1); } // erase the added separator
218   }
219
220   //  modifiers  -----------------------------------------------------------------------//
221
222 # ifdef BOOST_WINDOWS_API
223   BOOST_FILESYSTEM_DECL path& path::make_preferred()
224   {
225     std::replace(m_pathname.begin(), m_pathname.end(), L'/', L'\\');
226     return *this;
227   }
228 # endif
229
230   BOOST_FILESYSTEM_DECL path& path::remove_filename()
231   {
232     m_pathname.erase(m_parent_path_end());
233     return *this;
234   }
235
236   BOOST_FILESYSTEM_DECL path& path::remove_trailing_separator()
237   {
238     if (!m_pathname.empty()
239       && detail::is_directory_separator(m_pathname[m_pathname.size() - 1]))
240       m_pathname.erase(m_pathname.size() - 1);
241     return *this;
242   }
243
244   BOOST_FILESYSTEM_DECL path& path::replace_extension(const path& new_extension)
245   {
246     // erase existing extension, including the dot, if any
247     m_pathname.erase(m_pathname.size()-extension().m_pathname.size());
248
249     if (!new_extension.empty())
250     {
251       // append new_extension, adding the dot if necessary
252       if (new_extension.m_pathname[0] != dot)
253         m_pathname.push_back(dot);
254       m_pathname.append(new_extension.m_pathname);
255     }
256
257     return *this;
258   }
259
260   //  decomposition  -------------------------------------------------------------------//
261
262   BOOST_FILESYSTEM_DECL path path::root_path() const
263   {
264     path temp(root_name());
265     if (!root_directory().empty()) temp.m_pathname += root_directory().c_str();
266     return temp;
267   }
268
269   BOOST_FILESYSTEM_DECL path path::root_name() const
270   {
271     iterator itr(begin());
272
273     return (itr.m_pos != m_pathname.size()
274       && (
275           (itr.m_element.m_pathname.size() > 1
276             && detail::is_directory_separator(itr.m_element.m_pathname[0])
277             && detail::is_directory_separator(itr.m_element.m_pathname[1]))
278 #       ifdef BOOST_WINDOWS_API
279           || itr.m_element.m_pathname[itr.m_element.m_pathname.size()-1] == colon
280 #       endif
281       ))
282       ? itr.m_element
283       : path();
284   }
285
286   BOOST_FILESYSTEM_DECL path path::root_directory() const
287   {
288     size_type pos(root_directory_start(m_pathname, m_pathname.size()));
289
290     return pos == string_type::npos
291       ? path()
292       : path(m_pathname.c_str() + pos, m_pathname.c_str() + pos + 1);
293   }
294
295   BOOST_FILESYSTEM_DECL path path::relative_path() const
296   {
297     iterator itr(begin());
298
299     for (; itr.m_pos != m_pathname.size()
300       && (detail::is_directory_separator(itr.m_element.m_pathname[0])
301 #     ifdef BOOST_WINDOWS_API
302       || itr.m_element.m_pathname[itr.m_element.m_pathname.size()-1] == colon
303 #     endif
304     ); ++itr) {}
305
306     return path(m_pathname.c_str() + itr.m_pos);
307   }
308
309   BOOST_FILESYSTEM_DECL string_type::size_type path::m_parent_path_end() const
310   {
311     size_type end_pos(filename_pos(m_pathname, m_pathname.size()));
312
313     bool filename_was_separator(m_pathname.size()
314       && detail::is_directory_separator(m_pathname[end_pos]));
315
316     // skip separators unless root directory
317     size_type root_dir_pos(root_directory_start(m_pathname, end_pos));
318     for (;
319       end_pos > 0
320       && (end_pos-1) != root_dir_pos
321       && detail::is_directory_separator(m_pathname[end_pos-1])
322       ;
323       --end_pos) {}
324
325    return (end_pos == 1 && root_dir_pos == 0 && filename_was_separator)
326      ? string_type::npos
327      : end_pos;
328   }
329
330   BOOST_FILESYSTEM_DECL path path::parent_path() const
331   {
332    size_type end_pos(m_parent_path_end());
333    return end_pos == string_type::npos
334      ? path()
335      : path(m_pathname.c_str(), m_pathname.c_str() + end_pos);
336   }
337
338   BOOST_FILESYSTEM_DECL path path::filename() const
339   {
340     size_type pos(filename_pos(m_pathname, m_pathname.size()));
341     return (m_pathname.size()
342               && pos
343               && detail::is_directory_separator(m_pathname[pos])
344               && !is_root_separator(m_pathname, pos))
345       ? detail::dot_path()
346       : path(m_pathname.c_str() + pos);
347   }
348
349   BOOST_FILESYSTEM_DECL path path::stem() const
350   {
351     path name(filename());
352     if (name == detail::dot_path() || name == detail::dot_dot_path()) return name;
353     size_type pos(name.m_pathname.rfind(dot));
354     return pos == string_type::npos
355       ? name
356       : path(name.m_pathname.c_str(), name.m_pathname.c_str() + pos);
357   }
358
359   BOOST_FILESYSTEM_DECL path path::extension() const
360   {
361     path name(filename());
362     if (name == detail::dot_path() || name == detail::dot_dot_path()) return path();
363     size_type pos(name.m_pathname.rfind(dot));
364     return pos == string_type::npos
365       ? path()
366       : path(name.m_pathname.c_str() + pos);
367   }
368
369   //  lexical operations  --------------------------------------------------------------//
370
371   namespace detail
372   {
373     // C++14 provides a mismatch algorithm with four iterator arguments(), but earlier
374     // standard libraries didn't, so provide this needed functionality.
375     inline
376     std::pair<path::iterator, path::iterator> mismatch(path::iterator it1,
377       path::iterator it1end, path::iterator it2, path::iterator it2end)
378     {
379       for (; it1 != it1end && it2 != it2end && *it1 == *it2;)
380       {
381         ++it1;
382         ++it2;
383       }
384       return std::make_pair(it1, it2);
385     }
386   }
387
388   BOOST_FILESYSTEM_DECL path path::lexically_relative(const path& base) const
389   {
390     path::iterator b = begin(), e = end(), base_b = base.begin(), base_e = base.end();
391     std::pair<path::iterator, path::iterator> mm = detail::mismatch(b, e, base_b, base_e);
392     if (mm.first == b && mm.second == base_b)
393       return path();
394     if (mm.first == e && mm.second == base_e)
395       return detail::dot_path();
396
397     std::ptrdiff_t n = 0;
398     for (; mm.second != base_e; ++mm.second)
399     {
400       path const& p = *mm.second;
401       if (p == detail::dot_dot_path())
402         --n;
403       else if (!p.empty() && p != detail::dot_path())
404         ++n;
405     }
406     if (n < 0)
407       return path();
408     if (n == 0 && (mm.first == e || mm.first->empty()))
409       return detail::dot_path();
410
411     path tmp;
412     for (; n > 0; --n)
413       tmp /= detail::dot_dot_path();
414     for (; mm.first != e; ++mm.first)
415       tmp /= *mm.first;
416     return tmp;
417   }
418
419   //  normal  --------------------------------------------------------------------------//
420
421   BOOST_FILESYSTEM_DECL path path::lexically_normal() const
422   {
423     if (m_pathname.empty())
424       return *this;
425
426     path temp;
427     iterator start(begin());
428     iterator last(end());
429     iterator stop(last--);
430     for (iterator itr(start); itr != stop; ++itr)
431     {
432       // ignore "." except at start and last
433       if (itr->native().size() == 1
434         && (itr->native())[0] == dot
435         && itr != start
436         && itr != last) continue;
437
438       // ignore a name and following ".."
439       if (!temp.empty()
440         && itr->native().size() == 2
441         && (itr->native())[0] == dot
442         && (itr->native())[1] == dot) // dot dot
443       {
444         string_type lf(temp.filename().native());
445         if (lf.size() > 0
446           && (lf.size() != 1
447             || (lf[0] != dot
448               && lf[0] != separator))
449           && (lf.size() != 2
450             || (lf[0] != dot
451               && lf[1] != dot
452 #             ifdef BOOST_WINDOWS_API
453               && lf[1] != colon
454 #             endif
455                )
456              )
457           )
458         {
459           temp.remove_filename();
460           //// if not root directory, must also remove "/" if any
461           //if (temp.native().size() > 0
462           //  && temp.native()[temp.native().size()-1]
463           //    == separator)
464           //{
465           //  string_type::size_type rds(
466           //    root_directory_start(temp.native(), temp.native().size()));
467           //  if (rds == string_type::npos
468           //    || rds != temp.native().size()-1)
469           //  {
470           //    temp.m_pathname.erase(temp.native().size()-1);
471           //  }
472           //}
473
474           iterator next(itr);
475           if (temp.empty() && ++next != stop
476             && next == last && *last == detail::dot_path())
477           {
478             temp /= detail::dot_path();
479           }
480           continue;
481         }
482       }
483
484       temp /= *itr;
485     };
486
487     if (temp.empty())
488       temp /= detail::dot_path();
489     return temp;
490   }
491
492 }  // namespace filesystem
493 }  // namespace boost
494
495 //--------------------------------------------------------------------------------------//
496 //                                                                                      //
497 //                         class path helpers implementation                            //
498 //                                                                                      //
499 //--------------------------------------------------------------------------------------//
500
501 namespace
502 {
503
504   //  is_root_separator  ---------------------------------------------------------------//
505
506   bool is_root_separator(const string_type & str, size_type pos)
507     // pos is position of the separator
508   {
509     BOOST_ASSERT_MSG(!str.empty() && fs::detail::is_directory_separator(str[pos]),
510       "precondition violation");
511
512     // subsequent logic expects pos to be for leftmost slash of a set
513     while (pos > 0 && fs::detail::is_directory_separator(str[pos-1]))
514       --pos;
515
516     //  "/" [...]
517     if (pos == 0)
518       return true;
519
520 # ifdef BOOST_WINDOWS_API
521     //  "c:/" [...]
522     if (pos == 2 && is_letter(str[0]) && str[1] == colon)
523       return true;
524 # endif
525
526     //  "//" name "/"
527     if (pos < 3 || !fs::detail::is_directory_separator(str[0])
528       || !fs::detail::is_directory_separator(str[1]))
529       return false;
530
531     return str.find_first_of(separators, 2) == pos;
532   }
533
534   //  filename_pos  --------------------------------------------------------------------//
535
536   size_type filename_pos(const string_type & str,
537                           size_type end_pos) // end_pos is past-the-end position
538     // return 0 if str itself is filename (or empty)
539   {
540     // case: "//"
541     if (end_pos == 2
542       && fs::detail::is_directory_separator(str[0])
543       && fs::detail::is_directory_separator(str[1])) return 0;
544
545     // case: ends in "/"
546     if (end_pos && fs::detail::is_directory_separator(str[end_pos-1]))
547       return end_pos-1;
548
549     // set pos to start of last element
550     size_type pos(str.find_last_of(separators, end_pos-1));
551
552 #   ifdef BOOST_WINDOWS_API
553     if (pos == string_type::npos && end_pos > 1)
554       pos = str.find_last_of(colon, end_pos-2);
555 #   endif
556
557     return (pos == string_type::npos // path itself must be a filename (or empty)
558       || (pos == 1 && fs::detail::is_directory_separator(str[0]))) // or net
559         ? 0 // so filename is entire string
560         : pos + 1; // or starts after delimiter
561   }
562
563   //  root_directory_start  ------------------------------------------------------------//
564
565   size_type root_directory_start(const string_type & path, size_type size)
566   // return npos if no root_directory found
567   {
568
569 #   ifdef BOOST_WINDOWS_API
570     // case "c:/"
571     if (size > 2
572       && path[1] == colon
573       && fs::detail::is_directory_separator(path[2])) return 2;
574 #   endif
575
576     // case "//"
577     if (size == 2
578       && fs::detail::is_directory_separator(path[0])
579       && fs::detail::is_directory_separator(path[1])) return string_type::npos;
580
581 #   ifdef BOOST_WINDOWS_API
582     // case "\\?\"
583     if (size > 4
584       && fs::detail::is_directory_separator(path[0])
585       && fs::detail::is_directory_separator(path[1])
586       && path[2] == questionmark
587       && fs::detail::is_directory_separator(path[3]))
588     {
589       string_type::size_type pos(path.find_first_of(separators, 4));
590         return pos < size ? pos : string_type::npos;
591     }
592 #   endif
593
594     // case "//net {/}"
595     if (size > 3
596       && fs::detail::is_directory_separator(path[0])
597       && fs::detail::is_directory_separator(path[1])
598       && !fs::detail::is_directory_separator(path[2]))
599     {
600       string_type::size_type pos(path.find_first_of(separators, 2));
601       return pos < size ? pos : string_type::npos;
602     }
603
604     // case "/"
605     if (size > 0 && fs::detail::is_directory_separator(path[0])) return 0;
606
607     return string_type::npos;
608   }
609
610   //  first_element --------------------------------------------------------------------//
611   //   sets pos and len of first element, excluding extra separators
612   //   if src.empty(), sets pos,len, to 0,0.
613
614   void first_element(
615       const string_type & src,
616       size_type & element_pos,
617       size_type & element_size,
618       size_type size
619 )
620   {
621     if (size == string_type::npos) size = src.size();
622     element_pos = 0;
623     element_size = 0;
624     if (src.empty()) return;
625
626     string_type::size_type cur(0);
627
628     // deal with // [network]
629     if (size >= 2 && fs::detail::is_directory_separator(src[0])
630       && fs::detail::is_directory_separator(src[1])
631       && (size == 2
632         || !fs::detail::is_directory_separator(src[2])))
633     {
634       cur += 2;
635       element_size += 2;
636     }
637
638     // leading (not non-network) separator
639     else if (fs::detail::is_directory_separator(src[0]))
640     {
641       ++element_size;
642       // bypass extra leading separators
643       while (cur+1 < size
644         && fs::detail::is_directory_separator(src[cur+1]))
645       {
646         ++cur;
647         ++element_pos;
648       }
649       return;
650     }
651
652     // at this point, we have either a plain name, a network name,
653     // or (on Windows only) a device name
654
655     // find the end
656     while (cur < size
657 #     ifdef BOOST_WINDOWS_API
658       && src[cur] != colon
659 #     endif
660       && !fs::detail::is_directory_separator(src[cur]))
661     {
662       ++cur;
663       ++element_size;
664     }
665
666 #   ifdef BOOST_WINDOWS_API
667     if (cur == size) return;
668     // include device delimiter
669     if (src[cur] == colon)
670       { ++element_size; }
671 #   endif
672
673     return;
674   }
675
676 }  // unnamed namespace
677
678
679 namespace boost
680 {
681 namespace filesystem
682 {
683   namespace detail
684   {
685     BOOST_FILESYSTEM_DECL
686     int lex_compare(path::iterator first1, path::iterator last1,
687         path::iterator first2, path::iterator last2)
688     {
689       for (; first1 != last1 && first2 != last2;)
690       {
691         if (first1->native() < first2->native()) return -1;
692         if (first2->native() < first1->native()) return 1;
693         BOOST_ASSERT(first2->native() == first1->native());
694         ++first1;
695         ++first2;
696       }
697       if (first1 == last1 && first2 == last2)
698         return 0;
699       return first1 == last1 ? -1 : 1;
700     }
701
702     BOOST_FILESYSTEM_DECL
703     const path&  dot_path()
704     {
705 #   ifdef BOOST_WINDOWS_API
706       static const fs::path dot_pth(L".");
707 #   else
708       static const fs::path dot_pth(".");
709 #   endif
710       return dot_pth;
711     }
712
713     BOOST_FILESYSTEM_DECL
714     const path&  dot_dot_path()
715     {
716 #   ifdef BOOST_WINDOWS_API
717       static const fs::path dot_dot(L"..");
718 #   else
719       static const fs::path dot_dot("..");
720 #   endif
721       return dot_dot;
722     }
723   }
724
725 //--------------------------------------------------------------------------------------//
726 //                                                                                      //
727 //                        class path::iterator implementation                           //
728 //                                                                                      //
729 //--------------------------------------------------------------------------------------//
730
731   BOOST_FILESYSTEM_DECL path::iterator path::begin() const
732   {
733     iterator itr;
734     itr.m_path_ptr = this;
735     size_type element_size;
736     first_element(m_pathname, itr.m_pos, element_size);
737     itr.m_element = m_pathname.substr(itr.m_pos, element_size);
738     if (itr.m_element.m_pathname == preferred_separator_string)
739       itr.m_element.m_pathname = separator_string;  // needed for Windows, harmless on POSIX
740     return itr;
741   }
742
743   BOOST_FILESYSTEM_DECL path::iterator path::end() const
744   {
745     iterator itr;
746     itr.m_path_ptr = this;
747     itr.m_pos = m_pathname.size();
748     return itr;
749   }
750
751   BOOST_FILESYSTEM_DECL void path::m_path_iterator_increment(path::iterator & it)
752   {
753     BOOST_ASSERT_MSG(it.m_pos < it.m_path_ptr->m_pathname.size(),
754       "path::basic_iterator increment past end()");
755
756     // increment to position past current element; if current element is implicit dot,
757     // this will cause it.m_pos to represent the end iterator
758     it.m_pos += it.m_element.m_pathname.size();
759
760     // if the end is reached, we are done
761     if (it.m_pos == it.m_path_ptr->m_pathname.size())
762     {
763       it.m_element.clear();  // aids debugging, may release unneeded memory
764       return;
765     }
766
767     // both POSIX and Windows treat paths that begin with exactly two separators specially
768     bool was_net(it.m_element.m_pathname.size() > 2
769       && detail::is_directory_separator(it.m_element.m_pathname[0])
770       && detail::is_directory_separator(it.m_element.m_pathname[1])
771       && !detail::is_directory_separator(it.m_element.m_pathname[2]));
772
773     // process separator (Windows drive spec is only case not a separator)
774     if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
775     {
776       // detect root directory
777       if (was_net
778 #       ifdef BOOST_WINDOWS_API
779         // case "c:/"
780         || it.m_element.m_pathname[it.m_element.m_pathname.size()-1] == colon
781 #       endif
782          )
783       {
784         it.m_element.m_pathname = separator;  // generic format; see docs
785         return;
786       }
787
788       // skip separators until it.m_pos points to the start of the next element
789       while (it.m_pos != it.m_path_ptr->m_pathname.size()
790         && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
791         { ++it.m_pos; }
792
793       // detect trailing separator, and treat it as ".", per POSIX spec
794       if (it.m_pos == it.m_path_ptr->m_pathname.size()
795         && !is_root_separator(it.m_path_ptr->m_pathname, it.m_pos-1))
796       {
797         --it.m_pos;
798         it.m_element = detail::dot_path();
799         return;
800       }
801     }
802
803     // get m_element
804     size_type end_pos(it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos));
805     if (end_pos == string_type::npos)
806       end_pos = it.m_path_ptr->m_pathname.size();
807     it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos);
808   }
809
810   BOOST_FILESYSTEM_DECL void path::m_path_iterator_decrement(path::iterator & it)
811   {
812     BOOST_ASSERT_MSG(it.m_pos, "path::iterator decrement past begin()");
813
814     size_type end_pos(it.m_pos);
815
816     // if at end and there was a trailing non-root '/', return "."
817     if (it.m_pos == it.m_path_ptr->m_pathname.size()
818       && it.m_path_ptr->m_pathname.size() > 1
819       && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos-1])
820       && !is_root_separator(it.m_path_ptr->m_pathname, it.m_pos-1)
821        )
822     {
823       --it.m_pos;
824       it.m_element = detail::dot_path();
825       return;
826     }
827
828     size_type root_dir_pos(root_directory_start(it.m_path_ptr->m_pathname, end_pos));
829
830     // skip separators unless root directory
831     for (
832       ;
833       end_pos > 0
834       && (end_pos-1) != root_dir_pos
835       && detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos-1])
836       ;
837       --end_pos) {}
838
839     it.m_pos = filename_pos(it.m_path_ptr->m_pathname, end_pos);
840     it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos);
841     if (it.m_element.m_pathname == preferred_separator_string) // needed for Windows, harmless on POSIX
842       it.m_element.m_pathname = separator_string;    // generic format; see docs
843   }
844
845 }  // namespace filesystem
846 }  // namespace boost
847
848 namespace
849 {
850
851   //------------------------------------------------------------------------------------//
852   //                                locale helpers                                      //
853   //------------------------------------------------------------------------------------//
854
855   //  Prior versions of these locale and codecvt implementations tried to take advantage
856   //  of static initialization where possible, kept a local copy of the current codecvt
857   //  facet (to avoid codecvt() having to call use_facet()), and was not multi-threading
858   //  safe (again for efficiency).
859   //
860   //  This was error prone, and required different implementation techniques depending
861   //  on the compiler and also whether static or dynamic linking was used. Furthermore,
862   //  users could not easily provide their multi-threading safe wrappers because the
863   //  path interface requires the implementation itself to call codecvt() to obtain the
864   //  default facet, and the initialization of the static within path_locale() could race.
865   //
866   //  The code below is portable to all platforms, is much simpler, and hopefully will be
867   //  much more robust. Timing tests (on Windows, using a Visual C++ release build)
868   //  indicated the current code is roughly 9% slower than the previous code, and that
869   //  seems a small price to pay for better code that is easier to use.
870
871   std::locale default_locale()
872   {
873 # if defined(BOOST_WINDOWS_API)
874     std::locale global_loc = std::locale();
875     return std::locale(global_loc, new windows_file_codecvt);
876 # elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) \
877   || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
878     // "All BSD system functions expect their string parameters to be in UTF-8 encoding
879     // and nothing else." See
880     // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPInternational/Articles/FileEncodings.html
881     //
882     // "The kernel will reject any filename that is not a valid UTF-8 string, and it will
883     // even be normalized (to Unicode NFD) before stored on disk, at least when using HFS.
884     // The right way to deal with it would be to always convert the filename to UTF-8
885     // before trying to open/create a file." See
886     // http://lists.apple.com/archives/unix-porting/2007/Sep/msg00023.html
887     //
888     // "How a file name looks at the API level depends on the API. Current Carbon APIs
889     // handle file names as an array of UTF-16 characters; POSIX ones handle them as an
890     // array of UTF-8, which is why UTF-8 works well in Terminal. How it's stored on disk
891     // depends on the disk format; HFS+ uses UTF-16, but that's not important in most
892     // cases." See
893     // http://lists.apple.com/archives/applescript-users/2002/Sep/msg00319.html
894     //
895     // Many thanks to Peter Dimov for digging out the above references!
896
897     std::locale global_loc = std::locale();
898     return std::locale(global_loc, new boost::filesystem::detail::utf8_codecvt_facet);
899 # else  // Other POSIX
900     // ISO C calls std::locale("") "the locale-specific native environment", and this
901     // locale is the default for many POSIX-based operating systems such as Linux.
902     return std::locale("");
903 # endif
904   }
905
906   std::locale& path_locale()
907   // std::locale("") construction, needed on non-Apple POSIX systems, can throw
908   // (if environmental variables LC_MESSAGES or LANG are wrong, for example), so
909   // path_locale() provides lazy initialization via a local static to ensure that any
910   // exceptions occur after main() starts and so can be caught. Furthermore,
911   // path_locale() is only called if path::codecvt() or path::imbue() are themselves
912   // actually called, ensuring that an exception will only be thrown if std::locale("")
913   // is really needed.
914   {
915     // [locale] paragraph 6: Once a facet reference is obtained from a locale object by
916     // calling use_facet<>, that reference remains usable, and the results from member
917     // functions of it may be cached and re-used, as long as some locale object refers
918     // to that facet.
919     static std::locale loc(default_locale());
920 #ifdef BOOST_FILESYSTEM_DEBUG
921     std::cout << "***** path_locale() called" << std::endl;
922 #endif
923     return loc;
924   }
925 }  // unnamed namespace
926
927 //--------------------------------------------------------------------------------------//
928 //              path::codecvt() and path::imbue() implementation                        //
929 //--------------------------------------------------------------------------------------//
930
931 namespace boost
932 {
933 namespace filesystem
934 {
935   // See comments above
936
937   BOOST_FILESYSTEM_DECL const path::codecvt_type& path::codecvt()
938   {
939 #ifdef BOOST_FILESYSTEM_DEBUG
940     std::cout << "***** path::codecvt() called" << std::endl;
941 #endif
942     BOOST_ASSERT_MSG(&path_locale(), "boost::filesystem::path locale initialization error");
943
944     return std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t> >(path_locale());
945   }
946
947   BOOST_FILESYSTEM_DECL std::locale path::imbue(const std::locale& loc)
948   {
949 #ifdef BOOST_FILESYSTEM_DEBUG
950     std::cout << "***** path::imbue() called" << std::endl;
951 #endif
952     std::locale temp(path_locale());
953     path_locale() = loc;
954     return temp;
955   }
956
957 }  // namespace filesystem
958 }  // namespace boost