xref: /netbsd-src/external/gpl3/gcc/dist/libstdc++-v3/src/c++17/fs_ops.cc (revision 4d342c046e3288fb5a1edcd33cfec48c41c80664)
1 // Filesystem operations -*- C++ -*-
2 
3 // Copyright (C) 2014-2019 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 
31 #include <bits/largefile-config.h>
32 #include <filesystem>
33 #include <functional>
34 #include <ostream>
35 #include <stack>
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
49 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
50 # include <utime.h> // utime
51 #endif
52 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
53 # include <windows.h>
54 #endif
55 
56 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
57 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
58 #include "../filesystem/ops-common.h"
59 
60 #pragma GCC diagnostic ignored "-Wunused-parameter"
61 
62 namespace fs = std::filesystem;
63 namespace posix = std::filesystem::__gnu_posix;
64 
65 fs::path
66 fs::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,
73 					     ec));
74   return ret;
75 #else
76   if (p.empty())
77     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
78 	  make_error_code(std::errc::invalid_argument)));
79   return current_path() / p;
80 #endif
81 }
82 
83 fs::path
84 fs::absolute(const path& p, error_code& ec)
85 {
86   path ret;
87   if (p.empty())
88     {
89       ec = make_error_code(std::errc::invalid_argument);
90       return ret;
91     }
92   ec.clear();
93   if (p.is_absolute())
94     {
95       ret = p;
96       return ret;
97     }
98 
99 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
100   // s must remain null-terminated
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 
113   uint32_t len = 1024;
114   wstring buf;
115   do
116     {
117       buf.resize(len);
118       len = GetFullPathNameW(s.data(), len, buf.data(), nullptr);
119     }
120   while (len > buf.size());
121 
122   if (len == 0)
123     ec.assign((int)GetLastError(), std::system_category());
124   else
125     {
126       buf.resize(len);
127       ret = std::move(buf);
128     }
129 #else
130   ret = current_path(ec);
131   ret /= p;
132 #endif
133   return ret;
134 }
135 
136 namespace
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 
161   using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
162 }
163 
164 fs::path
165 fs::canonical(const path& p, error_code& ec)
166 {
167   path result;
168 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
169   const path pa = absolute(p.lexically_normal(), ec);
170 #else
171   const path pa = absolute(p, ec);
172 #endif
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)
180   using char_type = fs::path::value_type;
181   buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
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 
270 fs::path
271 fs::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 
281 void
282 fs::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 
290 namespace std::filesystem
291 {
292   // Need this as there's no 'perm_options::none' enumerator.
293   static inline bool is_set(fs::perm_options obj, fs::perm_options bits)
294   {
295     return (obj & bits) != fs::perm_options{};
296   }
297 }
298 
299 namespace
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 
306 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
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     }
315 #endif
316   };
317 }
318 
319 void
320 fs::copy(const path& from, const path& to, copy_options options,
321 	 error_code& ec)
322 {
323 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
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
334       ? posix::lstat(from.c_str(), &from_st)
335       : posix::stat(from.c_str(), &from_st))
336     {
337       ec.assign(errno, std::generic_category());
338       return;
339     }
340   if (use_lstat
341       ? posix::lstat(to.c_str(), &to_st)
342       : posix::stat(to.c_str(), &to_st))
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();
421 #else
422   ec = std::make_error_code(std::errc::not_supported);
423 #endif
424 }
425 
426 bool
427 fs::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 
437 bool
438 fs::copy_file(const path& from, const path& to, copy_options options,
439 	      error_code& ec)
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 
451 void
452 fs::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 
461 void
462 fs::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 
479 bool
480 fs::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 
490 bool
491 fs::create_directories(const path& p, error_code& ec)
492 {
493   if (p.empty())
494     {
495       ec = std::make_error_code(errc::invalid_argument);
496       return false;
497     }
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 
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 
514   std::stack<path> missing;
515   path pp = p;
516 
517   // Strip any trailing slash
518   if (pp.has_relative_path() && !pp.has_filename())
519     pp = pp.parent_path();
520 
521   do
522     {
523       const auto& filename = pp.filename();
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;
539 
540       st = status(pp, ec);
541       if (exists(st))
542 	{
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 	    }
550 	}
551 
552       if (ec && exists(st))
553 	return false;
554     }
555   while (st.type() == file_type::not_found);
556 
557   __glibcxx_assert(!missing.empty());
558 
559   bool created;
560   do
561     {
562       const path& top = missing.top();
563       created = create_directory(top, ec);
564       if (ec)
565 	return false;
566       missing.pop();
567     }
568   while (!missing.empty());
569 
570   return created;
571 }
572 
573 namespace
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
580     posix::mode_t mode
581       = 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::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::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::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.assign((int)GetLastError(), generic_category());
694 #else
695   ec = std::make_error_code(std::errc::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::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 #ifdef _GLIBCXX_HAVE_UNISTD_H
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::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::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::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;
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
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::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::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::make_error_code(std::errc::not_supported);
965     }
966 #else
967   ec = std::make_error_code(std::errc::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::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::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::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::make_error_code(std::errc::not_supported);
1133   else if (posix::chmod(p.c_str(), static_cast<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 (::lstat(p.c_str(), &st))
1179     {
1180       ec.assign(errno, std::generic_category());
1181       return result;
1182     }
1183   std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
1184   do
1185     {
1186       ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
1187       if (len == -1)
1188 	{
1189 	  ec.assign(errno, std::generic_category());
1190 	  return result;
1191 	}
1192       else if (len == (ssize_t)buf.size())
1193 	{
1194 	  if (buf.size() > 4096)
1195 	    {
1196 	      ec.assign(ENAMETOOLONG, std::generic_category());
1197 	      return result;
1198 	    }
1199 	  buf.resize(buf.size() * 2);
1200 	}
1201       else
1202 	{
1203 	  buf.resize(len);
1204 	  result.assign(buf);
1205 	  ec.clear();
1206 	  break;
1207 	}
1208     }
1209   while (true);
1210 #else
1211   ec = std::make_error_code(std::errc::not_supported);
1212 #endif
1213   return result;
1214 }
1215 
1216 fs::path
1217 fs::relative(const path& p, const path& base)
1218 {
1219   return weakly_canonical(p).lexically_relative(weakly_canonical(base));
1220 }
1221 
1222 fs::path
1223 fs::relative(const path& p, const path& base, error_code& ec)
1224 {
1225   auto result = weakly_canonical(p, ec);
1226   fs::path cbase;
1227   if (!ec)
1228     cbase = weakly_canonical(base, ec);
1229   if (!ec)
1230     result = result.lexically_relative(cbase);
1231   if (ec)
1232     result.clear();
1233   return result;
1234 }
1235 
1236 bool
1237 fs::remove(const path& p)
1238 {
1239   error_code ec;
1240   const bool result = fs::remove(p, ec);
1241   if (ec)
1242     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1243   return result;
1244 }
1245 
1246 bool
1247 fs::remove(const path& p, error_code& ec) noexcept
1248 {
1249 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1250   auto st = symlink_status(p, ec);
1251   if (exists(st))
1252     {
1253       if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1254 	  || DeleteFileW(p.c_str()))
1255 	{
1256 	  ec.clear();
1257 	  return true;
1258 	}
1259       else if (!ec)
1260 	ec.assign((int)GetLastError(), generic_category());
1261     }
1262   else if (status_known(st))
1263     ec.clear();
1264 #else
1265   if (::remove(p.c_str()) == 0)
1266     {
1267       ec.clear();
1268       return true;
1269     }
1270   else if (errno == ENOENT)
1271     ec.clear();
1272   else
1273     ec.assign(errno, std::generic_category());
1274 #endif
1275   return false;
1276 }
1277 
1278 
1279 std::uintmax_t
1280 fs::remove_all(const path& p)
1281 {
1282   error_code ec;
1283   const auto result = remove_all(p, ec);
1284   if (ec)
1285     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1286   return result;
1287 }
1288 
1289 std::uintmax_t
1290 fs::remove_all(const path& p, error_code& ec)
1291 {
1292   const auto s = symlink_status(p, ec);
1293   if (!status_known(s))
1294     return -1;
1295 
1296   ec.clear();
1297   if (s.type() == file_type::not_found)
1298     return 0;
1299 
1300   uintmax_t count = 0;
1301   if (s.type() == file_type::directory)
1302     {
1303       directory_iterator d(p, ec), end;
1304       while (!ec && d != end)
1305 	{
1306 	  const auto removed = fs::remove_all(d->path(), ec);
1307 	  if (removed == numeric_limits<uintmax_t>::max())
1308 	    return -1;
1309 	  count += removed;
1310 	  d.increment(ec);
1311 	  if (ec)
1312 	    return -1;
1313 	}
1314     }
1315 
1316   if (fs::remove(p, ec))
1317     ++count;
1318   return ec ? -1 : count;
1319 }
1320 
1321 void
1322 fs::rename(const path& from, const path& to)
1323 {
1324   error_code ec;
1325   rename(from, to, ec);
1326   if (ec)
1327     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1328 }
1329 
1330 void
1331 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1332 {
1333   if (posix::rename(from.c_str(), to.c_str()))
1334     ec.assign(errno, std::generic_category());
1335   else
1336     ec.clear();
1337 }
1338 
1339 void
1340 fs::resize_file(const path& p, uintmax_t size)
1341 {
1342   error_code ec;
1343   resize_file(p, size, ec);
1344   if (ec)
1345     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1346 }
1347 
1348 void
1349 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1350 {
1351   if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1352     ec.assign(EINVAL, std::generic_category());
1353   else if (posix::truncate(p.c_str(), size))
1354     ec.assign(errno, std::generic_category());
1355   else
1356     ec.clear();
1357 }
1358 
1359 
1360 fs::space_info
1361 fs::space(const path& p)
1362 {
1363   error_code ec;
1364   space_info s = space(p, ec);
1365   if (ec)
1366     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1367   return s;
1368 }
1369 
1370 fs::space_info
1371 fs::space(const path& p, error_code& ec) noexcept
1372 {
1373   space_info info = {
1374     static_cast<uintmax_t>(-1),
1375     static_cast<uintmax_t>(-1),
1376     static_cast<uintmax_t>(-1)
1377   };
1378 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1379 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1380   path dir = absolute(p);
1381   dir.remove_filename();
1382   auto str = dir.c_str();
1383 #else
1384   auto str = p.c_str();
1385 #endif
1386 
1387   do_space(str, info.capacity, info.free, info.available, ec);
1388 #endif // _GLIBCXX_HAVE_SYS_STAT_H
1389 
1390   return info;
1391 }
1392 
1393 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1394 fs::file_status
1395 fs::status(const fs::path& p, error_code& ec) noexcept
1396 {
1397   file_status status;
1398   auto str = p.c_str();
1399 
1400 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1401 #if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6
1402   // stat() fails if there's a trailing slash (PR 88881)
1403   path p2;
1404   if (p.has_relative_path() && !p.has_filename())
1405     {
1406       __try
1407 	{
1408 	  p2 = p.parent_path();
1409 	  str = p2.c_str();
1410 	}
1411       __catch(const bad_alloc&)
1412 	{
1413 	  ec = std::make_error_code(std::errc::not_enough_memory);
1414 	  return status;
1415 	}
1416       str = p2.c_str();
1417     }
1418 #endif
1419 #endif
1420 
1421   stat_type st;
1422   if (posix::stat(str, &st))
1423     {
1424       int err = errno;
1425       ec.assign(err, std::generic_category());
1426       if (is_not_found_errno(err))
1427 	status.type(file_type::not_found);
1428 #ifdef EOVERFLOW
1429       else if (err == EOVERFLOW)
1430 	status.type(file_type::unknown);
1431 #endif
1432     }
1433   else
1434     {
1435       status = make_file_status(st);
1436       ec.clear();
1437     }
1438   return status;
1439 }
1440 
1441 fs::file_status
1442 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1443 {
1444   file_status status;
1445   auto str = p.c_str();
1446 
1447 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1448 #if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6
1449   // stat() fails if there's a trailing slash (PR 88881)
1450   path p2;
1451   if (p.has_relative_path() && !p.has_filename())
1452     {
1453       __try
1454 	{
1455 	  p2 = p.parent_path();
1456 	  str = p2.c_str();
1457 	}
1458       __catch(const bad_alloc&)
1459 	{
1460 	  ec = std::make_error_code(std::errc::not_enough_memory);
1461 	  return status;
1462 	}
1463       str = p2.c_str();
1464     }
1465 #endif
1466 #endif
1467 
1468   stat_type st;
1469   if (posix::lstat(str, &st))
1470     {
1471       int err = errno;
1472       ec.assign(err, std::generic_category());
1473       if (is_not_found_errno(err))
1474 	status.type(file_type::not_found);
1475     }
1476   else
1477     {
1478       status = make_file_status(st);
1479       ec.clear();
1480     }
1481   return status;
1482 }
1483 #endif
1484 
1485 fs::file_status
1486 fs::status(const fs::path& p)
1487 {
1488   std::error_code ec;
1489   auto result = status(p, ec);
1490   if (result.type() == file_type::none)
1491     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1492   return result;
1493 }
1494 
1495 fs::file_status
1496 fs::symlink_status(const fs::path& p)
1497 {
1498   std::error_code ec;
1499   auto result = symlink_status(p, ec);
1500   if (result.type() == file_type::none)
1501     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1502   return result;
1503 }
1504 
1505 fs::path fs::temp_directory_path()
1506 {
1507   error_code ec;
1508   path tmp = temp_directory_path(ec);
1509   if (ec)
1510     _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1511   return tmp;
1512 }
1513 
1514 fs::path fs::temp_directory_path(error_code& ec)
1515 {
1516   path p;
1517 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1518   unsigned len = 1024;
1519   std::wstring buf;
1520   do
1521     {
1522       buf.resize(len);
1523       len = GetTempPathW(buf.size(), buf.data());
1524     } while (len > buf.size());
1525 
1526   if (len == 0)
1527     {
1528       ec.assign((int)GetLastError(), std::system_category());
1529       return p;
1530     }
1531   buf.resize(len);
1532   p = std::move(buf);
1533 #else
1534   const char* tmpdir = nullptr;
1535   const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1536   for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1537     tmpdir = ::getenv(*e);
1538   p = tmpdir ? tmpdir : "/tmp";
1539 #endif
1540   auto st = status(p, ec);
1541   if (ec)
1542     p.clear();
1543   else if (!is_directory(st))
1544     {
1545       p.clear();
1546       ec = std::make_error_code(std::errc::not_a_directory);
1547     }
1548   return p;
1549 }
1550 
1551 fs::path
1552 fs::weakly_canonical(const path& p)
1553 {
1554   path result;
1555   if (exists(status(p)))
1556     return canonical(p);
1557 
1558   path tmp;
1559   auto iter = p.begin(), end = p.end();
1560   // find leading elements of p that exist:
1561   while (iter != end)
1562     {
1563       tmp = result / *iter;
1564       if (exists(status(tmp)))
1565 	swap(result, tmp);
1566       else
1567 	break;
1568       ++iter;
1569     }
1570   // canonicalize:
1571   if (!result.empty())
1572     result = canonical(result);
1573   // append the non-existing elements:
1574   while (iter != end)
1575     result /= *iter++;
1576   // normalize:
1577   return result.lexically_normal();
1578 }
1579 
1580 fs::path
1581 fs::weakly_canonical(const path& p, error_code& ec)
1582 {
1583   path result;
1584   file_status st = status(p, ec);
1585   if (exists(st))
1586     return canonical(p, ec);
1587   else if (status_known(st))
1588     ec.clear();
1589   else
1590     return result;
1591 
1592   path tmp;
1593   auto iter = p.begin(), end = p.end();
1594   // find leading elements of p that exist:
1595   while (iter != end)
1596     {
1597       tmp = result / *iter;
1598       st = status(tmp, ec);
1599       if (exists(st))
1600 	swap(result, tmp);
1601       else
1602 	{
1603 	  if (status_known(st))
1604 	    ec.clear();
1605 	  break;
1606 	}
1607       ++iter;
1608     }
1609   // canonicalize:
1610   if (!ec && !result.empty())
1611     result = canonical(result, ec);
1612   if (ec)
1613     result.clear();
1614   else
1615     {
1616       // append the non-existing elements:
1617       while (iter != end)
1618 	result /= *iter++;
1619       // normalize:
1620       result = result.lexically_normal();
1621     }
1622   return result;
1623 }
1624