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