Fix timevar.cc build on systems that don't have CLOCK_MONOTONIC
[official-gcc.git] / libstdc++-v3 / src / c++17 / fs_ops.cc
blob946fefd9e449a62ad742a80736adf26dce0b0147
1 // Filesystem operations -*- C++ -*-
3 // Copyright (C) 2014-2024 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.
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.
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.
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/>.
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
27 # define NEED_DO_COPY_FILE
28 # define NEED_DO_SPACE
29 #endif
30 #ifndef _GNU_SOURCE
31 // Cygwin needs this for secure_getenv
32 # define _GNU_SOURCE 1
33 #endif
35 #include <bits/largefile-config.h>
36 #include <filesystem>
37 #include <functional>
38 #include <ostream>
39 #include <stack>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <errno.h>
43 #include <limits.h> // PATH_MAX
44 #ifdef _GLIBCXX_HAVE_FCNTL_H
45 # include <fcntl.h> // AT_FDCWD, AT_SYMLINK_NOFOLLOW
46 #endif
47 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
48 # include <sys/stat.h> // stat, utimensat, fchmodat
49 #endif
50 #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H
51 # include <sys/statvfs.h> // statvfs
52 #endif
53 #if !_GLIBCXX_USE_UTIMENSAT && _GLIBCXX_HAVE_UTIME_H
54 # include <utime.h> // utime
55 #endif
56 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
57 # define WIN32_LEAN_AND_MEAN
58 # include <windows.h>
59 #endif
61 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
62 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
63 #include "../filesystem/ops-common.h"
65 #pragma GCC diagnostic ignored "-Wunused-parameter"
67 namespace fs = std::filesystem;
68 namespace posix = std::filesystem::__gnu_posix;
70 fs::path
71 fs::absolute(const path& p)
73 error_code ec;
74 path ret = absolute(p, ec);
75 if (ec)
76 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make absolute path", p,
77 ec));
78 return ret;
81 fs::path
82 fs::absolute(const path& p, error_code& ec)
84 path ret;
85 if (p.empty())
87 ec = make_error_code(std::errc::invalid_argument);
88 return ret;
90 ec.clear();
91 if (p.is_absolute())
93 ret = p;
94 return ret;
97 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
98 // s must remain null-terminated
99 wstring_view s = p.native();
101 if (p.has_root_directory()) // implies !p.has_root_name()
103 // GetFullPathNameW("//") gives unwanted result (PR 88884).
104 // If there are multiple directory separators at the start,
105 // skip all but the last of them.
106 const auto pos = s.find_first_not_of(L"/\\");
107 __glibcxx_assert(pos != 0);
108 s.remove_prefix(std::min(s.length(), pos) - 1);
111 uint32_t len = 1024;
112 wstring buf;
115 buf.__resize_and_overwrite(len, [&s, &len](wchar_t* p, unsigned n) {
116 len = GetFullPathNameW(s.data(), n, p, nullptr);
117 return len > n ? 0 : len;
120 while (len > buf.size());
122 if (len == 0)
123 ec = __last_system_error();
124 else
125 ret = std::move(buf);
126 #else
127 ret = current_path(ec);
128 ret /= p;
129 #endif
130 return ret;
133 namespace
135 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
136 inline bool is_dot(wchar_t c) { return c == L'.'; }
137 #else
138 inline bool is_dot(char c) { return c == '.'; }
139 #endif
141 inline bool is_dot(const fs::path& path)
143 const auto& filename = path.native();
144 return filename.size() == 1 && is_dot(filename[0]);
147 inline bool is_dotdot(const fs::path& path)
149 const auto& filename = path.native();
150 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
153 struct free_as_in_malloc
155 void operator()(void* p) const { ::free(p); }
158 using char_ptr = std::unique_ptr<fs::path::value_type[], free_as_in_malloc>;
161 fs::path
162 fs::canonical(const path& p, error_code& ec)
164 path result;
165 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
166 const path pa = absolute(p.lexically_normal(), ec);
167 #else
168 const path pa = absolute(p, ec);
169 #endif
170 if (ec)
171 return result;
173 #ifdef _GLIBCXX_USE_REALPATH
174 char_ptr buf{ nullptr };
175 # if _XOPEN_VERSION < 700
176 // Not safe to call realpath(path, NULL)
177 using char_type = fs::path::value_type;
178 buf.reset( (char_type*)::malloc(PATH_MAX * sizeof(char_type)) );
179 # endif
180 if (char* rp = ::realpath(pa.c_str(), buf.get()))
182 if (buf == nullptr)
183 buf.reset(rp);
184 result.assign(rp);
185 ec.clear();
186 return result;
188 if (errno != ENAMETOOLONG)
190 ec.assign(errno, std::generic_category());
191 return result;
193 #endif
195 if (!exists(pa, ec))
197 if (!ec)
198 ec = make_error_code(std::errc::no_such_file_or_directory);
199 return result;
201 // else: we know there are (currently) no unresolvable symlink loops
203 result = pa.root_path();
205 deque<path> cmpts;
206 for (auto& f : pa.relative_path())
207 cmpts.push_back(f);
209 int max_allowed_symlinks = 40;
211 while (!cmpts.empty() && !ec)
213 path f = std::move(cmpts.front());
214 cmpts.pop_front();
216 if (f.empty())
218 // ignore empty element
220 else if (is_dot(f))
222 if (!is_directory(result, ec) && !ec)
223 ec.assign(ENOTDIR, std::generic_category());
225 else if (is_dotdot(f))
227 auto parent = result.parent_path();
228 if (parent.empty())
229 result = pa.root_path();
230 else
231 result.swap(parent);
233 else
235 result /= f;
237 if (is_symlink(result, ec))
239 path link = read_symlink(result, ec);
240 if (!ec)
242 if (--max_allowed_symlinks == 0)
243 ec.assign(ELOOP, std::generic_category());
244 else
246 if (link.is_absolute())
248 result = link.root_path();
249 link = link.relative_path();
251 else
252 result = result.parent_path();
254 cmpts.insert(cmpts.begin(), link.begin(), link.end());
261 if (ec || !exists(result, ec))
262 result.clear();
264 return result;
267 fs::path
268 fs::canonical(const path& p)
270 error_code ec;
271 path res = canonical(p, ec);
272 if (ec)
273 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot make canonical path",
274 p, ec));
275 return res;
278 void
279 fs::copy(const path& from, const path& to, copy_options options)
281 error_code ec;
282 copy(from, to, options, ec);
283 if (ec)
284 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy", from, to, ec));
287 namespace std::filesystem
289 // Need this as there's no 'perm_options::none' enumerator.
290 static inline bool is_set(fs::perm_options obj, fs::perm_options bits)
292 return (obj & bits) != fs::perm_options{};
296 namespace
298 struct internal_file_clock : fs::__file_clock
300 using __file_clock::_S_to_sys;
301 using __file_clock::_S_from_sys;
303 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
304 static fs::file_time_type
305 from_stat(const fs::stat_type& st, std::error_code& ec) noexcept
307 const auto sys_time = fs::file_time(st, ec);
308 if (sys_time == sys_time.min())
309 return fs::file_time_type::min();
310 return _S_from_sys(sys_time);
312 #endif
316 void
317 fs::copy(const path& from, const path& to, copy_options options,
318 error_code& ec)
320 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
321 const bool skip_symlinks = is_set(options, copy_options::skip_symlinks);
322 const bool create_symlinks = is_set(options, copy_options::create_symlinks);
323 const bool copy_symlinks = is_set(options, copy_options::copy_symlinks);
324 const bool use_lstat = create_symlinks || skip_symlinks;
326 file_status f, t;
327 stat_type from_st, to_st;
328 // _GLIBCXX_RESOLVE_LIB_DEFECTS
329 // 2681. filesystem::copy() cannot copy symlinks
330 if (use_lstat || copy_symlinks
331 ? posix::lstat(from.c_str(), &from_st)
332 : posix::stat(from.c_str(), &from_st))
334 ec.assign(errno, std::generic_category());
335 return;
337 if (use_lstat
338 ? posix::lstat(to.c_str(), &to_st)
339 : posix::stat(to.c_str(), &to_st))
341 if (!is_not_found_errno(errno))
343 ec.assign(errno, std::generic_category());
344 return;
346 t = file_status{file_type::not_found};
348 else
349 t = make_file_status(to_st);
350 f = make_file_status(from_st);
352 if (exists(t) && !is_other(t) && !is_other(f)
353 && fs::equiv_files(from.c_str(), from_st, to.c_str(), to_st, ec))
355 ec = std::make_error_code(std::errc::file_exists);
356 return;
358 if (is_other(f) || is_other(t))
360 ec = std::make_error_code(std::errc::invalid_argument);
361 return;
363 if (is_directory(f) && is_regular_file(t))
365 ec = std::make_error_code(std::errc::is_a_directory);
366 return;
369 if (is_symlink(f))
371 if (skip_symlinks)
372 ec.clear();
373 else if (!exists(t) && copy_symlinks)
374 copy_symlink(from, to, ec);
375 else
376 // Not clear what should be done here.
377 // "Otherwise report an error as specified in Error reporting (7)."
378 ec = std::make_error_code(std::errc::invalid_argument);
380 else if (is_regular_file(f))
382 if (is_set(options, copy_options::directories_only))
383 ec.clear();
384 else if (create_symlinks)
385 create_symlink(from, to, ec);
386 else if (is_set(options, copy_options::create_hard_links))
387 create_hard_link(from, to, ec);
388 else if (is_directory(t))
389 do_copy_file(from.c_str(), (to / from.filename()).c_str(),
390 copy_file_options(options), &from_st, nullptr, ec);
391 else
393 auto ptr = exists(t) ? &to_st : &from_st;
394 do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
395 &from_st, ptr, ec);
398 // _GLIBCXX_RESOLVE_LIB_DEFECTS
399 // 2682. filesystem::copy() won't create a symlink to a directory
400 else if (is_directory(f) && create_symlinks)
401 ec = std::make_error_code(errc::is_a_directory);
402 else if (is_directory(f) && (is_set(options, copy_options::recursive)
403 || options == copy_options::none))
405 if (!exists(t))
406 if (!create_directory(to, from, ec))
407 return;
408 // set an unused bit in options to disable further recursion
409 if (!is_set(options, copy_options::recursive))
410 options |= static_cast<copy_options>(4096);
411 for (const directory_entry& x : directory_iterator(from, ec))
413 copy(x.path(), to/x.path().filename(), options, ec);
414 if (ec)
415 return;
418 // _GLIBCXX_RESOLVE_LIB_DEFECTS
419 // 2683. filesystem::copy() says "no effects"
420 else
421 ec.clear();
422 #else
423 ec = std::make_error_code(std::errc::function_not_supported);
424 #endif
427 bool
428 fs::copy_file(const path& from, const path& to, copy_options option)
430 error_code ec;
431 bool result = copy_file(from, to, option, ec);
432 if (ec)
433 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy file", from, to,
434 ec));
435 return result;
438 bool
439 fs::copy_file(const path& from, const path& to, copy_options options,
440 error_code& ec)
442 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
443 return do_copy_file(from.c_str(), to.c_str(), copy_file_options(options),
444 nullptr, nullptr, ec);
445 #else
446 ec = std::make_error_code(std::errc::function_not_supported);
447 return false;
448 #endif
452 void
453 fs::copy_symlink(const path& existing_symlink, const path& new_symlink)
455 error_code ec;
456 copy_symlink(existing_symlink, new_symlink, ec);
457 if (ec)
458 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot copy symlink",
459 existing_symlink, new_symlink, ec));
462 void
463 fs::copy_symlink(const path& existing_symlink, const path& new_symlink,
464 error_code& ec) noexcept
466 auto p = read_symlink(existing_symlink, ec);
467 if (ec)
468 return;
469 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
470 if (is_directory(p))
472 create_directory_symlink(p, new_symlink, ec);
473 return;
475 #endif
476 create_symlink(p, new_symlink, ec);
480 bool
481 fs::create_directories(const path& p)
483 error_code ec;
484 bool result = create_directories(p, ec);
485 if (ec)
486 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot create directories", p,
487 ec));
488 return result;
491 bool
492 fs::create_directories(const path& p, error_code& ec)
494 if (p.empty())
496 ec = std::make_error_code(errc::invalid_argument);
497 return false;
500 file_status st = status(p, ec);
501 if (is_directory(st))
502 return false;
503 else if (ec && !status_known(st))
504 return false;
505 else if (exists(st))
507 if (!ec)
508 ec = std::make_error_code(std::errc::not_a_directory);
509 return false;
512 __glibcxx_assert(st.type() == file_type::not_found);
513 // !exists(p) so there must be at least one non-existent component in p.
515 std::stack<path> missing;
516 path pp = p;
518 // Strip any trailing slash
519 if (pp.has_relative_path() && !pp.has_filename())
520 pp = pp.parent_path();
524 const auto& filename = pp.filename();
525 if (is_dot(filename) || is_dotdot(filename))
526 pp = pp.parent_path();
527 else
529 missing.push(std::move(pp));
530 if (missing.size() > 1000) // sanity check
532 ec = std::make_error_code(std::errc::filename_too_long);
533 return false;
535 pp = missing.top().parent_path();
538 if (pp.empty())
539 break;
541 st = status(pp, ec);
542 if (exists(st))
544 if (ec)
545 return false;
546 if (!is_directory(st))
548 ec = std::make_error_code(std::errc::not_a_directory);
549 return false;
553 if (ec && exists(st))
554 return false;
556 while (st.type() == file_type::not_found);
558 __glibcxx_assert(!missing.empty());
560 bool created;
563 const path& top = missing.top();
564 created = create_directory(top, ec);
565 if (ec)
566 return false;
567 missing.pop();
569 while (!missing.empty());
571 return created;
574 namespace
576 bool
577 create_dir(const fs::path& p, fs::perms perm, std::error_code& ec)
579 bool created = false;
580 #if _GLIBCXX_USE_MKDIR
581 posix::mode_t mode = static_cast<std::underlying_type_t<fs::perms>>(perm);
582 if (posix::mkdir(p.c_str(), mode))
584 const int err = errno;
585 if (err != EEXIST || !is_directory(p, ec))
586 ec.assign(err, std::generic_category());
588 else
590 ec.clear();
591 created = true;
593 #else
594 ec = std::make_error_code(std::errc::function_not_supported);
595 #endif
596 return created;
598 } // namespace
600 bool
601 fs::create_directory(const path& p)
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;
611 bool
612 fs::create_directory(const path& p, error_code& ec) noexcept
614 return create_dir(p, perms::all, ec);
618 bool
619 fs::create_directory(const path& p, const path& attributes)
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;
629 bool
630 fs::create_directory(const path& p, const path& attributes,
631 error_code& ec) noexcept
633 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
634 stat_type st;
635 if (posix::stat(attributes.c_str(), &st))
637 ec.assign(errno, std::generic_category());
638 return false;
640 return create_dir(p, static_cast<perms>(st.st_mode), ec);
641 #else
642 ec = std::make_error_code(std::errc::function_not_supported);
643 return false;
644 #endif
648 void
649 fs::create_directory_symlink(const path& to, const path& new_symlink)
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));
658 void
659 fs::create_directory_symlink(const path& to, const path& new_symlink,
660 error_code& ec) noexcept
662 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
663 ec = std::make_error_code(std::errc::function_not_supported);
664 #else
665 create_symlink(to, new_symlink, ec);
666 #endif
670 void
671 fs::create_hard_link(const path& to, const path& new_hard_link)
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));
680 void
681 fs::create_hard_link(const path& to, const path& new_hard_link,
682 error_code& ec) noexcept
684 #ifdef _GLIBCXX_HAVE_LINK
685 if (::link(to.c_str(), new_hard_link.c_str()))
686 ec.assign(errno, std::generic_category());
687 else
688 ec.clear();
689 #elif defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
690 if (CreateHardLinkW(new_hard_link.c_str(), to.c_str(), NULL))
691 ec.clear();
692 else
693 ec = __last_system_error();
694 #else
695 ec = std::make_error_code(std::errc::function_not_supported);
696 #endif
699 void
700 fs::create_symlink(const path& to, const path& new_symlink)
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));
709 void
710 fs::create_symlink(const path& to, const path& new_symlink,
711 error_code& ec) noexcept
713 #ifdef _GLIBCXX_HAVE_SYMLINK
714 if (::symlink(to.c_str(), new_symlink.c_str()))
715 ec.assign(errno, std::generic_category());
716 else
717 ec.clear();
718 #else
719 ec = std::make_error_code(std::errc::function_not_supported);
720 #endif
724 fs::path
725 fs::current_path()
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;
734 fs::path
735 fs::current_path(error_code& ec)
737 path p;
738 #if _GLIBCXX_USE_GETCWD
739 #if defined __GLIBC__ || defined _GLIBCXX_FILESYSTEM_IS_WINDOWS
740 if (char_ptr cwd = char_ptr{posix::getcwd(nullptr, 0)})
742 p.assign(cwd.get());
743 ec.clear();
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)
764 using char_type = fs::path::value_type;
765 buf.reset((char_type*)malloc(size * sizeof(char_type)));
766 if (buf)
768 if (getcwd(buf.get(), size))
770 p.assign(buf.get());
771 ec.clear();
773 else if (errno != ERANGE)
775 ec.assign(errno, std::generic_category());
776 return {};
779 else
781 ec = std::make_error_code(std::errc::not_enough_memory);
782 return {};
785 #endif // __GLIBC__
786 #else // _GLIBCXX_USE_GETCWD
787 ec = std::make_error_code(std::errc::function_not_supported);
788 #endif
789 return p;
792 void
793 fs::current_path(const path& p)
795 error_code ec;
796 current_path(p, ec);
797 if (ec)
798 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set current path", ec));
801 void
802 fs::current_path(const path& p, error_code& ec) noexcept
804 #if _GLIBCXX_USE_CHDIR
805 if (posix::chdir(p.c_str()))
806 ec.assign(errno, std::generic_category());
807 else
808 ec.clear();
809 #else
810 ec = std::make_error_code(std::errc::function_not_supported);
811 #endif
814 bool
815 fs::equivalent(const path& p1, const path& p2)
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;
825 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
826 namespace
828 // An RAII type that opens a handle for an existing file.
829 struct auto_win_file_handle
831 explicit
832 auto_win_file_handle(const wchar_t* p, std::error_code& ec) noexcept
833 : handle(CreateFileW(p, 0,
834 FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
835 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)),
836 ec(ec)
838 if (handle == INVALID_HANDLE_VALUE)
839 ec = std::__last_system_error();
842 ~auto_win_file_handle()
843 { if (*this) CloseHandle(handle); }
845 explicit operator bool() const noexcept
846 { return handle != INVALID_HANDLE_VALUE; }
848 bool get_info() noexcept
850 if (GetFileInformationByHandle(handle, &info))
851 return true;
852 ec = std::__last_system_error();
853 return false;
856 HANDLE handle;
857 BY_HANDLE_FILE_INFORMATION info;
858 // Like errno, we only set this on error and never clear it.
859 // This propagates an error_code to the caller when something goes wrong,
860 // but the caller should not assume a non-zero ec means an error happened
861 // unless they explicitly cleared it before passing it to our constructor.
862 std::error_code& ec;
865 #endif
867 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
868 #ifdef NEED_DO_COPY_FILE // Only define this once, not in cow-fs_ops.o too
869 bool
870 fs::equiv_files([[maybe_unused]] const char_type* p1, const stat_type& st1,
871 [[maybe_unused]] const char_type* p2, const stat_type& st2,
872 [[maybe_unused]] error_code& ec)
874 #if ! _GLIBCXX_FILESYSTEM_IS_WINDOWS
875 // For POSIX the device ID and inode number uniquely identify a file.
876 return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
877 #else
878 // For Windows st_ino is not set, so can't be used to distinguish files.
879 // We can compare modes and device IDs as a cheap initial check:
880 if (st1.st_mode != st2.st_mode || st1.st_dev != st2.st_dev)
881 return false;
883 // Use GetFileInformationByHandle to get more info about the files.
884 if (auto_win_file_handle h1{p1, ec})
885 if (auto_win_file_handle h2{p2, ec})
886 if (h1.get_info() && h2.get_info())
887 return h1.info.dwVolumeSerialNumber == h2.info.dwVolumeSerialNumber
888 && h1.info.nFileIndexHigh == h2.info.nFileIndexHigh
889 && h1.info.nFileIndexLow == h2.info.nFileIndexLow;
890 return false;
891 #endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS
893 #endif // NEED_DO_COPY_FILE
894 #endif // _GLIBCXX_HAVE_SYS_STAT_H
896 bool
897 fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
899 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
900 int err = 0;
901 file_status s1, s2;
902 stat_type st1, st2;
903 if (posix::stat(p1.c_str(), &st1) == 0)
904 s1 = make_file_status(st1);
905 else if (is_not_found_errno(errno))
906 s1.type(file_type::not_found);
907 else
908 err = errno;
910 if (posix::stat(p2.c_str(), &st2) == 0)
911 s2 = make_file_status(st2);
912 else if (is_not_found_errno(errno))
913 s2.type(file_type::not_found);
914 else
915 err = errno;
917 if (exists(s1) && exists(s2))
919 if (is_other(s1) && is_other(s2))
921 ec = std::__unsupported();
922 return false;
924 ec.clear();
925 if (is_other(s1) || is_other(s2))
926 return false;
927 return fs::equiv_files(p1.c_str(), st1, p2.c_str(), st2, ec);
929 else if (!exists(s1) || !exists(s2))
930 ec = std::make_error_code(std::errc::no_such_file_or_directory);
931 else if (err)
932 ec.assign(err, std::generic_category());
933 else
934 ec.clear();
935 return false;
936 #else
937 ec = std::make_error_code(std::errc::function_not_supported);
938 #endif
939 return false;
942 std::uintmax_t
943 fs::file_size(const path& p)
945 error_code ec;
946 auto sz = file_size(p, ec);
947 if (ec)
948 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file size", p, ec));
949 return sz;
952 namespace
954 template<typename Accessor, typename T>
955 inline T
956 do_stat(const fs::path& p, std::error_code& ec, Accessor f, T deflt)
958 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
959 posix::stat_type st;
960 if (posix::stat(p.c_str(), &st))
962 ec.assign(errno, std::generic_category());
963 return deflt;
965 ec.clear();
966 return f(st);
967 #else
968 ec = std::make_error_code(std::errc::function_not_supported);
969 return deflt;
970 #endif
974 std::uintmax_t
975 fs::file_size(const path& p, error_code& ec) noexcept
977 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
978 struct S
980 S(const stat_type& st) : type(make_file_type(st)), size(st.st_size) { }
981 S() : type(file_type::not_found) { }
982 file_type type;
983 uintmax_t size;
985 auto s = do_stat(p, ec, [](const auto& st) { return S{st}; }, S{});
986 if (s.type == file_type::regular)
987 return s.size;
988 if (!ec)
990 if (s.type == file_type::directory)
991 ec = std::make_error_code(std::errc::is_a_directory);
992 else
993 ec = std::__unsupported();
995 #else
996 ec = std::make_error_code(std::errc::function_not_supported);
997 #endif
998 return -1;
1001 std::uintmax_t
1002 fs::hard_link_count(const path& p)
1004 error_code ec;
1005 auto count = hard_link_count(p, ec);
1006 if (ec)
1007 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get link count", p, ec));
1008 return count;
1011 std::uintmax_t
1012 fs::hard_link_count(const path& p, error_code& ec) noexcept
1014 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1015 auto_win_file_handle h(p.c_str(), ec);
1016 if (h && h.get_info())
1018 ec.clear();
1019 return static_cast<uintmax_t>(h.info.nNumberOfLinks);
1021 return static_cast<uintmax_t>(-1);
1022 #elif defined _GLIBCXX_HAVE_SYS_STAT_H
1023 return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink),
1024 static_cast<uintmax_t>(-1));
1025 #else
1026 ec = std::make_error_code(std::errc::function_not_supported);
1027 return static_cast<uintmax_t>(-1);
1028 #endif
1031 bool
1032 fs::is_empty(const path& p)
1034 error_code ec;
1035 bool e = is_empty(p, ec);
1036 if (ec)
1037 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty",
1038 p, ec));
1039 return e;
1042 bool
1043 fs::is_empty(const path& p, error_code& ec)
1045 auto s = status(p, ec);
1046 if (ec)
1047 return false;
1048 bool empty = fs::is_directory(s)
1049 ? fs::directory_iterator(p, ec) == fs::directory_iterator()
1050 : fs::file_size(p, ec) == 0;
1051 return ec ? false : empty;
1054 fs::file_time_type
1055 fs::last_write_time(const path& p)
1057 error_code ec;
1058 auto t = last_write_time(p, ec);
1059 if (ec)
1060 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get file time", p, ec));
1061 return t;
1064 fs::file_time_type
1065 fs::last_write_time(const path& p, error_code& ec) noexcept
1067 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1068 return do_stat(p, ec,
1069 [&ec](const auto& st) {
1070 return internal_file_clock::from_stat(st, ec);
1072 file_time_type::min());
1073 #else
1074 ec = std::make_error_code(std::errc::function_not_supported);
1075 return file_time_type::min();
1076 #endif
1079 void
1080 fs::last_write_time(const path& p, file_time_type new_time)
1082 error_code ec;
1083 last_write_time(p, new_time, ec);
1084 if (ec)
1085 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set file time", p, ec));
1088 void
1089 fs::last_write_time(const path& p,
1090 file_time_type new_time, error_code& ec) noexcept
1092 auto d = internal_file_clock::_S_to_sys(new_time).time_since_epoch();
1093 auto s = chrono::duration_cast<chrono::seconds>(d);
1094 #if _GLIBCXX_USE_UTIMENSAT
1095 auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s);
1096 if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9.
1098 --s;
1099 ns += chrono::seconds(1);
1101 struct ::timespec ts[2];
1102 ts[0].tv_sec = 0;
1103 ts[0].tv_nsec = UTIME_OMIT;
1104 ts[1].tv_sec = static_cast<std::time_t>(s.count());
1105 ts[1].tv_nsec = static_cast<long>(ns.count());
1106 if (::utimensat(AT_FDCWD, p.c_str(), ts, 0))
1107 ec.assign(errno, std::generic_category());
1108 else
1109 ec.clear();
1110 #elif _GLIBCXX_USE_UTIME && _GLIBCXX_HAVE_SYS_STAT_H
1111 posix::utimbuf times;
1112 times.modtime = s.count();
1113 times.actime = do_stat(p, ec, [](const auto& st) { return st.st_atime; },
1114 times.modtime);
1115 if (posix::utime(p.c_str(), &times))
1116 ec.assign(errno, std::generic_category());
1117 else
1118 ec.clear();
1119 #else
1120 ec = std::make_error_code(std::errc::function_not_supported);
1121 #endif
1124 void
1125 fs::permissions(const path& p, perms prms, perm_options opts)
1127 error_code ec;
1128 permissions(p, prms, opts, ec);
1129 if (ec)
1130 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec));
1133 void
1134 fs::permissions(const path& p, perms prms, perm_options opts,
1135 error_code& ec) noexcept
1137 #if _GLIBCXX_USE_FCHMODAT || _GLIBCXX_USE_CHMOD
1138 const bool replace = is_set(opts, perm_options::replace);
1139 const bool add = is_set(opts, perm_options::add);
1140 const bool remove = is_set(opts, perm_options::remove);
1141 const bool nofollow = is_set(opts, perm_options::nofollow);
1142 if (((int)replace + (int)add + (int)remove) != 1)
1144 ec = std::make_error_code(std::errc::invalid_argument);
1145 return;
1148 prms &= perms::mask;
1150 file_status st;
1151 if (add || remove || nofollow)
1153 st = nofollow ? symlink_status(p, ec) : status(p, ec);
1154 if (ec)
1155 return;
1156 auto curr = st.permissions();
1157 if (add)
1158 prms |= curr;
1159 else if (remove)
1160 prms = curr & ~prms;
1163 int err = 0;
1164 #if _GLIBCXX_USE_FCHMODAT
1165 const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0;
1166 if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
1167 err = errno;
1168 #else
1169 if (nofollow && is_symlink(st))
1170 ec = std::__unsupported();
1171 else if (posix::chmod(p.c_str(), static_cast<posix::mode_t>(prms)))
1172 err = errno;
1173 #endif
1175 if (err)
1176 ec.assign(err, std::generic_category());
1177 else
1178 ec.clear();
1179 #else
1180 ec = std::make_error_code(std::errc::function_not_supported);
1181 #endif
1184 fs::path
1185 fs::proximate(const path& p, const path& base)
1187 return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
1190 fs::path
1191 fs::proximate(const path& p, const path& base, error_code& ec)
1193 path result;
1194 const auto p2 = weakly_canonical(p, ec);
1195 if (!ec)
1197 const auto base2 = weakly_canonical(base, ec);
1198 if (!ec)
1199 result = p2.lexically_proximate(base2);
1201 return result;
1204 fs::path
1205 fs::read_symlink(const path& p)
1207 error_code ec;
1208 path tgt = read_symlink(p, ec);
1209 if (ec)
1210 _GLIBCXX_THROW_OR_ABORT(filesystem_error("read_symlink", p, ec));
1211 return tgt;
1214 fs::path fs::read_symlink(const path& p, error_code& ec)
1216 path result;
1217 #if defined(_GLIBCXX_HAVE_READLINK) && defined(_GLIBCXX_HAVE_SYS_STAT_H)
1218 stat_type st;
1219 if (posix::lstat(p.c_str(), &st))
1221 ec.assign(errno, std::generic_category());
1222 return result;
1224 else if (!fs::is_symlink(make_file_status(st)))
1226 ec.assign(EINVAL, std::generic_category());
1227 return result;
1230 std::string buf;
1231 size_t bufsz = st.st_size ? st.st_size + 1 : 128;
1234 ssize_t len;
1235 buf.__resize_and_overwrite(bufsz, [&p, &len](char* ptr, size_t n) {
1236 len = ::readlink(p.c_str(), ptr, n);
1237 return size_t(len) < n ? len : 0;
1239 if (buf.size())
1241 result.assign(std::move(buf));
1242 ec.clear();
1243 break;
1245 else if (len == -1)
1247 ec.assign(errno, std::generic_category());
1248 return result;
1250 else if (bufsz > 4096)
1252 ec.assign(ENAMETOOLONG, std::generic_category());
1253 return result;
1255 else
1256 bufsz *= 2;
1258 while (true);
1259 #else
1260 ec = std::make_error_code(std::errc::function_not_supported);
1261 #endif
1262 return result;
1265 fs::path
1266 fs::relative(const path& p, const path& base)
1268 return weakly_canonical(p).lexically_relative(weakly_canonical(base));
1271 fs::path
1272 fs::relative(const path& p, const path& base, error_code& ec)
1274 auto result = weakly_canonical(p, ec);
1275 fs::path cbase;
1276 if (!ec)
1277 cbase = weakly_canonical(base, ec);
1278 if (!ec)
1279 result = result.lexically_relative(cbase);
1280 if (ec)
1281 result.clear();
1282 return result;
1285 bool
1286 fs::remove(const path& p)
1288 error_code ec;
1289 const bool result = fs::remove(p, ec);
1290 if (ec)
1291 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove", p, ec));
1292 return result;
1295 bool
1296 fs::remove(const path& p, error_code& ec) noexcept
1298 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
1299 auto st = symlink_status(p, ec);
1300 if (exists(st))
1302 if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))
1303 || DeleteFileW(p.c_str()))
1305 ec.clear();
1306 return true;
1308 else if (!ec)
1309 ec = __last_system_error();
1311 else if (status_known(st))
1312 ec.clear();
1313 #else
1314 if (::remove(p.c_str()) == 0)
1316 ec.clear();
1317 return true;
1319 else if (errno == ENOENT)
1320 ec.clear();
1321 else
1322 ec.assign(errno, std::generic_category());
1323 #endif
1324 return false;
1327 std::uintmax_t
1328 fs::remove_all(const path& p)
1330 error_code ec;
1331 uintmax_t count = 0;
1332 recursive_directory_iterator dir(p, directory_options{64|128}, ec);
1333 switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
1335 case 0:
1336 // Iterate over the directory removing everything.
1338 const recursive_directory_iterator end;
1339 while (dir != end)
1341 dir.__erase(); // throws on error
1342 ++count;
1345 // Directory is empty now, will remove it below.
1346 break;
1347 #ifndef __AVR__
1348 case ENOENT:
1349 // Our work here is done.
1350 return 0;
1351 case ENOTDIR:
1352 case ELOOP: // POSIX says openat with O_NOFOLLOW sets ELOOP for a symlink.
1353 #if defined __FreeBSD__ || defined __DragonFly__
1354 case EMLINK: // Used instead of ELOOP
1355 #endif
1356 #if defined __NetBSD__ && defined EFTYPE
1357 case EFTYPE: // Used instead of ELOOP
1358 #endif
1359 // Not a directory, will remove below.
1360 break;
1361 #endif
1362 default:
1363 // An error occurred.
1364 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot remove all", p, ec));
1367 // Remove p itself, which is either a non-directory or is now empty.
1368 return count + fs::remove(p);
1371 std::uintmax_t
1372 fs::remove_all(const path& p, error_code& ec)
1374 uintmax_t count = 0;
1375 recursive_directory_iterator dir(p, directory_options{64|128}, ec);
1376 switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()
1378 case 0:
1379 // Iterate over the directory removing everything.
1381 const recursive_directory_iterator end;
1382 while (dir != end)
1384 dir.__erase(&ec);
1385 if (ec)
1386 return -1;
1387 ++count;
1390 // Directory is empty now, will remove it below.
1391 break;
1392 #ifndef __AVR__
1393 case ENOENT:
1394 // Our work here is done.
1395 ec.clear();
1396 return 0;
1397 case ENOTDIR:
1398 case ELOOP: // POSIX says openat with O_NOFOLLOW sets ELOOP for a symlink.
1399 #if defined __FreeBSD__ || defined __DragonFly__
1400 case EMLINK: // Used instead of ELOOP
1401 #endif
1402 #if defined __NetBSD__ && defined EFTYPE
1403 case EFTYPE: // Used instead of ELOOP
1404 #endif
1405 // Not a directory, will remove below.
1406 break;
1407 #endif
1408 default:
1409 // An error occurred.
1410 return -1;
1413 // Remove p itself, which is either a non-directory or is now empty.
1414 if (int last = fs::remove(p, ec); !ec)
1415 return count + last;
1416 return -1;
1419 void
1420 fs::rename(const path& from, const path& to)
1422 error_code ec;
1423 rename(from, to, ec);
1424 if (ec)
1425 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot rename", from, to, ec));
1428 void
1429 fs::rename(const path& from, const path& to, error_code& ec) noexcept
1431 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1432 const auto to_status = fs::status(to, ec);
1433 if (to_status.type() == file_type::not_found)
1434 ec.clear();
1435 else if (ec)
1436 return;
1438 if (fs::exists(to_status))
1440 const auto from_status = fs::status(from, ec);
1441 if (ec)
1442 return;
1444 if (fs::is_directory(to_status))
1446 if (!fs::is_directory(from_status))
1448 // Cannot rename a non-directory over an existing directory.
1449 ec = std::make_error_code(std::errc::is_a_directory);
1450 return;
1453 else if (fs::is_directory(from_status))
1455 // Cannot rename a directory over an existing non-directory.
1456 ec = std::make_error_code(std::errc::not_a_directory);
1457 return;
1460 #endif
1461 if (posix::rename(from.c_str(), to.c_str()))
1462 ec.assign(errno, std::generic_category());
1463 else
1464 ec.clear();
1467 void
1468 fs::resize_file(const path& p, uintmax_t size)
1470 error_code ec;
1471 resize_file(p, size, ec);
1472 if (ec)
1473 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot resize file", p, ec));
1476 void
1477 fs::resize_file(const path& p, uintmax_t size, error_code& ec) noexcept
1479 if (size > static_cast<uintmax_t>(std::numeric_limits<posix::off_t>::max()))
1480 ec.assign(EINVAL, std::generic_category());
1481 else if (posix::truncate(p.c_str(), size))
1482 ec.assign(errno, std::generic_category());
1483 else
1484 ec.clear();
1488 fs::space_info
1489 fs::space(const path& p)
1491 error_code ec;
1492 space_info s = space(p, ec);
1493 if (ec)
1494 _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot get free space", p, ec));
1495 return s;
1498 fs::space_info
1499 fs::space(const path& p, error_code& ec) noexcept
1501 space_info info = {
1502 static_cast<uintmax_t>(-1),
1503 static_cast<uintmax_t>(-1),
1504 static_cast<uintmax_t>(-1)
1506 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1507 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1508 path dir = absolute(p);
1509 dir.remove_filename();
1510 auto str = dir.c_str();
1511 #else
1512 auto str = p.c_str();
1513 #endif
1515 do_space(str, info.capacity, info.free, info.available, ec);
1516 #endif // _GLIBCXX_HAVE_SYS_STAT_H
1518 return info;
1521 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
1522 fs::file_status
1523 fs::status(const fs::path& p, error_code& ec) noexcept
1525 file_status status;
1526 auto str = p.c_str();
1528 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1529 // stat() fails if there's a trailing slash (PR 88881)
1530 path p2;
1531 if (p.has_relative_path() && !p.has_filename())
1533 __try
1535 p2 = p.parent_path();
1536 str = p2.c_str();
1538 __catch(const bad_alloc&)
1540 ec = std::make_error_code(std::errc::not_enough_memory);
1541 return status;
1543 str = p2.c_str();
1545 #endif
1547 stat_type st;
1548 if (posix::stat(str, &st))
1550 int err = errno;
1551 ec.assign(err, std::generic_category());
1552 if (is_not_found_errno(err))
1553 status.type(file_type::not_found);
1554 #ifdef EOVERFLOW
1555 else if (err == EOVERFLOW)
1556 status.type(file_type::unknown);
1557 #endif
1559 else
1561 status = make_file_status(st);
1562 ec.clear();
1564 return status;
1567 fs::file_status
1568 fs::symlink_status(const fs::path& p, std::error_code& ec) noexcept
1570 file_status status;
1571 auto str = p.c_str();
1573 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
1574 // stat() fails if there's a trailing slash (PR 88881)
1575 path p2;
1576 if (p.has_relative_path() && !p.has_filename())
1578 __try
1580 p2 = p.parent_path();
1581 str = p2.c_str();
1583 __catch(const bad_alloc&)
1585 ec = std::make_error_code(std::errc::not_enough_memory);
1586 return status;
1588 str = p2.c_str();
1590 #endif
1592 stat_type st;
1593 if (posix::lstat(str, &st))
1595 int err = errno;
1596 ec.assign(err, std::generic_category());
1597 if (is_not_found_errno(err))
1598 status.type(file_type::not_found);
1600 else
1602 status = make_file_status(st);
1603 ec.clear();
1605 return status;
1607 #endif
1609 fs::file_status
1610 fs::status(const fs::path& p)
1612 std::error_code ec;
1613 auto result = status(p, ec);
1614 if (result.type() == file_type::none)
1615 _GLIBCXX_THROW_OR_ABORT(filesystem_error("status", p, ec));
1616 return result;
1619 fs::file_status
1620 fs::symlink_status(const fs::path& p)
1622 std::error_code ec;
1623 auto result = symlink_status(p, ec);
1624 if (result.type() == file_type::none)
1625 _GLIBCXX_THROW_OR_ABORT(filesystem_error("symlink_status", p, ec));
1626 return result;
1629 fs::path
1630 fs::temp_directory_path()
1632 error_code ec;
1633 path p = fs::get_temp_directory_from_env(ec);
1634 if (!ec)
1636 auto st = status(p, ec);
1637 if (!ec && !is_directory(st))
1638 ec = std::make_error_code(std::errc::not_a_directory);
1640 if (ec)
1642 if (p.empty())
1643 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", ec));
1644 else
1645 _GLIBCXX_THROW_OR_ABORT(filesystem_error("temp_directory_path", p, ec));
1647 return p;
1650 fs::path
1651 fs::temp_directory_path(error_code& ec)
1653 path p = fs::get_temp_directory_from_env(ec);
1654 if (!ec)
1656 auto st = status(p, ec);
1657 if (ec)
1658 p.clear();
1659 else if (!is_directory(st))
1661 p.clear();
1662 ec = std::make_error_code(std::errc::not_a_directory);
1665 return p;
1668 fs::path
1669 fs::weakly_canonical(const path& p)
1671 path result;
1672 if (exists(status(p)))
1673 return canonical(p);
1675 path tmp;
1676 auto iter = p.begin(), end = p.end();
1677 // find leading elements of p that exist:
1678 while (iter != end)
1680 tmp = result / *iter;
1681 if (exists(status(tmp)))
1682 swap(result, tmp);
1683 else
1684 break;
1685 ++iter;
1687 // canonicalize:
1688 if (!result.empty())
1689 result = canonical(result);
1690 // append the non-existing elements:
1691 while (iter != end)
1692 result /= *iter++;
1693 // normalize:
1694 return result.lexically_normal();
1697 fs::path
1698 fs::weakly_canonical(const path& p, error_code& ec)
1700 path result;
1701 file_status st = status(p, ec);
1702 if (exists(st))
1703 return canonical(p, ec);
1704 else if (status_known(st))
1705 ec.clear();
1706 else
1707 return result;
1709 path tmp;
1710 auto iter = p.begin(), end = p.end();
1711 // find leading elements of p that exist:
1712 while (iter != end)
1714 tmp = result / *iter;
1715 st = status(tmp, ec);
1716 if (exists(st))
1717 swap(result, tmp);
1718 else
1720 if (status_known(st))
1721 ec.clear();
1722 break;
1724 ++iter;
1726 // canonicalize:
1727 if (!ec && !result.empty())
1728 result = canonical(result, ec);
1729 if (ec)
1730 result.clear();
1731 else
1733 // append the non-existing elements:
1734 while (iter != end)
1735 result /= *iter++;
1736 // normalize:
1737 result = result.lexically_normal();
1739 return result;