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