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