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