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