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