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