]> git.ipfire.org Git - thirdparty/gcc.git/blame - libstdc++-v3/src/filesystem/std-ops.cc
PR libstdc++/78870 support std::filesystem on Windows
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / std-ops.cc
CommitLineData
641cb5a6
JW
1// Filesystem operations -*- C++ -*-
2
85ec4feb 3// Copyright (C) 2014-2018 Free Software Foundation, Inc.
641cb5a6
JW
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
220645d0 27# define NEED_DO_COPY_FILE
9534a5e6 28# define NEED_DO_SPACE
641cb5a6
JW
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
9534a5e6
JW
56#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
57# include <windows.h>
58#endif
641cb5a6
JW
59
60#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
61#define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
62#include "ops-common.h"
63
641cb5a6 64namespace fs = std::filesystem;
9534a5e6 65namespace posix = std::filesystem::__gnu_posix;
641cb5a6
JW
66
67fs::path
68fs::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,
9534a5e6 75 ec));
641cb5a6
JW
76 return ret;
77#else
78 return current_path() / p;
79#endif
80}
81
82fs::path
83fs::absolute(const path& p, error_code& ec)
84{
8a49324e
JW
85 path ret;
86 if (p.empty())
87 {
88 ec = make_error_code(std::errc::no_such_file_or_directory);
89 return ret;
90 }
641cb5a6 91#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
9534a5e6
JW
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 }
641cb5a6
JW
110#else
111 ec.clear();
8a49324e
JW
112 ret = current_path();
113 ret /= p;
641cb5a6 114#endif
8a49324e 115 return ret;
641cb5a6
JW
116}
117
118namespace
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
9534a5e6 143 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
641cb5a6
JW
144}
145
146fs::path
147fs::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)
9534a5e6
JW
158 using char_type = fs::path::value_type;
159 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
641cb5a6
JW
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
248fs::path
249fs::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
259void
260fs::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
268namespace 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
220645d0 278#ifdef NEED_DO_COPY_FILE
641cb5a6 279bool
9534a5e6 280fs::do_copy_file(const path::value_type* from, const path::value_type* to,
641cb5a6
JW
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 {
9534a5e6 290 if (posix::stat(to, &st1))
641cb5a6
JW
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 {
9534a5e6 312 if (posix::stat(from, &st2))
641cb5a6
JW
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 {
9534a5e6
JW
370 ~CloseFD() { if (fd != -1) posix::close(fd); }
371 bool close() { return posix::close(std::exchange(fd, -1)) == 0; }
641cb5a6
JW
372 int fd;
373 };
374
9534a5e6 375 CloseFD in = { posix::open(from, O_RDONLY) };
641cb5a6
JW
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;
9534a5e6 386 CloseFD out = { posix::open(to, oflag, S_IWUSR) };
641cb5a6
JW
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
9534a5e6 396#if defined _GLIBCXX_USE_FCHMOD && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
641cb5a6 397 if (::fchmod(out.fd, from_st->st_mode))
9534a5e6 398#elif defined _GLIBCXX_USE_FCHMODAT && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
641cb5a6
JW
399 if (::fchmodat(AT_FDCWD, to, from_st->st_mode, 0))
400#else
9534a5e6 401 if (posix::chmod(to, from_st->st_mode))
641cb5a6
JW
402#endif
403 {
404 ec.assign(errno, std::generic_category());
405 return false;
406 }
407
c49bef17 408 size_t count = from_st->st_size;
9534a5e6 409#if defined _GLIBCXX_USE_SENDFILE && ! defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
2526c53a
JW
410 off_t offset = 0;
411 ssize_t n = ::sendfile(out.fd, in.fd, &offset, count);
c49bef17 412 if (n < 0 && errno != ENOSYS && errno != EINVAL)
641cb5a6 413 {
c49bef17
JW
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;
641cb5a6 429#endif // _GLIBCXX_USE_SENDFILE
c49bef17 430
2526c53a
JW
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);
c49bef17
JW
434
435 if (sbin.is_open())
436 in.fd = -1;
437 if (sbout.is_open())
438 out.fd = -1;
439
2526c53a
JW
440#ifdef _GLIBCXX_USE_SENDFILE
441 if (n != 0)
c49bef17 442 {
2526c53a
JW
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));
c49bef17 450 if (p1 == errpos || p2 == errpos)
641cb5a6
JW
451 {
452 ec = std::make_error_code(std::errc::io_error);
453 return false;
454 }
c49bef17 455 }
2526c53a 456#endif
c49bef17
JW
457
458 if (count && !(std::ostream(&sbout) << &sbin))
641cb5a6 459 {
c49bef17 460 ec = std::make_error_code(std::errc::io_error);
641cb5a6
JW
461 return false;
462 }
c49bef17 463 if (!sbout.close() || !sbin.close())
641cb5a6
JW
464 {
465 ec.assign(errno, std::generic_category());
466 return false;
467 }
641cb5a6
JW
468 ec.clear();
469 return true;
641cb5a6 470}
220645d0 471#endif // NEED_DO_COPY_FILE
641cb5a6
JW
472#endif // _GLIBCXX_HAVE_SYS_STAT_H
473
474void
475fs::copy(const path& from, const path& to, copy_options options,
29453a9f 476 error_code& ec)
641cb5a6
JW
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
9534a5e6
JW
488 ? posix::lstat(from.c_str(), &from_st)
489 : posix::stat(from.c_str(), &from_st))
641cb5a6
JW
490 {
491 ec.assign(errno, std::generic_category());
492 return;
493 }
494 if (use_lstat
9534a5e6
JW
495 ? posix::lstat(to.c_str(), &to_st)
496 : posix::stat(to.c_str(), &to_st))
641cb5a6
JW
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
577bool
578fs::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
588bool
589fs::copy_file(const path& from, const path& to, copy_options options,
29453a9f 590 error_code& ec)
641cb5a6
JW
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
602void
603fs::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
612void
613fs::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
630bool
631fs::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
641bool
29453a9f 642fs::create_directories(const path& p, error_code& ec)
641cb5a6
JW
643{
644 if (p.empty())
645 {
646 ec = std::make_error_code(errc::invalid_argument);
647 return false;
648 }
649 std::stack<path> missing;
650 path pp = p;
651
652 while (pp.has_filename() && status(pp, ec).type() == file_type::not_found)
653 {
654 ec.clear();
655 const auto& filename = pp.filename();
656 if (!is_dot(filename) && !is_dotdot(filename))
657 missing.push(pp);
658 pp = pp.parent_path();
659
660 if (missing.size() > 1000) // sanity check
661 {
662 ec = std::make_error_code(std::errc::filename_too_long);
663 return false;
664 }
665 }
666
667 if (ec || missing.empty())
668 return false;
669
670 do
671 {
672 const path& top = missing.top();
673 create_directory(top, ec);
674 if (ec && is_directory(top))
675 ec.clear();
676 missing.pop();
677 }
678 while (!missing.empty() && !ec);
679
680 return missing.empty();
681}
682
683namespace
684{
685 bool
686 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
687 {
688 bool created = false;
689#ifdef _GLIBCXX_HAVE_SYS_STAT_H
9534a5e6
JW
690 posix::mode_t mode
691 = static_cast<std::underlying_type_t<fs::perms>>(perm);
692 if (posix::mkdir(p.c_str(), mode))
641cb5a6
JW
693 {
694 const int err = errno;
311735db 695 if (err != EEXIST || !is_directory(p, ec))
641cb5a6 696 ec.assign(err, std::generic_category());
641cb5a6
JW
697 }
698 else
699 {
700 ec.clear();
701 created = true;
702 }
703#else
704 ec = std::make_error_code(std::errc::not_supported);
705#endif
706 return created;
707 }
708} // namespace
709
710bool
711fs::create_directory(const path& p)
712{
713 error_code ec;
714 bool result = create_directory(p, ec);
715 if (ec)
716 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
717 ec));
718 return result;
719}
720
721bool
722fs::create_directory(const path& p, error_code& ec) noexcept
723{
724 return create_dir(p, perms::all, ec);
725}
726
727
728bool
729fs::create_directory(const path& p, const path& attributes)
730{
731 error_code ec;
732 bool result = create_directory(p, attributes, ec);
733 if (ec)
734 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory", p,
735 ec));
736 return result;
737}
738
739bool
740fs::create_directory(const path& p, const path& attributes,
741 error_code& ec) noexcept
742{
743#ifdef _GLIBCXX_HAVE_SYS_STAT_H
744 stat_type st;
9534a5e6 745 if (posix::stat(attributes.c_str(), &st))
641cb5a6
JW
746 {
747 ec.assign(errno, std::generic_category());
748 return false;
749 }
750 return create_dir(p, static_cast<perms>(st.st_mode), ec);
751#else
752 ec = std::make_error_code(std::errc::not_supported);
753 return false;
754#endif
755}
756
757
758void
759fs::create_directory_symlink(const path& to, const path& new_symlink)
760{
761 error_code ec;
762 create_directory_symlink(to, new_symlink, ec);
763 if (ec)
764 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directory symlink",
765 to, new_symlink, ec));
766}
767
768void
769fs::create_directory_symlink(const path& to, const path& new_symlink,
770 error_code& ec) noexcept
771{
772#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
773 ec = std::make_error_code(std::errc::not_supported);
774#else
775 create_symlink(to, new_symlink, ec);
776#endif
777}
778
779
780void
781fs::create_hard_link(const path& to, const path& new_hard_link)
782{
783 error_code ec;
784 create_hard_link(to, new_hard_link, ec);
785 if (ec)
786 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create hard link",
9534a5e6 787 to, new_hard_link, ec));
641cb5a6
JW
788}
789
790void
791fs::create_hard_link(const path& to, const path& new_hard_link,
792 error_code& ec) noexcept
793{
9534a5e6 794#ifdef _GLIBCXX_HAVE_LINK
641cb5a6
JW
795 if (::link(to.c_str(), new_hard_link.c_str()))
796 ec.assign(errno, std::generic_category());
797 else
798 ec.clear();
9534a5e6
JW
799#elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
800 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
801 ec.clear();
802 else
803 ec.assign((int)GetLastError(), generic_category());
641cb5a6
JW
804#else
805 ec = std::make_error_code(std::errc::not_supported);
806#endif
807}
808
809void
810fs::create_symlink(const path& to, const path& new_symlink)
811{
812 error_code ec;
813 create_symlink(to, new_symlink, ec);
814 if (ec)
815 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create symlink",
816 to, new_symlink, ec));
817}
818
819void
820fs::create_symlink(const path& to, const path& new_symlink,
821 error_code& ec) noexcept
822{
9534a5e6 823#ifdef _GLIBCXX_HAVE_SYMLINK
641cb5a6
JW
824 if (::symlink(to.c_str(), new_symlink.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
834fs::path
835fs::current_path()
836{
837 error_code ec;
838 path p = current_path(ec);
839 if (ec)
840 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get current path", ec));
841 return p;
842}
843
844fs::path
845fs::current_path(error_code& ec)
846{
847 path p;
848#ifdef _GLIBCXX_HAVE_UNISTD_H
9534a5e6
JW
849#if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
850 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
641cb5a6
JW
851 {
852 p.assign(cwd.get());
853 ec.clear();
854 }
855 else
856 ec.assign(errno, std::generic_category());
857#else
9534a5e6 858#ifdef _PC_PATH_MAX
641cb5a6
JW
859 long path_max = pathconf(".", _PC_PATH_MAX);
860 size_t size;
861 if (path_max == -1)
862 size = 1024;
863 else if (path_max > 10240)
864 size = 10240;
865 else
866 size = path_max;
9534a5e6
JW
867#elif defined(PATH_MAX)
868 size_t size = PATH_MAX;
869#else
870 size_t size = 1024;
871#endif
641cb5a6
JW
872 for (char_ptr buf; p.empty(); size *= 2)
873 {
9534a5e6
JW
874 using char_type = fs::path::value_type;
875 buf.reset((char_type*)malloc(size * sizeof(char_type)));
641cb5a6
JW
876 if (buf)
877 {
878 if (getcwd(buf.get(), size))
879 {
880 p.assign(buf.get());
881 ec.clear();
882 }
883 else if (errno != ERANGE)
884 {
885 ec.assign(errno, std::generic_category());
886 return {};
887 }
888 }
889 else
890 {
891 ec = std::make_error_code(std::errc::not_enough_memory);
892 return {};
893 }
894 }
895#endif // __GLIBC__
896#else // _GLIBCXX_HAVE_UNISTD_H
897 ec = std::make_error_code(std::errc::not_supported);
898#endif
899 return p;
900}
901
902void
903fs::current_path(const path& p)
904{
905 error_code ec;
906 current_path(p, ec);
907 if (ec)
908 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
909}
910
911void
912fs::current_path(const path& p, error_code& ec) noexcept
913{
914#ifdef _GLIBCXX_HAVE_UNISTD_H
9534a5e6 915 if (posix::chdir(p.c_str()))
641cb5a6
JW
916 ec.assign(errno, std::generic_category());
917 else
918 ec.clear();
919#else
920 ec = std::make_error_code(std::errc::not_supported);
921#endif
922}
923
924bool
925fs::equivalent(const path& p1, const path& p2)
926{
927 error_code ec;
928 auto result = equivalent(p1, p2, ec);
929 if (ec)
930 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence",
931 p1, p2, ec));
932 return result;
933}
934
935bool
936fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
937{
938#ifdef _GLIBCXX_HAVE_SYS_STAT_H
939 int err = 0;
940 file_status s1, s2;
941 stat_type st1, st2;
9534a5e6 942 if (posix::stat(p1.c_str(), &st1) == 0)
641cb5a6
JW
943 s1 = make_file_status(st1);
944 else if (is_not_found_errno(errno))
945 s1.type(file_type::not_found);
946 else
947 err = errno;
948
9534a5e6 949 if (posix::stat(p2.c_str(), &st2) == 0)
641cb5a6
JW
950 s2 = make_file_status(st2);
951 else if (is_not_found_errno(errno))
952 s2.type(file_type::not_found);
953 else
954 err = errno;
955
956 if (exists(s1) && exists(s2))
957 {
958 if (is_other(s1) && is_other(s2))
959 {
960 ec = std::make_error_code(std::errc::not_supported);
961 return false;
962 }
963 ec.clear();
964 if (is_other(s1) || is_other(s2))
965 return false;
966 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
967 }
968 else if (!exists(s1) && !exists(s2))
969 ec = std::make_error_code(std::errc::no_such_file_or_directory);
970 else if (err)
971 ec.assign(err, std::generic_category());
972 else
973 ec.clear();
974 return false;
975#else
976 ec = std::make_error_code(std::errc::not_supported);
977#endif
978 return false;
979}
980
981std::uintmax_t
982fs::file_size(const path& p)
983{
984 error_code ec;
985 auto sz = file_size(p, ec);
986 if (ec)
987 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
988 return sz;
989}
990
991namespace
992{
993 template<typename Accessor, typename T>
994 inline T
995 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
996 {
997#ifdef _GLIBCXX_HAVE_SYS_STAT_H
9534a5e6
JW
998 posix::stat_type st;
999 if (posix::stat(p.c_str(), &st))
641cb5a6
JW
1000 {
1001 ec.assign(errno, std::generic_category());
1002 return deflt;
1003 }
1004 ec.clear();
1005 return f(st);
1006#else
1007 ec = std::make_error_code(std::errc::not_supported);
1008 return deflt;
1009#endif
1010 }
1011}
1012
1013std::uintmax_t
1014fs::file_size(const path& p, error_code& ec) noexcept
1015{
1016 struct S
1017 {
1018 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
1019 S() : type(file_type::not_found) { }
1020 file_type type;
1021 size_t size;
1022 };
1023 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
1024 if (s.type == file_type::regular)
1025 return s.size;
1026 if (!ec)
1027 {
1028 if (s.type == file_type::directory)
1029 ec = std::make_error_code(std::errc::is_a_directory);
1030 else
1031 ec = std::make_error_code(std::errc::not_supported);
1032 }
1033 return -1;
1034}
1035
1036std::uintmax_t
1037fs::hard_link_count(const path& p)
1038{
1039 error_code ec;
1040 auto count = hard_link_count(p, ec);
1041 if (ec)
1042 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
1043 return count;
1044}
1045
1046std::uintmax_t
1047fs::hard_link_count(const path& p, error_code& ec) noexcept
1048{
9534a5e6 1049 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
641cb5a6
JW
1050 static_cast<uintmax_t>(-1));
1051}
1052
1053bool
1054fs::is_empty(const path& p)
1055{
1056 error_code ec;
1057 bool e = is_empty(p, ec);
1058 if (ec)
1059 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
1060 p, ec));
1061 return e;
1062}
1063
1064bool
29453a9f 1065fs::is_empty(const path& p, error_code& ec)
641cb5a6
JW
1066{
1067 auto s = status(p, ec);
1068 if (ec)
1069 return false;
1070 bool empty = fs::is_directory(s)
1071 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
1072 : fs::file_size(p, ec) == 0;
1073 return ec ? false : empty;
1074}
1075
1076fs::file_time_type
1077fs::last_write_time(const path& p)
1078{
1079 error_code ec;
1080 auto t = last_write_time(p, ec);
1081 if (ec)
1082 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
1083 return t;
1084}
1085
1086fs::file_time_type
1087fs::last_write_time(const path& p, error_code& ec) noexcept
1088{
1089 return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); },
1090 file_time_type::min());
1091}
1092
1093void
1094fs::last_write_time(const path& p, file_time_type new_time)
1095{
1096 error_code ec;
1097 last_write_time(p, new_time, ec);
1098 if (ec)
1099 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
1100}
1101
1102void
1103fs::last_write_time(const path& p __attribute__((__unused__)),
1104 file_time_type new_time, error_code& ec) noexcept
1105{
1106 auto d = new_time.time_since_epoch();
1107 auto s = chrono::duration_cast<chrono::seconds>(d);
1108#if _GLIBCXX_USE_UTIMENSAT
1109 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
1110 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
1111 {
1112 --s;
1113 ns += chrono::seconds(1);
1114 }
1115 struct ::timespec ts[2];
1116 ts[0].tv_sec = 0;
1117 ts[0].tv_nsec = UTIME_OMIT;
1118 ts[1].tv_sec = static_cast<std::time_t>(s.count());
1119 ts[1].tv_nsec = static_cast<long>(ns.count());
1120 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
1121 ec.assign(errno, std::generic_category());
1122 else
1123 ec.clear();
1124#elif _GLIBCXX_HAVE_UTIME_H
9534a5e6 1125 posix::utimbuf times;
641cb5a6
JW
1126 times.modtime = s.count();
1127 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
1128 times.modtime);
9534a5e6 1129 if (posix::utime(p.c_str(), &times))
641cb5a6
JW
1130 ec.assign(errno, std::generic_category());
1131 else
1132 ec.clear();
1133#else
1134 ec = std::make_error_code(std::errc::not_supported);
1135#endif
1136}
1137
1138void
1139fs::permissions(const path& p, perms prms, perm_options opts)
1140{
1141 error_code ec;
1142 permissions(p, prms, opts, ec);
1143 if (ec)
1144 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
1145}
1146
1147void
1148fs::permissions(const path& p, perms prms, perm_options opts,
1149 error_code& ec) noexcept
1150{
1151 const bool replace = is_set(opts, perm_options::replace);
1152 const bool add = is_set(opts, perm_options::add);
1153 const bool remove = is_set(opts, perm_options::remove);
1154 const bool nofollow = is_set(opts, perm_options::nofollow);
1155 if (((int)replace + (int)add + (int)remove) != 1)
1156 {
1157 ec = std::make_error_code(std::errc::invalid_argument);
1158 return;
1159 }
1160
1161 prms &= perms::mask;
1162
1163 file_status st;
1164 if (add || remove || nofollow)
1165 {
1166 st = nofollow ? symlink_status(p, ec) : status(p, ec);
1167 if (ec)
1168 return;
1169 auto curr = st.permissions();
1170 if (add)
1171 prms |= curr;
1172 else if (remove)
1173 prms = curr & ~prms;
1174 }
1175
1176 int err = 0;
1177#if _GLIBCXX_USE_FCHMODAT
1178 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
1179 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
1180 err = errno;
1181#else
1182 if (nofollow && is_symlink(st))
1183 ec = std::make_error_code(std::errc::operation_not_supported);
9534a5e6 1184 else if (posix::chmod(p.c_str(), static_cast<mode_t>(prms)))
641cb5a6
JW
1185 err = errno;
1186#endif
1187
1188 if (err)
1189 ec.assign(err, std::generic_category());
1190 else
1191 ec.clear();
1192}
1193
1194fs::path
1195fs::proximate(const path& p, const path& base)
1196{
1197 return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
1198}
1199
1200fs::path
1201fs::proximate(const path& p, const path& base, error_code& ec)
1202{
1203 path result;
1204 const auto p2 = weakly_canonical(p, ec);
1205 if (!ec)
1206 {
1207 const auto base2 = weakly_canonical(base, ec);
1208 if (!ec)
1209 result = p2.lexically_proximate(base2);
1210 }
1211 return result;
1212}
1213
1214fs::path
1215fs::read_symlink(const path& p)
1216{
1217 error_code ec;
1218 path tgt = read_symlink(p, ec);
1219 if (ec)
1220 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1221 return tgt;
1222}
1223
9534a5e6 1224fs::path fs::read_symlink(const path& p [[gnu::unused]], error_code& ec)
641cb5a6 1225{
220645d0 1226 path result;
9534a5e6 1227#if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
641cb5a6
JW
1228 stat_type st;
1229 if (::lstat(p.c_str(), &st))
1230 {
1231 ec.assign(errno, std::generic_category());
220645d0 1232 return result;
641cb5a6 1233 }
220645d0
JW
1234 std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1235 do
641cb5a6 1236 {
220645d0
JW
1237 ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1238 if (len == -1)
1239 {
1240 ec.assign(errno, std::generic_category());
1241 return result;
1242 }
1243 else if (len == (ssize_t)buf.size())
1244 {
1245 if (buf.size() > 4096)
1246 {
1247 ec.assign(ENAMETOOLONG, std::generic_category());
1248 return result;
1249 }
1250 buf.resize(buf.size() * 2);
1251 }
1252 else
1253 {
1254 buf.resize(len);
1255 result.assign(buf);
1256 ec.clear();
1257 break;
1258 }
641cb5a6 1259 }
220645d0 1260 while (true);
641cb5a6
JW
1261#else
1262 ec = std::make_error_code(std::errc::not_supported);
641cb5a6 1263#endif
220645d0 1264 return result;
641cb5a6
JW
1265}
1266
1267fs::path
1268fs::relative(const path& p, const path& base)
1269{
1270 return weakly_canonical(p).lexically_relative(weakly_canonical(base));
1271}
1272
1273fs::path
1274fs::relative(const path& p, const path& base, error_code& ec)
1275{
1276 auto result = weakly_canonical(p, ec);
1277 fs::path cbase;
1278 if (!ec)
1279 cbase = weakly_canonical(base, ec);
1280 if (!ec)
1281 result = result.lexically_relative(cbase);
1282 if (ec)
1283 result.clear();
1284 return result;
1285}
1286
1287bool
1288fs::remove(const path& p)
1289{
1290 error_code ec;
4ca07db0 1291 const bool result = fs::remove(p, ec);
641cb5a6
JW
1292 if (ec)
1293 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1294 return result;
1295}
1296
1297bool
1298fs::remove(const path& p, error_code& ec) noexcept
1299{
9534a5e6
JW
1300#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1301 if (exists(symlink_status(p, ec)))
1302 {
1303 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1304 || DeleteFileW(p.c_str()))
1305 {
1306 ec.clear();
1307 return true;
1308 }
1309 else if (!ec)
1310 ec.assign((int)GetLastError(), generic_category());
1311 }
1312#else
388058dd 1313 if (::remove(p.c_str()) == 0)
994844d3 1314 {
388058dd
JW
1315 ec.clear();
1316 return true;
994844d3 1317 }
388058dd
JW
1318 else if (errno == ENOENT)
1319 ec.clear();
1320 else
1321 ec.assign(errno, std::generic_category());
9534a5e6 1322#endif
388058dd 1323 return false;
641cb5a6
JW
1324}
1325
1326
1327std::uintmax_t
1328fs::remove_all(const path& p)
1329{
1330 error_code ec;
388058dd 1331 const auto result = remove_all(p, ec);
641cb5a6
JW
1332 if (ec)
1333 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1334 return result;
1335}
1336
1337std::uintmax_t
29453a9f 1338fs::remove_all(const path& p, error_code& ec)
641cb5a6 1339{
994844d3
JW
1340 const auto s = symlink_status(p, ec);
1341 if (!status_known(s))
1342 return -1;
388058dd 1343
994844d3 1344 ec.clear();
388058dd
JW
1345 if (s.type() == file_type::not_found)
1346 return 0;
1347
641cb5a6 1348 uintmax_t count = 0;
994844d3 1349 if (s.type() == file_type::directory)
388058dd
JW
1350 {
1351 for (directory_iterator d(p, ec), end; !ec && d != end; d.increment(ec))
1352 count += fs::remove_all(d->path(), ec);
1353 if (ec.value() == ENOENT)
1354 ec.clear();
1355 else if (ec)
1356 return -1;
1357 }
1358
4ca07db0 1359 if (fs::remove(p, ec))
994844d3 1360 ++count;
4ca07db0 1361 return ec ? -1 : count;
641cb5a6
JW
1362}
1363
1364void
1365fs::rename(const path& from, const path& to)
1366{
1367 error_code ec;
1368 rename(from, to, ec);
1369 if (ec)
1370 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1371}
1372
1373void
1374fs::rename(const path& from, const path& to, error_code& ec) noexcept
1375{
9534a5e6 1376 if (posix::rename(from.c_str(), to.c_str()))
641cb5a6
JW
1377 ec.assign(errno, std::generic_category());
1378 else
1379 ec.clear();
1380}
1381
1382void
1383fs::resize_file(const path& p, uintmax_t size)
1384{
1385 error_code ec;
1386 resize_file(p, size, ec);
1387 if (ec)
1388 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1389}
1390
1391void
1392fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1393{
1394#ifdef _GLIBCXX_HAVE_UNISTD_H
1395 if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1396 ec.assign(EINVAL, std::generic_category());
9534a5e6 1397 else if (posix::truncate(p.c_str(), size))
641cb5a6
JW
1398 ec.assign(errno, std::generic_category());
1399 else
1400 ec.clear();
1401#else
1402 ec = std::make_error_code(std::errc::not_supported);
1403#endif
1404}
1405
1406
1407fs::space_info
1408fs::space(const path& p)
1409{
1410 error_code ec;
1411 space_info s = space(p, ec);
1412 if (ec)
1413 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1414 return s;
1415}
1416
9534a5e6
JW
1417#ifdef NEED_DO_SPACE
1418void
1419fs::do_space(const __gnu_posix::char_type* pathname,
1420 uintmax_t& capacity, uintmax_t& free, uintmax_t& available,
1421 std::error_code& ec)
641cb5a6 1422{
641cb5a6
JW
1423#ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
1424 struct ::statvfs f;
9534a5e6 1425 if (::statvfs(pathname, &f))
641cb5a6
JW
1426 ec.assign(errno, std::generic_category());
1427 else
1428 {
9534a5e6
JW
1429 if (f.f_frsize != (unsigned long)-1)
1430 {
1431 const uintmax_t fragment_size = f.f_frsize;
1432 const fsblkcnt_t unknown = -1;
1433 if (f.f_blocks != unknown)
1434 capacity = f.f_blocks * fragment_size;
1435 if (f.f_bfree != unknown)
1436 free = f.f_bfree * fragment_size;
1437 if (f.f_bavail != unknown)
1438 available = f.f_bavail * fragment_size;
1439 }
641cb5a6
JW
1440 ec.clear();
1441 }
9534a5e6
JW
1442#elif _GLIBCXX_FILESYSTEM_IS_WINDOWS
1443 ULARGE_INTEGER bytes_avail = {}, bytes_total = {}, bytes_free = {};
1444 if (GetDiskFreeSpaceExW(pathname, &bytes_avail, &bytes_total, &bytes_free))
1445 {
1446 if (bytes_total.QuadPart != 0)
1447 capacity = bytes_total.QuadPart;
1448 if (bytes_free.QuadPart != 0)
1449 free = bytes_free.QuadPart;
1450 if (bytes_avail.QuadPart != 0)
1451 available = bytes_avail.QuadPart;
1452 ec.clear();
1453 }
1454 else
1455 ec.assign((int)GetLastError(), std::system_category());
641cb5a6
JW
1456#else
1457 ec = std::make_error_code(std::errc::not_supported);
1458#endif
9534a5e6
JW
1459}
1460#endif // NEED_DO_SPACE
1461
1462fs::space_info
1463fs::space(const path& p, error_code& ec) noexcept
1464{
1465 space_info info = {
1466 static_cast<uintmax_t>(-1),
1467 static_cast<uintmax_t>(-1),
1468 static_cast<uintmax_t>(-1)
1469 };
1470#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1471 path dir = absolute(p);
1472 dir.remove_filename();
1473 auto str = dir.c_str();
1474#else
1475 auto str = p.c_str();
1476#endif
1477 do_space(str, info.capacity, info.free, info.available, ec);
641cb5a6
JW
1478 return info;
1479}
1480
1481#ifdef _GLIBCXX_HAVE_SYS_STAT_H
1482fs::file_status
1483fs::status(const fs::path& p, error_code& ec) noexcept
1484{
1485 file_status status;
1486 stat_type st;
9534a5e6 1487 if (posix::stat(p.c_str(), &st))
641cb5a6
JW
1488 {
1489 int err = errno;
1490 ec.assign(err, std::generic_category());
1491 if (is_not_found_errno(err))
1492 status.type(file_type::not_found);
1493#ifdef EOVERFLOW
1494 else if (err == EOVERFLOW)
1495 status.type(file_type::unknown);
1496#endif
1497 }
1498 else
1499 {
1500 status = make_file_status(st);
1501 ec.clear();
1502 }
1503 return status;
1504}
1505
1506fs::file_status
1507fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1508{
1509 file_status status;
1510 stat_type st;
9534a5e6 1511 if (posix::lstat(p.c_str(), &st))
641cb5a6
JW
1512 {
1513 int err = errno;
1514 ec.assign(err, std::generic_category());
1515 if (is_not_found_errno(err))
1516 status.type(file_type::not_found);
1517 }
1518 else
1519 {
1520 status = make_file_status(st);
1521 ec.clear();
1522 }
1523 return status;
1524}
1525#endif
1526
1527fs::file_status
1528fs::status(const fs::path& p)
1529{
1530 std::error_code ec;
1531 auto result = status(p, ec);
1532 if (result.type() == file_type::none)
1533 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1534 return result;
1535}
1536
1537fs::file_status
1538fs::symlink_status(const fs::path& p)
1539{
1540 std::error_code ec;
1541 auto result = symlink_status(p, ec);
1542 if (result.type() == file_type::none)
1543 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1544 return result;
1545}
1546
1547fs::path fs::temp_directory_path()
1548{
1549 error_code ec;
1550 path tmp = temp_directory_path(ec);
1551 if (ec)
1552 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1553 return tmp;
1554}
1555
1556fs::path fs::temp_directory_path(error_code& ec)
1557{
9534a5e6 1558 path p;
641cb5a6 1559#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
9534a5e6
JW
1560 unsigned len = 1024;
1561 std::wstring buf;
1562 do
1563 {
1564 buf.resize(len);
1565 len = GetTempPathW(buf.size(), buf.data());
1566 } while (len > buf.size());
1567
1568 if (len == 0)
1569 {
1570 ec.assign((int)GetLastError(), std::system_category());
1571 return p;
1572 }
1573 buf.resize(len);
1574 p = std::move(buf);
641cb5a6
JW
1575#else
1576 const char* tmpdir = nullptr;
1577 const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1578 for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1579 tmpdir = ::getenv(*e);
9534a5e6
JW
1580 p = tmpdir ? tmpdir : "/tmp";
1581#endif
641cb5a6 1582 auto st = status(p, ec);
9534a5e6
JW
1583 if (ec)
1584 p.clear();
1585 else if (!is_directory(st))
641cb5a6 1586 {
9534a5e6
JW
1587 p.clear();
1588 ec = std::make_error_code(std::errc::not_a_directory);
641cb5a6 1589 }
9534a5e6 1590 return p;
641cb5a6
JW
1591}
1592
1593fs::path
1594fs::weakly_canonical(const path& p)
1595{
1596 path result;
1597 if (exists(status(p)))
1598 return canonical(p);
1599
1600 path tmp;
1601 auto iter = p.begin(), end = p.end();
1602 // find leading elements of p that exist:
1603 while (iter != end)
1604 {
1605 tmp = result / *iter;
1606 if (exists(status(tmp)))
1607 swap(result, tmp);
1608 else
1609 break;
1610 ++iter;
1611 }
1612 // canonicalize:
8a49324e
JW
1613 if (!result.empty())
1614 result = canonical(result);
641cb5a6
JW
1615 // append the non-existing elements:
1616 while (iter != end)
1617 result /= *iter++;
1618 // normalize:
1619 return result.lexically_normal();
1620}
1621
1622fs::path
1623fs::weakly_canonical(const path& p, error_code& ec)
1624{
1625 path result;
1626 file_status st = status(p, ec);
1627 if (exists(st))
1628 return canonical(p, ec);
1629 else if (status_known(st))
1630 ec.clear();
1631 else
1632 return result;
1633
1634 path tmp;
1635 auto iter = p.begin(), end = p.end();
1636 // find leading elements of p that exist:
1637 while (iter != end)
1638 {
1639 tmp = result / *iter;
1640 st = status(tmp, ec);
1641 if (exists(st))
1642 swap(result, tmp);
1643 else
1644 {
1645 if (status_known(st))
1646 ec.clear();
1647 break;
1648 }
1649 ++iter;
1650 }
1651 // canonicalize:
8a49324e 1652 if (!ec && !result.empty())
641cb5a6
JW
1653 result = canonical(result, ec);
1654 if (ec)
1655 result.clear();
1656 else
1657 {
1658 // append the non-existing elements:
1659 while (iter != end)
1660 result /= *iter++;
1661 // normalize:
1662 result = result.lexically_normal();
1663 }
1664 return result;
1665}