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/zipos/zipos.mk			# │
 | ||||||
| include libc/stdio/stdio.mk			# │
 | include libc/stdio/stdio.mk			# │
 | ||||||
| include libc/time/time.mk			# │
 | include libc/time/time.mk			# │
 | ||||||
| include third_party/libcxx/libcxx.mk		# │
 |  | ||||||
| include net/net.mk				# │
 | include net/net.mk				# │
 | ||||||
| include third_party/vqsort/vqsort.mk		# │
 | include third_party/vqsort/vqsort.mk		# │
 | ||||||
| include libc/log/log.mk				# │
 | include libc/log/log.mk				# │
 | ||||||
| include third_party/getopt/getopt.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 third_party/bzip2/bzip2.mk		# │
 | ||||||
| include dsp/core/core.mk			# │
 | include dsp/core/core.mk			# │
 | ||||||
| include libc/x/x.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 libc/dns/dns.mk				# │  You can communicate with the network
 | ||||||
| include net/http/http.mk			# │
 | include net/http/http.mk			# │
 | ||||||
| include third_party/mbedtls/mbedtls.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 net/https/https.mk			# │
 | ||||||
| include third_party/regex/regex.mk		#─┘
 | include third_party/regex/regex.mk		#─┘
 | ||||||
| include third_party/tidy/tidy.mk | include third_party/tidy/tidy.mk | ||||||
|  |  | ||||||
|  | @ -8,16 +8,17 @@ | ||||||
| #   - Backtraces
 | #   - Backtraces
 | ||||||
| #   - Syscall tracing
 | #   - Syscall tracing
 | ||||||
| #   - Function tracing
 | #   - Function tracing
 | ||||||
| #   - No GDB debugging
 |  | ||||||
| #
 | #
 | ||||||
| ifeq ($(MODE),) | ifeq ($(MODE),) | ||||||
| ENABLE_FTRACE = 1 | ENABLE_FTRACE = 1 | ||||||
|  | CONFIG_OFLAGS ?= -g | ||||||
| CONFIG_CCFLAGS += -O2 $(BACKTRACES) | CONFIG_CCFLAGS += -O2 $(BACKTRACES) | ||||||
| CONFIG_CPPFLAGS += -DSYSDEBUG | CONFIG_CPPFLAGS += -DSYSDEBUG | ||||||
| TARGET_ARCH ?= -msse3 | TARGET_ARCH ?= -msse3 | ||||||
| endif | endif | ||||||
| ifeq ($(MODE), aarch64) | ifeq ($(MODE), aarch64) | ||||||
| ENABLE_FTRACE = 1 | ENABLE_FTRACE = 1 | ||||||
|  | CONFIG_OFLAGS ?= -g | ||||||
| CONFIG_CCFLAGS += -O2 $(BACKTRACES) | CONFIG_CCFLAGS += -O2 $(BACKTRACES) | ||||||
| CONFIG_CPPFLAGS += -DSYSDEBUG | CONFIG_CPPFLAGS += -DSYSDEBUG | ||||||
| endif | 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/execution				\
 | ||||||
| 	third_party/libcxx/experimental/__config		\
 | 	third_party/libcxx/experimental/__config		\
 | ||||||
| 	third_party/libcxx/filesystem				\
 | 	third_party/libcxx/filesystem				\
 | ||||||
|  | 	third_party/libcxx/filesystem_common.hh			\
 | ||||||
| 	third_party/libcxx/forward_list				\
 | 	third_party/libcxx/forward_list				\
 | ||||||
| 	third_party/libcxx/fstream				\
 | 	third_party/libcxx/fstream				\
 | ||||||
| 	third_party/libcxx/functional				\
 | 	third_party/libcxx/functional				\
 | ||||||
|  | @ -144,6 +145,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC =					\ | ||||||
| 	third_party/libcxx/chrono.cc				\
 | 	third_party/libcxx/chrono.cc				\
 | ||||||
| 	third_party/libcxx/condition_variable.cc		\
 | 	third_party/libcxx/condition_variable.cc		\
 | ||||||
| 	third_party/libcxx/condition_variable_destructor.cc	\
 | 	third_party/libcxx/condition_variable_destructor.cc	\
 | ||||||
|  | 	third_party/libcxx/directory_iterator.cc		\
 | ||||||
| 	third_party/libcxx/exception.cc				\
 | 	third_party/libcxx/exception.cc				\
 | ||||||
| 	third_party/libcxx/functional.cc			\
 | 	third_party/libcxx/functional.cc			\
 | ||||||
| 	third_party/libcxx/future.cc				\
 | 	third_party/libcxx/future.cc				\
 | ||||||
|  | @ -157,6 +159,7 @@ THIRD_PARTY_LIBCXX_A_SRCS_CC =					\ | ||||||
| 	third_party/libcxx/memory.cc				\
 | 	third_party/libcxx/memory.cc				\
 | ||||||
| 	third_party/libcxx/mutex.cc				\
 | 	third_party/libcxx/mutex.cc				\
 | ||||||
| 	third_party/libcxx/new.cc				\
 | 	third_party/libcxx/new.cc				\
 | ||||||
|  | 	third_party/libcxx/operations.cc			\
 | ||||||
| 	third_party/libcxx/optional.cc				\
 | 	third_party/libcxx/optional.cc				\
 | ||||||
| 	third_party/libcxx/random.cc				\
 | 	third_party/libcxx/random.cc				\
 | ||||||
| 	third_party/libcxx/regex.cc				\
 | 	third_party/libcxx/regex.cc				\
 | ||||||
|  | @ -188,6 +191,7 @@ THIRD_PARTY_LIBCXX_A_DIRECTDEPS =				\ | ||||||
| 	LIBC_NEXGEN32E						\
 | 	LIBC_NEXGEN32E						\
 | ||||||
| 	LIBC_RUNTIME						\
 | 	LIBC_RUNTIME						\
 | ||||||
| 	LIBC_STDIO						\
 | 	LIBC_STDIO						\
 | ||||||
|  | 	LIBC_SOCK						\
 | ||||||
| 	LIBC_STR						\
 | 	LIBC_STR						\
 | ||||||
| 	LIBC_SYSV						\
 | 	LIBC_SYSV						\
 | ||||||
| 	LIBC_TIME						\
 | 	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