1 // Filesystem operations -*- C++ -*-
2
3 // Copyright (C) 2014-2019 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
24
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
27 # define NEED_DO_COPY_FILE
28 # define NEED_DO_SPACE
29 #endif
30
31 #include <bits/largefile-config.h>
32 #include <filesystem>
33 #include <functional>
34 #include <ostream>
35 #include <stack>
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <limits.h> // PATH_MAX
40 #ifdef _GLIBCXX_HAVE_FCNTL_H
41 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW
42 #endif
43 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
44 # include <sys/stat.h> // stat, utimensat, fchmodat
45 #endif
46 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
47 # include <sys/statvfs.h> // statvfs
48 #endif
49 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
50 # include <utime.h> // utime
51 #endif
52 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
53 # include <windows.h>
54 #endif
55
56 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
57 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
58 #include "../filesystem/ops-common.h"
59
60 #pragma GCC diagnostic ignored "-Wunused-parameter"
61
62 namespace fs = std::filesystem;
63 namespace posix = std::filesystem::__gnu_posix;
64
65 fs::path
absolute(const path & p)66 fs::absolute(const path& p)
67 {
68 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
69 error_code ec;
70 path ret = absolute(p, ec);
71 if (ec)
72 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
73 ec));
74 return ret;
75 #else
76 if (p.empty())
77 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
78 make_error_code(std::errc::invalid_argument)));
79 return current_path() / p;
80 #endif
81 }
82
83 fs::path
absolute(const path & p,error_code & ec)84 fs::absolute(const path& p, error_code& ec)
85 {
86 path ret;
87 if (p.empty())
88 {
89 ec = make_error_code(std::errc::invalid_argument);
90 return ret;
91 }
92 ec.clear();
93 if (p.is_absolute())
94 {
95 ret = p;
96 return ret;
97 }
98
99 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
100 // s must remain null-terminated
101 wstring_view s = p.native();
102
103 if (p.has_root_directory()) // implies !p.has_root_name()
104 {
105 // GetFullPathNameW("//") gives unwanted result (PR 88884).
106 // If there are multiple directory separators at the start,
107 // skip all but the last of them.
108 const auto pos = s.find_first_not_of(L"/\\");
109 __glibcxx_assert(pos != 0);
110 s.remove_prefix(std::min(s.length(), pos) - 1);
111 }
112
113 uint32_t len = 1024;
114 wstring buf;
115 do
116 {
117 buf.resize(len);
118 len = GetFullPathNameW(s.data(), len, buf.data(), nullptr);
119 }
120 while (len > buf.size());
121
122 if (len == 0)
123 ec.assign((int)GetLastError(), std::system_category());
124 else
125 {
126 buf.resize(len);
127 ret = std::move(buf);
128 }
129 #else
130 ret = current_path(ec);
131 ret /= p;
132 #endif
133 return ret;
134 }
135
136 namespace
137 {
138 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
is_dot(wchar_t c)139 inline bool is_dot(wchar_t c) { return c == L'.'; }
140 #else
141 inline bool is_dot(char c) { return c == '.'; }
142 #endif
143
is_dot(const fs::path & path)144 inline bool is_dot(const fs::path& path)
145 {
146 const auto& filename = path.native();
147 return filename.size() == 1 && is_dot(filename[0]);
148 }
149
is_dotdot(const fs::path & path)150 inline bool is_dotdot(const fs::path& path)
151 {
152 const auto& filename = path.native();
153 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
154 }
155
156 struct free_as_in_malloc
157 {
operator ()__anon71c2cb970111::free_as_in_malloc158 void operator()(void* p) const { ::free(p); }
159 };
160
161 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
162 }
163
164 fs::path
canonical(const path & p,error_code & ec)165 fs::canonical(const path& p, error_code& ec)
166 {
167 path result;
168 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
169 const path pa = absolute(p.lexically_normal(), ec);
170 #else
171 const path pa = absolute(p, ec);
172 #endif
173 if (ec)
174 return result;
175
176 #ifdef _GLIBCXX_USE_REALPATH
177 char_ptr buf{ nullptr };
178 # if _XOPEN_VERSION < 700
179 // Not safe to call realpath(path, NULL)
180 using char_type = fs::path::value_type;
181 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
182 # endif
183 if (char* rp = ::realpath(pa.c_str(), buf.get()))
184 {
185 if (buf == nullptr)
186 buf.reset(rp);
187 result.assign(rp);
188 ec.clear();
189 return result;
190 }
191 if (errno != ENAMETOOLONG)
192 {
193 ec.assign(errno, std::generic_category());
194 return result;
195 }
196 #endif
197
198 if (!exists(pa, ec))
199 {
200 if (!ec)
201 ec = make_error_code(std::errc::no_such_file_or_directory);
202 return result;
203 }
204 // else: we know there are (currently) no unresolvable symlink loops
205
206 result = pa.root_path();
207
208 deque<path> cmpts;
209 for (auto& f : pa.relative_path())
210 cmpts.push_back(f);
211
212 int max_allowed_symlinks = 40;
213
214 while (!cmpts.empty() && !ec)
215 {
216 path f = std::move(cmpts.front());
217 cmpts.pop_front();
218
219 if (f.empty())
220 {
221 // ignore empty element
222 }
223 else if (is_dot(f))
224 {
225 if (!is_directory(result, ec) && !ec)
226 ec.assign(ENOTDIR, std::generic_category());
227 }
228 else if (is_dotdot(f))
229 {
230 auto parent = result.parent_path();
231 if (parent.empty())
232 result = pa.root_path();
233 else
234 result.swap(parent);
235 }
236 else
237 {
238 result /= f;
239
240 if (is_symlink(result, ec))
241 {
242 path link = read_symlink(result, ec);
243 if (!ec)
244 {
245 if (--max_allowed_symlinks == 0)
246 ec.assign(ELOOP, std::generic_category());
247 else
248 {
249 if (link.is_absolute())
250 {
251 result = link.root_path();
252 link = link.relative_path();
253 }
254 else
255 result = result.parent_path();
256
257 cmpts.insert(cmpts.begin(), link.begin(), link.end());
258 }
259 }
260 }
261 }
262 }
263
264 if (ec || !exists(result, ec))
265 result.clear();
266
267 return result;
268 }
269
270 fs::path
canonical(const path & p)271 fs::canonical(const path& p)
272 {
273 error_code ec;
274 path res = canonical(p, ec);
275 if (ec)
276 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path",
277 p, ec));
278 return res;
279 }
280
281 void
copy(const path & from,const path & to,copy_options options)282 fs::copy(const path& from, const path& to, copy_options options)
283 {
284 error_code ec;
285 copy(from, to, options, ec);
286 if (ec)
287 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
288 }
289
290 namespace std::filesystem
291 {
292 // Need this as there's no 'perm_options::none' enumerator.
is_set(fs::perm_options obj,fs::perm_options bits)293 static inline bool is_set(fs::perm_options obj, fs::perm_options bits)
294 {
295 return (obj & bits) != fs::perm_options{};
296 }
297 }
298
299 namespace
300 {
301 struct internal_file_clock : fs::__file_clock
302 {
303 using __file_clock::_S_to_sys;
304 using __file_clock::_S_from_sys;
305
306 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
307 static fs::file_time_type
from_stat__anon71c2cb970211::internal_file_clock308 from_stat(const fs::stat_type& st, std::error_code& ec) noexcept
309 {
310 const auto sys_time = fs::file_time(st, ec);
311 if (sys_time == sys_time.min())
312 return fs::file_time_type::min();
313 return _S_from_sys(sys_time);
314 }
315 #endif
316 };
317 }
318
319 void
copy(const path & from,const path & to,copy_options options,error_code & ec)320 fs::copy(const path& from, const path& to, copy_options options,
321 error_code& ec)
322 {
323 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
324 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
325 const bool create_symlinks = is_set(options, copy_options::create_symlinks);
326 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
327 const bool use_lstat = create_symlinks || skip_symlinks;
328
329 file_status f, t;
330 stat_type from_st, to_st;
331 // _GLIBCXX_RESOLVE_LIB_DEFECTS
332 // 2681. filesystem::copy() cannot copy symlinks
333 if (use_lstat || copy_symlinks
334 ? posix::lstat(from.c_str(), &from_st)
335 : posix::stat(from.c_str(), &from_st))
336 {
337 ec.assign(errno, std::generic_category());
338 return;
339 }
340 if (use_lstat
341 ? posix::lstat(to.c_str(), &to_st)
342 : posix::stat(to.c_str(), &to_st))
343 {
344 if (!is_not_found_errno(errno))
345 {
346 ec.assign(errno, std::generic_category());
347 return;
348 }
349 t = file_status{file_type::not_found};
350 }
351 else
352 t = make_file_status(to_st);
353 f = make_file_status(from_st);
354
355 if (exists(t) && !is_other(t) && !is_other(f)
356 && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
357 {
358 ec = std::make_error_code(std::errc::file_exists);
359 return;
360 }
361 if (is_other(f) || is_other(t))
362 {
363 ec = std::make_error_code(std::errc::not_supported);
364 return;
365 }
366 if (is_directory(f) && is_regular_file(t))
367 {
368 ec = std::make_error_code(std::errc::is_a_directory);
369 return;
370 }
371
372 if (is_symlink(f))
373 {
374 if (skip_symlinks)
375 ec.clear();
376 else if (!exists(t) && copy_symlinks)
377 copy_symlink(from, to, ec);
378 else
379 // Not clear what should be done here.
380 // "Otherwise report an error as specified in Error reporting (7)."
381 ec = std::make_error_code(std::errc::invalid_argument);
382 }
383 else if (is_regular_file(f))
384 {
385 if (is_set(options, copy_options::directories_only))
386 ec.clear();
387 else if (create_symlinks)
388 create_symlink(from, to, ec);
389 else if (is_set(options, copy_options::create_hard_links))
390 create_hard_link(from, to, ec);
391 else if (is_directory(t))
392 do_copy_file(from.c_str(), (to / from.filename()).c_str(),
393 copy_file_options(options), &from_st, nullptr, ec);
394 else
395 {
396 auto ptr = exists(t) ? &to_st : &from_st;
397 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
398 &from_st, ptr, ec);
399 }
400 }
401 // _GLIBCXX_RESOLVE_LIB_DEFECTS
402 // 2682. filesystem::copy() won't create a symlink to a directory
403 else if (is_directory(f) && create_symlinks)
404 ec = std::make_error_code(errc::is_a_directory);
405 else if (is_directory(f) && (is_set(options, copy_options::recursive)
406 || options == copy_options::none))
407 {
408 if (!exists(t))
409 if (!create_directory(to, from, ec))
410 return;
411 // set an unused bit in options to disable further recursion
412 if (!is_set(options, copy_options::recursive))
413 options |= static_cast<copy_options>(4096);
414 for (const directory_entry& x : directory_iterator(from))
415 copy(x.path(), to/x.path().filename(), options, ec);
416 }
417 // _GLIBCXX_RESOLVE_LIB_DEFECTS
418 // 2683. filesystem::copy() says "no effects"
419 else
420 ec.clear();
421 #else
422 ec = std::make_error_code(std::errc::not_supported);
423 #endif
424 }
425
426 bool
copy_file(const path & from,const path & to,copy_options option)427 fs::copy_file(const path& from, const path& to, copy_options option)
428 {
429 error_code ec;
430 bool result = copy_file(from, to, option, ec);
431 if (ec)
432 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
433 ec));
434 return result;
435 }
436
437 bool
copy_file(const path & from,const path & to,copy_options options,error_code & ec)438 fs::copy_file(const path& from, const path& to, copy_options options,
439 error_code& ec)
440 {
441 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
442 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
443 nullptr, nullptr, ec);
444 #else
445 ec = std::make_error_code(std::errc::not_supported);
446 return false;
447 #endif
448 }
449
450
451 void
copy_symlink(const path & existing_symlink,const path & new_symlink)452 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
453 {
454 error_code ec;
455 copy_symlink(existing_symlink, new_symlink, ec);
456 if (ec)
457 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
458 existing_symlink, new_symlink, ec));
459 }
460
461 void
copy_symlink(const path & existing_symlink,const path & new_symlink,error_code & ec)462 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
463 error_code& ec) noexcept
464 {
465 auto p = read_symlink(existing_symlink, ec);
466 if (ec)
467 return;
468 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
469 if (is_directory(p))
470 {
471 create_directory_symlink(p, new_symlink, ec);
472 return;
473 }
474 #endif
475 create_symlink(p, new_symlink, ec);
476 }
477
478
479 bool
create_directories(const path & p)480 fs::create_directories(const path& p)
481 {
482 error_code ec;
483 bool result = create_directories(p, ec);
484 if (ec)
485 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
486 ec));
487 return result;
488 }
489
490 bool
create_directories(const path & p,error_code & ec)491 fs::create_directories(const path& p, error_code& ec)
492 {
493 if (p.empty())
494 {
495 ec = std::make_error_code(errc::invalid_argument);
496 return false;
497 }
498
499 file_status st = symlink_status(p, ec);
500 if (is_directory(st))
501 return false;
502 else if (ec && !status_known(st))
503 return false;
504 else if (exists(st))
505 {
506 if (!ec)
507 ec = std::make_error_code(std::errc::not_a_directory);
508 return false;
509 }
510
511 __glibcxx_assert(st.type() == file_type::not_found);
512 // !exists(p) so there must be at least one non-existent component in p.
513
514 std::stack<path> missing;
515 path pp = p;
516
517 // Strip any trailing slash
518 if (pp.has_relative_path() && !pp.has_filename())
519 pp = pp.parent_path();
520
521 do
522 {
523 const auto& filename = pp.filename();
524 if (is_dot(filename) || is_dotdot(filename))
525 pp = pp.parent_path();
526 else
527 {
528 missing.push(std::move(pp));
529 if (missing.size() > 1000) // sanity check
530 {
531 ec = std::make_error_code(std::errc::filename_too_long);
532 return false;
533 }
534 pp = missing.top().parent_path();
535 }
536
537 if (pp.empty())
538 break;
539
540 st = status(pp, ec);
541 if (exists(st))
542 {
543 if (ec)
544 return false;
545 if (!is_directory(st))
546 {
547 ec = std::make_error_code(std::errc::not_a_directory);
548 return false;
549 }
550 }
551
552 if (ec && exists(st))
553 return false;
554 }
555 while (st.type() == file_type::not_found);
556
557 __glibcxx_assert(!missing.empty());
558
559 bool created;
560 do
561 {
562 const path& top = missing.top();
563 created = create_directory(top, ec);
564 if (ec)
565 return false;
566 missing.pop();
567 }
568 while (!missing.empty());
569
570 return created;
571 }
572
573 namespace
574 {
575 bool
create_dir(const fs::path & p,fs::perms perm,std::error_code & ec)576 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
577 {
578 bool created = false;
579 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
580 posix::mode_t mode
581 = static_cast<std::underlying_type_t<fs::perms>>(perm);
582 if (posix::mkdir(p.c_str(), mode))
583 {
584 const int err = errno;
585 if (err != EEXIST || !is_directory(p, ec))
586 ec.assign(err, std::generic_category());
587 }
588 else
589 {
590 ec.clear();
591 created = true;
592 }
593 #else
594 ec = std::make_error_code(std::errc::not_supported);
595 #endif
596 return created;
597 }
598 } // namespace
599
600 bool
create_directory(const path & p)601 fs::create_directory(const path& p)
602 {
603 error_code ec;
604 bool result = create_directory(p, ec);
605 if (ec)
606 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
607 ec));
608 return result;
609 }
610
611 bool
create_directory(const path & p,error_code & ec)612 fs::create_directory(const path& p, error_code& ec) noexcept
613 {
614 return create_dir(p, perms::all, ec);
615 }
616
617
618 bool
create_directory(const path & p,const path & attributes)619 fs::create_directory(const path& p, const path& attributes)
620 {
621 error_code ec;
622 bool result = create_directory(p, attributes, ec);
623 if (ec)
624 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
625 ec));
626 return result;
627 }
628
629 bool
create_directory(const path & p,const path & attributes,error_code & ec)630 fs::create_directory(const path& p, const path& attributes,
631 error_code& ec) noexcept
632 {
633 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
634 stat_type st;
635 if (posix::stat(attributes.c_str(), &st))
636 {
637 ec.assign(errno, std::generic_category());
638 return false;
639 }
640 return create_dir(p, static_cast<perms>(st.st_mode), ec);
641 #else
642 ec = std::make_error_code(std::errc::not_supported);
643 return false;
644 #endif
645 }
646
647
648 void
create_directory_symlink(const path & to,const path & new_symlink)649 fs::create_directory_symlink(const path& to, const path& new_symlink)
650 {
651 error_code ec;
652 create_directory_symlink(to, new_symlink, ec);
653 if (ec)
654 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
655 to, new_symlink, ec));
656 }
657
658 void
create_directory_symlink(const path & to,const path & new_symlink,error_code & ec)659 fs::create_directory_symlink(const path& to, const path& new_symlink,
660 error_code& ec) noexcept
661 {
662 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
663 ec = std::make_error_code(std::errc::not_supported);
664 #else
665 create_symlink(to, new_symlink, ec);
666 #endif
667 }
668
669
670 void
create_hard_link(const path & to,const path & new_hard_link)671 fs::create_hard_link(const path& to, const path& new_hard_link)
672 {
673 error_code ec;
674 create_hard_link(to, new_hard_link, ec);
675 if (ec)
676 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
677 to, new_hard_link, ec));
678 }
679
680 void
create_hard_link(const path & to,const path & new_hard_link,error_code & ec)681 fs::create_hard_link(const path& to, const path& new_hard_link,
682 error_code& ec) noexcept
683 {
684 #ifdef _GLIBCXX_HAVE_LINK
685 if (::link(to.c_str(), new_hard_link.c_str()))
686 ec.assign(errno, std::generic_category());
687 else
688 ec.clear();
689 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
690 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
691 ec.clear();
692 else
693 ec.assign((int)GetLastError(), system_category());
694 #else
695 ec = std::make_error_code(std::errc::not_supported);
696 #endif
697 }
698
699 void
create_symlink(const path & to,const path & new_symlink)700 fs::create_symlink(const path& to, const path& new_symlink)
701 {
702 error_code ec;
703 create_symlink(to, new_symlink, ec);
704 if (ec)
705 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
706 to, new_symlink, ec));
707 }
708
709 void
create_symlink(const path & to,const path & new_symlink,error_code & ec)710 fs::create_symlink(const path& to, const path& new_symlink,
711 error_code& ec) noexcept
712 {
713 #ifdef _GLIBCXX_HAVE_SYMLINK
714 if (::symlink(to.c_str(), new_symlink.c_str()))
715 ec.assign(errno, std::generic_category());
716 else
717 ec.clear();
718 #else
719 ec = std::make_error_code(std::errc::not_supported);
720 #endif
721 }
722
723
724 fs::path
current_path()725 fs::current_path()
726 {
727 error_code ec;
728 path p = current_path(ec);
729 if (ec)
730 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
731 return p;
732 }
733
734 fs::path
current_path(error_code & ec)735 fs::current_path(error_code& ec)
736 {
737 path p;
738 #ifdef _GLIBCXX_HAVE_UNISTD_H
739 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
740 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
741 {
742 p.assign(cwd.get());
743 ec.clear();
744 }
745 else
746 ec.assign(errno, std::generic_category());
747 #else
748 #ifdef _PC_PATH_MAX
749 long path_max = pathconf(".", _PC_PATH_MAX);
750 size_t size;
751 if (path_max == -1)
752 size = 1024;
753 else if (path_max > 10240)
754 size = 10240;
755 else
756 size = path_max;
757 #elif defined(PATH_MAX)
758 size_t size = PATH_MAX;
759 #else
760 size_t size = 1024;
761 #endif
762 for (char_ptr buf; p.empty(); size *= 2)
763 {
764 using char_type = fs::path::value_type;
765 buf.reset((char_type*)malloc(size * sizeof(char_type)));
766 if (buf)
767 {
768 if (getcwd(buf.get(), size))
769 {
770 p.assign(buf.get());
771 ec.clear();
772 }
773 else if (errno != ERANGE)
774 {
775 ec.assign(errno, std::generic_category());
776 return {};
777 }
778 }
779 else
780 {
781 ec = std::make_error_code(std::errc::not_enough_memory);
782 return {};
783 }
784 }
785 #endif // __GLIBC__
786 #else // _GLIBCXX_HAVE_UNISTD_H
787 ec = std::make_error_code(std::errc::not_supported);
788 #endif
789 return p;
790 }
791
792 void
current_path(const path & p)793 fs::current_path(const path& p)
794 {
795 error_code ec;
796 current_path(p, ec);
797 if (ec)
798 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
799 }
800
801 void
current_path(const path & p,error_code & ec)802 fs::current_path(const path& p, error_code& ec) noexcept
803 {
804 #ifdef _GLIBCXX_HAVE_UNISTD_H
805 if (posix::chdir(p.c_str()))
806 ec.assign(errno, std::generic_category());
807 else
808 ec.clear();
809 #else
810 ec = std::make_error_code(std::errc::not_supported);
811 #endif
812 }
813
814 bool
equivalent(const path & p1,const path & p2)815 fs::equivalent(const path& p1, const path& p2)
816 {
817 error_code ec;
818 auto result = equivalent(p1, p2, ec);
819 if (ec)
820 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
821 p1, p2, ec));
822 return result;
823 }
824
825 bool
equivalent(const path & p1,const path & p2,error_code & ec)826 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
827 {
828 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
829 int err = 0;
830 file_status s1, s2;
831 stat_type st1, st2;
832 if (posix::stat(p1.c_str(), &st1) == 0)
833 s1 = make_file_status(st1);
834 else if (is_not_found_errno(errno))
835 s1.type(file_type::not_found);
836 else
837 err = errno;
838
839 if (posix::stat(p2.c_str(), &st2) == 0)
840 s2 = make_file_status(st2);
841 else if (is_not_found_errno(errno))
842 s2.type(file_type::not_found);
843 else
844 err = errno;
845
846 if (exists(s1) && exists(s2))
847 {
848 if (is_other(s1) && is_other(s2))
849 {
850 ec = std::make_error_code(std::errc::not_supported);
851 return false;
852 }
853 ec.clear();
854 if (is_other(s1) || is_other(s2))
855 return false;
856 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
857 // st_ino is not set, so can't be used to distinguish files
858 if (st1.st_mode != st2.st_mode || st1.st_dev != st2.st_dev)
859 return false;
860
861 struct auto_handle {
862 explicit auto_handle(const path& p_)
863 : handle(CreateFileW(p_.c_str(), 0,
864 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
865 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0))
866 { }
867
868 ~auto_handle()
869 { if (*this) CloseHandle(handle); }
870
871 explicit operator bool() const
872 { return handle != INVALID_HANDLE_VALUE; }
873
874 bool get_info()
875 { return GetFileInformationByHandle(handle, &info); }
876
877 HANDLE handle;
878 BY_HANDLE_FILE_INFORMATION info;
879 };
880 auto_handle h1(p1);
881 auto_handle h2(p2);
882 if (!h1 || !h2)
883 {
884 if (!h1 && !h2)
885 ec.assign((int)GetLastError(), system_category());
886 return false;
887 }
888 if (!h1.get_info() || !h2.get_info())
889 {
890 ec.assign((int)GetLastError(), system_category());
891 return false;
892 }
893 return h1.info.dwVolumeSerialNumber == h2.info.dwVolumeSerialNumber
894 && h1.info.nFileIndexHigh == h2.info.nFileIndexHigh
895 && h1.info.nFileIndexLow == h2.info.nFileIndexLow;
896 #else
897 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
898 #endif
899 }
900 else if (!exists(s1) && !exists(s2))
901 ec = std::make_error_code(std::errc::no_such_file_or_directory);
902 else if (err)
903 ec.assign(err, std::generic_category());
904 else
905 ec.clear();
906 return false;
907 #else
908 ec = std::make_error_code(std::errc::not_supported);
909 #endif
910 return false;
911 }
912
913 std::uintmax_t
file_size(const path & p)914 fs::file_size(const path& p)
915 {
916 error_code ec;
917 auto sz = file_size(p, ec);
918 if (ec)
919 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
920 return sz;
921 }
922
923 namespace
924 {
925 template<typename Accessor, typename T>
926 inline T
do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)927 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
928 {
929 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
930 posix::stat_type st;
931 if (posix::stat(p.c_str(), &st))
932 {
933 ec.assign(errno, std::generic_category());
934 return deflt;
935 }
936 ec.clear();
937 return f(st);
938 #else
939 ec = std::make_error_code(std::errc::not_supported);
940 return deflt;
941 #endif
942 }
943 }
944
945 std::uintmax_t
file_size(const path & p,error_code & ec)946 fs::file_size(const path& p, error_code& ec) noexcept
947 {
948 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
949 struct S
950 {
951 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
952 S() : type(file_type::not_found) { }
953 file_type type;
954 uintmax_t size;
955 };
956 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
957 if (s.type == file_type::regular)
958 return s.size;
959 if (!ec)
960 {
961 if (s.type == file_type::directory)
962 ec = std::make_error_code(std::errc::is_a_directory);
963 else
964 ec = std::make_error_code(std::errc::not_supported);
965 }
966 #else
967 ec = std::make_error_code(std::errc::not_supported);
968 #endif
969 return -1;
970 }
971
972 std::uintmax_t
hard_link_count(const path & p)973 fs::hard_link_count(const path& p)
974 {
975 error_code ec;
976 auto count = hard_link_count(p, ec);
977 if (ec)
978 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
979 return count;
980 }
981
982 std::uintmax_t
hard_link_count(const path & p,error_code & ec)983 fs::hard_link_count(const path& p, error_code& ec) noexcept
984 {
985 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
986 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
987 static_cast<uintmax_t>(-1));
988 #else
989 ec = std::make_error_code(std::errc::not_supported);
990 return static_cast<uintmax_t>(-1);
991 #endif
992 }
993
994 bool
is_empty(const path & p)995 fs::is_empty(const path& p)
996 {
997 error_code ec;
998 bool e = is_empty(p, ec);
999 if (ec)
1000 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
1001 p, ec));
1002 return e;
1003 }
1004
1005 bool
is_empty(const path & p,error_code & ec)1006 fs::is_empty(const path& p, error_code& ec)
1007 {
1008 auto s = status(p, ec);
1009 if (ec)
1010 return false;
1011 bool empty = fs::is_directory(s)
1012 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
1013 : fs::file_size(p, ec) == 0;
1014 return ec ? false : empty;
1015 }
1016
1017 fs::file_time_type
last_write_time(const path & p)1018 fs::last_write_time(const path& p)
1019 {
1020 error_code ec;
1021 auto t = last_write_time(p, ec);
1022 if (ec)
1023 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
1024 return t;
1025 }
1026
1027 fs::file_time_type
last_write_time(const path & p,error_code & ec)1028 fs::last_write_time(const path& p, error_code& ec) noexcept
1029 {
1030 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1031 return do_stat(p, ec,
1032 [&ec](const auto& st) {
1033 return internal_file_clock::from_stat(st, ec);
1034 },
1035 file_time_type::min());
1036 #else
1037 ec = std::make_error_code(std::errc::not_supported);
1038 return file_time_type::min();
1039 #endif
1040 }
1041
1042 void
last_write_time(const path & p,file_time_type new_time)1043 fs::last_write_time(const path& p, file_time_type new_time)
1044 {
1045 error_code ec;
1046 last_write_time(p, new_time, ec);
1047 if (ec)
1048 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
1049 }
1050
1051 void
last_write_time(const path & p,file_time_type new_time,error_code & ec)1052 fs::last_write_time(const path& p,
1053 file_time_type new_time, error_code& ec) noexcept
1054 {
1055 auto d = internal_file_clock::_S_to_sys(new_time).time_since_epoch();
1056 auto s = chrono::duration_cast<chrono::seconds>(d);
1057 #if _GLIBCXX_USE_UTIMENSAT
1058 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
1059 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
1060 {
1061 --s;
1062 ns += chrono::seconds(1);
1063 }
1064 struct ::timespec ts[2];
1065 ts[0].tv_sec = 0;
1066 ts[0].tv_nsec = UTIME_OMIT;
1067 ts[1].tv_sec = static_cast<std::time_t>(s.count());
1068 ts[1].tv_nsec = static_cast<long>(ns.count());
1069 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
1070 ec.assign(errno, std::generic_category());
1071 else
1072 ec.clear();
1073 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
1074 posix::utimbuf times;
1075 times.modtime = s.count();
1076 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
1077 times.modtime);
1078 if (posix::utime(p.c_str(), ×))
1079 ec.assign(errno, std::generic_category());
1080 else
1081 ec.clear();
1082 #else
1083 ec = std::make_error_code(std::errc::not_supported);
1084 #endif
1085 }
1086
1087 void
permissions(const path & p,perms prms,perm_options opts)1088 fs::permissions(const path& p, perms prms, perm_options opts)
1089 {
1090 error_code ec;
1091 permissions(p, prms, opts, ec);
1092 if (ec)
1093 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
1094 }
1095
1096 void
permissions(const path & p,perms prms,perm_options opts,error_code & ec)1097 fs::permissions(const path& p, perms prms, perm_options opts,
1098 error_code& ec) noexcept
1099 {
1100 const bool replace = is_set(opts, perm_options::replace);
1101 const bool add = is_set(opts, perm_options::add);
1102 const bool remove = is_set(opts, perm_options::remove);
1103 const bool nofollow = is_set(opts, perm_options::nofollow);
1104 if (((int)replace + (int)add + (int)remove) != 1)
1105 {
1106 ec = std::make_error_code(std::errc::invalid_argument);
1107 return;
1108 }
1109
1110 prms &= perms::mask;
1111
1112 file_status st;
1113 if (add || remove || nofollow)
1114 {
1115 st = nofollow ? symlink_status(p, ec) : status(p, ec);
1116 if (ec)
1117 return;
1118 auto curr = st.permissions();
1119 if (add)
1120 prms |= curr;
1121 else if (remove)
1122 prms = curr & ~prms;
1123 }
1124
1125 int err = 0;
1126 #if _GLIBCXX_USE_FCHMODAT
1127 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
1128 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
1129 err = errno;
1130 #else
1131 if (nofollow && is_symlink(st))
1132 ec = std::make_error_code(std::errc::not_supported);
1133 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
1134 err = errno;
1135 #endif
1136
1137 if (err)
1138 ec.assign(err, std::generic_category());
1139 else
1140 ec.clear();
1141 }
1142
1143 fs::path
proximate(const path & p,const path & base)1144 fs::proximate(const path& p, const path& base)
1145 {
1146 return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
1147 }
1148
1149 fs::path
proximate(const path & p,const path & base,error_code & ec)1150 fs::proximate(const path& p, const path& base, error_code& ec)
1151 {
1152 path result;
1153 const auto p2 = weakly_canonical(p, ec);
1154 if (!ec)
1155 {
1156 const auto base2 = weakly_canonical(base, ec);
1157 if (!ec)
1158 result = p2.lexically_proximate(base2);
1159 }
1160 return result;
1161 }
1162
1163 fs::path
read_symlink(const path & p)1164 fs::read_symlink(const path& p)
1165 {
1166 error_code ec;
1167 path tgt = read_symlink(p, ec);
1168 if (ec)
1169 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1170 return tgt;
1171 }
1172
read_symlink(const path & p,error_code & ec)1173 fs::path fs::read_symlink(const path& p, error_code& ec)
1174 {
1175 path result;
1176 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
1177 stat_type st;
1178 if (posix::lstat(p.c_str(), &st))
1179 {
1180 ec.assign(errno, std::generic_category());
1181 return result;
1182 }
1183 else if (!fs::is_symlink(make_file_status(st)))
1184 {
1185 ec.assign(EINVAL, std::generic_category());
1186 return result;
1187 }
1188
1189 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1190 do
1191 {
1192 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1193 if (len == -1)
1194 {
1195 ec.assign(errno, std::generic_category());
1196 return result;
1197 }
1198 else if (len == (ssize_t)buf.size())
1199 {
1200 if (buf.size() > 4096)
1201 {
1202 ec.assign(ENAMETOOLONG, std::generic_category());
1203 return result;
1204 }
1205 buf.resize(buf.size() * 2);
1206 }
1207 else
1208 {
1209 buf.resize(len);
1210 result.assign(buf);
1211 ec.clear();
1212 break;
1213 }
1214 }
1215 while (true);
1216 #else
1217 ec = std::make_error_code(std::errc::not_supported);
1218 #endif
1219 return result;
1220 }
1221
1222 fs::path
relative(const path & p,const path & base)1223 fs::relative(const path& p, const path& base)
1224 {
1225 return weakly_canonical(p).lexically_relative(weakly_canonical(base));
1226 }
1227
1228 fs::path
relative(const path & p,const path & base,error_code & ec)1229 fs::relative(const path& p, const path& base, error_code& ec)
1230 {
1231 auto result = weakly_canonical(p, ec);
1232 fs::path cbase;
1233 if (!ec)
1234 cbase = weakly_canonical(base, ec);
1235 if (!ec)
1236 result = result.lexically_relative(cbase);
1237 if (ec)
1238 result.clear();
1239 return result;
1240 }
1241
1242 bool
remove(const path & p)1243 fs::remove(const path& p)
1244 {
1245 error_code ec;
1246 const bool result = fs::remove(p, ec);
1247 if (ec)
1248 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1249 return result;
1250 }
1251
1252 bool
remove(const path & p,error_code & ec)1253 fs::remove(const path& p, error_code& ec) noexcept
1254 {
1255 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1256 auto st = symlink_status(p, ec);
1257 if (exists(st))
1258 {
1259 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1260 || DeleteFileW(p.c_str()))
1261 {
1262 ec.clear();
1263 return true;
1264 }
1265 else if (!ec)
1266 ec.assign((int)GetLastError(), system_category());
1267 }
1268 else if (status_known(st))
1269 ec.clear();
1270 #else
1271 if (::remove(p.c_str()) == 0)
1272 {
1273 ec.clear();
1274 return true;
1275 }
1276 else if (errno == ENOENT)
1277 ec.clear();
1278 else
1279 ec.assign(errno, std::generic_category());
1280 #endif
1281 return false;
1282 }
1283
1284
1285 std::uintmax_t
remove_all(const path & p)1286 fs::remove_all(const path& p)
1287 {
1288 error_code ec;
1289 const auto result = remove_all(p, ec);
1290 if (ec)
1291 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1292 return result;
1293 }
1294
1295 std::uintmax_t
remove_all(const path & p,error_code & ec)1296 fs::remove_all(const path& p, error_code& ec)
1297 {
1298 const auto s = symlink_status(p, ec);
1299 if (!status_known(s))
1300 return -1;
1301
1302 ec.clear();
1303 if (s.type() == file_type::not_found)
1304 return 0;
1305
1306 uintmax_t count = 0;
1307 if (s.type() == file_type::directory)
1308 {
1309 directory_iterator d(p, ec), end;
1310 while (!ec && d != end)
1311 {
1312 const auto removed = fs::remove_all(d->path(), ec);
1313 if (removed == numeric_limits<uintmax_t>::max())
1314 return -1;
1315 count += removed;
1316 d.increment(ec);
1317 if (ec)
1318 return -1;
1319 }
1320 }
1321
1322 if (fs::remove(p, ec))
1323 ++count;
1324 return ec ? -1 : count;
1325 }
1326
1327 void
rename(const path & from,const path & to)1328 fs::rename(const path& from, const path& to)
1329 {
1330 error_code ec;
1331 rename(from, to, ec);
1332 if (ec)
1333 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1334 }
1335
1336 void
rename(const path & from,const path & to,error_code & ec)1337 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1338 {
1339 if (posix::rename(from.c_str(), to.c_str()))
1340 ec.assign(errno, std::generic_category());
1341 else
1342 ec.clear();
1343 }
1344
1345 void
resize_file(const path & p,uintmax_t size)1346 fs::resize_file(const path& p, uintmax_t size)
1347 {
1348 error_code ec;
1349 resize_file(p, size, ec);
1350 if (ec)
1351 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1352 }
1353
1354 void
resize_file(const path & p,uintmax_t size,error_code & ec)1355 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1356 {
1357 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1358 ec.assign(EINVAL, std::generic_category());
1359 else if (posix::truncate(p.c_str(), size))
1360 ec.assign(errno, std::generic_category());
1361 else
1362 ec.clear();
1363 }
1364
1365
1366 fs::space_info
space(const path & p)1367 fs::space(const path& p)
1368 {
1369 error_code ec;
1370 space_info s = space(p, ec);
1371 if (ec)
1372 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1373 return s;
1374 }
1375
1376 fs::space_info
space(const path & p,error_code & ec)1377 fs::space(const path& p, error_code& ec) noexcept
1378 {
1379 space_info info = {
1380 static_cast<uintmax_t>(-1),
1381 static_cast<uintmax_t>(-1),
1382 static_cast<uintmax_t>(-1)
1383 };
1384 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1385 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1386 path dir = absolute(p);
1387 dir.remove_filename();
1388 auto str = dir.c_str();
1389 #else
1390 auto str = p.c_str();
1391 #endif
1392
1393 do_space(str, info.capacity, info.free, info.available, ec);
1394 #endif // _GLIBCXX_HAVE_SYS_STAT_H
1395
1396 return info;
1397 }
1398
1399 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1400 fs::file_status
status(const fs::path & p,error_code & ec)1401 fs::status(const fs::path& p, error_code& ec) noexcept
1402 {
1403 file_status status;
1404 auto str = p.c_str();
1405
1406 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1407 #if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6
1408 // stat() fails if there's a trailing slash (PR 88881)
1409 path p2;
1410 if (p.has_relative_path() && !p.has_filename())
1411 {
1412 __try
1413 {
1414 p2 = p.parent_path();
1415 str = p2.c_str();
1416 }
1417 __catch(const bad_alloc&)
1418 {
1419 ec = std::make_error_code(std::errc::not_enough_memory);
1420 return status;
1421 }
1422 str = p2.c_str();
1423 }
1424 #endif
1425 #endif
1426
1427 stat_type st;
1428 if (posix::stat(str, &st))
1429 {
1430 int err = errno;
1431 ec.assign(err, std::generic_category());
1432 if (is_not_found_errno(err))
1433 status.type(file_type::not_found);
1434 #ifdef EOVERFLOW
1435 else if (err == EOVERFLOW)
1436 status.type(file_type::unknown);
1437 #endif
1438 }
1439 else
1440 {
1441 status = make_file_status(st);
1442 ec.clear();
1443 }
1444 return status;
1445 }
1446
1447 fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1448 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1449 {
1450 file_status status;
1451 auto str = p.c_str();
1452
1453 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1454 #if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6
1455 // stat() fails if there's a trailing slash (PR 88881)
1456 path p2;
1457 if (p.has_relative_path() && !p.has_filename())
1458 {
1459 __try
1460 {
1461 p2 = p.parent_path();
1462 str = p2.c_str();
1463 }
1464 __catch(const bad_alloc&)
1465 {
1466 ec = std::make_error_code(std::errc::not_enough_memory);
1467 return status;
1468 }
1469 str = p2.c_str();
1470 }
1471 #endif
1472 #endif
1473
1474 stat_type st;
1475 if (posix::lstat(str, &st))
1476 {
1477 int err = errno;
1478 ec.assign(err, std::generic_category());
1479 if (is_not_found_errno(err))
1480 status.type(file_type::not_found);
1481 }
1482 else
1483 {
1484 status = make_file_status(st);
1485 ec.clear();
1486 }
1487 return status;
1488 }
1489 #endif
1490
1491 fs::file_status
status(const fs::path & p)1492 fs::status(const fs::path& p)
1493 {
1494 std::error_code ec;
1495 auto result = status(p, ec);
1496 if (result.type() == file_type::none)
1497 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1498 return result;
1499 }
1500
1501 fs::file_status
symlink_status(const fs::path & p)1502 fs::symlink_status(const fs::path& p)
1503 {
1504 std::error_code ec;
1505 auto result = symlink_status(p, ec);
1506 if (result.type() == file_type::none)
1507 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1508 return result;
1509 }
1510
temp_directory_path()1511 fs::path fs::temp_directory_path()
1512 {
1513 error_code ec;
1514 path tmp = temp_directory_path(ec);
1515 if (ec)
1516 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1517 return tmp;
1518 }
1519
temp_directory_path(error_code & ec)1520 fs::path fs::temp_directory_path(error_code& ec)
1521 {
1522 path p;
1523 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1524 unsigned len = 1024;
1525 std::wstring buf;
1526 do
1527 {
1528 buf.resize(len);
1529 len = GetTempPathW(buf.size(), buf.data());
1530 } while (len > buf.size());
1531
1532 if (len == 0)
1533 {
1534 ec.assign((int)GetLastError(), std::system_category());
1535 return p;
1536 }
1537 buf.resize(len);
1538 p = std::move(buf);
1539 #else
1540 const char* tmpdir = nullptr;
1541 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1542 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1543 tmpdir = ::getenv(*e);
1544 p = tmpdir ? tmpdir : "/tmp";
1545 #endif
1546 auto st = status(p, ec);
1547 if (ec)
1548 p.clear();
1549 else if (!is_directory(st))
1550 {
1551 p.clear();
1552 ec = std::make_error_code(std::errc::not_a_directory);
1553 }
1554 return p;
1555 }
1556
1557 fs::path
weakly_canonical(const path & p)1558 fs::weakly_canonical(const path& p)
1559 {
1560 path result;
1561 if (exists(status(p)))
1562 return canonical(p);
1563
1564 path tmp;
1565 auto iter = p.begin(), end = p.end();
1566 // find leading elements of p that exist:
1567 while (iter != end)
1568 {
1569 tmp = result / *iter;
1570 if (exists(status(tmp)))
1571 swap(result, tmp);
1572 else
1573 break;
1574 ++iter;
1575 }
1576 // canonicalize:
1577 if (!result.empty())
1578 result = canonical(result);
1579 // append the non-existing elements:
1580 while (iter != end)
1581 result /= *iter++;
1582 // normalize:
1583 return result.lexically_normal();
1584 }
1585
1586 fs::path
weakly_canonical(const path & p,error_code & ec)1587 fs::weakly_canonical(const path& p, error_code& ec)
1588 {
1589 path result;
1590 file_status st = status(p, ec);
1591 if (exists(st))
1592 return canonical(p, ec);
1593 else if (status_known(st))
1594 ec.clear();
1595 else
1596 return result;
1597
1598 path tmp;
1599 auto iter = p.begin(), end = p.end();
1600 // find leading elements of p that exist:
1601 while (iter != end)
1602 {
1603 tmp = result / *iter;
1604 st = status(tmp, ec);
1605 if (exists(st))
1606 swap(result, tmp);
1607 else
1608 {
1609 if (status_known(st))
1610 ec.clear();
1611 break;
1612 }
1613 ++iter;
1614 }
1615 // canonicalize:
1616 if (!ec && !result.empty())
1617 result = canonical(result, ec);
1618 if (ec)
1619 result.clear();
1620 else
1621 {
1622 // append the non-existing elements:
1623 while (iter != end)
1624 result /= *iter++;
1625 // normalize:
1626 result = result.lexically_normal();
1627 }
1628 return result;
1629 }
1630