xref: /netbsd-src/external/gpl3/gcc/dist/libstdc++-v3/src/c++17/fs_ops.cc (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 // Filesystem operations -*- C++ -*-
2 
3 // Copyright (C) 2014-2022 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library.  This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
10 
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
19 
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23 // <http://www.gnu.org/licenses/>.
24 
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
27 # define NEED_DO_COPY_FILE
28 # define NEED_DO_SPACE
29 #endif
30 #ifndef _GNU_SOURCE
31 // Cygwin needs this for secure_getenv
32 # define _GNU_SOURCE 1
33 #endif
34 
35 #include <bits/largefile-config.h>
36 #include <filesystem>
37 #include <functional>
38 #include <ostream>
39 #include <stack>
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
53 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
54 # include <utime.h> // utime
55 #endif
56 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
57 # include <windows.h>
58 #endif
59 
60 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
61 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
62 #include "../filesystem/ops-common.h"
63 
64 #pragma GCC diagnostic ignored "-Wunused-parameter"
65 
66 namespace fs = std::filesystem;
67 namespace posix = std::filesystem::__gnu_posix;
68 
69 fs::path
70 fs::absolute(const path& p)
71 {
72   error_code ec;
73   path ret = absolute(p, ec);
74   if (ec)
75     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
76 					     ec));
77   return ret;
78 }
79 
80 fs::path
81 fs::absolute(const path& p, error_code& ec)
82 {
83   path ret;
84   if (p.empty())
85     {
86       ec = make_error_code(std::errc::invalid_argument);
87       return ret;
88     }
89   ec.clear();
90   if (p.is_absolute())
91     {
92       ret = p;
93       return ret;
94     }
95 
96 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
97   // s must remain null-terminated
98   wstring_view s = p.native();
99 
100   if (p.has_root_directory()) // implies !p.has_root_name()
101     {
102       // GetFullPathNameW("//") gives unwanted result (PR 88884).
103       // If there are multiple directory separators at the start,
104       // skip all but the last of them.
105       const auto pos = s.find_first_not_of(L"/\\");
106       __glibcxx_assert(pos != 0);
107       s.remove_prefix(std::min(s.length(), pos) - 1);
108     }
109 
110   uint32_t len = 1024;
111   wstring buf;
112   do
113     {
114       buf.resize(len);
115       len = GetFullPathNameW(s.data(), len, buf.data(), nullptr);
116     }
117   while (len > buf.size());
118 
119   if (len == 0)
120     ec = __last_system_error();
121   else
122     {
123       buf.resize(len);
124       ret = std::move(buf);
125     }
126 #else
127   ret = current_path(ec);
128   ret /= p;
129 #endif
130   return ret;
131 }
132 
133 namespace
134 {
135 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
136   inline bool is_dot(wchar_t c) { return c == L'.'; }
137 #else
138   inline bool is_dot(char c) { return c == '.'; }
139 #endif
140 
141   inline bool is_dot(const fs::path& path)
142   {
143     const auto& filename = path.native();
144     return filename.size() == 1 && is_dot(filename[0]);
145   }
146 
147   inline bool is_dotdot(const fs::path& path)
148   {
149     const auto& filename = path.native();
150     return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
151   }
152 
153   struct free_as_in_malloc
154   {
155     void operator()(void* p) const { ::free(p); }
156   };
157 
158   using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
159 }
160 
161 fs::path
162 fs::canonical(const path& p, error_code& ec)
163 {
164   path result;
165 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
166   const path pa = absolute(p.lexically_normal(), ec);
167 #else
168   const path pa = absolute(p, ec);
169 #endif
170   if (ec)
171     return result;
172 
173 #ifdef _GLIBCXX_USE_REALPATH
174   char_ptr buf{ nullptr };
175 # if _XOPEN_VERSION < 700
176   // Not safe to call realpath(path, NULL)
177   using char_type = fs::path::value_type;
178   buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
179 # endif
180   if (char* rp = ::realpath(pa.c_str(), buf.get()))
181     {
182       if (buf == nullptr)
183 	buf.reset(rp);
184       result.assign(rp);
185       ec.clear();
186       return result;
187     }
188   if (errno != ENAMETOOLONG)
189     {
190       ec.assign(errno, std::generic_category());
191       return result;
192     }
193 #endif
194 
195   if (!exists(pa, ec))
196     {
197       if (!ec)
198 	ec = make_error_code(std::errc::no_such_file_or_directory);
199       return result;
200     }
201   // else: we know there are (currently) no unresolvable symlink loops
202 
203   result = pa.root_path();
204 
205   deque<path> cmpts;
206   for (auto& f : pa.relative_path())
207     cmpts.push_back(f);
208 
209   int max_allowed_symlinks = 40;
210 
211   while (!cmpts.empty() && !ec)
212     {
213       path f = std::move(cmpts.front());
214       cmpts.pop_front();
215 
216       if (f.empty())
217 	{
218 	  // ignore empty element
219 	}
220       else if (is_dot(f))
221 	{
222 	  if (!is_directory(result, ec) && !ec)
223 	    ec.assign(ENOTDIR, std::generic_category());
224 	}
225       else if (is_dotdot(f))
226 	{
227 	  auto parent = result.parent_path();
228 	  if (parent.empty())
229 	    result = pa.root_path();
230 	  else
231 	    result.swap(parent);
232 	}
233       else
234 	{
235 	  result /= f;
236 
237 	  if (is_symlink(result, ec))
238 	    {
239 	      path link = read_symlink(result, ec);
240 	      if (!ec)
241 		{
242 		  if (--max_allowed_symlinks == 0)
243 		    ec.assign(ELOOP, std::generic_category());
244 		  else
245 		    {
246 		      if (link.is_absolute())
247 			{
248 			  result = link.root_path();
249 			  link = link.relative_path();
250 			}
251 		      else
252 			result = result.parent_path();
253 
254 		      cmpts.insert(cmpts.begin(), link.begin(), link.end());
255 		    }
256 		}
257 	    }
258 	}
259     }
260 
261   if (ec || !exists(result, ec))
262     result.clear();
263 
264   return result;
265 }
266 
267 fs::path
268 fs::canonical(const path& p)
269 {
270   error_code ec;
271   path res = canonical(p, ec);
272   if (ec)
273     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path",
274 					     p, ec));
275   return res;
276 }
277 
278 void
279 fs::copy(const path& from, const path& to, copy_options options)
280 {
281   error_code ec;
282   copy(from, to, options, ec);
283   if (ec)
284     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
285 }
286 
287 namespace std::filesystem
288 {
289   // Need this as there's no 'perm_options::none' enumerator.
290   static inline bool is_set(fs::perm_options obj, fs::perm_options bits)
291   {
292     return (obj & bits) != fs::perm_options{};
293   }
294 }
295 
296 namespace
297 {
298   struct internal_file_clock : fs::__file_clock
299   {
300     using __file_clock::_S_to_sys;
301     using __file_clock::_S_from_sys;
302 
303 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
304     static fs::file_time_type
305     from_stat(const fs::stat_type& st, std::error_code& ec) noexcept
306     {
307       const auto sys_time = fs::file_time(st, ec);
308       if (sys_time == sys_time.min())
309 	return fs::file_time_type::min();
310       return _S_from_sys(sys_time);
311     }
312 #endif
313   };
314 }
315 
316 void
317 fs::copy(const path& from, const path& to, copy_options options,
318 	 error_code& ec)
319 {
320 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
321   const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
322   const bool create_symlinks = is_set(options, copy_options::create_symlinks);
323   const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
324   const bool use_lstat = create_symlinks || skip_symlinks;
325 
326   file_status f, t;
327   stat_type from_st, to_st;
328   // _GLIBCXX_RESOLVE_LIB_DEFECTS
329   // 2681. filesystem::copy() cannot copy symlinks
330   if (use_lstat || copy_symlinks
331       ? posix::lstat(from.c_str(), &from_st)
332       : posix::stat(from.c_str(), &from_st))
333     {
334       ec.assign(errno, std::generic_category());
335       return;
336     }
337   if (use_lstat
338       ? posix::lstat(to.c_str(), &to_st)
339       : posix::stat(to.c_str(), &to_st))
340     {
341       if (!is_not_found_errno(errno))
342 	{
343 	  ec.assign(errno, std::generic_category());
344 	  return;
345 	}
346       t = file_status{file_type::not_found};
347     }
348   else
349     t = make_file_status(to_st);
350   f = make_file_status(from_st);
351 
352   if (exists(t) && !is_other(t) && !is_other(f)
353       && to_st.st_dev == from_st.st_dev && to_st.st_ino == from_st.st_ino)
354     {
355       ec = std::make_error_code(std::errc::file_exists);
356       return;
357     }
358   if (is_other(f) || is_other(t))
359     {
360       ec = std::make_error_code(std::errc::invalid_argument);
361       return;
362     }
363   if (is_directory(f) && is_regular_file(t))
364     {
365       ec = std::make_error_code(std::errc::is_a_directory);
366       return;
367     }
368 
369   if (is_symlink(f))
370     {
371       if (skip_symlinks)
372 	ec.clear();
373       else if (!exists(t) && copy_symlinks)
374 	copy_symlink(from, to, ec);
375       else
376 	// Not clear what should be done here.
377 	// "Otherwise report an error as specified in Error reporting (7)."
378 	ec = std::make_error_code(std::errc::invalid_argument);
379     }
380   else if (is_regular_file(f))
381     {
382       if (is_set(options, copy_options::directories_only))
383 	ec.clear();
384       else if (create_symlinks)
385 	create_symlink(from, to, ec);
386       else if (is_set(options, copy_options::create_hard_links))
387 	create_hard_link(from, to, ec);
388       else if (is_directory(t))
389 	do_copy_file(from.c_str(), (to / from.filename()).c_str(),
390 		     copy_file_options(options), &from_st, nullptr, ec);
391       else
392 	{
393 	  auto ptr = exists(t) ? &to_st : &from_st;
394 	  do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
395 		       &from_st, ptr, ec);
396 	}
397     }
398   // _GLIBCXX_RESOLVE_LIB_DEFECTS
399   // 2682. filesystem::copy() won't create a symlink to a directory
400   else if (is_directory(f) && create_symlinks)
401     ec = std::make_error_code(errc::is_a_directory);
402   else if (is_directory(f) && (is_set(options, copy_options::recursive)
403 			       || options == copy_options::none))
404     {
405       if (!exists(t))
406 	if (!create_directory(to, from, ec))
407 	  return;
408       // set an unused bit in options to disable further recursion
409       if (!is_set(options, copy_options::recursive))
410 	options |= static_cast<copy_options>(4096);
411       for (const directory_entry& x : directory_iterator(from, ec))
412 	{
413 	  copy(x.path(), to/x.path().filename(), options, ec);
414 	  if (ec)
415 	    return;
416 	}
417     }
418   // _GLIBCXX_RESOLVE_LIB_DEFECTS
419   // 2683. filesystem::copy() says "no effects"
420   else
421     ec.clear();
422 #else
423   ec = std::make_error_code(std::errc::function_not_supported);
424 #endif
425 }
426 
427 bool
428 fs::copy_file(const path& from, const path& to, copy_options option)
429 {
430   error_code ec;
431   bool result = copy_file(from, to, option, ec);
432   if (ec)
433     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
434 					     ec));
435   return result;
436 }
437 
438 bool
439 fs::copy_file(const path& from, const path& to, copy_options options,
440 	      error_code& ec)
441 {
442 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
443   return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
444 		      nullptr, nullptr, ec);
445 #else
446   ec = std::make_error_code(std::errc::function_not_supported);
447   return false;
448 #endif
449 }
450 
451 
452 void
453 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
454 {
455   error_code ec;
456   copy_symlink(existing_symlink, new_symlink, ec);
457   if (ec)
458     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
459 	  existing_symlink, new_symlink, ec));
460 }
461 
462 void
463 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
464 		 error_code& ec) noexcept
465 {
466   auto p = read_symlink(existing_symlink, ec);
467   if (ec)
468     return;
469 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
470   if (is_directory(p))
471     {
472       create_directory_symlink(p, new_symlink, ec);
473       return;
474     }
475 #endif
476   create_symlink(p, new_symlink, ec);
477 }
478 
479 
480 bool
481 fs::create_directories(const path& p)
482 {
483   error_code ec;
484   bool result = create_directories(p, ec);
485   if (ec)
486     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
487 					     ec));
488   return result;
489 }
490 
491 bool
492 fs::create_directories(const path& p, error_code& ec)
493 {
494   if (p.empty())
495     {
496       ec = std::make_error_code(errc::invalid_argument);
497       return false;
498     }
499 
500   file_status st = status(p, ec);
501   if (is_directory(st))
502     return false;
503   else if (ec && !status_known(st))
504     return false;
505   else if (exists(st))
506     {
507       if (!ec)
508 	ec = std::make_error_code(std::errc::not_a_directory);
509       return false;
510     }
511 
512   __glibcxx_assert(st.type() == file_type::not_found);
513   // !exists(p) so there must be at least one non-existent component in p.
514 
515   std::stack<path> missing;
516   path pp = p;
517 
518   // Strip any trailing slash
519   if (pp.has_relative_path() && !pp.has_filename())
520     pp = pp.parent_path();
521 
522   do
523     {
524       const auto& filename = pp.filename();
525       if (is_dot(filename) || is_dotdot(filename))
526 	pp = pp.parent_path();
527       else
528 	{
529 	  missing.push(std::move(pp));
530 	  if (missing.size() > 1000) // sanity check
531 	    {
532 	      ec = std::make_error_code(std::errc::filename_too_long);
533 	      return false;
534 	    }
535 	  pp = missing.top().parent_path();
536 	}
537 
538       if (pp.empty())
539 	break;
540 
541       st = status(pp, ec);
542       if (exists(st))
543 	{
544 	  if (ec)
545 	    return false;
546 	  if (!is_directory(st))
547 	    {
548 	      ec = std::make_error_code(std::errc::not_a_directory);
549 	      return false;
550 	    }
551 	}
552 
553       if (ec && exists(st))
554 	return false;
555     }
556   while (st.type() == file_type::not_found);
557 
558   __glibcxx_assert(!missing.empty());
559 
560   bool created;
561   do
562     {
563       const path& top = missing.top();
564       created = create_directory(top, ec);
565       if (ec)
566 	return false;
567       missing.pop();
568     }
569   while (!missing.empty());
570 
571   return created;
572 }
573 
574 namespace
575 {
576   bool
577   create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
578   {
579     bool created = false;
580 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
581     posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
582     if (posix::mkdir(p.c_str(), mode))
583       {
584 	const int err = errno;
585 	if (err != EEXIST || !is_directory(p, ec))
586 	  ec.assign(err, std::generic_category());
587       }
588     else
589       {
590 	ec.clear();
591 	created = true;
592       }
593 #else
594     ec = std::make_error_code(std::errc::function_not_supported);
595 #endif
596     return created;
597   }
598 } // namespace
599 
600 bool
601 fs::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 
611 bool
612 fs::create_directory(const path& p, error_code& ec) noexcept
613 {
614   return create_dir(p, perms::all, ec);
615 }
616 
617 
618 bool
619 fs::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 
629 bool
630 fs::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;
635   if (posix::stat(attributes.c_str(), &st))
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::function_not_supported);
643   return false;
644 #endif
645 }
646 
647 
648 void
649 fs::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 
658 void
659 fs::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::function_not_supported);
664 #else
665   create_symlink(to, new_symlink, ec);
666 #endif
667 }
668 
669 
670 void
671 fs::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",
677 					     to, new_hard_link, ec));
678 }
679 
680 void
681 fs::create_hard_link(const path& to, const path& new_hard_link,
682 		     error_code& ec) noexcept
683 {
684 #ifdef _GLIBCXX_HAVE_LINK
685   if (::link(to.c_str(), new_hard_link.c_str()))
686     ec.assign(errno, std::generic_category());
687   else
688     ec.clear();
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 = __last_system_error();
694 #else
695   ec = std::make_error_code(std::errc::function_not_supported);
696 #endif
697 }
698 
699 void
700 fs::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 
709 void
710 fs::create_symlink(const path& to, const path& new_symlink,
711 		   error_code& ec) noexcept
712 {
713 #ifdef _GLIBCXX_HAVE_SYMLINK
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::function_not_supported);
720 #endif
721 }
722 
723 
724 fs::path
725 fs::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 
734 fs::path
735 fs::current_path(error_code& ec)
736 {
737   path p;
738 #if defined _GLIBCXX_HAVE_UNISTD_H && ! defined __AVR__
739 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
740   if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
741     {
742       p.assign(cwd.get());
743       ec.clear();
744     }
745   else
746     ec.assign(errno, std::generic_category());
747 #else
748 #ifdef _PC_PATH_MAX
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;
757 #elif defined(PATH_MAX)
758   size_t size = PATH_MAX;
759 #else
760   size_t size = 1024;
761 #endif
762   for (char_ptr buf; p.empty(); size *= 2)
763     {
764       using char_type = fs::path::value_type;
765       buf.reset((char_type*)malloc(size * sizeof(char_type)));
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::function_not_supported);
788 #endif
789   return p;
790 }
791 
792 void
793 fs::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 
801 void
802 fs::current_path(const path& p, error_code& ec) noexcept
803 {
804 #ifdef _GLIBCXX_HAVE_UNISTD_H
805   if (posix::chdir(p.c_str()))
806     ec.assign(errno, std::generic_category());
807   else
808     ec.clear();
809 #else
810   ec = std::make_error_code(std::errc::function_not_supported);
811 #endif
812 }
813 
814 bool
815 fs::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 
825 bool
826 fs::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;
832   if (posix::stat(p1.c_str(), &st1) == 0)
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 
839   if (posix::stat(p2.c_str(), &st2) == 0)
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::__unsupported();
851 	  return false;
852 	}
853       ec.clear();
854       if (is_other(s1) || is_other(s2))
855 	return false;
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 = __last_system_error();
886 	  return false;
887 	}
888       if (!h1.get_info() || !h2.get_info())
889 	{
890 	  ec = __last_system_error();
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
897       return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
898 #endif
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::function_not_supported);
909 #endif
910   return false;
911 }
912 
913 std::uintmax_t
914 fs::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 
923 namespace
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
930       posix::stat_type st;
931       if (posix::stat(p.c_str(), &st))
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::function_not_supported);
940       return deflt;
941 #endif
942     }
943 }
944 
945 std::uintmax_t
946 fs::file_size(const path& p, error_code& ec) noexcept
947 {
948 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
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;
954     uintmax_t size;
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::__unsupported();
965     }
966 #else
967   ec = std::make_error_code(std::errc::function_not_supported);
968 #endif
969   return -1;
970 }
971 
972 std::uintmax_t
973 fs::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 
982 std::uintmax_t
983 fs::hard_link_count(const path& p, error_code& ec) noexcept
984 {
985 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
986   return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
987 		 static_cast<uintmax_t>(-1));
988 #else
989   ec = std::make_error_code(std::errc::function_not_supported);
990   return static_cast<uintmax_t>(-1);
991 #endif
992 }
993 
994 bool
995 fs::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 
1005 bool
1006 fs::is_empty(const path& p, error_code& ec)
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 
1017 fs::file_time_type
1018 fs::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 
1027 fs::file_time_type
1028 fs::last_write_time(const path& p, error_code& ec) noexcept
1029 {
1030 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1031   return do_stat(p, ec,
1032 		 [&ec](const auto& st) {
1033 		     return internal_file_clock::from_stat(st, ec);
1034 		 },
1035 		 file_time_type::min());
1036 #else
1037   ec = std::make_error_code(std::errc::function_not_supported);
1038   return file_time_type::min();
1039 #endif
1040 }
1041 
1042 void
1043 fs::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 
1051 void
1052 fs::last_write_time(const path& p,
1053 		    file_time_type new_time, error_code& ec) noexcept
1054 {
1055   auto d = internal_file_clock::_S_to_sys(new_time).time_since_epoch();
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();
1073 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
1074   posix::utimbuf times;
1075   times.modtime = s.count();
1076   times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
1077 			 times.modtime);
1078   if (posix::utime(p.c_str(), &times))
1079     ec.assign(errno, std::generic_category());
1080   else
1081     ec.clear();
1082 #else
1083   ec = std::make_error_code(std::errc::function_not_supported);
1084 #endif
1085 }
1086 
1087 void
1088 fs::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 
1096 void
1097 fs::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))
1132     ec = std::__unsupported();
1133   else if (posix::chmod(p.c_str(), static_cast<posix::mode_t>(prms)))
1134     err = errno;
1135 #endif
1136 
1137   if (err)
1138     ec.assign(err, std::generic_category());
1139   else
1140     ec.clear();
1141 }
1142 
1143 fs::path
1144 fs::proximate(const path& p, const path& base)
1145 {
1146   return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
1147 }
1148 
1149 fs::path
1150 fs::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 
1163 fs::path
1164 fs::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 
1173 fs::path fs::read_symlink(const path& p, error_code& ec)
1174 {
1175   path result;
1176 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
1177   stat_type st;
1178   if (posix::lstat(p.c_str(), &st))
1179     {
1180       ec.assign(errno, std::generic_category());
1181       return result;
1182     }
1183   else if (!fs::is_symlink(make_file_status(st)))
1184     {
1185       ec.assign(EINVAL, std::generic_category());
1186       return result;
1187     }
1188 
1189   std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1190   do
1191     {
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 	}
1214     }
1215   while (true);
1216 #else
1217   ec = std::make_error_code(std::errc::function_not_supported);
1218 #endif
1219   return result;
1220 }
1221 
1222 fs::path
1223 fs::relative(const path& p, const path& base)
1224 {
1225   return weakly_canonical(p).lexically_relative(weakly_canonical(base));
1226 }
1227 
1228 fs::path
1229 fs::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 
1242 bool
1243 fs::remove(const path& p)
1244 {
1245   error_code ec;
1246   const bool result = fs::remove(p, ec);
1247   if (ec)
1248     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1249   return result;
1250 }
1251 
1252 bool
1253 fs::remove(const path& p, error_code& ec) noexcept
1254 {
1255 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1256   auto st = symlink_status(p, ec);
1257   if (exists(st))
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 = __last_system_error();
1267     }
1268   else if (status_known(st))
1269     ec.clear();
1270 #else
1271   if (::remove(p.c_str()) == 0)
1272     {
1273       ec.clear();
1274       return true;
1275     }
1276   else if (errno == ENOENT)
1277     ec.clear();
1278   else
1279     ec.assign(errno, std::generic_category());
1280 #endif
1281   return false;
1282 }
1283 
1284 std::uintmax_t
1285 fs::remove_all(const path& p)
1286 {
1287   error_code ec;
1288   uintmax_t count = 0;
1289   recursive_directory_iterator dir(p, directory_options{64|128}, ec);
1290   switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
1291   {
1292   case 0:
1293     // Iterate over the directory removing everything.
1294     {
1295       const recursive_directory_iterator end;
1296       while (dir != end)
1297 	{
1298 	  dir.__erase(); // throws on error
1299 	  ++count;
1300 	}
1301     }
1302     // Directory is empty now, will remove it below.
1303     break;
1304 #ifndef __AVR__
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 #endif
1313   default:
1314     // An error occurred.
1315     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1316   }
1317 
1318   // Remove p itself, which is either a non-directory or is now empty.
1319   return count + fs::remove(p);
1320 }
1321 
1322 std::uintmax_t
1323 fs::remove_all(const path& p, error_code& ec)
1324 {
1325   uintmax_t count = 0;
1326   recursive_directory_iterator dir(p, directory_options{64|128}, ec);
1327   switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
1328   {
1329   case 0:
1330     // Iterate over the directory removing everything.
1331     {
1332       const recursive_directory_iterator end;
1333       while (dir != end)
1334 	{
1335 	  dir.__erase(&ec);
1336 	  if (ec)
1337 	    return -1;
1338 	  ++count;
1339 	}
1340     }
1341     // Directory is empty now, will remove it below.
1342     break;
1343 #ifndef __AVR__
1344   case ENOENT:
1345     // Our work here is done.
1346     ec.clear();
1347     return 0;
1348   case ENOTDIR:
1349   case ELOOP:
1350     // Not a directory, will remove below.
1351     break;
1352 #endif
1353   default:
1354     // An error occurred.
1355     return -1;
1356   }
1357 
1358   // Remove p itself, which is either a non-directory or is now empty.
1359   if (int last = fs::remove(p, ec); !ec)
1360     return count + last;
1361   return -1;
1362 }
1363 
1364 void
1365 fs::rename(const path& from, const path& to)
1366 {
1367   error_code ec;
1368   rename(from, to, ec);
1369   if (ec)
1370     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1371 }
1372 
1373 void
1374 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1375 {
1376 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1377   const auto to_status = fs::status(to, ec);
1378   if (to_status.type() == file_type::not_found)
1379     ec.clear();
1380   else if (ec)
1381     return;
1382 
1383   if (fs::exists(to_status))
1384   {
1385     const auto from_status = fs::status(from, ec);
1386     if (ec)
1387       return;
1388 
1389     if (fs::is_directory(to_status))
1390     {
1391       if (!fs::is_directory(from_status))
1392       {
1393 	// Cannot rename a non-directory over an existing directory.
1394 	ec = std::make_error_code(std::errc::is_a_directory);
1395 	return;
1396       }
1397     }
1398     else if (fs::is_directory(from_status))
1399     {
1400       // Cannot rename a directory over an existing non-directory.
1401       ec = std::make_error_code(std::errc::not_a_directory);
1402       return;
1403     }
1404   }
1405 #endif
1406   if (posix::rename(from.c_str(), to.c_str()))
1407     ec.assign(errno, std::generic_category());
1408   else
1409     ec.clear();
1410 }
1411 
1412 void
1413 fs::resize_file(const path& p, uintmax_t size)
1414 {
1415   error_code ec;
1416   resize_file(p, size, ec);
1417   if (ec)
1418     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1419 }
1420 
1421 void
1422 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1423 {
1424   if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max()))
1425     ec.assign(EINVAL, std::generic_category());
1426   else if (posix::truncate(p.c_str(), size))
1427     ec.assign(errno, std::generic_category());
1428   else
1429     ec.clear();
1430 }
1431 
1432 
1433 fs::space_info
1434 fs::space(const path& p)
1435 {
1436   error_code ec;
1437   space_info s = space(p, ec);
1438   if (ec)
1439     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1440   return s;
1441 }
1442 
1443 fs::space_info
1444 fs::space(const path& p, error_code& ec) noexcept
1445 {
1446   space_info info = {
1447     static_cast<uintmax_t>(-1),
1448     static_cast<uintmax_t>(-1),
1449     static_cast<uintmax_t>(-1)
1450   };
1451 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1452 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1453   path dir = absolute(p);
1454   dir.remove_filename();
1455   auto str = dir.c_str();
1456 #else
1457   auto str = p.c_str();
1458 #endif
1459 
1460   do_space(str, info.capacity, info.free, info.available, ec);
1461 #endif // _GLIBCXX_HAVE_SYS_STAT_H
1462 
1463   return info;
1464 }
1465 
1466 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1467 fs::file_status
1468 fs::status(const fs::path& p, error_code& ec) noexcept
1469 {
1470   file_status status;
1471   auto str = p.c_str();
1472 
1473 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1474   // stat() fails if there's a trailing slash (PR 88881)
1475   path p2;
1476   if (p.has_relative_path() && !p.has_filename())
1477     {
1478       __try
1479 	{
1480 	  p2 = p.parent_path();
1481 	  str = p2.c_str();
1482 	}
1483       __catch(const bad_alloc&)
1484 	{
1485 	  ec = std::make_error_code(std::errc::not_enough_memory);
1486 	  return status;
1487 	}
1488       str = p2.c_str();
1489     }
1490 #endif
1491 
1492   stat_type st;
1493   if (posix::stat(str, &st))
1494     {
1495       int err = errno;
1496       ec.assign(err, std::generic_category());
1497       if (is_not_found_errno(err))
1498 	status.type(file_type::not_found);
1499 #ifdef EOVERFLOW
1500       else if (err == EOVERFLOW)
1501 	status.type(file_type::unknown);
1502 #endif
1503     }
1504   else
1505     {
1506       status = make_file_status(st);
1507       ec.clear();
1508     }
1509   return status;
1510 }
1511 
1512 fs::file_status
1513 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1514 {
1515   file_status status;
1516   auto str = p.c_str();
1517 
1518 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1519   // stat() fails if there's a trailing slash (PR 88881)
1520   path p2;
1521   if (p.has_relative_path() && !p.has_filename())
1522     {
1523       __try
1524 	{
1525 	  p2 = p.parent_path();
1526 	  str = p2.c_str();
1527 	}
1528       __catch(const bad_alloc&)
1529 	{
1530 	  ec = std::make_error_code(std::errc::not_enough_memory);
1531 	  return status;
1532 	}
1533       str = p2.c_str();
1534     }
1535 #endif
1536 
1537   stat_type st;
1538   if (posix::lstat(str, &st))
1539     {
1540       int err = errno;
1541       ec.assign(err, std::generic_category());
1542       if (is_not_found_errno(err))
1543 	status.type(file_type::not_found);
1544     }
1545   else
1546     {
1547       status = make_file_status(st);
1548       ec.clear();
1549     }
1550   return status;
1551 }
1552 #endif
1553 
1554 fs::file_status
1555 fs::status(const fs::path& p)
1556 {
1557   std::error_code ec;
1558   auto result = status(p, ec);
1559   if (result.type() == file_type::none)
1560     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1561   return result;
1562 }
1563 
1564 fs::file_status
1565 fs::symlink_status(const fs::path& p)
1566 {
1567   std::error_code ec;
1568   auto result = symlink_status(p, ec);
1569   if (result.type() == file_type::none)
1570     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1571   return result;
1572 }
1573 
1574 fs::path
1575 fs::temp_directory_path()
1576 {
1577   error_code ec;
1578   path tmp = temp_directory_path(ec);
1579   if (ec)
1580     _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1581   return tmp;
1582 }
1583 
1584 fs::path
1585 fs::temp_directory_path(error_code& ec)
1586 {
1587   path p = fs::get_temp_directory_from_env(ec);
1588   if (ec)
1589     return p;
1590   auto st = status(p, ec);
1591   if (ec)
1592     p.clear();
1593   else if (!is_directory(st))
1594     {
1595       p.clear();
1596       ec = std::make_error_code(std::errc::not_a_directory);
1597     }
1598   return p;
1599 }
1600 
1601 fs::path
1602 fs::weakly_canonical(const path& p)
1603 {
1604   path result;
1605   if (exists(status(p)))
1606     return canonical(p);
1607 
1608   path tmp;
1609   auto iter = p.begin(), end = p.end();
1610   // find leading elements of p that exist:
1611   while (iter != end)
1612     {
1613       tmp = result / *iter;
1614       if (exists(status(tmp)))
1615 	swap(result, tmp);
1616       else
1617 	break;
1618       ++iter;
1619     }
1620   // canonicalize:
1621   if (!result.empty())
1622     result = canonical(result);
1623   // append the non-existing elements:
1624   while (iter != end)
1625     result /= *iter++;
1626   // normalize:
1627   return result.lexically_normal();
1628 }
1629 
1630 fs::path
1631 fs::weakly_canonical(const path& p, error_code& ec)
1632 {
1633   path result;
1634   file_status st = status(p, ec);
1635   if (exists(st))
1636     return canonical(p, ec);
1637   else if (status_known(st))
1638     ec.clear();
1639   else
1640     return result;
1641 
1642   path tmp;
1643   auto iter = p.begin(), end = p.end();
1644   // find leading elements of p that exist:
1645   while (iter != end)
1646     {
1647       tmp = result / *iter;
1648       st = status(tmp, ec);
1649       if (exists(st))
1650 	swap(result, tmp);
1651       else
1652 	{
1653 	  if (status_known(st))
1654 	    ec.clear();
1655 	  break;
1656 	}
1657       ++iter;
1658     }
1659   // canonicalize:
1660   if (!ec && !result.empty())
1661     result = canonical(result, ec);
1662   if (ec)
1663     result.clear();
1664   else
1665     {
1666       // append the non-existing elements:
1667       while (iter != end)
1668 	result /= *iter++;
1669       // normalize:
1670       result = result.lexically_normal();
1671     }
1672   return result;
1673 }
1674