Ticket #10228: path.cpp

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