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