mirror of
				https://github.com/jart/cosmopolitan.git
				synced 2025-10-26 11:10:58 +00:00 
			
		
		
		
	Add libcxx filesystem modules
This commit is contained in:
		
							parent
							
								
									a092fda388
								
							
						
					
					
						commit
						0c43c98de1
					
				
					 6 changed files with 2612 additions and 4 deletions
				
			
		
							
								
								
									
										6
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -142,13 +142,10 @@ include libc/thread/thread.mk			# │  You can finally call malloc() | |||
| include libc/zipos/zipos.mk			# │
 | ||||
| include libc/stdio/stdio.mk			# │
 | ||||
| include libc/time/time.mk			# │
 | ||||
| include third_party/libcxx/libcxx.mk		# │
 | ||||
| include net/net.mk				# │
 | ||||
| include third_party/vqsort/vqsort.mk		# │
 | ||||
| include libc/log/log.mk				# │
 | ||||
| include third_party/getopt/getopt.mk		# │
 | ||||
| include third_party/ggml/ggml.mk		# │
 | ||||
| include third_party/radpajama/radpajama.mk	# │
 | ||||
| include third_party/bzip2/bzip2.mk		# │
 | ||||
| include dsp/core/core.mk			# │
 | ||||
| include libc/x/x.mk				# │
 | ||||
|  | @ -166,6 +163,9 @@ include dsp/tty/tty.mk				# ├──ONLINE RUNTIME | |||
| include libc/dns/dns.mk				# │  You can communicate with the network
 | ||||
| include net/http/http.mk			# │
 | ||||
| include third_party/mbedtls/mbedtls.mk		# │
 | ||||
| include third_party/libcxx/libcxx.mk		# │
 | ||||
| include third_party/ggml/ggml.mk		# │
 | ||||
| include third_party/radpajama/radpajama.mk	# │
 | ||||
| include net/https/https.mk			# │
 | ||||
| include third_party/regex/regex.mk		#─┘
 | ||||
| include third_party/tidy/tidy.mk | ||||
|  |  | |||
|  | @ -8,16 +8,17 @@ | |||
| #   - Backtraces
 | ||||
| #   - Syscall tracing
 | ||||
| #   - Function tracing
 | ||||
| #   - No GDB debugging
 | ||||
| #
 | ||||
| ifeq ($(MODE),) | ||||
| ENABLE_FTRACE = 1 | ||||
| CONFIG_OFLAGS ?= -g | ||||
| CONFIG_CCFLAGS += -O2 $(BACKTRACES) | ||||
| CONFIG_CPPFLAGS += -DSYSDEBUG | ||||
| TARGET_ARCH ?= -msse3 | ||||
| endif | ||||
| ifeq ($(MODE), aarch64) | ||||
| ENABLE_FTRACE = 1 | ||||
| CONFIG_OFLAGS ?= -g | ||||
| CONFIG_CCFLAGS += -O2 $(BACKTRACES) | ||||
| CONFIG_CPPFLAGS += -DSYSDEBUG | ||||
| endif | ||||
|  |  | |||
							
								
								
									
										393
									
								
								third_party/libcxx/directory_iterator.cc
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								third_party/libcxx/directory_iterator.cc
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,393 @@ | |||
| // clang-format off
 | ||||
| //===------------------ directory_iterator.cpp ----------------------------===//
 | ||||
| //
 | ||||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | ||||
| // See https://llvm.org/LICENSE.txt for license information.
 | ||||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | ||||
| //
 | ||||
| //===----------------------------------------------------------------------===//
 | ||||
| 
 | ||||
| #include "third_party/libcxx/filesystem" | ||||
| #include "third_party/libcxx/__config" | ||||
| #include "libc/calls/struct/dirent.h" | ||||
| #include "libc/sysv/consts/dt.h" | ||||
| #include "third_party/libcxx/filesystem_common.hh" | ||||
| 
 | ||||
| _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM | ||||
| 
 | ||||
