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