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