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