| namespace detail { | ||||
| namespace { | ||||
| 
 | ||||
| #if !defined(_LIBCPP_WIN32API) | ||||
| 
 | ||||
| #if defined(DT_BLK) | ||||
| template <class DirEntT, class = decltype(DirEntT::d_type)> | ||||
| static file_type get_file_type(DirEntT* ent, int) { | ||||
|   switch (ent->d_type) { | ||||
|   case DT_BLK: | ||||
|     return file_type::block; | ||||
|   case DT_CHR: | ||||
|     return file_type::character; | ||||
|   case DT_DIR: | ||||
|     return file_type::directory; | ||||
|   case DT_FIFO: | ||||
|     return file_type::fifo; | ||||
|   case DT_LNK: | ||||
|     return file_type::symlink; | ||||
|   case DT_REG: | ||||
|     return file_type::regular; | ||||
|   case DT_SOCK: | ||||
|     return file_type::socket; | ||||
|   // Unlike in lstat, hitting "unknown" here simply means that the underlying
 | ||||
|   // filesystem doesn't support d_type. Report is as 'none' so we correctly
 | ||||
|   // set the cache to empty.
 | ||||
|   case DT_UNKNOWN: | ||||
|     break; | ||||
|   } | ||||
|   return file_type::none; | ||||
| } | ||||
| #endif // defined(DT_BLK)
 | ||||
| 
 | ||||
| template <class DirEntT> | ||||
| static file_type get_file_type(DirEntT* ent, long) { | ||||
|   return file_type::none; | ||||
| } | ||||
| 
 | ||||
| static pair<string_view, file_type> posix_readdir(DIR* dir_stream, | ||||
|                                                   error_code& ec) { | ||||
|   struct dirent* dir_entry_ptr = nullptr; | ||||
|   errno = 0; // zero errno in order to detect errors
 | ||||
|   ec.clear(); | ||||
|   if ((dir_entry_ptr = ::readdir(dir_stream)) == nullptr) { | ||||
|     if (errno) | ||||
|       ec = capture_errno(); | ||||
|     return {}; | ||||
|   } else { | ||||
|     return {dir_entry_ptr->d_name, get_file_type(dir_entry_ptr, 0)}; | ||||
|   } | ||||
| } | ||||
| #else | ||||
| 
 | ||||
| static file_type get_file_type(const WIN32_FIND_DATA& data) { | ||||
|   //auto attrs = data.dwFileAttributes;
 | ||||
|   // FIXME(EricWF)
 | ||||
|   return file_type::unknown; | ||||
| } | ||||
| static uintmax_t get_file_size(const WIN32_FIND_DATA& data) { | ||||
|   return (data.nFileSizeHigh * (MAXDWORD + 1)) + data.nFileSizeLow; | ||||
| } | ||||
| static file_time_type get_write_time(const WIN32_FIND_DATA& data) { | ||||
|   ULARGE_INTEGER tmp; | ||||
|   const FILETIME& time = data.ftLastWriteTime; | ||||
|   tmp.u.LowPart = time.dwLowDateTime; | ||||
|   tmp.u.HighPart = time.dwHighDateTime; | ||||
|   return file_time_type(file_time_type::duration(tmp.QuadPart)); | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| } // namespace
 | ||||
| } // namespace detail
 | ||||
| 
 | ||||
| using detail::ErrorHandler; | ||||
| 
 | ||||
| #if defined(_LIBCPP_WIN32API) | ||||
| class __dir_stream { | ||||
| public: | ||||
|   __dir_stream() = delete; | ||||
|   __dir_stream& operator=(const __dir_stream&) = delete; | ||||
| 
 | ||||
|   __dir_stream(__dir_stream&& __ds) noexcept : __stream_(__ds.__stream_), | ||||
|                                                __root_(move(__ds.__root_)), | ||||
|                                                __entry_(move(__ds.__entry_)) { | ||||
|     __ds.__stream_ = INVALID_HANDLE_VALUE; | ||||
|   } | ||||
| 
 | ||||
|   __dir_stream(const path& root, directory_options opts, error_code& ec) | ||||
|       : __stream_(INVALID_HANDLE_VALUE), __root_(root) { | ||||
|     __stream_ = ::FindFirstFile(root.c_str(), &__data_); | ||||
|     if (__stream_ == INVALID_HANDLE_VALUE) { | ||||
|       ec = error_code(::GetLastError(), generic_category()); | ||||
|       const bool ignore_permission_denied = | ||||
|           bool(opts & directory_options::skip_permission_denied); | ||||
|       if (ignore_permission_denied && ec.value() == ERROR_ACCESS_DENIED) | ||||
|         ec.clear(); | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   ~__dir_stream() noexcept { | ||||
|     if (__stream_ == INVALID_HANDLE_VALUE) | ||||
|       return; | ||||
|     close(); | ||||
|   } | ||||
| 
 | ||||
|   bool good() const noexcept { return __stream_ != INVALID_HANDLE_VALUE; } | ||||
| 
 | ||||
|   bool advance(error_code& ec) { | ||||
|     while (::FindNextFile(__stream_, &__data_)) { | ||||
|       if (!strcmp(__data_.cFileName, ".") || strcmp(__data_.cFileName, "..")) | ||||
|         continue; | ||||
|       // FIXME: Cache more of this
 | ||||
|       //directory_entry::__cached_data cdata;
 | ||||
|       //cdata.__type_ = get_file_type(__data_);
 | ||||
|       //cdata.__size_ = get_file_size(__data_);
 | ||||
|       //cdata.__write_time_ = get_write_time(__data_);
 | ||||
|       __entry_.__assign_iter_entry( | ||||
|           __root_ / __data_.cFileName, | ||||
|           directory_entry::__create_iter_result(detail::get_file_type(__data))); | ||||
|       return true; | ||||
|     } | ||||
|     ec = error_code(::GetLastError(), generic_category()); | ||||
|     close(); | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   error_code close() noexcept { | ||||
|     error_code ec; | ||||
|     if (!::FindClose(__stream_)) | ||||
|       ec = error_code(::GetLastError(), generic_category()); | ||||
|     __stream_ = INVALID_HANDLE_VALUE; | ||||
|     return ec; | ||||
|   } | ||||
| 
 | ||||
|   HANDLE __stream_{INVALID_HANDLE_VALUE}; | ||||
|   WIN32_FIND_DATA __data_; | ||||
| 
 | ||||
| public: | ||||
|   path __root_; | ||||
|   directory_entry __entry_; | ||||
| }; | ||||
| #else | ||||
| class __dir_stream { | ||||
| public: | ||||
|   __dir_stream() = delete; | ||||
|   __dir_stream& operator=(const __dir_stream&) = delete; | ||||
| 
 | ||||
|   __dir_stream(__dir_stream&& other) noexcept : __stream_(other.__stream_), | ||||
|                                                 __root_(move(other.__root_)), | ||||
|                                                 __entry_(move(other.__entry_)) { | ||||
|     other.__stream_ = nullptr; | ||||
|   } | ||||
| 
 | ||||
|   __dir_stream(const path& root, directory_options opts, error_code& ec) | ||||
|       : __stream_(nullptr), __root_(root) { | ||||
|     if ((__stream_ = ::opendir(root.c_str())) == nullptr) { | ||||
|       ec = detail::capture_errno(); | ||||
|       const bool allow_eacess = | ||||
|           bool(opts & directory_options::skip_permission_denied); | ||||
|       if (allow_eacess && ec.value() == EACCES) | ||||
|         ec.clear(); | ||||
|       return; | ||||
|     } | ||||
|     advance(ec); | ||||
|   } | ||||
| 
 | ||||
|   ~__dir_stream() noexcept { | ||||
|     if (__stream_) | ||||
|       close(); | ||||
|   } | ||||
| 
 | ||||
|   bool good() const noexcept { return __stream_ != nullptr; } | ||||
| 
 | ||||
|   bool advance(error_code& ec) { | ||||
|     while (true) { | ||||
|       auto str_type_pair = detail::posix_readdir(__stream_, ec); | ||||
|       auto& str = str_type_pair.first; | ||||
|       if (str == "." || str == "..") { | ||||
|         continue; | ||||
|       } else if (ec || str.empty()) { | ||||
|         close(); | ||||
|         return false; | ||||
|       } else { | ||||
|         __entry_.__assign_iter_entry( | ||||
|             __root_ / str, | ||||
|             directory_entry::__create_iter_result(str_type_pair.second)); | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   error_code close() noexcept { | ||||
|     error_code m_ec; | ||||
|     if (::closedir(__stream_) == -1) | ||||
|       m_ec = detail::capture_errno(); | ||||
|     __stream_ = nullptr; | ||||
|     return m_ec; | ||||
|   } | ||||
| 
 | ||||
|   DIR* __stream_{nullptr}; | ||||
| 
 | ||||
| public: | ||||
|   path __root_; | ||||
|   directory_entry __entry_; | ||||
| }; | ||||
| #endif | ||||
| 
 | ||||
| // directory_iterator
 | ||||
| 
 | ||||
| directory_iterator::directory_iterator(const path& p, error_code* ec, | ||||
|                                        directory_options opts) { | ||||
|   ErrorHandler<void> err("directory_iterator::directory_iterator(...)", ec, &p); | ||||
| 
 | ||||
|   error_code m_ec; | ||||
|   __imp_ = make_shared<__dir_stream>(p, opts, m_ec); | ||||
|   if (ec) | ||||
|     *ec = m_ec; | ||||
|   if (!__imp_->good()) { | ||||
|     __imp_.reset(); | ||||
|     if (m_ec) | ||||
|       err.report(m_ec); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| directory_iterator& directory_iterator::__increment(error_code* ec) { | ||||
|   _LIBCPP_ASSERT(__imp_, "Attempting to increment an invalid iterator"); | ||||
|   ErrorHandler<void> err("directory_iterator::operator++()", ec); | ||||
| 
 | ||||
|   error_code m_ec; | ||||
|   if (!__imp_->advance(m_ec)) { | ||||
|     path root = move(__imp_->__root_); | ||||
|     __imp_.reset(); | ||||
|     if (m_ec) | ||||
|       err.report(m_ec, "at root \"%s\"", root); | ||||
|   } | ||||
|   return *this; | ||||
| } | ||||
| 
 | ||||
| directory_entry const& directory_iterator::__dereference() const { | ||||
|   _LIBCPP_ASSERT(__imp_, "Attempting to dereference an invalid iterator"); | ||||
|   return __imp_->__entry_; | ||||
| } | ||||
| 
 | ||||
| // recursive_directory_iterator
 | ||||
| 
 | ||||
| struct recursive_directory_iterator::__shared_imp { | ||||
|   stack<__dir_stream> __stack_; | ||||
|   directory_options __options_; | ||||
| }; | ||||
| 
 | ||||
| recursive_directory_iterator::recursive_directory_iterator( | ||||
|     const path& p, directory_options opt, error_code* ec) | ||||
|     : __imp_(nullptr), __rec_(true) { | ||||
|   ErrorHandler<void> err("recursive_directory_iterator", ec, &p); | ||||
| 
 | ||||
|   error_code m_ec; | ||||
|   __dir_stream new_s(p, opt, m_ec); | ||||
|   if (m_ec) | ||||
|     err.report(m_ec); | ||||
|   if (m_ec || !new_s.good()) | ||||
|     return; | ||||
| 
 | ||||
|   __imp_ = make_shared<__shared_imp>(); | ||||
|   __imp_->__options_ = opt; | ||||
|   __imp_->__stack_.push(move(new_s)); | ||||
| } | ||||
| 
 | ||||
| void recursive_directory_iterator::__pop(error_code* ec) { | ||||
|   _LIBCPP_ASSERT(__imp_, "Popping the end iterator"); | ||||
|   if (ec) | ||||
|     ec->clear(); | ||||
|   __imp_->__stack_.pop(); | ||||
|   if (__imp_->__stack_.size() == 0) | ||||
|     __imp_.reset(); | ||||
|   else | ||||
|     __advance(ec); | ||||
| } | ||||
| 
 | ||||
| directory_options recursive_directory_iterator::options() const { | ||||
|   return __imp_->__options_; | ||||
| } | ||||
| 
 | ||||
| int recursive_directory_iterator::depth() const { | ||||
|   return __imp_->__stack_.size() - 1; | ||||
| } | ||||
| 
 | ||||
| const directory_entry& recursive_directory_iterator::__dereference() const { | ||||
|   return __imp_->__stack_.top().__entry_; | ||||
| } | ||||
| 
 | ||||
| recursive_directory_iterator& | ||||
| recursive_directory_iterator::__increment(error_code* ec) { | ||||
|   if (ec) | ||||
|     ec->clear(); | ||||
|   if (recursion_pending()) { | ||||
|     if (__try_recursion(ec) || (ec && *ec)) | ||||
|       return *this; | ||||
|   } | ||||
|   __rec_ = true; | ||||
|   __advance(ec); | ||||
|   return *this; | ||||
| } | ||||
| 
 | ||||
| void recursive_directory_iterator::__advance(error_code* ec) { | ||||
|   ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec); | ||||
| 
 | ||||
|   const directory_iterator end_it; | ||||
|   auto& stack = __imp_->__stack_; | ||||
|   error_code m_ec; | ||||
|   while (stack.size() > 0) { | ||||
|     if (stack.top().advance(m_ec)) | ||||
|       return; | ||||
|     if (m_ec) | ||||
|       break; | ||||
|     stack.pop(); | ||||
|   } | ||||
| 
 | ||||
|   if (m_ec) { | ||||
|     path root = move(stack.top().__root_); | ||||
|     __imp_.reset(); | ||||
|     err.report(m_ec, "at root \"%s\"", root); | ||||
|   } else { | ||||
|     __imp_.reset(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| bool recursive_directory_iterator::__try_recursion(error_code* ec) { | ||||
|   ErrorHandler<void> err("recursive_directory_iterator::operator++()", ec); | ||||
| 
 | ||||
|   bool rec_sym = bool(options() & directory_options::follow_directory_symlink); | ||||
| 
 | ||||
|   auto& curr_it = __imp_->__stack_.top(); | ||||
| 
 | ||||
|   bool skip_rec = false; | ||||
|   error_code m_ec; | ||||
|   if (!rec_sym) { | ||||
|     file_status st(curr_it.__entry_.__get_sym_ft(&m_ec)); | ||||
|     if (m_ec && status_known(st)) | ||||
|       m_ec.clear(); | ||||
|     if (m_ec || is_symlink(st) || !is_directory(st)) | ||||
|       skip_rec = true; | ||||
|   } else { | ||||
|     file_status st(curr_it.__entry_.__get_ft(&m_ec)); | ||||
|     if (m_ec && status_known(st)) | ||||
|       m_ec.clear(); | ||||
|     if (m_ec || !is_directory(st)) | ||||
|       skip_rec = true; | ||||
|   } | ||||
| 
 | ||||
|   if (!skip_rec) { | ||||
|     __dir_stream new_it(curr_it.__entry_.path(), __imp_->__options_, m_ec); | ||||
|     if (new_it.good()) { | ||||
|       __imp_->__stack_.push(move(new_it)); | ||||
|       return true; | ||||
|     } | ||||
|   } | ||||
|   if (m_ec) { | ||||
|     const bool allow_eacess = | ||||
|         bool(__imp_->__options_ & directory_options::skip_permission_denied); | ||||
|     if (m_ec.value() == EACCES && allow_eacess) { | ||||
|       if (ec) | ||||
|         ec->clear(); | ||||
|     } else { | ||||
|       path at_ent = move(curr_it.__entry_.__p_); | ||||
|       __imp_.reset(); | ||||
|       err.report(m_ec, "attempting recursion into \"%s\"", at_ent); | ||||
|     } | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| _LIBCPP_END_NAMESPACE_FILESYSTEM | ||||
							
								
								
									
										425
									
								
								third_party/libcxx/filesystem_common.hh
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										425
									
								
								third_party/libcxx/filesystem_common.hh
									
										
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,425 @@ | |||
| // clang-format off
 | ||||
| //===----------------------------------------------------------------------===////
 | ||||
| //
 | ||||
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | ||||
| // See https://llvm.org/LICENSE.txt for license information.
 | ||||
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | ||||
| //
 | ||||
| //===----------------------------------------------------------------------===////
 | ||||
| 
 | ||||
| #ifndef FILESYSTEM_COMMON_H | ||||
| #define FILESYSTEM_COMMON_H | ||||
| 
 | ||||
| #include "third_party/libcxx/__config" | ||||
| #include "third_party/libcxx/filesystem" | ||||
| #include "third_party/libcxx/filesystem_common.hh" | ||||
| #include "third_party/libcxx/array" | ||||
| #include "third_party/libcxx/chrono" | ||||
| #include "third_party/libcxx/cstdlib" | ||||
| #include "libc/calls/struct/stat.h" | ||||
| #include "libc/sysv/consts/at.h" | ||||
| #include "third_party/libcxx/climits" | ||||
| 
 | ||||
| #define _LIBCPP_USE_UTIMENSAT | ||||
| 
 | ||||
| #if defined(__GNUC__) | ||||
| #pragma GCC diagnostic push | ||||
| #pragma GCC diagnostic ignored "-Wunused-function" | ||||
| #endif | ||||
| 
 | ||||
| _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM | ||||
| 
 | ||||
| namespace detail { | ||||
| namespace { | ||||
| 
 | ||||
| static string format_string_imp(const char* msg, ...) { | ||||
|   // we might need a second shot at this, so pre-emptivly make a copy
 | ||||
|   struct GuardVAList { | ||||
|     va_list& target; | ||||
|     bool active = true; | ||||
|     GuardVAList(va_list& target) : target(target), active(true) {} | ||||
|     void clear() { | ||||
|       if (active) | ||||
|         va_end(target); | ||||
|       active = false; | ||||
|     } | ||||
|     ~GuardVAList() { | ||||
|       if (active) | ||||
|         va_end(target); | ||||
|     } | ||||
|   }; | ||||
|   va_list args; | ||||
|   va_start(args, msg); | ||||
|   GuardVAList args_guard(args); | ||||
| 
 | ||||
|   va_list args_cp; | ||||
|   va_copy(args_cp, args); | ||||
|   GuardVAList args_copy_guard(args_cp); | ||||
| 
 | ||||
|   std::string result; | ||||
| 
 | ||||
|   array<char, 256> local_buff; | ||||
|   size_t size_with_null = local_buff.size(); | ||||
|   auto ret = ::vsnprintf(local_buff.data(), size_with_null, msg, args_cp); | ||||
| 
 | ||||
|   args_copy_guard.clear(); | ||||
| 
 | ||||
|   // handle empty expansion
 | ||||
|   if (ret == 0) | ||||
|     return result; | ||||
|   if (static_cast<size_t>(ret) < size_with_null) { | ||||
|     result.assign(local_buff.data(), static_cast<size_t>(ret)); | ||||
|     return result; | ||||
|   } | ||||
| 
 | ||||
|   // we did not provide a long enough buffer on our first attempt. The
 | ||||
|   // return value is the number of bytes (excluding the null byte) that are
 | ||||
|   // needed for formatting.
 | ||||
|   size_with_null = static_cast<size_t>(ret) + 1; | ||||
|   result.__resize_default_init(size_with_null - 1); | ||||
|   ret = ::vsnprintf(&result[0], size_with_null, msg, args); | ||||
|   _LIBCPP_ASSERT(static_cast<size_t>(ret) == (size_with_null - 1), "TODO"); | ||||
| 
 | ||||
|   return result; | ||||
| } | ||||
| 
 | ||||
| const char* unwrap(string const& s) { return s.c_str(); } | ||||
| const char* unwrap(path const& p) { return p.native().c_str(); } | ||||
| template <class Arg> | ||||
| Arg const& unwrap(Arg const& a) { | ||||
|   static_assert(!is_class<Arg>::value, "cannot pass class here"); | ||||
|   return a; | ||||
| } | ||||
| 
 | ||||
| template <class... Args> | ||||
| string format_string(const char* fmt, Args const&... args) { | ||||
|   return format_string_imp(fmt, unwrap(args)...); | ||||
| } | ||||
| 
 | ||||
| error_code capture_errno() { | ||||
|   _LIBCPP_ASSERT(errno, "Expected errno to be non-zero"); | ||||
|   return error_code(errno, generic_category()); | ||||
| } | ||||
| 
 | ||||
| template <class T> | ||||
| T error_value(); | ||||
| template <> | ||||
| _LIBCPP_CONSTEXPR_AFTER_CXX11 void error_value<void>() {} | ||||
| template <> | ||||
| bool error_value<bool>() { | ||||
|   return false; | ||||
| } | ||||
| template <> | ||||
| uintmax_t error_value<uintmax_t>() { | ||||
|   return uintmax_t(-1); | ||||
| } | ||||
| template <> | ||||
| _LIBCPP_CONSTEXPR_AFTER_CXX11 file_time_type error_value<file_time_type>() { | ||||
|   return file_time_type::min(); | ||||
| } | ||||
| template <> | ||||
| path error_value<path>() { | ||||
|   return {}; | ||||
| } | ||||
| 
 | ||||
| template <class T> | ||||
| struct ErrorHandler { | ||||
|   const char* func_name; | ||||
|   error_code* ec = nullptr; | ||||
|   const path* p1 = nullptr; | ||||
|   const path* p2 = nullptr; | ||||
| 
 | ||||
|   ErrorHandler(const char* fname, error_code* ec, const path* p1 = nullptr, | ||||
|                const path* p2 = nullptr) | ||||
|       : func_name(fname), ec(ec), p1(p1), p2(p2) { | ||||
|     if (ec) | ||||
|       ec->clear(); | ||||
|   } | ||||
| 
 | ||||
|   T report(const error_code& m_ec) const { | ||||
|     if (ec) { | ||||
|       *ec = m_ec; | ||||
|       return error_value<T>(); | ||||
|     } | ||||
|     string what = string("in ") + func_name; | ||||
|     switch (bool(p1) + bool(p2)) { | ||||
|     case 0: | ||||
|       __throw_filesystem_error(what, m_ec); | ||||
|     case 1: | ||||
|       __throw_filesystem_error(what, *p1, m_ec); | ||||
|     case 2: | ||||
|       __throw_filesystem_error(what, *p1, *p2, m_ec); | ||||
|     } | ||||
|     _LIBCPP_UNREACHABLE(); | ||||
|   } | ||||
| 
 | ||||
|   template <class... Args> | ||||
|   T report(const error_code& m_ec, const char* msg, Args const&... args) const { | ||||
|     if (ec) { | ||||
|       *ec = m_ec; | ||||
|       return error_value<T>(); | ||||
|     } | ||||
|     string what = | ||||
|         string("in ") + func_name + ": " + format_string(msg, args...); | ||||
|     switch (bool(p1) + bool(p2)) { | ||||
|     case 0: | ||||
|       __throw_filesystem_error(what, m_ec); | ||||
|     case 1: | ||||
|       __throw_filesystem_error(what, *p1, m_ec); | ||||
|     case 2: | ||||
|       __throw_filesystem_error(what, *p1, *p2, m_ec); | ||||
|     } | ||||
|     _LIBCPP_UNREACHABLE(); | ||||
|   } | ||||
| 
 | ||||
|   T report(errc const& err) const { return report(make_error_code(err)); } | ||||
| 
 | ||||
|   template <class... Args> | ||||
|   T report(errc const& err, const char* msg, Args const&... args) const { | ||||
|     return report(make_error_code(err), msg, args...); | ||||
|   } | ||||
| 
 | ||||
| private: | ||||
|   ErrorHandler(ErrorHandler const&) = delete; | ||||
|   ErrorHandler& operator=(ErrorHandler const&) = delete; | ||||
| }; | ||||
| 
 | ||||
| using chrono::duration; | ||||
| using chrono::duration_cast; | ||||
| 
 | ||||
| using TimeSpec = struct ::timespec; | ||||
| using StatT = struct ::stat; | ||||
| 
 | ||||
| template <class FileTimeT, class TimeT, | ||||
|           bool IsFloat = is_floating_point<typename FileTimeT::rep>::value> | ||||
| struct time_util_base { | ||||
|   using rep = typename FileTimeT::rep; | ||||
|   using fs_duration = typename FileTimeT::duration; | ||||
|   using fs_seconds = duration<rep>; | ||||
|   using fs_nanoseconds = duration<rep, nano>; | ||||
|   using fs_microseconds = duration<rep, micro>; | ||||
| 
 | ||||
|   static constexpr rep max_seconds = | ||||
|       duration_cast<fs_seconds>(FileTimeT::duration::max()).count(); | ||||
| 
 | ||||
|   static constexpr rep max_nsec = | ||||
|       duration_cast<fs_nanoseconds>(FileTimeT::duration::max() - | ||||
|                                     fs_seconds(max_seconds)) | ||||
|           .count(); | ||||
| 
 | ||||
|   static constexpr rep min_seconds = | ||||
|       duration_cast<fs_seconds>(FileTimeT::duration::min()).count(); | ||||
| 
 | ||||
|   static constexpr rep min_nsec_timespec = | ||||
|       duration_cast<fs_nanoseconds>( | ||||
|           (FileTimeT::duration::min() - fs_seconds(min_seconds)) + | ||||
|           fs_seconds(1)) | ||||
|           .count(); | ||||
| 
 | ||||
| private: | ||||
| #if _LIBCPP_STD_VER > 11 && !defined(_LIBCPP_HAS_NO_CXX14_CONSTEXPR) | ||||
|   static constexpr fs_duration get_min_nsecs() { | ||||
|     return duration_cast<fs_duration>( | ||||
|         fs_nanoseconds(min_nsec_timespec) - | ||||
|         duration_cast<fs_nanoseconds>(fs_seconds(1))); | ||||
|   } | ||||
|   // Static assert that these values properly round trip.
 | ||||
|   static_assert(fs_seconds(min_seconds) + get_min_nsecs() == | ||||
|                     FileTimeT::duration::min(), | ||||
|                 "value doesn't roundtrip"); | ||||
| 
 | ||||
|   static constexpr bool check_range() { | ||||
|     // This kinda sucks, but it's what happens when we don't have __int128_t.
 | ||||
|     if (sizeof(TimeT) == sizeof(rep)) { | ||||
|       typedef duration<long long, ratio<3600 * 24 * 365> > Years; | ||||
|       return duration_cast<Years>(fs_seconds(max_seconds)) > Years(250) && | ||||
|              duration_cast<Years>(fs_seconds(min_seconds)) < Years(-250); | ||||
|     } | ||||
|     return max_seconds >= numeric_limits<TimeT>::max() && | ||||
|            min_seconds <= numeric_limits<TimeT>::min(); | ||||
|   } | ||||
|   static_assert(check_range(), "the representable range is unacceptable small"); | ||||
| #endif | ||||
| }; | ||||
| 
 | ||||
| template <class FileTimeT, class TimeT> | ||||
| struct time_util_base<FileTimeT, TimeT, true> { | ||||
|   using rep = typename FileTimeT::rep; | ||||
|   using fs_duration = typename FileTimeT::duration; | ||||
|   using fs_seconds = duration<rep>; | ||||
|   using fs_nanoseconds = duration<rep, nano>; | ||||
|   using fs_microseconds = duration<rep, micro>; | ||||
| 
 | ||||
|   static const rep max_seconds; | ||||
|   static const rep max_nsec; | ||||
|   static const rep min_seconds; | ||||
|   static const rep min_nsec_timespec; | ||||
| }; | ||||
| 
 | ||||
| template <class FileTimeT, class TimeT> | ||||
| const typename FileTimeT::rep | ||||
|     time_util_base<FileTimeT, TimeT, true>::max_seconds = | ||||
|         duration_cast<fs_seconds>(FileTimeT::duration::max()).count(); | ||||
| 
 | ||||
| template <class FileTimeT, class TimeT> | ||||
| const typename FileTimeT::rep time_util_base<FileTimeT, TimeT, true>::max_nsec = | ||||
|     duration_cast<fs_nanoseconds>(FileTimeT::duration::max() - | ||||
|                                   fs_seconds(max_seconds)) | ||||
|         .count(); | ||||
| 
 | ||||
| template <class FileTimeT, class TimeT> | ||||
| const typename FileTimeT::rep | ||||
|     time_util_base<FileTimeT, TimeT, true>::min_seconds = | ||||
|         duration_cast<fs_seconds>(FileTimeT::duration::min()).count(); | ||||
| 
 | ||||
| template <class FileTimeT, class TimeT> | ||||
| const typename FileTimeT::rep | ||||
|     time_util_base<FileTimeT, TimeT, true>::min_nsec_timespec = | ||||
|         duration_cast<fs_nanoseconds>((FileTimeT::duration::min() - | ||||
|                                        fs_seconds(min_seconds)) + | ||||
|                                       fs_seconds(1)) | ||||
|             .count(); | ||||
| 
 | ||||
| template <class FileTimeT, class TimeT, class TimeSpecT> | ||||
| struct time_util : time_util_base<FileTimeT, TimeT> { | ||||
|   using Base = time_util_base<FileTimeT, TimeT>; | ||||
|   using Base::max_nsec; | ||||
|   using Base::max_seconds; | ||||
|   using Base::min_nsec_timespec; | ||||
|   using Base::min_seconds; | ||||
| 
 | ||||
|   using typename Base::fs_duration; | ||||
|   using typename Base::fs_microseconds; | ||||
|   using typename Base::fs_nanoseconds; | ||||
|   using typename Base::fs_seconds; | ||||
| 
 | ||||
| public: | ||||
|   template <class CType, class ChronoType> | ||||
|   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool checked_set(CType* out, | ||||
|                                                         ChronoType time) { | ||||
|     using Lim = numeric_limits<CType>; | ||||
|     if (time > Lim::max() || time < Lim::min()) | ||||
|       return false; | ||||
|     *out = static_cast<CType>(time); | ||||
|     return true; | ||||
|   } | ||||
| 
 | ||||
|   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(TimeSpecT tm) { | ||||
|     if (tm.tv_sec >= 0) { | ||||
|       return tm.tv_sec < max_seconds || | ||||
|              (tm.tv_sec == max_seconds && tm.tv_nsec <= max_nsec); | ||||
|     } else if (tm.tv_sec == (min_seconds - 1)) { | ||||
|       return tm.tv_nsec >= min_nsec_timespec; | ||||
|     } else { | ||||
|       return tm.tv_sec >= min_seconds; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool is_representable(FileTimeT tm) { | ||||
|     auto secs = duration_cast<fs_seconds>(tm.time_since_epoch()); | ||||
|     auto nsecs = duration_cast<fs_nanoseconds>(tm.time_since_epoch() - secs); | ||||
|     if (nsecs.count() < 0) { | ||||
|       secs = secs + fs_seconds(1); | ||||
|       nsecs = nsecs + fs_seconds(1); | ||||
|     } | ||||
|     using TLim = numeric_limits<TimeT>; | ||||
|     if (secs.count() >= 0) | ||||
|       return secs.count() <= TLim::max(); | ||||
|     return secs.count() >= TLim::min(); | ||||
|   } | ||||
| 
 | ||||
|   static _LIBCPP_CONSTEXPR_AFTER_CXX11 FileTimeT | ||||
|   convert_from_timespec(TimeSpecT tm) { | ||||
|     if (tm.tv_sec >= 0 || tm.tv_nsec == 0) { | ||||
|       return FileTimeT(fs_seconds(tm.tv_sec) + | ||||
|                        duration_cast<fs_duration>(fs_nanoseconds(tm.tv_nsec))); | ||||
|     } else { // tm.tv_sec < 0
 | ||||
|       auto adj_subsec = duration_cast<fs_duration>(fs_seconds(1) - | ||||
|                                                    fs_nanoseconds(tm.tv_nsec)); | ||||
|       auto Dur = fs_seconds(tm.tv_sec + 1) - adj_subsec; | ||||
|       return FileTimeT(Dur); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   template <class SubSecT> | ||||
|   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool | ||||
|   set_times_checked(TimeT* sec_out, SubSecT* subsec_out, FileTimeT tp) { | ||||
|     auto dur = tp.time_since_epoch(); | ||||
|     auto sec_dur = duration_cast<fs_seconds>(dur); | ||||
|     auto subsec_dur = duration_cast<fs_nanoseconds>(dur - sec_dur); | ||||
|     // The tv_nsec and tv_usec fields must not be negative so adjust accordingly
 | ||||
|     if (subsec_dur.count() < 0) { | ||||
|       if (sec_dur.count() > min_seconds) { | ||||
|         sec_dur = sec_dur - fs_seconds(1); | ||||
|         subsec_dur = subsec_dur + fs_seconds(1); | ||||
|       } else { | ||||
|         subsec_dur = fs_nanoseconds::zero(); | ||||
|       } | ||||
|     } | ||||
|     return checked_set(sec_out, sec_dur.count()) && | ||||
|            checked_set(subsec_out, subsec_dur.count()); | ||||
|   } | ||||
|   static _LIBCPP_CONSTEXPR_AFTER_CXX11 bool convert_to_timespec(TimeSpecT& dest, | ||||
|                                                                 FileTimeT tp) { | ||||
|     if (!is_representable(tp)) | ||||
|       return false; | ||||
|     return set_times_checked(&dest.tv_sec, &dest.tv_nsec, tp); | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| using fs_time = time_util<file_time_type, time_t, TimeSpec>; | ||||
| 
 | ||||
| #if defined(__APPLE__) | ||||
| TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; } | ||||
| TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; } | ||||
| #else | ||||
| TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; } | ||||
| TimeSpec extract_atime(StatT const& st) { return st.st_atim; } | ||||
| #endif | ||||
| 
 | ||||
| // allow the utimes implementation to compile even it we're not going
 | ||||
| // to use it.
 | ||||
| 
 | ||||
| bool posix_utimes(const path& p, std::array<TimeSpec, 2> const& TS, | ||||
|                   error_code& ec) { | ||||
|   using namespace chrono; | ||||
|   auto Convert = [](long nsec) { | ||||
|     using int_type = decltype(std::declval< ::timeval>().tv_usec); | ||||
|     auto dur = duration_cast<microseconds>(nanoseconds(nsec)).count(); | ||||
|     return static_cast<int_type>(dur); | ||||
|   }; | ||||
|   struct ::timeval ConvertedTS[2] = {{TS[0].tv_sec, Convert(TS[0].tv_nsec)}, | ||||
|                                      {TS[1].tv_sec, Convert(TS[1].tv_nsec)}}; | ||||
|   if (::utimes(p.c_str(), ConvertedTS) == -1) { | ||||
|     ec = capture_errno(); | ||||
|     return true; | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| 
 | ||||
| #if defined(_LIBCPP_USE_UTIMENSAT) | ||||
| bool posix_utimensat(const path& p, std::array<TimeSpec, 2> const& TS, | ||||
|                      error_code& ec) { | ||||
|   if (::utimensat(AT_FDCWD, p.c_str(), TS.data(), 0) == -1) { | ||||
|     ec = capture_errno(); | ||||
|     return true; | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| bool set_file_times(const path& p, std::array<TimeSpec, 2> const& TS, | ||||
|                     error_code& ec) { | ||||
| #if !defined(_LIBCPP_USE_UTIMENSAT) | ||||
|   return posix_utimes(p, TS, ec); | ||||
| #else | ||||
|   return posix_utimensat(p, TS, ec); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| } // namespace
 | ||||
| } // end namespace detail
 | ||||
| 
 | ||||
| _LIBCPP_END_NAMESPACE_FILESYSTEM | ||||
| 
 | ||||
| #endif // FILESYSTEM_COMMON_H
 | ||||
							
								
								
									
										4
									
								
								third_party/libcxx/libcxx.mk
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								third_party/libcxx/libcxx.mk
									
										
									
									
										vendored
									
									
								
							|  | @ -75,6 +75,7 @@ THIRD_PARTY_LIBCXX_A_HDRS =					\ | |||
| 	third_party/libcxx/execution				\
 | ||||
| 	third_party/libcxx/experimental/__config		\
 | ||||
| 	third_party/libcxx/filesystem				\
 | ||||
| 	third_party/libcxx/filesystem_common.hh			\
 | ||||
| 	third_party/libcxx/forward_list				\
 | ||||
| 	third_party/libcxx/fstream				\
 | ||||
| 	third_party/libcxx/functional				\
 | ||||
|  | @ -144,6 +145,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC =					\ | |||
| 	third_party/libcxx/chrono.cc				\
 | ||||
| 	third_party/libcxx/condition_variable.cc		\
 | ||||
| 	third_party/libcxx/condition_variable_destructor.cc	\
 | ||||
| 	third_party/libcxx/directory_iterator.cc		\
 | ||||
| 	third_party/libcxx/exception.cc				\
 | ||||
| 	third_party/libcxx/functional.cc			\
 | ||||
| 	third_party/libcxx/future.cc				\
 | ||||
|  | @ -157,6 +159,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC =					\ | |||
| 	third_party/libcxx/memory.cc				\
 | ||||
| 	third_party/libcxx/mutex.cc				\
 | ||||
| 	third_party/libcxx/new.cc				\
 | ||||
| 	third_party/libcxx/operations.cc			\
 | ||||
| 	third_party/libcxx/optional.cc				\
 | ||||
| 	third_party/libcxx/random.cc				\
 | ||||
| 	third_party/libcxx/regex.cc				\
 | ||||
|  | @ -188,6 +191,7 @@ THIRD_PARTY_LIBCXX_A_DIRECTDEPS =				\ | |||
| 	LIBC_NEXGEN32E						\
 | ||||
| 	LIBC_RUNTIME						\
 | ||||
| 	LIBC_STDIO						\
 | ||||
| 	LIBC_SOCK						\
 | ||||
| 	LIBC_STR						\
 | ||||
| 	LIBC_SYSV						\
 | ||||
| 	LIBC_TIME						\
 | ||||
|  |  | |||
							
								
								
									
										1785
									
								
								third_party/libcxx/operations.cc
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1785
									
								
								third_party/libcxx/operations.cc
									
										
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue