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
absolute(const path & p)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
absolute(const path & p,error_code & ec)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
is_dot(wchar_t c)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 
is_dot(const fs::path & path)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 
is_dotdot(const fs::path & path)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   {
operator ()__anon71c2cb970111::free_as_in_malloc158     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
canonical(const path & p,error_code & ec)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
canonical(const path & p)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
copy(const path & from,const path & to,copy_options options)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.
is_set(fs::perm_options obj,fs::perm_options bits)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
from_stat__anon71c2cb970211::internal_file_clock308     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
copy(const path & from,const path & to,copy_options options,error_code & ec)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
copy_file(const path & from,const path & to,copy_options option)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
copy_file(const path & from,const path & to,copy_options options,error_code & ec)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
copy_symlink(const path & existing_symlink,const path & new_symlink)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
copy_symlink(const path & existing_symlink,const path & new_symlink,error_code & ec)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
create_directories(const path & p)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
create_directories(const path & p,error_code & ec)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
create_dir(const fs::path & p,fs::perms perm,std::error_code & ec)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
create_directory(const path & p)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
create_directory(const path & p,error_code & ec)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
create_directory(const path & p,const path & attributes)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
create_directory(const path & p,const path & attributes,error_code & ec)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
create_directory_symlink(const path & to,const path & new_symlink)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
create_directory_symlink(const path & to,const path & new_symlink,error_code & ec)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
create_hard_link(const path & to,const path & new_hard_link)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
create_hard_link(const path & to,const path & new_hard_link,error_code & ec)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(), system_category());
694 #else
695   ec = std::make_error_code(std::errc::not_supported);
696 #endif
697 }
698 
699 void
create_symlink(const path & to,const path & new_symlink)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
create_symlink(const path & to,const path & new_symlink,error_code & ec)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
current_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
current_path(error_code & ec)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
current_path(const path & p)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
current_path(const path & p,error_code & ec)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
equivalent(const path & p1,const path & p2)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
equivalent(const path & p1,const path & p2,error_code & ec)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(), system_category());
886 	  return false;
887 	}
888       if (!h1.get_info() || !h2.get_info())
889 	{
890 	  ec.assign((int)GetLastError(), system_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
file_size(const path & p)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
do_stat(const fs::path & p,std::error_code & ec,Accessor f,T deflt)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
file_size(const path & p,error_code & ec)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
hard_link_count(const path & p)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
hard_link_count(const path & p,error_code & ec)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
is_empty(const path & p)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
is_empty(const path & p,error_code & ec)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
last_write_time(const path & p)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
last_write_time(const path & p,error_code & ec)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
last_write_time(const path & p,file_time_type new_time)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
last_write_time(const path & p,file_time_type new_time,error_code & ec)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
permissions(const path & p,perms prms,perm_options opts)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
permissions(const path & p,perms prms,perm_options opts,error_code & ec)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
proximate(const path & p,const path & base)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
proximate(const path & p,const path & base,error_code & ec)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
read_symlink(const path & p)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 
read_symlink(const path & p,error_code & ec)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::not_supported);
1218 #endif
1219   return result;
1220 }
1221 
1222 fs::path
relative(const path & p,const path & base)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
relative(const path & p,const path & base,error_code & ec)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
remove(const path & p)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
remove(const path & p,error_code & ec)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.assign((int)GetLastError(), system_category());
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 
1285 std::uintmax_t
remove_all(const path & p)1286 fs::remove_all(const path& p)
1287 {
1288   error_code ec;
1289   const auto result = remove_all(p, ec);
1290   if (ec)
1291     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1292   return result;
1293 }
1294 
1295 std::uintmax_t
remove_all(const path & p,error_code & ec)1296 fs::remove_all(const path& p, error_code& ec)
1297 {
1298   const auto s = symlink_status(p, ec);
1299   if (!status_known(s))
1300     return -1;
1301 
1302   ec.clear();
1303   if (s.type() == file_type::not_found)
1304     return 0;
1305 
1306   uintmax_t count = 0;
1307   if (s.type() == file_type::directory)
1308     {
1309       directory_iterator d(p, ec), end;
1310       while (!ec && d != end)
1311 	{
1312 	  const auto removed = fs::remove_all(d->path(), ec);
1313 	  if (removed == numeric_limits<uintmax_t>::max())
1314 	    return -1;
1315 	  count += removed;
1316 	  d.increment(ec);
1317 	  if (ec)
1318 	    return -1;
1319 	}
1320     }
1321 
1322   if (fs::remove(p, ec))
1323     ++count;
1324   return ec ? -1 : count;
1325 }
1326 
1327 void
rename(const path & from,const path & to)1328 fs::rename(const path& from, const path& to)
1329 {
1330   error_code ec;
1331   rename(from, to, ec);
1332   if (ec)
1333     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1334 }
1335 
1336 void
rename(const path & from,const path & to,error_code & ec)1337 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1338 {
1339   if (posix::rename(from.c_str(), to.c_str()))
1340     ec.assign(errno, std::generic_category());
1341   else
1342     ec.clear();
1343 }
1344 
1345 void
resize_file(const path & p,uintmax_t size)1346 fs::resize_file(const path& p, uintmax_t size)
1347 {
1348   error_code ec;
1349   resize_file(p, size, ec);
1350   if (ec)
1351     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1352 }
1353 
1354 void
resize_file(const path & p,uintmax_t size,error_code & ec)1355 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1356 {
1357   if (size > static_cast<uintmax_t>(std::numeric_limits<off_t>::max()))
1358     ec.assign(EINVAL, std::generic_category());
1359   else if (posix::truncate(p.c_str(), size))
1360     ec.assign(errno, std::generic_category());
1361   else
1362     ec.clear();
1363 }
1364 
1365 
1366 fs::space_info
space(const path & p)1367 fs::space(const path& p)
1368 {
1369   error_code ec;
1370   space_info s = space(p, ec);
1371   if (ec)
1372     _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1373   return s;
1374 }
1375 
1376 fs::space_info
space(const path & p,error_code & ec)1377 fs::space(const path& p, error_code& ec) noexcept
1378 {
1379   space_info info = {
1380     static_cast<uintmax_t>(-1),
1381     static_cast<uintmax_t>(-1),
1382     static_cast<uintmax_t>(-1)
1383   };
1384 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1385 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1386   path dir = absolute(p);
1387   dir.remove_filename();
1388   auto str = dir.c_str();
1389 #else
1390   auto str = p.c_str();
1391 #endif
1392 
1393   do_space(str, info.capacity, info.free, info.available, ec);
1394 #endif // _GLIBCXX_HAVE_SYS_STAT_H
1395 
1396   return info;
1397 }
1398 
1399 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1400 fs::file_status
status(const fs::path & p,error_code & ec)1401 fs::status(const fs::path& p, error_code& ec) noexcept
1402 {
1403   file_status status;
1404   auto str = p.c_str();
1405 
1406 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1407 #if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6
1408   // stat() fails if there's a trailing slash (PR 88881)
1409   path p2;
1410   if (p.has_relative_path() && !p.has_filename())
1411     {
1412       __try
1413 	{
1414 	  p2 = p.parent_path();
1415 	  str = p2.c_str();
1416 	}
1417       __catch(const bad_alloc&)
1418 	{
1419 	  ec = std::make_error_code(std::errc::not_enough_memory);
1420 	  return status;
1421 	}
1422       str = p2.c_str();
1423     }
1424 #endif
1425 #endif
1426 
1427   stat_type st;
1428   if (posix::stat(str, &st))
1429     {
1430       int err = errno;
1431       ec.assign(err, std::generic_category());
1432       if (is_not_found_errno(err))
1433 	status.type(file_type::not_found);
1434 #ifdef EOVERFLOW
1435       else if (err == EOVERFLOW)
1436 	status.type(file_type::unknown);
1437 #endif
1438     }
1439   else
1440     {
1441       status = make_file_status(st);
1442       ec.clear();
1443     }
1444   return status;
1445 }
1446 
1447 fs::file_status
symlink_status(const fs::path & p,std::error_code & ec)1448 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1449 {
1450   file_status status;
1451   auto str = p.c_str();
1452 
1453 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1454 #if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6
1455   // stat() fails if there's a trailing slash (PR 88881)
1456   path p2;
1457   if (p.has_relative_path() && !p.has_filename())
1458     {
1459       __try
1460 	{
1461 	  p2 = p.parent_path();
1462 	  str = p2.c_str();
1463 	}
1464       __catch(const bad_alloc&)
1465 	{
1466 	  ec = std::make_error_code(std::errc::not_enough_memory);
1467 	  return status;
1468 	}
1469       str = p2.c_str();
1470     }
1471 #endif
1472 #endif
1473 
1474   stat_type st;
1475   if (posix::lstat(str, &st))
1476     {
1477       int err = errno;
1478       ec.assign(err, std::generic_category());
1479       if (is_not_found_errno(err))
1480 	status.type(file_type::not_found);
1481     }
1482   else
1483     {
1484       status = make_file_status(st);
1485       ec.clear();
1486     }
1487   return status;
1488 }
1489 #endif
1490 
1491 fs::file_status
status(const fs::path & p)1492 fs::status(const fs::path& p)
1493 {
1494   std::error_code ec;
1495   auto result = status(p, ec);
1496   if (result.type() == file_type::none)
1497     _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1498   return result;
1499 }
1500 
1501 fs::file_status
symlink_status(const fs::path & p)1502 fs::symlink_status(const fs::path& p)
1503 {
1504   std::error_code ec;
1505   auto result = symlink_status(p, ec);
1506   if (result.type() == file_type::none)
1507     _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1508   return result;
1509 }
1510 
temp_directory_path()1511 fs::path fs::temp_directory_path()
1512 {
1513   error_code ec;
1514   path tmp = temp_directory_path(ec);
1515   if (ec)
1516     _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1517   return tmp;
1518 }
1519 
temp_directory_path(error_code & ec)1520 fs::path fs::temp_directory_path(error_code& ec)
1521 {
1522   path p;
1523 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1524   unsigned len = 1024;
1525   std::wstring buf;
1526   do
1527     {
1528       buf.resize(len);
1529       len = GetTempPathW(buf.size(), buf.data());
1530     } while (len > buf.size());
1531 
1532   if (len == 0)
1533     {
1534       ec.assign((int)GetLastError(), std::system_category());
1535       return p;
1536     }
1537   buf.resize(len);
1538   p = std::move(buf);
1539 #else
1540   const char* tmpdir = nullptr;
1541   const char* env[] = { "TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr };
1542   for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e)
1543     tmpdir = ::getenv(*e);
1544   p = tmpdir ? tmpdir : "/tmp";
1545 #endif
1546   auto st = status(p, ec);
1547   if (ec)
1548     p.clear();
1549   else if (!is_directory(st))
1550     {
1551       p.clear();
1552       ec = std::make_error_code(std::errc::not_a_directory);
1553     }
1554   return p;
1555 }
1556 
1557 fs::path
weakly_canonical(const path & p)1558 fs::weakly_canonical(const path& p)
1559 {
1560   path result;
1561   if (exists(status(p)))
1562     return canonical(p);
1563 
1564   path tmp;
1565   auto iter = p.begin(), end = p.end();
1566   // find leading elements of p that exist:
1567   while (iter != end)
1568     {
1569       tmp = result / *iter;
1570       if (exists(status(tmp)))
1571 	swap(result, tmp);
1572       else
1573 	break;
1574       ++iter;
1575     }
1576   // canonicalize:
1577   if (!result.empty())
1578     result = canonical(result);
1579   // append the non-existing elements:
1580   while (iter != end)
1581     result /= *iter++;
1582   // normalize:
1583   return result.lexically_normal();
1584 }
1585 
1586 fs::path
weakly_canonical(const path & p,error_code & ec)1587 fs::weakly_canonical(const path& p, error_code& ec)
1588 {
1589   path result;
1590   file_status st = status(p, ec);
1591   if (exists(st))
1592     return canonical(p, ec);
1593   else if (status_known(st))
1594     ec.clear();
1595   else
1596     return result;
1597 
1598   path tmp;
1599   auto iter = p.begin(), end = p.end();
1600   // find leading elements of p that exist:
1601   while (iter != end)
1602     {
1603       tmp = result / *iter;
1604       st = status(tmp, ec);
1605       if (exists(st))
1606 	swap(result, tmp);
1607       else
1608 	{
1609 	  if (status_known(st))
1610 	    ec.clear();
1611 	  break;
1612 	}
1613       ++iter;
1614     }
1615   // canonicalize:
1616   if (!ec && !result.empty())
1617     result = canonical(result, ec);
1618   if (ec)
1619     result.clear();
1620   else
1621     {
1622       // append the non-existing elements:
1623       while (iter != end)
1624 	result /= *iter++;
1625       // normalize:
1626       result = result.lexically_normal();
1627     }
1628   return result;
1629 }
1630