| //--------------------------------------------------------------------------------------- |
| // |
| // ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17 |
| // |
| //--------------------------------------------------------------------------------------- |
| // |
| // Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com> |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in all |
| // copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| // SOFTWARE. |
| // |
| //--------------------------------------------------------------------------------------- |
| // |
| // To dynamically select std::filesystem where available, you could use: |
| // |
| // #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>) |
| // #include <filesystem> |
| // namespace fs = std::filesystem; |
| // #else |
| // #include <ghc/filesystem.hpp> |
| // namespace fs = ghc::filesystem; |
| // #endif |
| // |
| //--------------------------------------------------------------------------------------- |
| #ifndef GHC_FILESYSTEM_H |
| #define GHC_FILESYSTEM_H |
| |
| // #define BSD manifest constant only in |
| // sys/param.h |
| #ifndef _WIN32 |
| #include <sys/param.h> |
| #endif |
| |
| #ifndef GHC_OS_DETECTED |
| #if defined(__APPLE__) && defined(__MACH__) |
| #define GHC_OS_MACOS |
| #elif defined(__linux__) |
| #define GHC_OS_LINUX |
| #if defined(__ANDROID__) |
| #define GHC_OS_ANDROID |
| #endif |
| #elif defined(_WIN64) |
| #define GHC_OS_WINDOWS |
| #define GHC_OS_WIN64 |
| #elif defined(_WIN32) |
| #define GHC_OS_WINDOWS |
| #define GHC_OS_WIN32 |
| #elif defined(__svr4__) |
| #define GHC_OS_SYS5R4 |
| #elif defined(BSD) |
| #define GHC_OS_BSD |
| #else |
| #error "Operating system currently not supported!" |
| #endif |
| #define GHC_OS_DETECTED |
| #endif |
| |
| #if defined(GHC_FILESYSTEM_IMPLEMENTATION) |
| #define GHC_EXPAND_IMPL |
| #define GHC_INLINE |
| #ifdef GHC_OS_WINDOWS |
| #define GHC_FS_API |
| #define GHC_FS_API_CLASS |
| #else |
| #define GHC_FS_API __attribute__((visibility("default"))) |
| #define GHC_FS_API_CLASS __attribute__((visibility("default"))) |
| #endif |
| #elif defined(GHC_FILESYSTEM_FWD) |
| #define GHC_INLINE |
| #ifdef GHC_OS_WINDOWS |
| #define GHC_FS_API extern |
| #define GHC_FS_API_CLASS |
| #else |
| #define GHC_FS_API extern |
| #define GHC_FS_API_CLASS |
| #endif |
| #else |
| #define GHC_EXPAND_IMPL |
| #define GHC_INLINE inline |
| #define GHC_FS_API |
| #define GHC_FS_API_CLASS |
| #endif |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| #ifdef GHC_OS_WINDOWS |
| #include <windows.h> |
| // additional includes |
| #include <shellapi.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <wchar.h> |
| #include <winioctl.h> |
| #else |
| #include <dirent.h> |
| #include <fcntl.h> |
| #include <langinfo.h> |
| #include <sys/param.h> |
| #include <sys/stat.h> |
| #include <sys/statvfs.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <limits.h> |
| #ifdef GHC_OS_ANDROID |
| #include <android/api-level.h> |
| #endif |
| #endif |
| #ifdef GHC_OS_MACOS |
| #include <Availability.h> |
| #endif |
| |
| #include <algorithm> |
| #include <cctype> |
| #include <chrono> |
| #include <clocale> |
| #include <cstdlib> |
| #include <cstring> |
| #include <fstream> |
| #include <functional> |
| #include <memory> |
| #include <stack> |
| #include <stdexcept> |
| #include <string> |
| #include <system_error> |
| #include <type_traits> |
| #include <utility> |
| #include <vector> |
| |
| #else // GHC_EXPAND_IMPL |
| #include <chrono> |
| #include <fstream> |
| #include <memory> |
| #include <stack> |
| #include <stdexcept> |
| #include <string> |
| #include <system_error> |
| #ifdef GHC_OS_WINDOWS |
| #include <vector> |
| #endif |
| #endif // GHC_EXPAND_IMPL |
| |
| //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): |
| //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories |
| // configure LWG conformance () |
| #define LWG_2682_BEHAVIOUR |
| //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular |
| // file with that name, it is superceded by P1164R1, so only activate if really needed |
| // #define LWG_2935_BEHAVIOUR |
| //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) |
| #define LWG_2937_BEHAVIOUR |
| //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // UTF8-Everywhere is the original behaviour of ghc::filesystem. With this define you can |
| // enable the more standard conforming implementation option that uses wstring on Windows |
| // as ghc::filesystem::string_type. |
| // #define GHC_WIN_WSTRING_STRING_TYPE |
| //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| // Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, |
| // instead of replacing them with the unicode replacement character (U+FFFD). |
| // #define GHC_RAISE_UNICODE_ERRORS |
| //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
| |
| // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) |
| #define GHC_FILESYSTEM_VERSION 10302L |
| |
| namespace ghc { |
| namespace filesystem { |
| |
| // temporary existing exception type for yet unimplemented parts |
| class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error |
| { |
| public: |
| not_implemented_exception() |
| : std::logic_error("function not implemented yet.") |
| { |
| } |
| }; |
| |
| template<typename char_type> |
| class path_helper_base |
| { |
| public: |
| using value_type = char_type; |
| #ifdef GHC_OS_WINDOWS |
| static constexpr value_type preferred_separator = '\\'; |
| #else |
| static constexpr value_type preferred_separator = '/'; |
| #endif |
| }; |
| |
| #if __cplusplus < 201703L |
| template <typename char_type> |
| constexpr char_type path_helper_base<char_type>::preferred_separator; |
| #endif |
| |
| // 30.10.8 class path |
| class GHC_FS_API_CLASS path |
| #if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_WSTRING_STRING_TYPE) |
| #define GHC_USE_WCHAR_T |
| : private path_helper_base<std::wstring::value_type> |
| { |
| public: |
| using path_helper_base<std::wstring::value_type>::value_type; |
| #else |
| : private path_helper_base<std::string::value_type> |
| { |
| public: |
| using path_helper_base<std::string::value_type>::value_type; |
| #endif |
| using string_type = std::basic_string<value_type>; |
| using path_helper_base<value_type>::preferred_separator; |
| |
| // 30.10.10.1 enumeration format |
| /// The path format in wich the constructor argument is given. |
| enum format { |
| generic_format, ///< The generic format, internally used by |
| ///< ghc::filesystem::path with slashes |
| native_format, ///< The format native to the current platform this code |
| ///< is build for |
| auto_format, ///< Try to auto-detect the format, fallback to native |
| }; |
| |
| template <class T> |
| struct _is_basic_string : std::false_type |
| { |
| }; |
| template <class CharT, class Traits, class Alloc> |
| struct _is_basic_string<std::basic_string<CharT, Traits, Alloc>> : std::true_type |
| { |
| }; |
| #ifdef __cpp_lib_string_view |
| template <class CharT> |
| struct _is_basic_string<std::basic_string_view<CharT>> : std::true_type |
| { |
| }; |
| #endif |
| |
| template <typename T1, typename T2 = void> |
| using path_type = typename std::enable_if<!std::is_same<path, T1>::value, path>::type; |
| #ifdef GHC_USE_WCHAR_T |
| template <typename T> |
| using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value || |
| std::is_same<wchar_t const*, typename std::decay<T>::type>::value || std::is_same<wchar_t*, typename std::decay<T>::type>::value, |
| path>::type; |
| template <typename T> |
| using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value, path>::type; |
| #else |
| template <typename T> |
| using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value, path>::type; |
| template <typename T> |
| using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type; |
| #endif |
| // 30.10.8.4.1 constructors and destructor |
| path() noexcept; |
| path(const path& p); |
| path(path&& p) noexcept; |
| path(string_type&& source, format fmt = auto_format); |
| template <class Source, typename = path_from_string<Source>> |
| path(const Source& source, format fmt = auto_format); |
| template <class InputIterator> |
| path(InputIterator first, InputIterator last, format fmt = auto_format); |
| template <class Source, typename = path_from_string<Source>> |
| path(const Source& source, const std::locale& loc, format fmt = auto_format); |
| template <class InputIterator> |
| path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format); |
| ~path(); |
| |
| // 30.10.8.4.2 assignments |
| path& operator=(const path& p); |
| path& operator=(path&& p) noexcept; |
| path& operator=(string_type&& source); |
| path& assign(string_type&& source); |
| template <class Source> |
| path& operator=(const Source& source); |
| template <class Source> |
| path& assign(const Source& source); |
| template <class InputIterator> |
| path& assign(InputIterator first, InputIterator last); |
| |
| // 30.10.8.4.3 appends |
| path& operator/=(const path& p); |
| template <class Source> |
| path& operator/=(const Source& source); |
| template <class Source> |
| path& append(const Source& source); |
| template <class InputIterator> |
| path& append(InputIterator first, InputIterator last); |
| |
| // 30.10.8.4.4 concatenation |
| path& operator+=(const path& x); |
| path& operator+=(const string_type& x); |
| #ifdef __cpp_lib_string_view |
| path& operator+=(std::basic_string_view<value_type> x); |
| #endif |
| path& operator+=(const value_type* x); |
| path& operator+=(value_type x); |
| template <class Source> |
| path_from_string<Source>& operator+=(const Source& x); |
| template <class EcharT> |
| path_type_EcharT<EcharT>& operator+=(EcharT x); |
| template <class Source> |
| path& concat(const Source& x); |
| template <class InputIterator> |
| path& concat(InputIterator first, InputIterator last); |
| |
| // 30.10.8.4.5 modifiers |
| void clear() noexcept; |
| path& make_preferred(); |
| path& remove_filename(); |
| path& replace_filename(const path& replacement); |
| path& replace_extension(const path& replacement = path()); |
| void swap(path& rhs) noexcept; |
| |
| // 30.10.8.4.6 native format observers |
| const string_type& native() const; // this implementation doesn't support noexcept for native() |
| const value_type* c_str() const; // this implementation doesn't support noexcept for c_str() |
| operator string_type() const; |
| template <class EcharT, class traits = std::char_traits<EcharT>, class Allocator = std::allocator<EcharT>> |
| std::basic_string<EcharT, traits, Allocator> string(const Allocator& a = Allocator()) const; |
| std::string string() const; |
| std::wstring wstring() const; |
| std::string u8string() const; |
| std::u16string u16string() const; |
| std::u32string u32string() const; |
| |
| // 30.10.8.4.7 generic format observers |
| template <class EcharT, class traits = std::char_traits<EcharT>, class Allocator = std::allocator<EcharT>> |
| std::basic_string<EcharT, traits, Allocator> generic_string(const Allocator& a = Allocator()) const; |
| const std::string& generic_string() const; // this is different from the standard, that returns by value |
| std::wstring generic_wstring() const; |
| std::string generic_u8string() const; |
| std::u16string generic_u16string() const; |
| std::u32string generic_u32string() const; |
| |
| // 30.10.8.4.8 compare |
| int compare(const path& p) const noexcept; |
| int compare(const string_type& s) const; |
| #ifdef __cpp_lib_string_view |
| int compare(std::basic_string_view<value_type> s) const; |
| #endif |
| int compare(const value_type* s) const; |
| |
| // 30.10.8.4.9 decomposition |
| path root_name() const; |
| path root_directory() const; |
| path root_path() const; |
| path relative_path() const; |
| path parent_path() const; |
| path filename() const; |
| path stem() const; |
| path extension() const; |
| |
| // 30.10.8.4.10 query |
| bool empty() const noexcept; |
| bool has_root_name() const; |
| bool has_root_directory() const; |
| bool has_root_path() const; |
| bool has_relative_path() const; |
| bool has_parent_path() const; |
| bool has_filename() const; |
| bool has_stem() const; |
| bool has_extension() const; |
| bool is_absolute() const; |
| bool is_relative() const; |
| |
| // 30.10.8.4.11 generation |
| path lexically_normal() const; |
| path lexically_relative(const path& base) const; |
| path lexically_proximate(const path& base) const; |
| |
| // 30.10.8.5 iterators |
| class iterator; |
| using const_iterator = iterator; |
| iterator begin() const; |
| iterator end() const; |
| |
| private: |
| using impl_value_type = std::string::value_type; |
| using impl_string_type = std::basic_string<impl_value_type>; |
| friend class directory_iterator; |
| void append_name(const char* name); |
| static constexpr impl_value_type generic_separator = '/'; |
| template <typename InputIterator> |
| class input_iterator_range |
| { |
| public: |
| typedef InputIterator iterator; |
| typedef InputIterator const_iterator; |
| typedef typename InputIterator::difference_type difference_type; |
| |
| input_iterator_range(const InputIterator& first, const InputIterator& last) |
| : _first(first) |
| , _last(last) |
| { |
| } |
| |
| InputIterator begin() const { return _first; } |
| InputIterator end() const { return _last; } |
| |
| private: |
| InputIterator _first; |
| InputIterator _last; |
| }; |
| friend void swap(path& lhs, path& rhs) noexcept; |
| friend size_t hash_value(const path& p) noexcept; |
| static void postprocess_path_with_format(impl_string_type& p, format fmt); |
| impl_string_type _path; |
| #ifdef GHC_OS_WINDOWS |
| impl_string_type native_impl() const; |
| mutable string_type _native_cache; |
| #else |
| const impl_string_type& native_impl() const; |
| #endif |
| }; |
| |
| // 30.10.8.6 path non-member functions |
| GHC_FS_API void swap(path& lhs, path& rhs) noexcept; |
| GHC_FS_API size_t hash_value(const path& p) noexcept; |
| GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept; |
| GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept; |
| GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept; |
| GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept; |
| GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept; |
| GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept; |
| |
| GHC_FS_API path operator/(const path& lhs, const path& rhs); |
| |
| // 30.10.8.6.1 path inserter and extractor |
| template <class charT, class traits> |
| std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const path& p); |
| template <class charT, class traits> |
| std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, path& p); |
| |
| // 30.10.8.6.2 path factory functions |
| template <class Source, typename = path::path_from_string<Source>> |
| path u8path(const Source& source); |
| template <class InputIterator> |
| path u8path(InputIterator first, InputIterator last); |
| |
| // 30.10.9 class filesystem_error |
| class GHC_FS_API_CLASS filesystem_error : public std::system_error |
| { |
| public: |
| filesystem_error(const std::string& what_arg, std::error_code ec); |
| filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec); |
| filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec); |
| const path& path1() const noexcept; |
| const path& path2() const noexcept; |
| const char* what() const noexcept override; |
| |
| private: |
| std::string _what_arg; |
| std::error_code _ec; |
| path _p1, _p2; |
| }; |
| |
| class GHC_FS_API_CLASS path::iterator |
| { |
| public: |
| using value_type = const path; |
| using difference_type = std::ptrdiff_t; |
| using pointer = const path*; |
| using reference = const path&; |
| using iterator_category = std::bidirectional_iterator_tag; |
| |
| iterator(); |
| iterator(const impl_string_type::const_iterator& first, const impl_string_type::const_iterator& last, const impl_string_type::const_iterator& pos); |
| iterator& operator++(); |
| iterator operator++(int); |
| iterator& operator--(); |
| iterator operator--(int); |
| bool operator==(const iterator& other) const; |
| bool operator!=(const iterator& other) const; |
| reference operator*() const; |
| pointer operator->() const; |
| |
| private: |
| impl_string_type::const_iterator increment(const std::string::const_iterator& pos) const; |
| impl_string_type::const_iterator decrement(const std::string::const_iterator& pos) const; |
| void updateCurrent(); |
| impl_string_type::const_iterator _first; |
| impl_string_type::const_iterator _last; |
| impl_string_type::const_iterator _root; |
| impl_string_type::const_iterator _iter; |
| path _current; |
| }; |
| |
| struct space_info |
| { |
| uintmax_t capacity; |
| uintmax_t free; |
| uintmax_t available; |
| }; |
| |
| // 30.10.10, enumerations |
| enum class file_type { |
| none, |
| not_found, |
| regular, |
| directory, |
| symlink, |
| block, |
| character, |
| fifo, |
| socket, |
| unknown, |
| }; |
| |
| enum class perms : uint16_t { |
| none = 0, |
| |
| owner_read = 0400, |
| owner_write = 0200, |
| owner_exec = 0100, |
| owner_all = 0700, |
| |
| group_read = 040, |
| group_write = 020, |
| group_exec = 010, |
| group_all = 070, |
| |
| others_read = 04, |
| others_write = 02, |
| others_exec = 01, |
| others_all = 07, |
| |
| all = 0777, |
| set_uid = 04000, |
| set_gid = 02000, |
| sticky_bit = 01000, |
| |
| mask = 07777, |
| unknown = 0xffff |
| }; |
| |
| enum class perm_options : uint16_t { |
| replace = 3, |
| add = 1, |
| remove = 2, |
| nofollow = 4, |
| }; |
| |
| enum class copy_options : uint16_t { |
| none = 0, |
| |
| skip_existing = 1, |
| overwrite_existing = 2, |
| update_existing = 4, |
| |
| recursive = 8, |
| |
| copy_symlinks = 0x10, |
| skip_symlinks = 0x20, |
| |
| directories_only = 0x40, |
| create_symlinks = 0x80, |
| create_hard_links = 0x100 |
| }; |
| |
| enum class directory_options : uint16_t { |
| none = 0, |
| follow_directory_symlink = 1, |
| skip_permission_denied = 2, |
| }; |
| |
| // 30.10.11 class file_status |
| class GHC_FS_API_CLASS file_status |
| { |
| public: |
| // 30.10.11.1 constructors and destructor |
| file_status() noexcept; |
| explicit file_status(file_type ft, perms prms = perms::unknown) noexcept; |
| file_status(const file_status&) noexcept; |
| file_status(file_status&&) noexcept; |
| ~file_status(); |
| // assignments: |
| file_status& operator=(const file_status&) noexcept; |
| file_status& operator=(file_status&&) noexcept; |
| // 30.10.11.3 modifiers |
| void type(file_type ft) noexcept; |
| void permissions(perms prms) noexcept; |
| // 30.10.11.2 observers |
| file_type type() const noexcept; |
| perms permissions() const noexcept; |
| |
| private: |
| file_type _type; |
| perms _perms; |
| }; |
| |
| using file_time_type = std::chrono::time_point<std::chrono::system_clock>; |
| |
| // 30.10.12 Class directory_entry |
| class GHC_FS_API_CLASS directory_entry |
| { |
| public: |
| // 30.10.12.1 constructors and destructor |
| directory_entry() noexcept = default; |
| directory_entry(const directory_entry&) = default; |
| directory_entry(directory_entry&&) noexcept = default; |
| explicit directory_entry(const path& p); |
| directory_entry(const path& p, std::error_code& ec); |
| ~directory_entry(); |
| |
| // assignments: |
| directory_entry& operator=(const directory_entry&) = default; |
| directory_entry& operator=(directory_entry&&) noexcept = default; |
| |
| // 30.10.12.2 modifiers |
| void assign(const path& p); |
| void assign(const path& p, std::error_code& ec); |
| void replace_filename(const path& p); |
| void replace_filename(const path& p, std::error_code& ec); |
| void refresh(); |
| void refresh(std::error_code& ec) noexcept; |
| |
| // 30.10.12.3 observers |
| const filesystem::path& path() const noexcept; |
| operator const filesystem::path&() const noexcept; |
| bool exists() const; |
| bool exists(std::error_code& ec) const noexcept; |
| bool is_block_file() const; |
| bool is_block_file(std::error_code& ec) const noexcept; |
| bool is_character_file() const; |
| bool is_character_file(std::error_code& ec) const noexcept; |
| bool is_directory() const; |
| bool is_directory(std::error_code& ec) const noexcept; |
| bool is_fifo() const; |
| bool is_fifo(std::error_code& ec) const noexcept; |
| bool is_other() const; |
| bool is_other(std::error_code& ec) const noexcept; |
| bool is_regular_file() const; |
| bool is_regular_file(std::error_code& ec) const noexcept; |
| bool is_socket() const; |
| bool is_socket(std::error_code& ec) const noexcept; |
| bool is_symlink() const; |
| bool is_symlink(std::error_code& ec) const noexcept; |
| uintmax_t file_size() const; |
| uintmax_t file_size(std::error_code& ec) const noexcept; |
| uintmax_t hard_link_count() const; |
| uintmax_t hard_link_count(std::error_code& ec) const noexcept; |
| file_time_type last_write_time() const; |
| file_time_type last_write_time(std::error_code& ec) const noexcept; |
| |
| file_status status() const; |
| file_status status(std::error_code& ec) const noexcept; |
| |
| file_status symlink_status() const; |
| file_status symlink_status(std::error_code& ec) const noexcept; |
| bool operator<(const directory_entry& rhs) const noexcept; |
| bool operator==(const directory_entry& rhs) const noexcept; |
| bool operator!=(const directory_entry& rhs) const noexcept; |
| bool operator<=(const directory_entry& rhs) const noexcept; |
| bool operator>(const directory_entry& rhs) const noexcept; |
| bool operator>=(const directory_entry& rhs) const noexcept; |
| |
| private: |
| friend class directory_iterator; |
| filesystem::path _path; |
| file_status _status; |
| file_status _symlink_status; |
| uintmax_t _file_size = 0; |
| #ifndef GHC_OS_WINDOWS |
| uintmax_t _hard_link_count = 0; |
| #endif |
| time_t _last_write_time = 0; |
| }; |
| |
| // 30.10.13 Class directory_iterator |
| class GHC_FS_API_CLASS directory_iterator |
| { |
| public: |
| class GHC_FS_API_CLASS proxy |
| { |
| public: |
| const directory_entry& operator*() const& noexcept { return _dir_entry; } |
| directory_entry operator*() && noexcept { return std::move(_dir_entry); } |
| |
| private: |
| explicit proxy(const directory_entry& dir_entry) |
| : _dir_entry(dir_entry) |
| { |
| } |
| friend class directory_iterator; |
| friend class recursive_directory_iterator; |
| directory_entry _dir_entry; |
| }; |
| using iterator_category = std::input_iterator_tag; |
| using value_type = directory_entry; |
| using difference_type = std::ptrdiff_t; |
| using pointer = const directory_entry*; |
| using reference = const directory_entry&; |
| |
| // 30.10.13.1 member functions |
| directory_iterator() noexcept; |
| explicit directory_iterator(const path& p); |
| directory_iterator(const path& p, directory_options options); |
| directory_iterator(const path& p, std::error_code& ec) noexcept; |
| directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; |
| directory_iterator(const directory_iterator& rhs); |
| directory_iterator(directory_iterator&& rhs) noexcept; |
| ~directory_iterator(); |
| directory_iterator& operator=(const directory_iterator& rhs); |
| directory_iterator& operator=(directory_iterator&& rhs) noexcept; |
| const directory_entry& operator*() const; |
| const directory_entry* operator->() const; |
| directory_iterator& operator++(); |
| directory_iterator& increment(std::error_code& ec) noexcept; |
| |
| // other members as required by 27.2.3, input iterators |
| proxy operator++(int) |
| { |
| proxy p{**this}; |
| ++*this; |
| return p; |
| } |
| bool operator==(const directory_iterator& rhs) const; |
| bool operator!=(const directory_iterator& rhs) const; |
| |
| private: |
| friend class recursive_directory_iterator; |
| class impl; |
| std::shared_ptr<impl> _impl; |
| }; |
| |
| // 30.10.13.2 directory_iterator non-member functions |
| GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept; |
| GHC_FS_API directory_iterator end(const directory_iterator&) noexcept; |
| |
| // 30.10.14 class recursive_directory_iterator |
| class GHC_FS_API_CLASS recursive_directory_iterator |
| { |
| public: |
| using iterator_category = std::input_iterator_tag; |
| using value_type = directory_entry; |
| using difference_type = std::ptrdiff_t; |
| using pointer = const directory_entry*; |
| using reference = const directory_entry&; |
| |
| // 30.10.14.1 constructors and destructor |
| recursive_directory_iterator() noexcept; |
| explicit recursive_directory_iterator(const path& p); |
| recursive_directory_iterator(const path& p, directory_options options); |
| recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; |
| recursive_directory_iterator(const path& p, std::error_code& ec) noexcept; |
| recursive_directory_iterator(const recursive_directory_iterator& rhs); |
| recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept; |
| ~recursive_directory_iterator(); |
| |
| // 30.10.14.1 observers |
| directory_options options() const; |
| int depth() const; |
| bool recursion_pending() const; |
| |
| const directory_entry& operator*() const; |
| const directory_entry* operator->() const; |
| |
| // 30.10.14.1 modifiers recursive_directory_iterator& |
| recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs); |
| recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept; |
| recursive_directory_iterator& operator++(); |
| recursive_directory_iterator& increment(std::error_code& ec) noexcept; |
| |
| void pop(); |
| void pop(std::error_code& ec); |
| void disable_recursion_pending(); |
| |
| // other members as required by 27.2.3, input iterators |
| directory_iterator::proxy operator++(int) |
| { |
| directory_iterator::proxy proxy{**this}; |
| ++*this; |
| return proxy; |
| } |
| bool operator==(const recursive_directory_iterator& rhs) const; |
| bool operator!=(const recursive_directory_iterator& rhs) const; |
| |
| private: |
| struct recursive_directory_iterator_impl |
| { |
| directory_options _options; |
| bool _recursion_pending; |
| std::stack<directory_iterator> _dir_iter_stack; |
| recursive_directory_iterator_impl(directory_options options, bool recursion_pending) |
| : _options(options) |
| , _recursion_pending(recursion_pending) |
| { |
| } |
| }; |
| std::shared_ptr<recursive_directory_iterator_impl> _impl; |
| }; |
| |
| // 30.10.14.2 directory_iterator non-member functions |
| GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; |
| GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept; |
| |
| // 30.10.15 filesystem operations |
| GHC_FS_API path absolute(const path& p); |
| GHC_FS_API path absolute(const path& p, std::error_code& ec); |
| |
| GHC_FS_API path canonical(const path& p); |
| GHC_FS_API path canonical(const path& p, std::error_code& ec); |
| |
| GHC_FS_API void copy(const path& from, const path& to); |
| GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept; |
| GHC_FS_API void copy(const path& from, const path& to, copy_options options); |
| GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept; |
| |
| GHC_FS_API bool copy_file(const path& from, const path& to); |
| GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept; |
| GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option); |
| GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept; |
| |
| GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink); |
| GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept; |
| |
| GHC_FS_API bool create_directories(const path& p); |
| GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API bool create_directory(const path& p); |
| GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API bool create_directory(const path& p, const path& attributes); |
| GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept; |
| |
| GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink); |
| GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; |
| |
| GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link); |
| GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept; |
| |
| GHC_FS_API void create_symlink(const path& to, const path& new_symlink); |
| GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; |
| |
| GHC_FS_API path current_path(); |
| GHC_FS_API path current_path(std::error_code& ec); |
| GHC_FS_API void current_path(const path& p); |
| GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API bool exists(file_status s) noexcept; |
| GHC_FS_API bool exists(const path& p); |
| GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API bool equivalent(const path& p1, const path& p2); |
| GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept; |
| |
| GHC_FS_API uintmax_t file_size(const path& p); |
| GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API uintmax_t hard_link_count(const path& p); |
| GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API bool is_block_file(file_status s) noexcept; |
| GHC_FS_API bool is_block_file(const path& p); |
| GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API bool is_character_file(file_status s) noexcept; |
| GHC_FS_API bool is_character_file(const path& p); |
| GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API bool is_directory(file_status s) noexcept; |
| GHC_FS_API bool is_directory(const path& p); |
| GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API bool is_empty(const path& p); |
| GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API bool is_fifo(file_status s) noexcept; |
| GHC_FS_API bool is_fifo(const path& p); |
| GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API bool is_other(file_status s) noexcept; |
| GHC_FS_API bool is_other(const path& p); |
| GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API bool is_regular_file(file_status s) noexcept; |
| GHC_FS_API bool is_regular_file(const path& p); |
| GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API bool is_socket(file_status s) noexcept; |
| GHC_FS_API bool is_socket(const path& p); |
| GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API bool is_symlink(file_status s) noexcept; |
| GHC_FS_API bool is_symlink(const path& p); |
| GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API file_time_type last_write_time(const path& p); |
| GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; |
| GHC_FS_API void last_write_time(const path& p, file_time_type new_time); |
| GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept; |
| |
| GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace); |
| GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept; |
| GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec); |
| |
| GHC_FS_API path proximate(const path& p, std::error_code& ec); |
| GHC_FS_API path proximate(const path& p, const path& base = current_path()); |
| GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec); |
| |
| GHC_FS_API path read_symlink(const path& p); |
| GHC_FS_API path read_symlink(const path& p, std::error_code& ec); |
| |
| GHC_FS_API path relative(const path& p, std::error_code& ec); |
| GHC_FS_API path relative(const path& p, const path& base = current_path()); |
| GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec); |
| |
| GHC_FS_API bool remove(const path& p); |
| GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API uintmax_t remove_all(const path& p); |
| GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API void rename(const path& from, const path& to); |
| GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept; |
| |
| GHC_FS_API void resize_file(const path& p, uintmax_t size); |
| GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept; |
| |
| GHC_FS_API space_info space(const path& p); |
| GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API file_status status(const path& p); |
| GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API bool status_known(file_status s) noexcept; |
| |
| GHC_FS_API file_status symlink_status(const path& p); |
| GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept; |
| |
| GHC_FS_API path temp_directory_path(); |
| GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept; |
| |
| GHC_FS_API path weakly_canonical(const path& p); |
| GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept; |
| |
| // Non-C++17 add-on std::fstream wrappers with path |
| template <class charT, class traits = std::char_traits<charT>> |
| class basic_filebuf : public std::basic_filebuf<charT, traits> |
| { |
| public: |
| basic_filebuf() {} |
| ~basic_filebuf() override {} |
| basic_filebuf(const basic_filebuf&) = delete; |
| const basic_filebuf& operator=(const basic_filebuf&) = delete; |
| basic_filebuf<charT, traits>* open(const path& p, std::ios_base::openmode mode) |
| { |
| #if defined(GHC_OS_WINDOWS) && !defined(__GNUC__) |
| return std::basic_filebuf<charT, traits>::open(p.wstring().c_str(), mode) ? this : 0; |
| #else |
| return std::basic_filebuf<charT, traits>::open(p.string().c_str(), mode) ? this : 0; |
| #endif |
| } |
| }; |
| |
| template <class charT, class traits = std::char_traits<charT>> |
| class basic_ifstream : public std::basic_ifstream<charT, traits> |
| { |
| public: |
| basic_ifstream() {} |
| #if defined(GHC_OS_WINDOWS) && !defined(__GNUC__) |
| explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) |
| : std::basic_ifstream<charT, traits>(p.wstring().c_str(), mode) |
| { |
| } |
| void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream<charT, traits>::open(p.wstring().c_str(), mode); } |
| #else |
| explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) |
| : std::basic_ifstream<charT, traits>(p.string().c_str(), mode) |
| { |
| } |
| void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream<charT, traits>::open(p.string().c_str(), mode); } |
| #endif |
| basic_ifstream(const basic_ifstream&) = delete; |
| const basic_ifstream& operator=(const basic_ifstream&) = delete; |
| ~basic_ifstream() override {} |
| }; |
| |
| template <class charT, class traits = std::char_traits<charT>> |
| class basic_ofstream : public std::basic_ofstream<charT, traits> |
| { |
| public: |
| basic_ofstream() {} |
| #if defined(GHC_OS_WINDOWS) && !defined(__GNUC__) |
| explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) |
| : std::basic_ofstream<charT, traits>(p.wstring().c_str(), mode) |
| { |
| } |
| void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream<charT, traits>::open(p.wstring().c_str(), mode); } |
| #else |
| explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) |
| : std::basic_ofstream<charT, traits>(p.string().c_str(), mode) |
| { |
| } |
| void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream<charT, traits>::open(p.string().c_str(), mode); } |
| #endif |
| basic_ofstream(const basic_ofstream&) = delete; |
| const basic_ofstream& operator=(const basic_ofstream&) = delete; |
| ~basic_ofstream() override {} |
| }; |
| |
| template <class charT, class traits = std::char_traits<charT>> |
| class basic_fstream : public std::basic_fstream<charT, traits> |
| { |
| public: |
| basic_fstream() {} |
| #if defined(GHC_OS_WINDOWS) && !defined(__GNUC__) |
| explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) |
| : std::basic_fstream<charT, traits>(p.wstring().c_str(), mode) |
| { |
| } |
| void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream<charT, traits>::open(p.wstring().c_str(), mode); } |
| #else |
| explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) |
| : std::basic_fstream<charT, traits>(p.string().c_str(), mode) |
| { |
| } |
| void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream<charT, traits>::open(p.string().c_str(), mode); } |
| #endif |
| basic_fstream(const basic_fstream&) = delete; |
| const basic_fstream& operator=(const basic_fstream&) = delete; |
| ~basic_fstream() override {} |
| }; |
| |
| typedef basic_filebuf<char> filebuf; |
| typedef basic_filebuf<wchar_t> wfilebuf; |
| typedef basic_ifstream<char> ifstream; |
| typedef basic_ifstream<wchar_t> wifstream; |
| typedef basic_ofstream<char> ofstream; |
| typedef basic_ofstream<wchar_t> wofstream; |
| typedef basic_fstream<char> fstream; |
| typedef basic_fstream<wchar_t> wfstream; |
| |
| class GHC_FS_API_CLASS u8arguments |
| { |
| public: |
| u8arguments(int& argc, char**& argv); |
| ~u8arguments() |
| { |
| _refargc = _argc; |
| _refargv = _argv; |
| } |
| |
| bool valid() const { return _isvalid; } |
| |
| private: |
| int _argc; |
| char** _argv; |
| int& _refargc; |
| char**& _refargv; |
| bool _isvalid; |
| #ifdef GHC_OS_WINDOWS |
| std::vector<std::string> _args; |
| std::vector<char*> _argp; |
| #endif |
| }; |
| |
| //------------------------------------------------------------------------------------------------- |
| // Implementation |
| //------------------------------------------------------------------------------------------------- |
| |
| namespace detail { |
| // GHC_FS_API void postprocess_path_with_format(path::impl_string_type& p, path::format fmt); |
| enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; |
| GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); |
| GHC_FS_API bool is_surrogate(uint32_t c); |
| GHC_FS_API bool is_high_surrogate(uint32_t c); |
| GHC_FS_API bool is_low_surrogate(uint32_t c); |
| GHC_FS_API unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint); |
| enum class portable_error { |
| none = 0, |
| exists, |
| not_found, |
| not_supported, |
| not_implemented, |
| invalid_argument, |
| is_a_directory, |
| }; |
| GHC_FS_API std::error_code make_error_code(portable_error err); |
| #ifdef GHC_OS_WINDOWS |
| GHC_FS_API std::error_code make_system_error(uint32_t err = 0); |
| #else |
| GHC_FS_API std::error_code make_system_error(int err = 0); |
| #endif |
| } // namespace detail |
| |
| namespace detail { |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| GHC_INLINE std::error_code make_error_code(portable_error err) |
| { |
| #ifdef GHC_OS_WINDOWS |
| switch (err) { |
| case portable_error::none: |
| return std::error_code(); |
| case portable_error::exists: |
| return std::error_code(ERROR_ALREADY_EXISTS, std::system_category()); |
| case portable_error::not_found: |
| return std::error_code(ERROR_PATH_NOT_FOUND, std::system_category()); |
| case portable_error::not_supported: |
| return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); |
| case portable_error::not_implemented: |
| return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category()); |
| case portable_error::invalid_argument: |
| return std::error_code(ERROR_INVALID_PARAMETER, std::system_category()); |
| case portable_error::is_a_directory: |
| #ifdef ERROR_DIRECTORY_NOT_SUPPORTED |
| return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); |
| #else |
| return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); |
| #endif |
| } |
| #else |
| switch (err) { |
| case portable_error::none: |
| return std::error_code(); |
| case portable_error::exists: |
| return std::error_code(EEXIST, std::system_category()); |
| case portable_error::not_found: |
| return std::error_code(ENOENT, std::system_category()); |
| case portable_error::not_supported: |
| return std::error_code(ENOTSUP, std::system_category()); |
| case portable_error::not_implemented: |
| return std::error_code(ENOSYS, std::system_category()); |
| case portable_error::invalid_argument: |
| return std::error_code(EINVAL, std::system_category()); |
| case portable_error::is_a_directory: |
| return std::error_code(EISDIR, std::system_category()); |
| } |
| #endif |
| return std::error_code(); |
| } |
| |
| #ifdef GHC_OS_WINDOWS |
| GHC_INLINE std::error_code make_system_error(uint32_t err) |
| { |
| return std::error_code(err ? static_cast<int>(err) : static_cast<int>(::GetLastError()), std::system_category()); |
| } |
| #else |
| GHC_INLINE std::error_code make_system_error(int err) |
| { |
| return std::error_code(err ? err : errno, std::system_category()); |
| } |
| #endif |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| template <typename Enum> |
| using EnableBitmask = typename std::enable_if<std::is_same<Enum, perms>::value || std::is_same<Enum, perm_options>::value || std::is_same<Enum, copy_options>::value || std::is_same<Enum, directory_options>::value, Enum>::type; |
| } // namespace detail |
| |
| template <typename Enum> |
| detail::EnableBitmask<Enum> operator&(Enum X, Enum Y) |
| { |
| using underlying = typename std::underlying_type<Enum>::type; |
| return static_cast<Enum>(static_cast<underlying>(X) & static_cast<underlying>(Y)); |
| } |
| |
| template <typename Enum> |
| detail::EnableBitmask<Enum> operator|(Enum X, Enum Y) |
| { |
| using underlying = typename std::underlying_type<Enum>::type; |
| return static_cast<Enum>(static_cast<underlying>(X) | static_cast<underlying>(Y)); |
| } |
| |
| template <typename Enum> |
| detail::EnableBitmask<Enum> operator^(Enum X, Enum Y) |
| { |
| using underlying = typename std::underlying_type<Enum>::type; |
| return static_cast<Enum>(static_cast<underlying>(X) ^ static_cast<underlying>(Y)); |
| } |
| |
| template <typename Enum> |
| detail::EnableBitmask<Enum> operator~(Enum X) |
| { |
| using underlying = typename std::underlying_type<Enum>::type; |
| return static_cast<Enum>(~static_cast<underlying>(X)); |
| } |
| |
| template <typename Enum> |
| detail::EnableBitmask<Enum>& operator&=(Enum& X, Enum Y) |
| { |
| X = X & Y; |
| return X; |
| } |
| |
| template <typename Enum> |
| detail::EnableBitmask<Enum>& operator|=(Enum& X, Enum Y) |
| { |
| X = X | Y; |
| return X; |
| } |
| |
| template <typename Enum> |
| detail::EnableBitmask<Enum>& operator^=(Enum& X, Enum Y) |
| { |
| X = X ^ Y; |
| return X; |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| namespace detail { |
| |
| GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi) |
| { |
| return (static_cast<uint32_t>(c - lo) < (hi - lo + 1)); |
| } |
| |
| GHC_INLINE bool is_surrogate(uint32_t c) |
| { |
| return in_range(c, 0xd800, 0xdfff); |
| } |
| |
| GHC_INLINE bool is_high_surrogate(uint32_t c) |
| { |
| return (c & 0xfffffc00) == 0xd800; |
| } |
| |
| GHC_INLINE bool is_low_surrogate(uint32_t c) |
| { |
| return (c & 0xfffffc00) == 0xdc00; |
| } |
| |
| GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode) |
| { |
| if (unicode <= 0x7f) { |
| str.push_back(static_cast<char>(unicode)); |
| } |
| else if (unicode >= 0x80 && unicode <= 0x7ff) { |
| str.push_back(static_cast<char>((unicode >> 6) + 192)); |
| str.push_back(static_cast<char>((unicode & 0x3f) + 128)); |
| } |
| else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) { |
| str.push_back(static_cast<char>((unicode >> 12) + 224)); |
| str.push_back(static_cast<char>(((unicode & 0xfff) >> 6) + 128)); |
| str.push_back(static_cast<char>((unicode & 0x3f) + 128)); |
| } |
| else if (unicode >= 0x10000 && unicode <= 0x10ffff) { |
| str.push_back(static_cast<char>((unicode >> 18) + 240)); |
| str.push_back(static_cast<char>(((unicode & 0x3ffff) >> 12) + 128)); |
| str.push_back(static_cast<char>(((unicode & 0xfff) >> 6) + 128)); |
| str.push_back(static_cast<char>((unicode & 0x3f) + 128)); |
| } |
| else { |
| #ifdef GHC_RAISE_UNICODE_ERRORS |
| throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence)); |
| #else |
| appendUTF8(str, 0xfffd); |
| #endif |
| } |
| } |
| |
| // Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/) |
| // and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding; |
| // Generating debugging and shrinking my own DFA from scratch was a day of fun! |
| GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint) |
| { |
| static const uint32_t utf8_state_info[] = { |
| // encoded states |
| 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, |
| 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u, |
| }; |
| uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; |
| codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment); |
| return state == S_RJCT ? static_cast<unsigned>(S_RJCT) : static_cast<unsigned>((utf8_state_info[category + 16] >> (state << 2)) & 0xf); |
| } |
| |
| GHC_INLINE bool validUtf8(const std::string& utf8String) |
| { |
| std::string::const_iterator iter = utf8String.begin(); |
| unsigned utf8_state = S_STRT; |
| std::uint32_t codepoint = 0; |
| while (iter < utf8String.end()) { |
| if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_RJCT) { |
| return false; |
| } |
| } |
| if (utf8_state) { |
| return false; |
| } |
| return true; |
| } |
| |
| } // namespace detail |
| |
| #endif |
| |
| namespace detail { |
| |
| template <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 1)>::type* = nullptr> |
| inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) |
| { |
| return StringType(utf8String.begin(), utf8String.end(), alloc); |
| } |
| |
| template <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 2)>::type* = nullptr> |
| inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) |
| { |
| StringType result(alloc); |
| result.reserve(utf8String.length()); |
| std::string::const_iterator iter = utf8String.begin(); |
| unsigned utf8_state = S_STRT; |
| std::uint32_t codepoint = 0; |
| while (iter < utf8String.end()) { |
| if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_STRT) { |
| if (codepoint <= 0xffff) { |
| result += static_cast<typename StringType::value_type>(codepoint); |
| } |
| else { |
| codepoint -= 0x10000; |
| result += static_cast<typename StringType::value_type>((codepoint >> 10) + 0xd800); |
| result += static_cast<typename StringType::value_type>((codepoint & 0x3ff) + 0xdc00); |
| } |
| codepoint = 0; |
| } |
| else if (utf8_state == S_RJCT) { |
| #ifdef GHC_RAISE_UNICODE_ERRORS |
| throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); |
| #else |
| result += static_cast<typename StringType::value_type>(0xfffd); |
| utf8_state = S_STRT; |
| codepoint = 0; |
| #endif |
| } |
| } |
| if (utf8_state) { |
| #ifdef GHC_RAISE_UNICODE_ERRORS |
| throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); |
| #else |
| result += static_cast<typename StringType::value_type>(0xfffd); |
| #endif |
| } |
| return result; |
| } |
| |
| template <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 4)>::type* = nullptr> |
| inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) |
| { |
| StringType result(alloc); |
| result.reserve(utf8String.length()); |
| std::string::const_iterator iter = utf8String.begin(); |
| unsigned utf8_state = S_STRT; |
| std::uint32_t codepoint = 0; |
| while (iter < utf8String.end()) { |
| if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast<uint8_t>(*iter++), codepoint)) == S_STRT) { |
| result += static_cast<typename StringType::value_type>(codepoint); |
| codepoint = 0; |
| } |
| else if (utf8_state == S_RJCT) { |
| #ifdef GHC_RAISE_UNICODE_ERRORS |
| throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); |
| #else |
| result += static_cast<typename StringType::value_type>(0xfffd); |
| utf8_state = S_STRT; |
| codepoint = 0; |
| #endif |
| } |
| } |
| if (utf8_state) { |
| #ifdef GHC_RAISE_UNICODE_ERRORS |
| throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); |
| #else |
| result += static_cast<typename StringType::value_type>(0xfffd); |
| #endif |
| } |
| return result; |
| } |
| |
| template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 1), int>::type size = 1> |
| inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString) |
| { |
| return std::string(unicodeString.begin(), unicodeString.end()); |
| } |
| |
| template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 2), int>::type size = 2> |
| inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString) |
| { |
| std::string result; |
| for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) { |
| char32_t c = *iter; |
| if (is_surrogate(c)) { |
| ++iter; |
| if (iter != unicodeString.end() && is_high_surrogate(c) && is_low_surrogate(*iter)) { |
| appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00); |
| } |
| else { |
| #ifdef GHC_RAISE_UNICODE_ERRORS |
| throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence)); |
| #else |
| appendUTF8(result, 0xfffd); |
| if(iter == unicodeString.end()) { |
| break; |
| } |
| #endif |
| } |
| } |
| else { |
| appendUTF8(result, c); |
| } |
| } |
| return result; |
| } |
| |
| template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 4), int>::type size = 4> |
| inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString) |
| { |
| std::string result; |
| for (auto c : unicodeString) { |
| appendUTF8(result, static_cast<uint32_t>(c)); |
| } |
| return result; |
| } |
| |
| template <typename charT> |
| inline std::string toUtf8(const charT* unicodeString) |
| { |
| return toUtf8(std::basic_string<charT, std::char_traits<charT>>(unicodeString)); |
| } |
| |
| } // namespace detail |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| namespace detail { |
| |
| GHC_INLINE bool startsWith(const std::string& what, const std::string& with) |
| { |
| return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); |
| } |
| |
| } // namespace detail |
| |
| GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, path::format fmt) |
| { |
| #ifdef GHC_RAISE_UNICODE_ERRORS |
| if(!detail::validUtf8(p)) { |
| path t; |
| t._path = p; |
| throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence)); |
| } |
| #endif |
| switch (fmt) { |
| #ifndef GHC_OS_WINDOWS |
| case path::auto_format: |
| case path::native_format: |
| #endif |
| case path::generic_format: |
| // nothing to do |
| break; |
| #ifdef GHC_OS_WINDOWS |
| case path::auto_format: |
| case path::native_format: |
| if (detail::startsWith(p, std::string("\\\\?\\"))) { |
| // remove Windows long filename marker |
| p.erase(0, 4); |
| if (detail::startsWith(p, std::string("UNC\\"))) { |
| p.erase(0, 2); |
| p[0] = '\\'; |
| } |
| } |
| for (auto& c : p) { |
| if (c == '\\') { |
| c = '/'; |
| } |
| } |
| break; |
| #endif |
| } |
| if (p.length() > 2 && p[0] == '/' && p[1] == '/' && p[2] != '/') { |
| std::string::iterator new_end = std::unique(p.begin() + 2, p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == '/'; }); |
| p.erase(new_end, p.end()); |
| } |
| else { |
| std::string::iterator new_end = std::unique(p.begin(), p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == '/'; }); |
| p.erase(new_end, p.end()); |
| } |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| template <class Source, typename> |
| inline path::path(const Source& source, format fmt) |
| : _path(detail::toUtf8(source)) |
| { |
| postprocess_path_with_format(_path, fmt); |
| } |
| template <> |
| inline path::path(const std::wstring& source, format fmt) |
| { |
| _path = detail::toUtf8(source); |
| postprocess_path_with_format(_path, fmt); |
| } |
| template <> |
| inline path::path(const std::u16string& source, format fmt) |
| { |
| _path = detail::toUtf8(source); |
| postprocess_path_with_format(_path, fmt); |
| } |
| template <> |
| inline path::path(const std::u32string& source, format fmt) |
| { |
| _path = detail::toUtf8(source); |
| postprocess_path_with_format(_path, fmt); |
| } |
| |
| #ifdef __cpp_lib_string_view |
| template <> |
| inline path::path(const std::string_view& source, format fmt) |
| { |
| _path = detail::toUtf8(std::string(source)); |
| postprocess_path_with_format(_path, fmt); |
| } |
| #endif |
| |
| template <class Source, typename> |
| inline path u8path(const Source& source) |
| { |
| return path(source); |
| } |
| template <class InputIterator> |
| inline path u8path(InputIterator first, InputIterator last) |
| { |
| return path(first, last); |
| } |
| |
| template <class InputIterator> |
| inline path::path(InputIterator first, InputIterator last, format fmt) |
| : path(std::basic_string<typename std::iterator_traits<InputIterator>::value_type>(first, last), fmt) |
| { |
| // delegated |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| namespace detail { |
| |
| GHC_INLINE bool equals_simple_insensitive(const char* str1, const char* str2) |
| { |
| #ifdef GHC_OS_WINDOWS |
| #ifdef __GNUC__ |
| while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) { |
| if (*str1++ == 0) |
| return true; |
| } |
| return false; |
| #else |
| return 0 == ::_stricmp(str1, str2); |
| #endif |
| #else |
| return 0 == ::strcasecmp(str1, str2); |
| #endif |
| } |
| |
| GHC_INLINE const char* strerror_adapter(char* gnu, char*) |
| { |
| return gnu; |
| } |
| |
| GHC_INLINE const char* strerror_adapter(int posix, char* buffer) |
| { |
| if(posix) { |
| return "Error in strerror_r!"; |
| } |
| return buffer; |
| } |
| |
| template <typename ErrorNumber> |
| GHC_INLINE std::string systemErrorText(ErrorNumber code = 0) |
| { |
| #if defined(GHC_OS_WINDOWS) |
| LPVOID msgBuf; |
| DWORD dw = code ? static_cast<DWORD>(code) : ::GetLastError(); |
| FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, NULL); |
| std::string msg = toUtf8(std::wstring((LPWSTR)msgBuf)); |
| LocalFree(msgBuf); |
| return msg; |
| #else |
| char buffer[512]; |
| return strerror_adapter(strerror_r(code ? code : errno, buffer, sizeof(buffer)), buffer); |
| #endif |
| } |
| |
| #ifdef GHC_OS_WINDOWS |
| using CreateSymbolicLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, DWORD); |
| using CreateHardLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); |
| |
| GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool to_directory, std::error_code& ec) |
| { |
| std::error_code tec; |
| auto fs = status(target_name, tec); |
| if ((fs.type() == file_type::directory && !to_directory) || (fs.type() == file_type::regular && to_directory)) { |
| ec = detail::make_error_code(detail::portable_error::not_supported); |
| return; |
| } |
| #if defined(__GNUC__) && __GNUC__ >= 8 |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wcast-function-type" |
| #endif |
| static CreateSymbolicLinkW_fp api_call = reinterpret_cast<CreateSymbolicLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); |
| #if defined(__GNUC__) && __GNUC__ >= 8 |
| #pragma GCC diagnostic pop |
| #endif |
| if (api_call) { |
| if (api_call(detail::fromUtf8<std::wstring>(new_symlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), to_directory ? 1 : 0) == 0) { |
| auto result = ::GetLastError(); |
| if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(detail::fromUtf8<std::wstring>(new_symlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), to_directory ? 3 : 2) != 0) { |
| return; |
| } |
| ec = detail::make_system_error(result); |
| } |
| } |
| else { |
| ec = detail::make_system_error(ERROR_NOT_SUPPORTED); |
| } |
| } |
| |
| GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) |
| { |
| #if defined(__GNUC__) && __GNUC__ >= 8 |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wcast-function-type" |
| #endif |
| static CreateHardLinkW_fp api_call = reinterpret_cast<CreateHardLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); |
| #if defined(__GNUC__) && __GNUC__ >= 8 |
| #pragma GCC diagnostic pop |
| #endif |
| if (api_call) { |
| if (api_call(detail::fromUtf8<std::wstring>(new_hardlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), NULL) == 0) { |
| ec = detail::make_system_error(); |
| } |
| } |
| else { |
| ec = detail::make_system_error(ERROR_NOT_SUPPORTED); |
| } |
| } |
| #else |
| GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec) |
| { |
| if (::symlink(target_name.c_str(), new_symlink.c_str()) != 0) { |
| ec = detail::make_system_error(); |
| } |
| } |
| |
| GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) |
| { |
| if (::link(target_name.c_str(), new_hardlink.c_str()) != 0) { |
| ec = detail::make_system_error(); |
| } |
| } |
| #endif |
| |
| template <typename T> |
| GHC_INLINE file_status file_status_from_st_mode(T mode) |
| { |
| #ifdef GHC_OS_WINDOWS |
| file_type ft = file_type::unknown; |
| if ((mode & _S_IFDIR) == _S_IFDIR) { |
| ft = file_type::directory; |
| } |
| else if ((mode & _S_IFREG) == _S_IFREG) { |
| ft = file_type::regular; |
| } |
| else if ((mode & _S_IFCHR) == _S_IFCHR) { |
| ft = file_type::character; |
| } |
| perms prms = static_cast<perms>(mode & 0xfff); |
| return file_status(ft, prms); |
| #else |
| file_type ft = file_type::unknown; |
| if (S_ISDIR(mode)) { |
| ft = file_type::directory; |
| } |
| else if (S_ISREG(mode)) { |
| ft = file_type::regular; |
| } |
| else if (S_ISCHR(mode)) { |
| ft = file_type::character; |
| } |
| else if (S_ISBLK(mode)) { |
| ft = file_type::block; |
| } |
| else if (S_ISFIFO(mode)) { |
| ft = file_type::fifo; |
| } |
| else if (S_ISLNK(mode)) { |
| ft = file_type::symlink; |
| } |
| else if (S_ISSOCK(mode)) { |
| ft = file_type::socket; |
| } |
| perms prms = static_cast<perms>(mode & 0xfff); |
| return file_status(ft, prms); |
| #endif |
| } |
| |
| GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) |
| { |
| #ifdef GHC_OS_WINDOWS |
| #ifndef REPARSE_DATA_BUFFER_HEADER_SIZE |
| typedef struct _REPARSE_DATA_BUFFER |
| { |
| ULONG ReparseTag; |
| USHORT ReparseDataLength; |
| USHORT Reserved; |
| union |
| { |
| struct |
| { |
| USHORT SubstituteNameOffset; |
| USHORT SubstituteNameLength; |
| USHORT PrintNameOffset; |
| USHORT PrintNameLength; |
| ULONG Flags; |
| WCHAR PathBuffer[1]; |
| } SymbolicLinkReparseBuffer; |
| struct |
| { |
| USHORT SubstituteNameOffset; |
| USHORT SubstituteNameLength; |
| USHORT PrintNameOffset; |
| USHORT PrintNameLength; |
| WCHAR PathBuffer[1]; |
| } MountPointReparseBuffer; |
| struct |
| { |
| UCHAR DataBuffer[1]; |
| } GenericReparseBuffer; |
| } DUMMYUNIONNAME; |
| } REPARSE_DATA_BUFFER; |
| #ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE |
| #define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) |
| #endif |
| #endif |
| |
| std::shared_ptr<void> file(CreateFileW(p.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); |
| if (file.get() == INVALID_HANDLE_VALUE) { |
| ec = detail::make_system_error(); |
| return path(); |
| } |
| |
| std::shared_ptr<REPARSE_DATA_BUFFER> reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free); |
| ULONG bufferUsed; |
| path result; |
| if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { |
| if (IsReparseTagMicrosoft(reparseData->ReparseTag)) { |
| switch (reparseData->ReparseTag) { |
| case IO_REPARSE_TAG_SYMLINK: |
| result = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR)); |
| break; |
| case IO_REPARSE_TAG_MOUNT_POINT: |
| result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR)); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| else { |
| ec = detail::make_system_error(); |
| } |
| return result; |
| #else |
| size_t bufferSize = 256; |
| while (true) { |
| std::vector<char> buffer(bufferSize, static_cast<char>(0)); |
| auto rc = ::readlink(p.c_str(), buffer.data(), buffer.size()); |
| if (rc < 0) { |
| ec = detail::make_system_error(); |
| return path(); |
| } |
| else if (rc < static_cast<int>(bufferSize)) { |
| return path(std::string(buffer.data(), static_cast<std::string::size_type>(rc))); |
| } |
| bufferSize *= 2; |
| } |
| return path(); |
| #endif |
| } |
| |
| #ifdef GHC_OS_WINDOWS |
| GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft) |
| { |
| ULARGE_INTEGER ull; |
| ull.LowPart = ft.dwLowDateTime; |
| ull.HighPart = ft.dwHighDateTime; |
| return static_cast<time_t>(ull.QuadPart / 10000000ULL - 11644473600ULL); |
| } |
| |
| GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) |
| { |
| LONGLONG ll; |
| ll = Int32x32To64(t, 10000000) + 116444736000000000; |
| ft.dwLowDateTime = static_cast<DWORD>(ll); |
| ft.dwHighDateTime = static_cast<DWORD>(ll >> 32); |
| } |
| |
| template <typename INFO> |
| GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info) |
| { |
| return static_cast<uintmax_t>(-1); |
| } |
| |
| template <> |
| GHC_INLINE uintmax_t hard_links_from_INFO<BY_HANDLE_FILE_INFORMATION>(const BY_HANDLE_FILE_INFORMATION* info) |
| { |
| return info->nNumberOfLinks; |
| } |
| |
| template <typename INFO> |
| GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code&, uintmax_t* sz = nullptr, time_t* lwt = nullptr) noexcept |
| { |
| file_type ft = file_type::unknown; |
| if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { |
| ft = file_type::symlink; |
| } |
| else { |
| if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { |
| ft = file_type::directory; |
| } |
| else { |
| ft = file_type::regular; |
| } |
| } |
| perms prms = perms::owner_read | perms::group_read | perms::others_read; |
| if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { |
| prms = prms | perms::owner_write | perms::group_write | perms::others_write; |
| } |
| std::string ext = p.extension().generic_string(); |
| if (equals_simple_insensitive(ext.c_str(), ".exe") || equals_simple_insensitive(ext.c_str(), ".cmd") || equals_simple_insensitive(ext.c_str(), ".bat") || equals_simple_insensitive(ext.c_str(), ".com")) { |
| prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec; |
| } |
| if (sz) { |
| *sz = static_cast<uintmax_t>(info->nFileSizeHigh) << (sizeof(info->nFileSizeHigh) * 8) | info->nFileSizeLow; |
| } |
| if (lwt) { |
| *lwt = detail::timeFromFILETIME(info->ftLastWriteTime); |
| } |
| return file_status(ft, prms); |
| } |
| |
| #endif |
| |
| GHC_INLINE bool is_not_found_error(std::error_code& ec) |
| { |
| #ifdef GHC_OS_WINDOWS |
| return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME; |
| #else |
| return ec.value() == ENOENT || ec.value() == ENOTDIR; |
| #endif |
| } |
| |
| GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr) noexcept |
| { |
| #ifdef GHC_OS_WINDOWS |
| file_status fs; |
| WIN32_FILE_ATTRIBUTE_DATA attr; |
| if (!GetFileAttributesExW(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), GetFileExInfoStandard, &attr)) { |
| ec = detail::make_system_error(); |
| } |
| else { |
| ec.clear(); |
| fs = detail::status_from_INFO(p, &attr, ec, sz, lwt); |
| if (nhl) { |
| *nhl = 0; |
| } |
| if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { |
| fs.type(file_type::symlink); |
| } |
| } |
| if (detail::is_not_found_error(ec)) { |
| return file_status(file_type::not_found); |
| } |
| return ec ? file_status(file_type::none) : fs; |
| #else |
| (void)sz; |
| (void)nhl; |
| (void)lwt; |
| struct ::stat fs; |
| auto result = ::lstat(p.c_str(), &fs); |
| if (result == 0) { |
| ec.clear(); |
| file_status f_s = detail::file_status_from_st_mode(fs.st_mode); |
| return f_s; |
| } |
| ec = detail::make_system_error(); |
| if (detail::is_not_found_error(ec)) { |
| return file_status(file_type::not_found, perms::unknown); |
| } |
| return file_status(file_type::none); |
| #endif |
| } |
| |
| GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status* sls = nullptr, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr, int recurse_count = 0) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| if (recurse_count > 16) { |
| ec = detail::make_system_error(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/); |
| return file_status(file_type::unknown); |
| } |
| WIN32_FILE_ATTRIBUTE_DATA attr; |
| if (!::GetFileAttributesExW(p.wstring().c_str(), GetFileExInfoStandard, &attr)) { |
| ec = detail::make_system_error(); |
| } |
| else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { |
| path target = resolveSymlink(p, ec); |
| file_status result; |
| if (!ec && !target.empty()) { |
| if (sls) { |
| *sls = status_from_INFO(p, &attr, ec); |
| } |
| return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1); |
| } |
| return file_status(file_type::unknown); |
| } |
| if (ec) { |
| if (detail::is_not_found_error(ec)) { |
| return file_status(file_type::not_found); |
| } |
| return file_status(file_type::none); |
| } |
| if (nhl) { |
| *nhl = 0; |
| } |
| return detail::status_from_INFO(p, &attr, ec, sz, lwt); |
| #else |
| (void)recurse_count; |
| struct ::stat st; |
| auto result = ::lstat(p.c_str(), &st); |
| if (result == 0) { |
| ec.clear(); |
| file_status fs = detail::file_status_from_st_mode(st.st_mode); |
| if (fs.type() == file_type::symlink) { |
| result = ::stat(p.c_str(), &st); |
| if (result == 0) { |
| if (sls) { |
| *sls = fs; |
| } |
| fs = detail::file_status_from_st_mode(st.st_mode); |
| } |
| } |
| if (sz) { |
| *sz = static_cast<uintmax_t>(st.st_size); |
| } |
| if (nhl) { |
| *nhl = st.st_nlink; |
| } |
| if (lwt) { |
| *lwt = st.st_mtime; |
| } |
| return fs; |
| } |
| else { |
| ec = detail::make_system_error(); |
| if (detail::is_not_found_error(ec)) { |
| return file_status(file_type::not_found, perms::unknown); |
| } |
| return file_status(file_type::none); |
| } |
| #endif |
| } |
| |
| } // namespace detail |
| |
| GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) |
| : _argc(argc) |
| , _argv(argv) |
| , _refargc(argc) |
| , _refargv(argv) |
| , _isvalid(false) |
| { |
| #ifdef GHC_OS_WINDOWS |
| LPWSTR* p; |
| p = ::CommandLineToArgvW(::GetCommandLineW(), &argc); |
| _args.reserve(static_cast<size_t>(argc)); |
| _argp.reserve(static_cast<size_t>(argc)); |
| for (size_t i = 0; i < static_cast<size_t>(argc); ++i) { |
| _args.push_back(detail::toUtf8(std::wstring(p[i]))); |
| _argp.push_back((char*)_args[i].data()); |
| } |
| argv = _argp.data(); |
| ::LocalFree(p); |
| _isvalid = true; |
| #else |
| std::setlocale(LC_ALL, ""); |
| #if defined(__ANDROID__) && __ANDROID_API__ < 26 |
| _isvalid = true; |
| #else |
| if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) { |
| _isvalid = true; |
| } |
| #endif |
| #endif |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.1 constructors and destructor |
| |
| GHC_INLINE path::path() noexcept {} |
| |
| GHC_INLINE path::path(const path& p) |
| : _path(p._path) |
| { |
| } |
| |
| GHC_INLINE path::path(path&& p) noexcept |
| : _path(std::move(p._path)) |
| { |
| } |
| |
| GHC_INLINE path::path(string_type&& source, format fmt) |
| #ifdef GHC_USE_WCHAR_T |
| : _path(detail::toUtf8(source)) |
| #else |
| : _path(std::move(source)) |
| #endif |
| { |
| postprocess_path_with_format(_path, fmt); |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| template <class Source, typename> |
| inline path::path(const Source& source, const std::locale& loc, format fmt) |
| : path(source, fmt) |
| { |
| std::string locName = loc.name(); |
| if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { |
| throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); |
| } |
| } |
| |
| template <class InputIterator> |
| inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt) |
| : path(std::basic_string<typename std::iterator_traits<InputIterator>::value_type>(first, last), fmt) |
| { |
| std::string locName = loc.name(); |
| if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { |
| throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); |
| } |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| GHC_INLINE path::~path() {} |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.2 assignments |
| |
| GHC_INLINE path& path::operator=(const path& p) |
| { |
| _path = p._path; |
| return *this; |
| } |
| |
| GHC_INLINE path& path::operator=(path&& p) noexcept |
| { |
| _path = std::move(p._path); |
| return *this; |
| } |
| |
| GHC_INLINE path& path::operator=(path::string_type&& source) |
| { |
| return assign(source); |
| } |
| |
| GHC_INLINE path& path::assign(path::string_type&& source) |
| { |
| #ifdef GHC_USE_WCHAR_T |
| _path = detail::toUtf8(source); |
| #else |
| _path = std::move(source); |
| #endif |
| postprocess_path_with_format(_path, native_format); |
| return *this; |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| template <class Source> |
| inline path& path::operator=(const Source& source) |
| { |
| return assign(source); |
| } |
| |
| template <class Source> |
| inline path& path::assign(const Source& source) |
| { |
| _path.assign(detail::toUtf8(source)); |
| postprocess_path_with_format(_path, native_format); |
| return *this; |
| } |
| |
| template <> |
| inline path& path::assign<path>(const path& source) |
| { |
| _path = source._path; |
| return *this; |
| } |
| |
| template <class InputIterator> |
| inline path& path::assign(InputIterator first, InputIterator last) |
| { |
| _path.assign(first, last); |
| postprocess_path_with_format(_path, native_format); |
| return *this; |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.3 appends |
| |
| GHC_INLINE path& path::operator/=(const path& p) |
| { |
| if (p.empty()) { |
| // was: if ((!has_root_directory() && is_absolute()) || has_filename()) |
| if (!_path.empty() && _path[_path.length() - 1] != '/' && _path[_path.length() - 1] != ':') { |
| _path += '/'; |
| } |
| return *this; |
| } |
| if ((p.is_absolute() && (_path != root_name() || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) { |
| assign(p); |
| return *this; |
| } |
| if (p.has_root_directory()) { |
| assign(root_name()); |
| } |
| else if ((!has_root_directory() && is_absolute()) || has_filename()) { |
| _path += '/'; |
| } |
| auto iter = p.begin(); |
| bool first = true; |
| if (p.has_root_name()) { |
| ++iter; |
| } |
| while (iter != p.end()) { |
| if (!first && !(!_path.empty() && _path[_path.length() - 1] == '/')) { |
| _path += '/'; |
| } |
| first = false; |
| _path += (*iter++).generic_string(); |
| } |
| return *this; |
| } |
| |
| GHC_INLINE void path::append_name(const char* name) |
| { |
| if (_path.empty()) { |
| this->operator/=(path(name)); |
| } |
| else { |
| if (_path.back() != path::generic_separator) { |
| _path.push_back(path::generic_separator); |
| } |
| _path += name; |
| } |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| template <class Source> |
| inline path& path::operator/=(const Source& source) |
| { |
| return append(source); |
| } |
| |
| template <class Source> |
| inline path& path::append(const Source& source) |
| { |
| return this->operator/=(path(detail::toUtf8(source))); |
| } |
| |
| template <> |
| inline path& path::append<path>(const path& p) |
| { |
| return this->operator/=(p); |
| } |
| |
| template <class InputIterator> |
| inline path& path::append(InputIterator first, InputIterator last) |
| { |
| std::basic_string<typename std::iterator_traits<InputIterator>::value_type> part(first, last); |
| return append(part); |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.4 concatenation |
| |
| GHC_INLINE path& path::operator+=(const path& x) |
| { |
| return concat(x._path); |
| } |
| |
| GHC_INLINE path& path::operator+=(const string_type& x) |
| { |
| return concat(x); |
| } |
| |
| #ifdef __cpp_lib_string_view |
| GHC_INLINE path& path::operator+=(std::basic_string_view<value_type> x) |
| { |
| return concat(x); |
| } |
| #endif |
| |
| GHC_INLINE path& path::operator+=(const value_type* x) |
| { |
| return concat(string_type(x)); |
| } |
| |
| GHC_INLINE path& path::operator+=(value_type x) |
| { |
| #ifdef GHC_OS_WINDOWS |
| if (x == '\\') { |
| x = generic_separator; |
| } |
| #endif |
| if (_path.empty() || _path.back() != generic_separator) { |
| #ifdef GHC_USE_WCHAR_T |
| _path += detail::toUtf8(string_type(1, x)); |
| #else |
| _path += x; |
| #endif |
| } |
| return *this; |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| template <class Source> |
| inline path::path_from_string<Source>& path::operator+=(const Source& x) |
| { |
| return concat(x); |
| } |
| |
| template <class EcharT> |
| inline path::path_type_EcharT<EcharT>& path::operator+=(EcharT x) |
| { |
| std::basic_string<EcharT> part(1, x); |
| concat(detail::toUtf8(part)); |
| return *this; |
| } |
| |
| template <class Source> |
| inline path& path::concat(const Source& x) |
| { |
| path p(x); |
| postprocess_path_with_format(p._path, native_format); |
| _path += p._path; |
| return *this; |
| } |
| template <class InputIterator> |
| inline path& path::concat(InputIterator first, InputIterator last) |
| { |
| _path.append(first, last); |
| postprocess_path_with_format(_path, native_format); |
| return *this; |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.5 modifiers |
| GHC_INLINE void path::clear() noexcept |
| { |
| _path.clear(); |
| } |
| |
| GHC_INLINE path& path::make_preferred() |
| { |
| // as this filesystem implementation only uses generic_format |
| // internally, this must be a no-op |
| return *this; |
| } |
| |
| GHC_INLINE path& path::remove_filename() |
| { |
| if (has_filename()) { |
| _path.erase(_path.size() - filename()._path.size()); |
| } |
| return *this; |
| } |
| |
| GHC_INLINE path& path::replace_filename(const path& replacement) |
| { |
| remove_filename(); |
| return append(replacement); |
| } |
| |
| GHC_INLINE path& path::replace_extension(const path& replacement) |
| { |
| if (has_extension()) { |
| _path.erase(_path.size() - extension()._path.size()); |
| } |
| if (!replacement.empty() && replacement._path[0] != '.') { |
| _path += '.'; |
| } |
| return concat(replacement); |
| } |
| |
| GHC_INLINE void path::swap(path& rhs) noexcept |
| { |
| _path.swap(rhs._path); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.6, native format observers |
| #ifdef GHC_OS_WINDOWS |
| GHC_INLINE path::impl_string_type path::native_impl() const |
| { |
| impl_string_type result; |
| if (is_absolute() && _path.length() > MAX_PATH - 10) { |
| // expand long Windows filenames with marker |
| if (has_root_name() && _path[0] == '/') { |
| result = "\\\\?\\UNC" + _path.substr(1); |
| } |
| else { |
| result = "\\\\?\\" + _path; |
| } |
| } |
| else { |
| result = _path; |
| } |
| /*if (has_root_name() && root_name()._path[0] == '/') { |
| return _path; |
| }*/ |
| for (auto& c : result) { |
| if (c == '/') { |
| c = '\\'; |
| } |
| } |
| return result; |
| } |
| #else |
| GHC_INLINE const path::impl_string_type& path::native_impl() const |
| { |
| return _path; |
| } |
| #endif |
| |
| GHC_INLINE const path::string_type& path::native() const |
| { |
| #ifdef GHC_OS_WINDOWS |
| #ifdef GHC_USE_WCHAR_T |
| _native_cache = detail::fromUtf8<string_type>(native_impl()); |
| #else |
| _native_cache = native_impl(); |
| #endif |
| return _native_cache; |
| #else |
| return _path; |
| #endif |
| } |
| |
| GHC_INLINE const path::value_type* path::c_str() const |
| { |
| return native().c_str(); |
| } |
| |
| GHC_INLINE path::operator path::string_type() const |
| { |
| return native(); |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| template <class EcharT, class traits, class Allocator> |
| inline std::basic_string<EcharT, traits, Allocator> path::string(const Allocator& a) const |
| { |
| return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(native_impl(), a); |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| GHC_INLINE std::string path::string() const |
| { |
| return native_impl(); |
| } |
| |
| GHC_INLINE std::wstring path::wstring() const |
| { |
| #ifdef GHC_USE_WCHAR_T |
| return native(); |
| #else |
| return detail::fromUtf8<std::wstring>(native()); |
| #endif |
| } |
| |
| GHC_INLINE std::string path::u8string() const |
| { |
| return native_impl(); |
| } |
| |
| GHC_INLINE std::u16string path::u16string() const |
| { |
| return detail::fromUtf8<std::u16string>(native_impl()); |
| } |
| |
| GHC_INLINE std::u32string path::u32string() const |
| { |
| return detail::fromUtf8<std::u32string>(native_impl()); |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.7, generic format observers |
| template <class EcharT, class traits, class Allocator> |
| inline std::basic_string<EcharT, traits, Allocator> path::generic_string(const Allocator& a) const |
| { |
| return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(_path, a); |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| GHC_INLINE const std::string& path::generic_string() const |
| { |
| return _path; |
| } |
| |
| GHC_INLINE std::wstring path::generic_wstring() const |
| { |
| return detail::fromUtf8<std::wstring>(_path); |
| } |
| |
| GHC_INLINE std::string path::generic_u8string() const |
| { |
| return _path; |
| } |
| |
| GHC_INLINE std::u16string path::generic_u16string() const |
| { |
| return detail::fromUtf8<std::u16string>(_path); |
| } |
| |
| GHC_INLINE std::u32string path::generic_u32string() const |
| { |
| return detail::fromUtf8<std::u32string>(_path); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.8, compare |
| GHC_INLINE int path::compare(const path& p) const noexcept |
| { |
| return native().compare(p.native()); |
| } |
| |
| GHC_INLINE int path::compare(const string_type& s) const |
| { |
| return native().compare(path(s).native()); |
| } |
| |
| #ifdef __cpp_lib_string_view |
| GHC_INLINE int path::compare(std::basic_string_view<value_type> s) const |
| { |
| return native().compare(path(s).native()); |
| } |
| #endif |
| |
| GHC_INLINE int path::compare(const value_type* s) const |
| { |
| return native().compare(path(s).native()); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.9, decomposition |
| GHC_INLINE path path::root_name() const |
| { |
| #ifdef GHC_OS_WINDOWS |
| if (_path.length() >= 2 && std::toupper(static_cast<unsigned char>(_path[0])) >= 'A' && std::toupper(static_cast<unsigned char>(_path[0])) <= 'Z' && _path[1] == ':') { |
| return path(_path.substr(0, 2)); |
| } |
| #endif |
| if (_path.length() > 2 && _path[0] == '/' && _path[1] == '/' && _path[2] != '/' && std::isprint(_path[2])) { |
| impl_string_type::size_type pos = _path.find_first_of("/\\", 3); |
| if (pos == impl_string_type::npos) { |
| return path(_path); |
| } |
| else { |
| return path(_path.substr(0, pos)); |
| } |
| } |
| return path(); |
| } |
| |
| GHC_INLINE path path::root_directory() const |
| { |
| path root = root_name(); |
| if (_path.length() > root._path.length() && _path[root._path.length()] == '/') { |
| return path("/"); |
| } |
| return path(); |
| } |
| |
| GHC_INLINE path path::root_path() const |
| { |
| return root_name().generic_string() + root_directory().generic_string(); |
| } |
| |
| GHC_INLINE path path::relative_path() const |
| { |
| std::string root = root_path()._path; |
| return path(_path.substr((std::min)(root.length(), _path.length())), generic_format); |
| } |
| |
| GHC_INLINE path path::parent_path() const |
| { |
| if (has_relative_path()) { |
| if (empty() || begin() == --end()) { |
| return path(); |
| } |
| else { |
| path pp; |
| for (string_type s : input_iterator_range<iterator>(begin(), --end())) { |
| if (s == "/") { |
| // don't use append to join a path- |
| pp += s; |
| } |
| else { |
| pp /= s; |
| } |
| } |
| return pp; |
| } |
| } |
| else { |
| return *this; |
| } |
| } |
| |
| GHC_INLINE path path::filename() const |
| { |
| return relative_path().empty() ? path() : path(*--end()); |
| } |
| |
| GHC_INLINE path path::stem() const |
| { |
| impl_string_type fn = filename().string(); |
| if (fn != "." && fn != "..") { |
| impl_string_type::size_type n = fn.rfind('.'); |
| if (n != impl_string_type::npos && n != 0) { |
| return path{fn.substr(0, n)}; |
| } |
| } |
| return path{fn}; |
| } |
| |
| GHC_INLINE path path::extension() const |
| { |
| impl_string_type fn = filename().string(); |
| impl_string_type::size_type pos = fn.find_last_of('.'); |
| if (pos == std::string::npos || pos == 0) { |
| return ""; |
| } |
| return fn.substr(pos); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.10, query |
| GHC_INLINE bool path::empty() const noexcept |
| { |
| return _path.empty(); |
| } |
| |
| GHC_INLINE bool path::has_root_name() const |
| { |
| return !root_name().empty(); |
| } |
| |
| GHC_INLINE bool path::has_root_directory() const |
| { |
| return !root_directory().empty(); |
| } |
| |
| GHC_INLINE bool path::has_root_path() const |
| { |
| return !root_path().empty(); |
| } |
| |
| GHC_INLINE bool path::has_relative_path() const |
| { |
| return !relative_path().empty(); |
| } |
| |
| GHC_INLINE bool path::has_parent_path() const |
| { |
| return !parent_path().empty(); |
| } |
| |
| GHC_INLINE bool path::has_filename() const |
| { |
| return !filename().empty(); |
| } |
| |
| GHC_INLINE bool path::has_stem() const |
| { |
| return !stem().empty(); |
| } |
| |
| GHC_INLINE bool path::has_extension() const |
| { |
| return !extension().empty(); |
| } |
| |
| GHC_INLINE bool path::is_absolute() const |
| { |
| #ifdef GHC_OS_WINDOWS |
| return has_root_name() && has_root_directory(); |
| #else |
| return has_root_directory(); |
| #endif |
| } |
| |
| GHC_INLINE bool path::is_relative() const |
| { |
| return !is_absolute(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.4.11, generation |
| GHC_INLINE path path::lexically_normal() const |
| { |
| path dest; |
| bool lastDotDot = false; |
| for (string_type s : *this) { |
| if (s == ".") { |
| dest /= ""; |
| continue; |
| } |
| else if (s == ".." && !dest.empty()) { |
| auto root = root_path(); |
| if (dest == root) { |
| continue; |
| } |
| else if (*(--dest.end()) != "..") { |
| if (dest._path.back() == generic_separator) { |
| dest._path.pop_back(); |
| } |
| dest.remove_filename(); |
| continue; |
| } |
| } |
| if (!(s.empty() && lastDotDot)) { |
| dest /= s; |
| } |
| lastDotDot = s == ".."; |
| } |
| if (dest.empty()) { |
| dest = "."; |
| } |
| return dest; |
| } |
| |
| GHC_INLINE path path::lexically_relative(const path& base) const |
| { |
| if (root_name() != base.root_name() || is_absolute() != base.is_absolute() || (!has_root_directory() && base.has_root_directory())) { |
| return path(); |
| } |
| const_iterator a = begin(), b = base.begin(); |
| while (a != end() && b != base.end() && *a == *b) { |
| ++a; |
| ++b; |
| } |
| if (a == end() && b == base.end()) { |
| return path("."); |
| } |
| int count = 0; |
| for (const auto& element : input_iterator_range<const_iterator>(b, base.end())) { |
| if (element != "." && element != "" && element != "..") { |
| ++count; |
| } |
| else if (element == "..") { |
| --count; |
| } |
| } |
| if (count < 0) { |
| return path(); |
| } |
| path result; |
| for (int i = 0; i < count; ++i) { |
| result /= ".."; |
| } |
| for (const auto& element : input_iterator_range<const_iterator>(a, end())) { |
| result /= element; |
| } |
| return result; |
| } |
| |
| GHC_INLINE path path::lexically_proximate(const path& base) const |
| { |
| path result = lexically_relative(base); |
| return result.empty() ? *this : result; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.5, iterators |
| GHC_INLINE path::iterator::iterator() {} |
| |
| GHC_INLINE path::iterator::iterator(const path::impl_string_type::const_iterator& first, const path::impl_string_type::const_iterator& last, const path::impl_string_type::const_iterator& pos) |
| : _first(first) |
| , _last(last) |
| , _iter(pos) |
| { |
| updateCurrent(); |
| // find the position of a potential root directory slash |
| #ifdef GHC_OS_WINDOWS |
| if (_last - _first >= 3 && std::toupper(static_cast<unsigned char>(*first)) >= 'A' && std::toupper(static_cast<unsigned char>(*first)) <= 'Z' && *(first + 1) == ':' && *(first + 2) == '/') { |
| _root = _first + 2; |
| } |
| else |
| #endif |
| { |
| if (_first != _last && *_first == '/') { |
| if (_last - _first >= 2 && *(_first + 1) == '/' && !(_last - _first >= 3 && *(_first + 2) == '/')) { |
| _root = increment(_first); |
| } |
| else { |
| _root = _first; |
| } |
| } |
| else { |
| _root = _last; |
| } |
| } |
| } |
| |
| GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const |
| { |
| path::impl_string_type::const_iterator i = pos; |
| bool fromStart = i == _first; |
| if (i != _last) { |
| // we can only sit on a slash if it is a network name or a root |
| if (*i++ == '/') { |
| if (i != _last && *i == '/') { |
| if (fromStart && !(i + 1 != _last && *(i + 1) == '/')) { |
| // leadind double slashes detected, treat this and the |
| // following until a slash as one unit |
| i = std::find(++i, _last, '/'); |
| } |
| else { |
| // skip redundant slashes |
| while (i != _last && *i == '/') { |
| ++i; |
| } |
| } |
| } |
| } |
| else { |
| if (fromStart && i != _last && *i == ':') { |
| ++i; |
| } |
| else { |
| i = std::find(i, _last, '/'); |
| } |
| } |
| } |
| return i; |
| } |
| |
| GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const |
| { |
| path::impl_string_type::const_iterator i = pos; |
| if (i != _first) { |
| --i; |
| // if this is now the root slash or the trailing slash, we are done, |
| // else check for network name |
| if (i != _root && (pos != _last || *i != '/')) { |
| #ifdef GHC_OS_WINDOWS |
| static const std::string seps = "/:"; |
| i = std::find_first_of(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), seps.begin(), seps.end()).base(); |
| if (i > _first && *i == ':') { |
| i++; |
| } |
| #else |
| i = std::find(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), '/').base(); |
| #endif |
| // Now we have to check if this is a network name |
| if (i - _first == 2 && *_first == '/' && *(_first + 1) == '/') { |
| i -= 2; |
| } |
| } |
| } |
| return i; |
| } |
| |
| GHC_INLINE void path::iterator::updateCurrent() |
| { |
| if (_iter != _first && _iter != _last && (*_iter == '/' && _iter != _root) && (_iter + 1 == _last)) { |
| _current = ""; |
| } |
| else { |
| _current.assign(_iter, increment(_iter)); |
| if (_current.generic_string().size() > 1 && _current.generic_string()[0] == '/' && _current.generic_string()[_current.generic_string().size() - 1] == '/') { |
| // shrink successive slashes to one |
| _current = "/"; |
| } |
| } |
| } |
| |
| GHC_INLINE path::iterator& path::iterator::operator++() |
| { |
| _iter = increment(_iter); |
| while (_iter != _last && // we didn't reach the end |
| _iter != _root && // this is not a root position |
| *_iter == '/' && // we are on a slash |
| (_iter + 1) != _last // the slash is not the last char |
| ) { |
| ++_iter; |
| } |
| updateCurrent(); |
| return *this; |
| } |
| |
| GHC_INLINE path::iterator path::iterator::operator++(int) |
| { |
| path::iterator i{*this}; |
| ++(*this); |
| return i; |
| } |
| |
| GHC_INLINE path::iterator& path::iterator::operator--() |
| { |
| _iter = decrement(_iter); |
| updateCurrent(); |
| return *this; |
| } |
| |
| GHC_INLINE path::iterator path::iterator::operator--(int) |
| { |
| auto i = *this; |
| --(*this); |
| return i; |
| } |
| |
| GHC_INLINE bool path::iterator::operator==(const path::iterator& other) const |
| { |
| return _iter == other._iter; |
| } |
| |
| GHC_INLINE bool path::iterator::operator!=(const path::iterator& other) const |
| { |
| return _iter != other._iter; |
| } |
| |
| GHC_INLINE path::iterator::reference path::iterator::operator*() const |
| { |
| return _current; |
| } |
| |
| GHC_INLINE path::iterator::pointer path::iterator::operator->() const |
| { |
| return &_current; |
| } |
| |
| GHC_INLINE path::iterator path::begin() const |
| { |
| return iterator(_path.begin(), _path.end(), _path.begin()); |
| } |
| |
| GHC_INLINE path::iterator path::end() const |
| { |
| return iterator(_path.begin(), _path.end(), _path.end()); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.6, path non-member functions |
| GHC_INLINE void swap(path& lhs, path& rhs) noexcept |
| { |
| swap(lhs._path, rhs._path); |
| } |
| |
| GHC_INLINE size_t hash_value(const path& p) noexcept |
| { |
| return std::hash<std::string>()(p.generic_string()); |
| } |
| |
| GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept |
| { |
| return lhs.generic_string() == rhs.generic_string(); |
| } |
| |
| GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept |
| { |
| return lhs.generic_string() != rhs.generic_string(); |
| } |
| |
| GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept |
| { |
| return lhs.generic_string() < rhs.generic_string(); |
| } |
| |
| GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept |
| { |
| return lhs.generic_string() <= rhs.generic_string(); |
| } |
| |
| GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept |
| { |
| return lhs.generic_string() > rhs.generic_string(); |
| } |
| |
| GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept |
| { |
| return lhs.generic_string() >= rhs.generic_string(); |
| } |
| |
| GHC_INLINE path operator/(const path& lhs, const path& rhs) |
| { |
| path result(lhs); |
| result /= rhs; |
| return result; |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.8.6.1 path inserter and extractor |
| template <class charT, class traits> |
| inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const path& p) |
| { |
| os << "\""; |
| auto ps = p.string<charT, traits>(); |
| for (auto c : ps) { |
| if (c == '"' || c == '\\') { |
| os << '\\'; |
| } |
| os << c; |
| } |
| os << "\""; |
| return os; |
| } |
| |
| template <class charT, class traits> |
| inline std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, path& p) |
| { |
| std::basic_string<charT, traits> tmp; |
| charT c; |
| is >> c; |
| if (c == '"') { |
| auto sf = is.flags(); |
| is >> std::noskipws; |
| while (is) { |
| auto c2 = is.get(); |
| if (is) { |
| if (c2 == '\\') { |
| c2 = is.get(); |
| if (is) { |
| tmp += static_cast<charT>(c2); |
| } |
| } |
| else if (c2 == '"') { |
| break; |
| } |
| else { |
| tmp += static_cast<charT>(c2); |
| } |
| } |
| } |
| if ((sf & std::ios_base::skipws) == std::ios_base::skipws) { |
| is >> std::skipws; |
| } |
| p = path(tmp); |
| } |
| else { |
| is >> tmp; |
| p = path(static_cast<charT>(c) + tmp); |
| } |
| return is; |
| } |
| |
| #ifdef GHC_EXPAND_IMPL |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.9 Class filesystem_error |
| GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, std::error_code ec) |
| : std::system_error(ec, what_arg) |
| , _what_arg(what_arg) |
| , _ec(ec) |
| { |
| } |
| |
| GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec) |
| : std::system_error(ec, what_arg) |
| , _what_arg(what_arg) |
| , _ec(ec) |
| , _p1(p1) |
| { |
| if (!_p1.empty()) { |
| _what_arg += ": '" + _p1.u8string() + "'"; |
| } |
| } |
| |
| GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec) |
| : std::system_error(ec, what_arg) |
| , _what_arg(what_arg) |
| , _ec(ec) |
| , _p1(p1) |
| , _p2(p2) |
| { |
| if (!_p1.empty()) { |
| _what_arg += ": '" + _p1.u8string() + "'"; |
| } |
| if (!_p2.empty()) { |
| _what_arg += ", '" + _p2.u8string() + "'"; |
| } |
| } |
| |
| GHC_INLINE const path& filesystem_error::path1() const noexcept |
| { |
| return _p1; |
| } |
| |
| GHC_INLINE const path& filesystem_error::path2() const noexcept |
| { |
| return _p2; |
| } |
| |
| GHC_INLINE const char* filesystem_error::what() const noexcept |
| { |
| return _what_arg.c_str(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.15, filesystem operations |
| GHC_INLINE path absolute(const path& p) |
| { |
| std::error_code ec; |
| path result = absolute(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE path absolute(const path& p, std::error_code& ec) |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| if (p.empty()) { |
| return absolute(current_path(ec), ec) / ""; |
| } |
| ULONG size = ::GetFullPathNameW(p.wstring().c_str(), 0, 0, 0); |
| if (size) { |
| std::vector<wchar_t> buf(size, 0); |
| ULONG s2 = GetFullPathNameW(p.wstring().c_str(), size, buf.data(), nullptr); |
| if (s2 && s2 < size) { |
| path result = path(std::wstring(buf.data(), s2)); |
| if (p.filename() == ".") { |
| result /= "."; |
| } |
| return result; |
| } |
| } |
| ec = detail::make_system_error(); |
| return path(); |
| #else |
| path base = current_path(ec); |
| if (!ec) { |
| if (p.empty()) { |
| return base / p; |
| } |
| if (p.has_root_name()) { |
| if (p.has_root_directory()) { |
| return p; |
| } |
| else { |
| return p.root_name() / base.root_directory() / base.relative_path() / p.relative_path(); |
| } |
| } |
| else { |
| if (p.has_root_directory()) { |
| return base.root_name() / p; |
| } |
| else { |
| return base / p; |
| } |
| } |
| } |
| ec = detail::make_system_error(); |
| return path(); |
| #endif |
| } |
| |
| GHC_INLINE path canonical(const path& p) |
| { |
| std::error_code ec; |
| auto result = canonical(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE path canonical(const path& p, std::error_code& ec) |
| { |
| if (p.empty()) { |
| ec = detail::make_error_code(detail::portable_error::not_found); |
| return path(); |
| } |
| path work = p.is_absolute() ? p : absolute(p, ec); |
| path root = work.root_path(); |
| path result; |
| |
| auto fs = status(work, ec); |
| if (ec) { |
| return path(); |
| } |
| if (fs.type() == file_type::not_found) { |
| ec = detail::make_error_code(detail::portable_error::not_found); |
| return path(); |
| } |
| bool redo; |
| do { |
| redo = false; |
| result.clear(); |
| for (auto pe : work) { |
| if (pe.empty() || pe == ".") { |
| continue; |
| } |
| else if (pe == "..") { |
| result = result.parent_path(); |
| continue; |
| } |
| else if ((result / pe).string().length() <= root.string().length()) { |
| result /= pe; |
| continue; |
| } |
| auto sls = symlink_status(result / pe, ec); |
| if (ec) { |
| return path(); |
| } |
| if (is_symlink(sls)) { |
| redo = true; |
| auto target = read_symlink(result / pe, ec); |
| if (ec) { |
| return path(); |
| } |
| if (target.is_absolute()) { |
| result = target; |
| continue; |
| } |
| else { |
| result /= target; |
| continue; |
| } |
| } |
| else { |
| result /= pe; |
| } |
| } |
| work = result; |
| } while (redo); |
| ec.clear(); |
| return result; |
| } |
| |
| GHC_INLINE void copy(const path& from, const path& to) |
| { |
| copy(from, to, copy_options::none); |
| } |
| |
| GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept |
| { |
| copy(from, to, copy_options::none, ec); |
| } |
| |
| GHC_INLINE void copy(const path& from, const path& to, copy_options options) |
| { |
| std::error_code ec; |
| copy(from, to, options, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); |
| } |
| } |
| |
| GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept |
| { |
| std::error_code tec; |
| file_status fs_from, fs_to; |
| ec.clear(); |
| if ((options & (copy_options::skip_symlinks | copy_options::copy_symlinks | copy_options::create_symlinks)) != copy_options::none) { |
| fs_from = symlink_status(from, ec); |
| } |
| else { |
| fs_from = status(from, ec); |
| } |
| if (!exists(fs_from)) { |
| if (!ec) { |
| ec = detail::make_error_code(detail::portable_error::not_found); |
| } |
| return; |
| } |
| if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none) { |
| fs_to = symlink_status(to, tec); |
| } |
| else { |
| fs_to = status(to, tec); |
| } |
| if (is_other(fs_from) || is_other(fs_to) || (is_directory(fs_from) && is_regular_file(fs_to)) || (exists(fs_to) && equivalent(from, to, ec))) { |
| ec = detail::make_error_code(detail::portable_error::invalid_argument); |
| } |
| else if (is_symlink(fs_from)) { |
| if ((options & copy_options::skip_symlinks) == copy_options::none) { |
| if (!exists(fs_to) && (options & copy_options::copy_symlinks) != copy_options::none) { |
| copy_symlink(from, to, ec); |
| } |
| else { |
| ec = detail::make_error_code(detail::portable_error::invalid_argument); |
| } |
| } |
| } |
| else if (is_regular_file(fs_from)) { |
| if ((options & copy_options::directories_only) == copy_options::none) { |
| if ((options & copy_options::create_symlinks) != copy_options::none) { |
| create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec); |
| } |
| else if ((options & copy_options::create_hard_links) != copy_options::none) { |
| create_hard_link(from, to, ec); |
| } |
| else if (is_directory(fs_to)) { |
| copy_file(from, to / from.filename(), options, ec); |
| } |
| else { |
| copy_file(from, to, options, ec); |
| } |
| } |
| } |
| #ifdef LWG_2682_BEHAVIOUR |
| else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { |
| ec = detail::make_error_code(detail::portable_error::is_a_directory); |
| } |
| #endif |
| else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) { |
| if (!exists(fs_to)) { |
| create_directory(to, from, ec); |
| if (ec) { |
| return; |
| } |
| } |
| for (auto iter = directory_iterator(from, ec); iter != directory_iterator(); iter.increment(ec)) { |
| if (!ec) { |
| copy(iter->path(), to / iter->path().filename(), options | static_cast<copy_options>(0x8000), ec); |
| } |
| if (ec) { |
| return; |
| } |
| } |
| } |
| return; |
| } |
| |
| GHC_INLINE bool copy_file(const path& from, const path& to) |
| { |
| return copy_file(from, to, copy_options::none); |
| } |
| |
| GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept |
| { |
| return copy_file(from, to, copy_options::none, ec); |
| } |
| |
| GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option) |
| { |
| std::error_code ec; |
| auto result = copy_file(from, to, option, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept |
| { |
| std::error_code tecf, tect; |
| auto sf = status(from, tecf); |
| auto st = status(to, tect); |
| bool overwrite = false; |
| ec.clear(); |
| if (!is_regular_file(sf)) { |
| ec = tecf; |
| return false; |
| } |
| if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) { |
| ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); |
| return false; |
| } |
| if (exists(st)) { |
| if ((options & copy_options::update_existing) == copy_options::update_existing) { |
| auto from_time = last_write_time(from, ec); |
| if (ec) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| auto to_time = last_write_time(to, ec); |
| if (ec) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| if (from_time <= to_time) { |
| return false; |
| } |
| } |
| overwrite = true; |
| } |
| #ifdef GHC_OS_WINDOWS |
| if (!::CopyFileW(detail::fromUtf8<std::wstring>(from.u8string()).c_str(), detail::fromUtf8<std::wstring>(to.u8string()).c_str(), !overwrite)) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| return true; |
| #else |
| std::vector<char> buffer(16384, '\0'); |
| int in = -1, out = -1; |
| if ((in = ::open(from.c_str(), O_RDONLY)) < 0) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| std::shared_ptr<void> guard_in(nullptr, [in](void*) { ::close(in); }); |
| int mode = O_CREAT | O_WRONLY | O_TRUNC; |
| if (!overwrite) { |
| mode |= O_EXCL; |
| } |
| if ((out = ::open(to.c_str(), mode, static_cast<int>(sf.permissions() & perms::all))) < 0) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| std::shared_ptr<void> guard_out(nullptr, [out](void*) { ::close(out); }); |
| ssize_t br, bw; |
| while ((br = ::read(in, buffer.data(), buffer.size())) > 0) { |
| ssize_t offset = 0; |
| do { |
| if ((bw = ::write(out, buffer.data() + offset, static_cast<size_t>(br))) > 0) { |
| br -= bw; |
| offset += bw; |
| } |
| else if (bw < 0) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| } while (br); |
| } |
| return true; |
| #endif |
| } |
| |
| GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink) |
| { |
| std::error_code ec; |
| copy_symlink(existing_symlink, new_symlink, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), existing_symlink, new_symlink, ec); |
| } |
| } |
| |
| GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| auto to = read_symlink(existing_symlink, ec); |
| if (!ec) { |
| if (exists(to, ec) && is_directory(to, ec)) { |
| create_directory_symlink(to, new_symlink, ec); |
| } |
| else { |
| create_symlink(to, new_symlink, ec); |
| } |
| } |
| } |
| |
| GHC_INLINE bool create_directories(const path& p) |
| { |
| std::error_code ec; |
| auto result = create_directories(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept |
| { |
| path current; |
| ec.clear(); |
| bool didCreate = false; |
| for (path::string_type part : p) { |
| current /= part; |
| if (current != p.root_name() && current != p.root_path()) { |
| std::error_code tec; |
| auto fs = status(current, tec); |
| if (tec && fs.type() != file_type::not_found) { |
| ec = tec; |
| return false; |
| } |
| if (!exists(fs)) { |
| create_directory(current, ec); |
| if (ec) { |
| std::error_code tmp_ec; |
| if (is_directory(current, tmp_ec)) { |
| ec.clear(); |
| } else { |
| return false; |
| } |
| } |
| didCreate = true; |
| } |
| #ifndef LWG_2935_BEHAVIOUR |
| else if (!is_directory(fs)) { |
| ec = detail::make_error_code(detail::portable_error::exists); |
| return false; |
| } |
| #endif |
| } |
| } |
| return didCreate; |
| } |
| |
| GHC_INLINE bool create_directory(const path& p) |
| { |
| std::error_code ec; |
| auto result = create_directory(p, path(), ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept |
| { |
| return create_directory(p, path(), ec); |
| } |
| |
| GHC_INLINE bool create_directory(const path& p, const path& attributes) |
| { |
| std::error_code ec; |
| auto result = create_directory(p, attributes, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept |
| { |
| std::error_code tec; |
| ec.clear(); |
| auto fs = status(p, tec); |
| #ifdef LWG_2935_BEHAVIOUR |
| if (status_known(fs) && exists(fs)) { |
| return false; |
| } |
| #else |
| if (status_known(fs) && exists(fs) && is_directory(fs)) { |
| return false; |
| } |
| #endif |
| #ifdef GHC_OS_WINDOWS |
| if (!attributes.empty()) { |
| if (!::CreateDirectoryExW(detail::fromUtf8<std::wstring>(attributes.u8string()).c_str(), detail::fromUtf8<std::wstring>(p.u8string()).c_str(), NULL)) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| } |
| else if (!::CreateDirectoryW(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), NULL)) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| #else |
| ::mode_t attribs = static_cast<mode_t>(perms::all); |
| if (!attributes.empty()) { |
| struct ::stat fileStat; |
| if (::stat(attributes.c_str(), &fileStat) != 0) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| attribs = fileStat.st_mode; |
| } |
| if (::mkdir(p.c_str(), attribs) != 0) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| #endif |
| return true; |
| } |
| |
| GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink) |
| { |
| std::error_code ec; |
| create_directory_symlink(to, new_symlink, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); |
| } |
| } |
| |
| GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept |
| { |
| detail::create_symlink(to, new_symlink, true, ec); |
| } |
| |
| GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link) |
| { |
| std::error_code ec; |
| create_hard_link(to, new_hard_link, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), to, new_hard_link, ec); |
| } |
| } |
| |
| GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept |
| { |
| detail::create_hardlink(to, new_hard_link, ec); |
| } |
| |
| GHC_INLINE void create_symlink(const path& to, const path& new_symlink) |
| { |
| std::error_code ec; |
| create_symlink(to, new_symlink, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); |
| } |
| } |
| |
| GHC_INLINE void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept |
| { |
| detail::create_symlink(to, new_symlink, false, ec); |
| } |
| |
| GHC_INLINE path current_path() |
| { |
| std::error_code ec; |
| auto result = current_path(ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE path current_path(std::error_code& ec) |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| DWORD pathlen = ::GetCurrentDirectoryW(0, 0); |
| std::unique_ptr<wchar_t[]> buffer(new wchar_t[size_t(pathlen) + 1]); |
| if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) { |
| ec = detail::make_system_error(); |
| return path(); |
| } |
| return path(std::wstring(buffer.get()), path::native_format); |
| #else |
| size_t pathlen = static_cast<size_t>(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); |
| std::unique_ptr<char[]> buffer(new char[pathlen + 1]); |
| if (::getcwd(buffer.get(), pathlen) == nullptr) { |
| ec = detail::make_system_error(); |
| return path(); |
| } |
| return path(buffer.get()); |
| #endif |
| } |
| |
| GHC_INLINE void current_path(const path& p) |
| { |
| std::error_code ec; |
| current_path(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| } |
| |
| GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| if (!::SetCurrentDirectoryW(detail::fromUtf8<std::wstring>(p.u8string()).c_str())) { |
| ec = detail::make_system_error(); |
| } |
| #else |
| if (::chdir(p.string().c_str()) == -1) { |
| ec = detail::make_system_error(); |
| } |
| #endif |
| } |
| |
| GHC_INLINE bool exists(file_status s) noexcept |
| { |
| return status_known(s) && s.type() != file_type::not_found; |
| } |
| |
| GHC_INLINE bool exists(const path& p) |
| { |
| return exists(status(p)); |
| } |
| |
| GHC_INLINE bool exists(const path& p, std::error_code& ec) noexcept |
| { |
| file_status s = status(p, ec); |
| if (status_known(s)) { |
| ec.clear(); |
| } |
| return exists(s); |
| } |
| |
| GHC_INLINE bool equivalent(const path& p1, const path& p2) |
| { |
| std::error_code ec; |
| bool result = equivalent(p1, p2, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p1, p2, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| std::shared_ptr<void> file1(::CreateFileW(p1.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); |
| auto e1 = ::GetLastError(); |
| std::shared_ptr<void> file2(::CreateFileW(p2.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); |
| if (file1.get() == INVALID_HANDLE_VALUE || file2.get() == INVALID_HANDLE_VALUE) { |
| #ifdef LWG_2937_BEHAVIOUR |
| ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); |
| #else |
| if (file1 == file2) { |
| ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); |
| } |
| #endif |
| return false; |
| } |
| BY_HANDLE_FILE_INFORMATION inf1, inf2; |
| if (!::GetFileInformationByHandle(file1.get(), &inf1)) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| if (!::GetFileInformationByHandle(file2.get(), &inf2)) { |
| ec = detail::make_system_error(); |
| return false; |
| } |
| return inf1.ftLastWriteTime.dwLowDateTime == inf2.ftLastWriteTime.dwLowDateTime && inf1.ftLastWriteTime.dwHighDateTime == inf2.ftLastWriteTime.dwHighDateTime && inf1.nFileIndexHigh == inf2.nFileIndexHigh && inf1.nFileIndexLow == inf2.nFileIndexLow && |
| inf1.nFileSizeHigh == inf2.nFileSizeHigh && inf1.nFileSizeLow == inf2.nFileSizeLow && inf1.dwVolumeSerialNumber == inf2.dwVolumeSerialNumber; |
| #else |
| struct ::stat s1, s2; |
| auto rc1 = ::stat(p1.c_str(), &s1); |
| auto e1 = errno; |
| auto rc2 = ::stat(p2.c_str(), &s2); |
| if (rc1 || rc2) { |
| #ifdef LWG_2937_BEHAVIOUR |
| ec = detail::make_system_error(e1 ? e1 : errno); |
| #else |
| if (rc1 && rc2) { |
| ec = detail::make_system_error(e1 ? e1 : errno); |
| } |
| #endif |
| return false; |
| } |
| return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime; |
| #endif |
| } |
| |
| GHC_INLINE uintmax_t file_size(const path& p) |
| { |
| std::error_code ec; |
| auto result = file_size(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| WIN32_FILE_ATTRIBUTE_DATA attr; |
| if (!GetFileAttributesExW(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), GetFileExInfoStandard, &attr)) { |
| ec = detail::make_system_error(); |
| return static_cast<uintmax_t>(-1); |
| } |
| return static_cast<uintmax_t>(attr.nFileSizeHigh) << (sizeof(attr.nFileSizeHigh) * 8) | attr.nFileSizeLow; |
| #else |
| struct ::stat fileStat; |
| if (::stat(p.c_str(), &fileStat) == -1) { |
| ec = detail::make_system_error(); |
| return static_cast<uintmax_t>(-1); |
| } |
| return static_cast<uintmax_t>(fileStat.st_size); |
| #endif |
| } |
| |
| GHC_INLINE uintmax_t hard_link_count(const path& p) |
| { |
| std::error_code ec; |
| auto result = hard_link_count(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| uintmax_t result = static_cast<uintmax_t>(-1); |
| std::shared_ptr<void> file(::CreateFileW(p.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); |
| BY_HANDLE_FILE_INFORMATION inf; |
| if (file.get() == INVALID_HANDLE_VALUE) { |
| ec = detail::make_system_error(); |
| } |
| else { |
| if (!::GetFileInformationByHandle(file.get(), &inf)) { |
| ec = detail::make_system_error(); |
| } |
| else { |
| result = inf.nNumberOfLinks; |
| } |
| } |
| return result; |
| #else |
| uintmax_t result = 0; |
| file_status fs = detail::status_ex(p, ec, nullptr, nullptr, &result, nullptr); |
| if (fs.type() == file_type::not_found) { |
| ec = detail::make_error_code(detail::portable_error::not_found); |
| } |
| return ec ? static_cast<uintmax_t>(-1) : result; |
| #endif |
| } |
| |
| GHC_INLINE bool is_block_file(file_status s) noexcept |
| { |
| return s.type() == file_type::block; |
| } |
| |
| GHC_INLINE bool is_block_file(const path& p) |
| { |
| return is_block_file(status(p)); |
| } |
| |
| GHC_INLINE bool is_block_file(const path& p, std::error_code& ec) noexcept |
| { |
| return is_block_file(status(p, ec)); |
| } |
| |
| GHC_INLINE bool is_character_file(file_status s) noexcept |
| { |
| return s.type() == file_type::character; |
| } |
| |
| GHC_INLINE bool is_character_file(const path& p) |
| { |
| return is_character_file(status(p)); |
| } |
| |
| GHC_INLINE bool is_character_file(const path& p, std::error_code& ec) noexcept |
| { |
| return is_character_file(status(p, ec)); |
| } |
| |
| GHC_INLINE bool is_directory(file_status s) noexcept |
| { |
| return s.type() == file_type::directory; |
| } |
| |
| GHC_INLINE bool is_directory(const path& p) |
| { |
| return is_directory(status(p)); |
| } |
| |
| GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept |
| { |
| return is_directory(status(p, ec)); |
| } |
| |
| GHC_INLINE bool is_empty(const path& p) |
| { |
| if (is_directory(p)) { |
| return directory_iterator(p) == directory_iterator(); |
| } |
| else { |
| return file_size(p) == 0; |
| } |
| } |
| |
| GHC_INLINE bool is_empty(const path& p, std::error_code& ec) noexcept |
| { |
| auto fs = status(p, ec); |
| if (ec) { |
| return false; |
| } |
| if (is_directory(fs)) { |
| directory_iterator iter(p, ec); |
| if (ec) { |
| return false; |
| } |
| return iter == directory_iterator(); |
| } |
| else { |
| auto sz = file_size(p, ec); |
| if (ec) { |
| return false; |
| } |
| return sz == 0; |
| } |
| } |
| |
| GHC_INLINE bool is_fifo(file_status s) noexcept |
| { |
| return s.type() == file_type::fifo; |
| } |
| |
| GHC_INLINE bool is_fifo(const path& p) |
| { |
| return is_fifo(status(p)); |
| } |
| |
| GHC_INLINE bool is_fifo(const path& p, std::error_code& ec) noexcept |
| { |
| return is_fifo(status(p, ec)); |
| } |
| |
| GHC_INLINE bool is_other(file_status s) noexcept |
| { |
| return exists(s) && !is_regular_file(s) && !is_directory(s) && !is_symlink(s); |
| } |
| |
| GHC_INLINE bool is_other(const path& p) |
| { |
| return is_other(status(p)); |
| } |
| |
| GHC_INLINE bool is_other(const path& p, std::error_code& ec) noexcept |
| { |
| return is_other(status(p, ec)); |
| } |
| |
| GHC_INLINE bool is_regular_file(file_status s) noexcept |
| { |
| return s.type() == file_type::regular; |
| } |
| |
| GHC_INLINE bool is_regular_file(const path& p) |
| { |
| return is_regular_file(status(p)); |
| } |
| |
| GHC_INLINE bool is_regular_file(const path& p, std::error_code& ec) noexcept |
| { |
| return is_regular_file(status(p, ec)); |
| } |
| |
| GHC_INLINE bool is_socket(file_status s) noexcept |
| { |
| return s.type() == file_type::socket; |
| } |
| |
| GHC_INLINE bool is_socket(const path& p) |
| { |
| return is_socket(status(p)); |
| } |
| |
| GHC_INLINE bool is_socket(const path& p, std::error_code& ec) noexcept |
| { |
| return is_socket(status(p, ec)); |
| } |
| |
| GHC_INLINE bool is_symlink(file_status s) noexcept |
| { |
| return s.type() == file_type::symlink; |
| } |
| |
| GHC_INLINE bool is_symlink(const path& p) |
| { |
| return is_symlink(symlink_status(p)); |
| } |
| |
| GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept |
| { |
| return is_symlink(symlink_status(p, ec)); |
| } |
| |
| GHC_INLINE file_time_type last_write_time(const path& p) |
| { |
| std::error_code ec; |
| auto result = last_write_time(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE file_time_type last_write_time(const path& p, std::error_code& ec) noexcept |
| { |
| time_t result = 0; |
| ec.clear(); |
| file_status fs = detail::status_ex(p, ec, nullptr, nullptr, nullptr, &result); |
| return ec ? (file_time_type::min)() : std::chrono::system_clock::from_time_t(result); |
| } |
| |
| GHC_INLINE void last_write_time(const path& p, file_time_type new_time) |
| { |
| std::error_code ec; |
| last_write_time(p, new_time, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| } |
| |
| GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| auto d = new_time.time_since_epoch(); |
| #ifdef GHC_OS_WINDOWS |
| std::shared_ptr<void> file(::CreateFileW(p.wstring().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL), ::CloseHandle); |
| FILETIME ft; |
| auto tt = std::chrono::duration_cast<std::chrono::microseconds>(d).count() * 10 + 116444736000000000; |
| ft.dwLowDateTime = static_cast<DWORD>(tt); |
| ft.dwHighDateTime = static_cast<DWORD>(tt >> 32); |
| if (!::SetFileTime(file.get(), 0, 0, &ft)) { |
| ec = detail::make_system_error(); |
| } |
| #elif defined(GHC_OS_MACOS) |
| #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED |
| #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 |
| struct ::stat fs; |
| if (::stat(p.c_str(), &fs) == 0) { |
| struct ::timeval tv[2]; |
| tv[0].tv_sec = fs.st_atimespec.tv_sec; |
| tv[0].tv_usec = static_cast<int>(fs.st_atimespec.tv_nsec / 1000); |
| tv[1].tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count(); |
| tv[1].tv_usec = static_cast<int>(std::chrono::duration_cast<std::chrono::microseconds>(d).count() % 1000000); |
| if (::utimes(p.c_str(), tv) == 0) { |
| return; |
| } |
| } |
| ec = detail::make_system_error(); |
| return; |
| #else |
| struct ::timespec times[2]; |
| times[0].tv_sec = 0; |
| times[0].tv_nsec = UTIME_OMIT; |
| times[1].tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count(); |
| times[1].tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000; |
| if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { |
| ec = detail::make_system_error(); |
| } |
| return; |
| #endif |
| #endif |
| #else |
| struct ::timespec times[2]; |
| times[0].tv_sec = 0; |
| times[0].tv_nsec = UTIME_OMIT; |
| times[1].tv_sec = static_cast<decltype(times[1].tv_sec)>(std::chrono::duration_cast<std::chrono::seconds>(d).count()); |
| times[1].tv_nsec = static_cast<decltype(times[1].tv_nsec)>(std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000); |
| if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { |
| ec = detail::make_system_error(); |
| } |
| return; |
| #endif |
| } |
| |
| GHC_INLINE void permissions(const path& p, perms prms, perm_options opts) |
| { |
| std::error_code ec; |
| permissions(p, prms, opts, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| } |
| |
| GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noexcept |
| { |
| permissions(p, prms, perm_options::replace, ec); |
| } |
| |
| GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) |
| { |
| if (static_cast<int>(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) { |
| ec = detail::make_error_code(detail::portable_error::invalid_argument); |
| return; |
| } |
| auto fs = symlink_status(p, ec); |
| if ((opts & perm_options::replace) != perm_options::replace) { |
| if ((opts & perm_options::add) == perm_options::add) { |
| prms = fs.permissions() | prms; |
| } |
| else { |
| prms = fs.permissions() & ~prms; |
| } |
| } |
| #ifdef GHC_OS_WINDOWS |
| #ifdef __GNUC__ |
| auto oldAttr = GetFileAttributesW(p.wstring().c_str()); |
| if (oldAttr != INVALID_FILE_ATTRIBUTES) { |
| DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast<DWORD>(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY; |
| if (oldAttr == newAttr || SetFileAttributesW(p.wstring().c_str(), newAttr)) { |
| return; |
| } |
| } |
| ec = detail::make_system_error(); |
| #else |
| int mode = 0; |
| if ((prms & perms::owner_read) == perms::owner_read) { |
| mode |= _S_IREAD; |
| } |
| if ((prms & perms::owner_write) == perms::owner_write) { |
| mode |= _S_IWRITE; |
| } |
| if (::_wchmod(p.wstring().c_str(), mode) != 0) { |
| ec = detail::make_system_error(); |
| } |
| #endif |
| #else |
| if ((opts & perm_options::nofollow) != perm_options::nofollow) { |
| if (::chmod(p.c_str(), static_cast<mode_t>(prms)) != 0) { |
| ec = detail::make_system_error(); |
| } |
| } |
| #endif |
| } |
| |
| GHC_INLINE path proximate(const path& p, std::error_code& ec) |
| { |
| return proximate(p, current_path(), ec); |
| } |
| |
| GHC_INLINE path proximate(const path& p, const path& base) |
| { |
| return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); |
| } |
| |
| GHC_INLINE path proximate(const path& p, const path& base, std::error_code& ec) |
| { |
| return weakly_canonical(p, ec).lexically_proximate(weakly_canonical(base, ec)); |
| } |
| |
| GHC_INLINE path read_symlink(const path& p) |
| { |
| std::error_code ec; |
| auto result = read_symlink(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE path read_symlink(const path& p, std::error_code& ec) |
| { |
| file_status fs = symlink_status(p, ec); |
| if (fs.type() != file_type::symlink) { |
| ec = detail::make_error_code(detail::portable_error::invalid_argument); |
| return path(); |
| } |
| auto result = detail::resolveSymlink(p, ec); |
| return ec ? path() : result; |
| } |
| |
| GHC_INLINE path relative(const path& p, std::error_code& ec) |
| { |
| return relative(p, current_path(ec), ec); |
| } |
| |
| GHC_INLINE path relative(const path& p, const path& base) |
| { |
| return weakly_canonical(p).lexically_relative(weakly_canonical(base)); |
| } |
| |
| GHC_INLINE path relative(const path& p, const path& base, std::error_code& ec) |
| { |
| return weakly_canonical(p, ec).lexically_relative(weakly_canonical(base, ec)); |
| } |
| |
| GHC_INLINE bool remove(const path& p) |
| { |
| std::error_code ec; |
| auto result = remove(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| std::wstring np = detail::fromUtf8<std::wstring>(p.u8string()); |
| DWORD attr = GetFileAttributesW(np.c_str()); |
| if (attr == INVALID_FILE_ATTRIBUTES) { |
| auto error = ::GetLastError(); |
| if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) { |
| return false; |
| } |
| ec = detail::make_system_error(error); |
| } |
| if (!ec) { |
| if (attr & FILE_ATTRIBUTE_DIRECTORY) { |
| if (!RemoveDirectoryW(np.c_str())) { |
| ec = detail::make_system_error(); |
| } |
| } |
| else { |
| if (!DeleteFileW(np.c_str())) { |
| ec = detail::make_system_error(); |
| } |
| } |
| } |
| #else |
| if (::remove(p.c_str()) == -1) { |
| auto error = errno; |
| if (error == ENOENT) { |
| return false; |
| } |
| ec = detail::make_system_error(); |
| } |
| #endif |
| return ec ? false : true; |
| } |
| |
| GHC_INLINE uintmax_t remove_all(const path& p) |
| { |
| std::error_code ec; |
| auto result = remove_all(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| uintmax_t count = 0; |
| if (p == "/") { |
| ec = detail::make_error_code(detail::portable_error::not_supported); |
| return static_cast<uintmax_t>(-1); |
| } |
| std::error_code tec; |
| auto fs = status(p, tec); |
| if (exists(fs) && is_directory(fs)) { |
| for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { |
| if (ec) { |
| break; |
| } |
| if (!iter->is_symlink() && iter->is_directory()) { |
| count += remove_all(iter->path(), ec); |
| if (ec) { |
| return static_cast<uintmax_t>(-1); |
| } |
| } |
| else { |
| remove(iter->path(), ec); |
| if (ec) { |
| return static_cast<uintmax_t>(-1); |
| } |
| ++count; |
| } |
| } |
| } |
| if (!ec) { |
| if (remove(p, ec)) { |
| ++count; |
| } |
| } |
| if (ec) { |
| return static_cast<uintmax_t>(-1); |
| } |
| return count; |
| } |
| |
| GHC_INLINE void rename(const path& from, const path& to) |
| { |
| std::error_code ec; |
| rename(from, to, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); |
| } |
| } |
| |
| GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| if (from != to) { |
| if (!MoveFileExW(detail::fromUtf8<std::wstring>(from.u8string()).c_str(), detail::fromUtf8<std::wstring>(to.u8string()).c_str(), (DWORD)MOVEFILE_REPLACE_EXISTING)) { |
| ec = detail::make_system_error(); |
| } |
| } |
| #else |
| if (from != to) { |
| if (::rename(from.c_str(), to.c_str()) != 0) { |
| ec = detail::make_system_error(); |
| } |
| } |
| #endif |
| } |
| |
| GHC_INLINE void resize_file(const path& p, uintmax_t size) |
| { |
| std::error_code ec; |
| resize_file(p, size, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| } |
| |
| GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| LARGE_INTEGER lisize; |
| lisize.QuadPart = static_cast<LONGLONG>(size); |
| if(lisize.QuadPart < 0) { |
| #ifdef ERROR_FILE_TOO_LARGE |
| ec = detail::make_system_error(ERROR_FILE_TOO_LARGE); |
| #else |
| ec = detail::make_system_error(223); |
| #endif |
| return; |
| } |
| std::shared_ptr<void> file(CreateFileW(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle); |
| if (file.get() == INVALID_HANDLE_VALUE) { |
| ec = detail::make_system_error(); |
| } |
| else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) { |
| ec = detail::make_system_error(); |
| } |
| #else |
| if (::truncate(p.c_str(), static_cast<off_t>(size)) != 0) { |
| ec = detail::make_system_error(); |
| } |
| #endif |
| } |
| |
| GHC_INLINE space_info space(const path& p) |
| { |
| std::error_code ec; |
| auto result = space(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| ULARGE_INTEGER freeBytesAvailableToCaller = {0, 0}; |
| ULARGE_INTEGER totalNumberOfBytes = {0, 0}; |
| ULARGE_INTEGER totalNumberOfFreeBytes = {0, 0}; |
| if (!GetDiskFreeSpaceExW(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { |
| ec = detail::make_system_error(); |
| return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)}; |
| } |
| return {static_cast<uintmax_t>(totalNumberOfBytes.QuadPart), static_cast<uintmax_t>(totalNumberOfFreeBytes.QuadPart), static_cast<uintmax_t>(freeBytesAvailableToCaller.QuadPart)}; |
| #elif !defined(__ANDROID__) || __ANDROID_API__ >= 19 |
| struct ::statvfs sfs; |
| if (::statvfs(p.c_str(), &sfs) != 0) { |
| ec = detail::make_system_error(); |
| return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)}; |
| } |
| return {static_cast<uintmax_t>(sfs.f_blocks * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bfree * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bavail * sfs.f_frsize)}; |
| #else |
| (void)p; |
| ec = detail::make_error_code(detail::portable_error::not_supported); |
| return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)}; |
| #endif |
| } |
| |
| GHC_INLINE file_status status(const path& p) |
| { |
| std::error_code ec; |
| auto result = status(p, ec); |
| if (result.type() == file_type::none) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE file_status status(const path& p, std::error_code& ec) noexcept |
| { |
| return detail::status_ex(p, ec); |
| } |
| |
| GHC_INLINE bool status_known(file_status s) noexcept |
| { |
| return s.type() != file_type::none; |
| } |
| |
| GHC_INLINE file_status symlink_status(const path& p) |
| { |
| std::error_code ec; |
| auto result = symlink_status(p, ec); |
| if (result.type() == file_type::none) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept |
| { |
| return detail::symlink_status_ex(p, ec); |
| } |
| |
| GHC_INLINE path temp_directory_path() |
| { |
| std::error_code ec; |
| path result = temp_directory_path(ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE path temp_directory_path(std::error_code& ec) noexcept |
| { |
| ec.clear(); |
| #ifdef GHC_OS_WINDOWS |
| wchar_t buffer[512]; |
| auto rc = GetTempPathW(511, buffer); |
| if (!rc || rc > 511) { |
| ec = detail::make_system_error(); |
| return path(); |
| } |
| return path(std::wstring(buffer)); |
| #else |
| static const char* temp_vars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr}; |
| const char* temp_path = nullptr; |
| for (auto temp_name = temp_vars; *temp_name != nullptr; ++temp_name) { |
| temp_path = std::getenv(*temp_name); |
| if (temp_path) { |
| return path(temp_path); |
| } |
| } |
| return path("/tmp"); |
| #endif |
| } |
| |
| GHC_INLINE path weakly_canonical(const path& p) |
| { |
| std::error_code ec; |
| auto result = weakly_canonical(p, ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); |
| } |
| return result; |
| } |
| |
| GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept |
| { |
| path result; |
| ec.clear(); |
| bool scan = true; |
| for (auto pe : p) { |
| if (scan) { |
| std::error_code tec; |
| if (exists(result / pe, tec)) { |
| result /= pe; |
| } |
| else { |
| if (ec) { |
| return path(); |
| } |
| scan = false; |
| if (!result.empty()) { |
| result = canonical(result, ec) / pe; |
| if (ec) { |
| break; |
| } |
| } |
| else { |
| result /= pe; |
| } |
| } |
| } |
| else { |
| result /= pe; |
| } |
| } |
| if (scan) { |
| if (!result.empty()) { |
| result = canonical(result, ec); |
| } |
| } |
| return ec ? path() : result.lexically_normal(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.11 class file_status |
| // 30.10.11.1 constructors and destructor |
| GHC_INLINE file_status::file_status() noexcept |
| : file_status(file_type::none) |
| { |
| } |
| |
| GHC_INLINE file_status::file_status(file_type ft, perms prms) noexcept |
| : _type(ft) |
| , _perms(prms) |
| { |
| } |
| |
| GHC_INLINE file_status::file_status(const file_status& other) noexcept |
| : _type(other._type) |
| , _perms(other._perms) |
| { |
| } |
| |
| GHC_INLINE file_status::file_status(file_status&& other) noexcept |
| : _type(other._type) |
| , _perms(other._perms) |
| { |
| } |
| |
| GHC_INLINE file_status::~file_status() {} |
| |
| // assignments: |
| GHC_INLINE file_status& file_status::operator=(const file_status& rhs) noexcept |
| { |
| _type = rhs._type; |
| _perms = rhs._perms; |
| return *this; |
| } |
| |
| GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept |
| { |
| _type = rhs._type; |
| _perms = rhs._perms; |
| return *this; |
| } |
| |
| // 30.10.11.3 modifiers |
| GHC_INLINE void file_status::type(file_type ft) noexcept |
| { |
| _type = ft; |
| } |
| |
| GHC_INLINE void file_status::permissions(perms prms) noexcept |
| { |
| _perms = prms; |
| } |
| |
| // 30.10.11.2 observers |
| GHC_INLINE file_type file_status::type() const noexcept |
| { |
| return _type; |
| } |
| |
| GHC_INLINE perms file_status::permissions() const noexcept |
| { |
| return _perms; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.12 class directory_entry |
| // 30.10.12.1 constructors and destructor |
| // directory_entry::directory_entry() noexcept = default; |
| // directory_entry::directory_entry(const directory_entry&) = default; |
| // directory_entry::directory_entry(directory_entry&&) noexcept = default; |
| GHC_INLINE directory_entry::directory_entry(const filesystem::path& p) |
| : _path(p) |
| , _file_size(0) |
| #ifndef GHC_OS_WINDOWS |
| , _hard_link_count(0) |
| #endif |
| , _last_write_time(0) |
| { |
| refresh(); |
| } |
| |
| GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec) |
| : _path(p) |
| , _file_size(0) |
| #ifndef GHC_OS_WINDOWS |
| , _hard_link_count(0) |
| #endif |
| , _last_write_time(0) |
| { |
| refresh(ec); |
| } |
| |
| GHC_INLINE directory_entry::~directory_entry() {} |
| |
| // assignments: |
| // directory_entry& directory_entry::operator=(const directory_entry&) = default; |
| // directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default; |
| |
| // 30.10.12.2 directory_entry modifiers |
| GHC_INLINE void directory_entry::assign(const filesystem::path& p) |
| { |
| _path = p; |
| refresh(); |
| } |
| |
| GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec) |
| { |
| _path = p; |
| refresh(ec); |
| } |
| |
| GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p) |
| { |
| _path.replace_filename(p); |
| refresh(); |
| } |
| |
| GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec) |
| { |
| _path.replace_filename(p); |
| refresh(ec); |
| } |
| |
| GHC_INLINE void directory_entry::refresh() |
| { |
| std::error_code ec; |
| refresh(ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); |
| } |
| } |
| |
| GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept |
| { |
| #ifdef GHC_OS_WINDOWS |
| _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, nullptr, &_last_write_time); |
| #else |
| _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, &_hard_link_count, &_last_write_time); |
| #endif |
| } |
| |
| // 30.10.12.3 directory_entry observers |
| GHC_INLINE const filesystem::path& directory_entry::path() const noexcept |
| { |
| return _path; |
| } |
| |
| GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept |
| { |
| return _path; |
| } |
| |
| GHC_INLINE bool directory_entry::exists() const |
| { |
| return filesystem::exists(status()); |
| } |
| |
| GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept |
| { |
| return filesystem::exists(status(ec)); |
| } |
| |
| GHC_INLINE bool directory_entry::is_block_file() const |
| { |
| return filesystem::is_block_file(status()); |
| } |
| GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept |
| { |
| return filesystem::is_block_file(status(ec)); |
| } |
| |
| GHC_INLINE bool directory_entry::is_character_file() const |
| { |
| return filesystem::is_character_file(status()); |
| } |
| |
| GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept |
| { |
| return filesystem::is_character_file(status(ec)); |
| } |
| |
| GHC_INLINE bool directory_entry::is_directory() const |
| { |
| return filesystem::is_directory(status()); |
| } |
| |
| GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept |
| { |
| return filesystem::is_directory(status(ec)); |
| } |
| |
| GHC_INLINE bool directory_entry::is_fifo() const |
| { |
| return filesystem::is_fifo(status()); |
| } |
| |
| GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept |
| { |
| return filesystem::is_fifo(status(ec)); |
| } |
| |
| GHC_INLINE bool directory_entry::is_other() const |
| { |
| return filesystem::is_other(status()); |
| } |
| |
| GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept |
| { |
| return filesystem::is_other(status(ec)); |
| } |
| |
| GHC_INLINE bool directory_entry::is_regular_file() const |
| { |
| return filesystem::is_regular_file(status()); |
| } |
| |
| GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept |
| { |
| return filesystem::is_regular_file(status(ec)); |
| } |
| |
| GHC_INLINE bool directory_entry::is_socket() const |
| { |
| return filesystem::is_socket(status()); |
| } |
| |
| GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept |
| { |
| return filesystem::is_socket(status(ec)); |
| } |
| |
| GHC_INLINE bool directory_entry::is_symlink() const |
| { |
| return filesystem::is_symlink(symlink_status()); |
| } |
| |
| GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept |
| { |
| return filesystem::is_symlink(symlink_status(ec)); |
| } |
| |
| GHC_INLINE uintmax_t directory_entry::file_size() const |
| { |
| if (_status.type() != file_type::none) { |
| return _file_size; |
| } |
| return filesystem::file_size(path()); |
| } |
| |
| GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept |
| { |
| if (_status.type() != file_type::none) { |
| ec.clear(); |
| return _file_size; |
| } |
| return filesystem::file_size(path(), ec); |
| } |
| |
| GHC_INLINE uintmax_t directory_entry::hard_link_count() const |
| { |
| #ifndef GHC_OS_WINDOWS |
| if (_status.type() != file_type::none) { |
| return _hard_link_count; |
| } |
| #endif |
| return filesystem::hard_link_count(path()); |
| } |
| |
| GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept |
| { |
| #ifndef GHC_OS_WINDOWS |
| if (_status.type() != file_type::none) { |
| ec.clear(); |
| return _hard_link_count; |
| } |
| #endif |
| return filesystem::hard_link_count(path(), ec); |
| } |
| |
| GHC_INLINE file_time_type directory_entry::last_write_time() const |
| { |
| if (_status.type() != file_type::none) { |
| return std::chrono::system_clock::from_time_t(_last_write_time); |
| } |
| return filesystem::last_write_time(path()); |
| } |
| |
| GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept |
| { |
| if (_status.type() != file_type::none) { |
| ec.clear(); |
| return std::chrono::system_clock::from_time_t(_last_write_time); |
| } |
| return filesystem::last_write_time(path(), ec); |
| } |
| |
| GHC_INLINE file_status directory_entry::status() const |
| { |
| if (_status.type() != file_type::none) { |
| return _status; |
| } |
| return filesystem::status(path()); |
| } |
| |
| GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept |
| { |
| if (_status.type() != file_type::none) { |
| ec.clear(); |
| return _status; |
| } |
| return filesystem::status(path(), ec); |
| } |
| |
| GHC_INLINE file_status directory_entry::symlink_status() const |
| { |
| if (_symlink_status.type() != file_type::none) { |
| return _symlink_status; |
| } |
| return filesystem::symlink_status(path()); |
| } |
| |
| GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept |
| { |
| if (_symlink_status.type() != file_type::none) { |
| ec.clear(); |
| return _symlink_status; |
| } |
| return filesystem::symlink_status(path(), ec); |
| } |
| |
| GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept |
| { |
| return _path < rhs._path; |
| } |
| |
| GHC_INLINE bool directory_entry::operator==(const directory_entry& rhs) const noexcept |
| { |
| return _path == rhs._path; |
| } |
| |
| GHC_INLINE bool directory_entry::operator!=(const directory_entry& rhs) const noexcept |
| { |
| return _path != rhs._path; |
| } |
| |
| GHC_INLINE bool directory_entry::operator<=(const directory_entry& rhs) const noexcept |
| { |
| return _path <= rhs._path; |
| } |
| |
| GHC_INLINE bool directory_entry::operator>(const directory_entry& rhs) const noexcept |
| { |
| return _path > rhs._path; |
| } |
| |
| GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const noexcept |
| { |
| return _path >= rhs._path; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.13 class directory_iterator |
| |
| #ifdef GHC_OS_WINDOWS |
| class directory_iterator::impl |
| { |
| public: |
| impl(const path& p, directory_options options) |
| : _base(p) |
| , _options(options) |
| , _dirHandle(INVALID_HANDLE_VALUE) |
| { |
| if (!_base.empty()) { |
| ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW)); |
| if ((_dirHandle = FindFirstFileW(detail::fromUtf8<std::wstring>((_base / "*").u8string()).c_str(), &_findData)) != INVALID_HANDLE_VALUE) { |
| if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { |
| increment(_ec); |
| } |
| else { |
| _current = _base / std::wstring(_findData.cFileName); |
| copyToDirEntry(_ec); |
| } |
| } |
| else { |
| auto error = ::GetLastError(); |
| _base = filesystem::path(); |
| if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { |
| _ec = detail::make_system_error(); |
| } |
| } |
| } |
| } |
| impl(const impl& other) = delete; |
| ~impl() |
| { |
| if (_dirHandle != INVALID_HANDLE_VALUE) { |
| FindClose(_dirHandle); |
| _dirHandle = INVALID_HANDLE_VALUE; |
| } |
| } |
| void increment(std::error_code& ec) |
| { |
| if (_dirHandle != INVALID_HANDLE_VALUE) { |
| do { |
| if (FindNextFileW(_dirHandle, &_findData)) { |
| _current = _base; |
| try { |
| _current.append_name(detail::toUtf8(_findData.cFileName).c_str()); |
| } |
| catch(filesystem_error& fe) { |
| ec = fe.code(); |
| return; |
| } |
| copyToDirEntry(ec); |
| } |
| else { |
| auto err = ::GetLastError(); |
| if(err != ERROR_NO_MORE_FILES) { |
| _ec = ec = detail::make_system_error(err); |
| } |
| FindClose(_dirHandle); |
| _dirHandle = INVALID_HANDLE_VALUE; |
| _current = filesystem::path(); |
| break; |
| } |
| } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L".."); |
| } |
| else { |
| ec = _ec; |
| } |
| } |
| void copyToDirEntry(std::error_code& ec) |
| { |
| _dir_entry._path = _current; |
| if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { |
| _dir_entry._status = detail::status_ex(_current, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time); |
| } |
| else { |
| _dir_entry._status = detail::status_from_INFO(_current, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time); |
| _dir_entry._symlink_status = _dir_entry._status; |
| } |
| if (ec) { |
| if (_dir_entry._status.type() != file_type::none && _dir_entry._symlink_status.type() != file_type::none) { |
| ec.clear(); |
| } |
| else { |
| _dir_entry._file_size = static_cast<uintmax_t>(-1); |
| _dir_entry._last_write_time = 0; |
| } |
| } |
| } |
| path _base; |
| directory_options _options; |
| WIN32_FIND_DATAW _findData; |
| HANDLE _dirHandle; |
| path _current; |
| directory_entry _dir_entry; |
| std::error_code _ec; |
| }; |
| #else |
| // POSIX implementation |
| class directory_iterator::impl |
| { |
| public: |
| impl(const path& path, directory_options options) |
| : _base(path) |
| , _options(options) |
| , _dir(nullptr) |
| , _entry(nullptr) |
| { |
| if (!path.empty()) { |
| _dir = ::opendir(path.native().c_str()); |
| } |
| if (!path.empty()) { |
| if (!_dir) { |
| auto error = errno; |
| _base = filesystem::path(); |
| if (error != EACCES || (options & directory_options::skip_permission_denied) == directory_options::none) { |
| _ec = detail::make_system_error(); |
| } |
| } |
| else { |
| increment(_ec); |
| } |
| } |
| } |
| impl(const impl& other) = delete; |
| ~impl() |
| { |
| if (_dir) { |
| ::closedir(_dir); |
| } |
| } |
| void increment(std::error_code& ec) |
| { |
| if (_dir) { |
| do { |
| errno = 0; |
| _entry = readdir(_dir); |
| if (_entry) { |
| _current = _base; |
| _current.append_name(_entry->d_name); |
| _dir_entry = directory_entry(_current, ec); |
| } |
| else { |
| ::closedir(_dir); |
| _dir = nullptr; |
| _current = path(); |
| if (errno) { |
| ec = detail::make_system_error(); |
| } |
| break; |
| } |
| } while (std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0); |
| } |
| } |
| path _base; |
| directory_options _options; |
| path _current; |
| DIR* _dir; |
| struct ::dirent* _entry; |
| directory_entry _dir_entry; |
| std::error_code _ec; |
| }; |
| #endif |
| |
| // 30.10.13.1 member functions |
| GHC_INLINE directory_iterator::directory_iterator() noexcept |
| : _impl(new impl(path(), directory_options::none)) |
| { |
| } |
| |
| GHC_INLINE directory_iterator::directory_iterator(const path& p) |
| : _impl(new impl(p, directory_options::none)) |
| { |
| if (_impl->_ec) { |
| throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); |
| } |
| _impl->_ec.clear(); |
| } |
| |
| GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options) |
| : _impl(new impl(p, options)) |
| { |
| if (_impl->_ec) { |
| throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); |
| } |
| } |
| |
| GHC_INLINE directory_iterator::directory_iterator(const path& p, std::error_code& ec) noexcept |
| : _impl(new impl(p, directory_options::none)) |
| { |
| if (_impl->_ec) { |
| ec = _impl->_ec; |
| } |
| } |
| |
| GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept |
| : _impl(new impl(p, options)) |
| { |
| if (_impl->_ec) { |
| ec = _impl->_ec; |
| } |
| } |
| |
| GHC_INLINE directory_iterator::directory_iterator(const directory_iterator& rhs) |
| : _impl(rhs._impl) |
| { |
| } |
| |
| GHC_INLINE directory_iterator::directory_iterator(directory_iterator&& rhs) noexcept |
| : _impl(std::move(rhs._impl)) |
| { |
| } |
| |
| GHC_INLINE directory_iterator::~directory_iterator() {} |
| |
| GHC_INLINE directory_iterator& directory_iterator::operator=(const directory_iterator& rhs) |
| { |
| _impl = rhs._impl; |
| return *this; |
| } |
| |
| GHC_INLINE directory_iterator& directory_iterator::operator=(directory_iterator&& rhs) noexcept |
| { |
| _impl = std::move(rhs._impl); |
| return *this; |
| } |
| |
| GHC_INLINE const directory_entry& directory_iterator::operator*() const |
| { |
| return _impl->_dir_entry; |
| } |
| |
| GHC_INLINE const directory_entry* directory_iterator::operator->() const |
| { |
| return &_impl->_dir_entry; |
| } |
| |
| GHC_INLINE directory_iterator& directory_iterator::operator++() |
| { |
| std::error_code ec; |
| _impl->increment(ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_current, ec); |
| } |
| return *this; |
| } |
| |
| GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec) noexcept |
| { |
| _impl->increment(ec); |
| return *this; |
| } |
| |
| GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const |
| { |
| return _impl->_current == rhs._impl->_current; |
| } |
| |
| GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const |
| { |
| return _impl->_current != rhs._impl->_current; |
| } |
| |
| // 30.10.13.2 directory_iterator non-member functions |
| |
| GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept |
| { |
| return iter; |
| } |
| |
| GHC_INLINE directory_iterator end(const directory_iterator&) noexcept |
| { |
| return directory_iterator(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // 30.10.14 class recursive_directory_iterator |
| |
| GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept |
| : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) |
| { |
| _impl->_dir_iter_stack.push(directory_iterator()); |
| } |
| |
| GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p) |
| : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) |
| { |
| _impl->_dir_iter_stack.push(directory_iterator(p)); |
| } |
| |
| GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options) |
| : _impl(new recursive_directory_iterator_impl(options, true)) |
| { |
| _impl->_dir_iter_stack.push(directory_iterator(p, options)); |
| } |
| |
| GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept |
| : _impl(new recursive_directory_iterator_impl(options, true)) |
| { |
| _impl->_dir_iter_stack.push(directory_iterator(p, options, ec)); |
| } |
| |
| GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept |
| : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) |
| { |
| _impl->_dir_iter_stack.push(directory_iterator(p, ec)); |
| } |
| |
| GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs) |
| : _impl(rhs._impl) |
| { |
| } |
| |
| GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept |
| : _impl(std::move(rhs._impl)) |
| { |
| } |
| |
| GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {} |
| |
| // 30.10.14.1 observers |
| GHC_INLINE directory_options recursive_directory_iterator::options() const |
| { |
| return _impl->_options; |
| } |
| |
| GHC_INLINE int recursive_directory_iterator::depth() const |
| { |
| return static_cast<int>(_impl->_dir_iter_stack.size() - 1); |
| } |
| |
| GHC_INLINE bool recursive_directory_iterator::recursion_pending() const |
| { |
| return _impl->_recursion_pending; |
| } |
| |
| GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const |
| { |
| return *(_impl->_dir_iter_stack.top()); |
| } |
| |
| GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const |
| { |
| return &(*(_impl->_dir_iter_stack.top())); |
| } |
| |
| // 30.10.14.1 modifiers recursive_directory_iterator& |
| GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs) |
| { |
| _impl = rhs._impl; |
| return *this; |
| } |
| |
| GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept |
| { |
| _impl = std::move(rhs._impl); |
| return *this; |
| } |
| |
| GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator++() |
| { |
| std::error_code ec; |
| increment(ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); |
| } |
| return *this; |
| } |
| |
| GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept |
| { |
| if (recursion_pending() && is_directory((*this)->status()) && (!is_symlink((*this)->symlink_status()) || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { |
| _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); |
| } |
| else { |
| _impl->_dir_iter_stack.top().increment(ec); |
| } |
| if (!ec) { |
| while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { |
| _impl->_dir_iter_stack.pop(); |
| _impl->_dir_iter_stack.top().increment(ec); |
| } |
| } |
| else if (!_impl->_dir_iter_stack.empty()) { |
| _impl->_dir_iter_stack.pop(); |
| } |
| _impl->_recursion_pending = true; |
| return *this; |
| } |
| |
| GHC_INLINE void recursive_directory_iterator::pop() |
| { |
| std::error_code ec; |
| pop(ec); |
| if (ec) { |
| throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); |
| } |
| } |
| |
| GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) |
| { |
| if (depth() == 0) { |
| *this = recursive_directory_iterator(); |
| } |
| else { |
| do { |
| _impl->_dir_iter_stack.pop(); |
| _impl->_dir_iter_stack.top().increment(ec); |
| } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); |
| } |
| } |
| |
| GHC_INLINE void recursive_directory_iterator::disable_recursion_pending() |
| { |
| _impl->_recursion_pending = false; |
| } |
| |
| // other members as required by 27.2.3, input iterators |
| GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const |
| { |
| return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top(); |
| } |
| |
| GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const |
| { |
| return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top(); |
| } |
| |
| // 30.10.14.2 directory_iterator non-member functions |
| GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept |
| { |
| return iter; |
| } |
| |
| GHC_INLINE recursive_directory_iterator end(const recursive_directory_iterator&) noexcept |
| { |
| return recursive_directory_iterator(); |
| } |
| |
| #endif // GHC_EXPAND_IMPL |
| |
| } // namespace filesystem |
| } // namespace ghc |
| |
| // cleanup some macros |
| #undef GHC_INLINE |
| #undef GHC_EXPAND_IMPL |
| |
| #endif // GHC_FILESYSTEM_H |