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