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