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