// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP_FILESYSTEM
#define _LIBCPP_FILESYSTEM

/*
    filesystem synopsis

    namespace std::filesystem {

    // `class path` from http://eel.is/c++draft/fs.class.path.general#6
    class path {
      public:
        using value_type  = see below;
        using string_type = basic_string<value_type>;
        static constexpr value_type preferred_separator = see below;

        enum format;

        path() noexcept;
        path(const path& p);
        path(path&& p) noexcept;
        path(string_type&& source, format fmt = auto_format);
        template<class Source>
          path(const Source& source, format fmt = auto_format);
        template<class InputIterator>
          path(InputIterator first, InputIterator last, format fmt = auto_format);
        template<class Source>
          path(const Source& source, const locale& loc, format fmt = auto_format);
        template<class InputIterator>
          path(InputIterator first, InputIterator last, const locale& loc, format fmt = auto_format);
        ~path();

        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);

        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);

        path& operator+=(const path& x);
        path& operator+=(const string_type& x);
        path& operator+=(basic_string_view<value_type> x);
        path& operator+=(const value_type* x);
        path& operator+=(value_type x);
        template<class Source>
          path& operator+=(const Source& x);
        template<class EcharT>
          path& operator+=(EcharT x);
        template<class Source>
          path& concat(const Source& x);
        template<class InputIterator>
          path& concat(InputIterator first, InputIterator last);

        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;

        friend bool operator==(const path& lhs, const path& rhs) noexcept;
        friend bool operator!=(const path& lhs, const path& rhs) noexcept;             // removed in C++20
        friend bool operator< (const path& lhs, const path& rhs) noexcept;             // removed in C++20
        friend bool operator<=(const path& lhs, const path& rhs) noexcept;             // removed in C++20
        friend bool operator> (const path& lhs, const path& rhs) noexcept;             // removed in C++20
        friend bool operator>=(const path& lhs, const path& rhs) noexcept;             // removed in C++20
        friend strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept; // C++20

        friend path operator/(const path& lhs, const path& rhs);

        const string_type& native() const noexcept;
        const value_type*  c_str() const noexcept;
        operator string_type() const;

        template<class EcharT, class traits = char_traits<EcharT>,
                 class Allocator = allocator<EcharT>>
          basic_string<EcharT, traits, Allocator>
            string(const Allocator& a = Allocator()) const;
        std::string    string() const;
        std::wstring   wstring() const;
        std::u8string  u8string() const;
        std::u16string u16string() const;
        std::u32string u32string() const;

        template<class EcharT, class traits = char_traits<EcharT>,
                 class Allocator = allocator<EcharT>>
          basic_string<EcharT, traits, Allocator>
            generic_string(const Allocator& a = Allocator()) const;
        std::string    generic_string() const;
        std::wstring   generic_wstring() const;
        std::u8string  generic_u8string() const;
        std::u16string generic_u16string() const;
        std::u32string generic_u32string() const;

        int compare(const path& p) const noexcept;
        int compare(const string_type& s) const;
        int compare(basic_string_view<value_type> s) const;
        int compare(const value_type* s) const;

        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;

        [[nodiscard]] 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;

        path lexically_normal() const;
        path lexically_relative(const path& base) const;
        path lexically_proximate(const path& base) const;

        class iterator;
        using const_iterator = iterator;

        iterator begin() const;
        iterator end() const;

        template<class charT, class traits>
          friend basic_ostream<charT, traits>&
            operator<<(basic_ostream<charT, traits>& os, const path& p);
        template<class charT, class traits>
          friend basic_istream<charT, traits>&
            operator>>(basic_istream<charT, traits>& is, path& p);
    };

    void swap(path& lhs, path& rhs) noexcept;
    size_t hash_value(const path& p) noexcept;

    // [fs.path.hash], hash support
    template<> struct hash<filesystem::path>;

    template <class Source>
      path u8path(const Source& source);
    template <class InputIterator>
      path u8path(InputIterator first, InputIterator last);

    class filesystem_error;

    class directory_entry {
    public:
      directory_entry() noexcept = default;
      directory_entry(const directory_entry&) = default;
      directory_entry(directory_entry&&) noexcept = default;
      explicit directory_entry(const filesystem::path& p);
      directory_entry(const filesystem::path& p, error_code& ec);
      ~directory_entry();

      directory_entry& operator=(const directory_entry&) = default;
      directory_entry& operator=(directory_entry&&) noexcept = default;

      void assign(const filesystem::path& p);
      void assign(const filesystem::path& p, error_code& ec);
      void replace_filename(const filesystem::path& p);
      void replace_filename(const filesystem::path& p, error_code& ec);
      void refresh();
      void refresh(error_code& ec) noexcept;

      const filesystem::path& path() const noexcept;
      operator const filesystem::path&() const noexcept;
      bool exists() const;
      bool exists(error_code& ec) const noexcept;
      bool is_block_file() const;
      bool is_block_file(error_code& ec) const noexcept;
      bool is_character_file() const;
      bool is_character_file(error_code& ec) const noexcept;
      bool is_directory() const;
      bool is_directory(error_code& ec) const noexcept;
      bool is_fifo() const;
      bool is_fifo(error_code& ec) const noexcept;
      bool is_other() const;
      bool is_other(error_code& ec) const noexcept;
      bool is_regular_file() const;
      bool is_regular_file(error_code& ec) const noexcept;
      bool is_socket() const;
      bool is_socket(error_code& ec) const noexcept;
      bool is_symlink() const;
      bool is_symlink(error_code& ec) const noexcept;
      uintmax_t file_size() const;
      uintmax_t file_size(error_code& ec) const noexcept;
      uintmax_t hard_link_count() const;
      uintmax_t hard_link_count(error_code& ec) const noexcept;
      file_time_type last_write_time() const;
      file_time_type last_write_time(error_code& ec) const noexcept;
      file_status status() const;
      file_status status(error_code& ec) const noexcept;
      file_status symlink_status() const;
      file_status symlink_status(error_code& ec) const noexcept;

      bool operator==(const directory_entry& rhs) const noexcept;
      bool operator!=(const directory_entry& rhs) const noexcept;             // removed  in C++20
      bool operator< (const directory_entry& rhs) const noexcept;             // removed  in C++20
      bool operator<=(const directory_entry& rhs) const noexcept;             // removed  in C++20
      bool operator> (const directory_entry& rhs) const noexcept;             // removed  in C++20
      bool operator>=(const directory_entry& rhs) const noexcept;             // removed  in C++20
      strong_ordering operator<=>(const directory_entry& rhs) const noexcept; // since C++20

      template<class charT, class traits>
        friend basic_ostream<charT, traits>&
          operator<<(basic_ostream<charT, traits>& os, const directory_entry& d);

    private:
      filesystem::path pathobject;        // exposition only
      friend class directory_iterator;    // exposition only
    };

  class directory_iterator {
  public:
    using iterator_category = input_iterator_tag;
    using value_type        = directory_entry;
    using difference_type   = ptrdiff_t;
    using pointer           = const directory_entry*;
    using reference         = const directory_entry&;

    // [fs.dir.itr.members], member functions
    directory_iterator() noexcept;
    explicit directory_iterator(const path& p);
    directory_iterator(const path& p, directory_options options);
    directory_iterator(const path& p, error_code& ec);
    directory_iterator(const path& p, directory_options options,
                       error_code& ec);
    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(error_code& ec);

    bool operator==(default_sentinel_t) const noexcept {          // since C++20
      return *this == directory_iterator();
    }

    // other members as required by [input.iterators], input iterators
  };

    // enable directory_iterator range-based for statements
    directory_iterator begin(directory_iterator iter) noexcept;
    directory_iterator end(directory_iterator) noexcept;

class recursive_directory_iterator {
  public:
    using iterator_category = input_iterator_tag;
    using value_type        = directory_entry;
    using difference_type   = ptrdiff_t;
    using pointer           = const directory_entry*;
    using reference         = const directory_entry&;

    // [fs.rec.dir.itr.members], 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,
                                 error_code& ec);
    recursive_directory_iterator(const path& p, error_code& ec);
    recursive_directory_iterator(const recursive_directory_iterator& rhs);
    recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept;
    ~recursive_directory_iterator();

    // [fs.rec.dir.itr.members], observers
    directory_options  options() const;
    int                depth() const;
    bool               recursion_pending() const;

    const directory_entry& operator*() const;
    const directory_entry* operator->() const;

    // [fs.rec.dir.itr.members], modifiers
    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(error_code& ec);

    void pop();
    void pop(error_code& ec);
    void disable_recursion_pending();

    bool operator==(default_sentinel_t) const noexcept {          // since C++20
      return *this == recursive_directory_iterator();
    }

    // other members as required by [input.iterators], input iterators
  };

    // enable recursive_directory_iterator range-based for statements
    recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept;
    recursive_directory_iterator end(recursive_directory_iterator) noexcept;

  class file_status {
  public:
    // [fs.file.status.cons], constructors and destructor
    file_status() noexcept : file_status(file_type::none) {}
    explicit file_status(file_type ft,
                         perms prms = perms::unknown) noexcept;
    file_status(const file_status&) noexcept = default;
    file_status(file_status&&) noexcept = default;
    ~file_status();

    // assignments
    file_status& operator=(const file_status&) noexcept = default;
    file_status& operator=(file_status&&) noexcept = default;

    // [fs.file.status.mods], modifiers
    void       type(file_type ft) noexcept;
    void       permissions(perms prms) noexcept;

    // [fs.file.status.obs], observers
    file_type  type() const noexcept;
    perms      permissions() const noexcept;

    friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept
      { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); } // C++20
  };

    struct space_info
    {
      uintmax_t capacity;
      uintmax_t free;
      uintmax_t available;

      friend bool operator==(const space_info&, const space_info&) = default; // C++20
    };

    enum class file_type;
    enum class perms;
    enum class perm_options;
    enum class copy_options;
    enum class directory_options;

    typedef chrono::time_point<trivial-clock>  file_time_type;

    // operational functions

    path absolute(const path& p);
    path absolute(const path& p, error_code &ec);

    path canonical(const path& p);
    path canonical(const path& p, error_code& ec);

    void copy(const path& from, const path& to);
    void copy(const path& from, const path& to, error_code& ec);
    void copy(const path& from, const path& to, copy_options options);
    void copy(const path& from, const path& to, copy_options options,
                   error_code& ec);

    bool copy_file(const path& from, const path& to);
    bool copy_file(const path& from, const path& to, error_code& ec);
    bool copy_file(const path& from, const path& to, copy_options option);
    bool copy_file(const path& from, const path& to, copy_options option,
                           error_code& ec);

    void copy_symlink(const path& existing_symlink, const path& new_symlink);
    void copy_symlink(const path& existing_symlink, const path& new_symlink,
                              error_code& ec) noexcept;

    bool create_directories(const path& p);
    bool create_directories(const path& p, error_code& ec);

    bool create_directory(const path& p);
    bool create_directory(const path& p, error_code& ec) noexcept;

    bool create_directory(const path& p, const path& attributes);
    bool create_directory(const path& p, const path& attributes,
                                  error_code& ec) noexcept;

    void create_directory_symlink(const path& to, const path& new_symlink);
    void create_directory_symlink(const path& to, const path& new_symlink,
                                          error_code& ec) noexcept;

    void create_hard_link(const path& to, const path& new_hard_link);
    void create_hard_link(const path& to, const path& new_hard_link,
                                  error_code& ec) noexcept;

    void create_symlink(const path& to, const path& new_symlink);
    void create_symlink(const path& to, const path& new_symlink,
                                error_code& ec) noexcept;

    path current_path();
    path current_path(error_code& ec);
    void current_path(const path& p);
    void current_path(const path& p, error_code& ec) noexcept;

    bool exists(file_status s) noexcept;
    bool exists(const path& p);
    bool exists(const path& p, error_code& ec) noexcept;

    bool equivalent(const path& p1, const path& p2);
    bool equivalent(const path& p1, const path& p2, error_code& ec) noexcept;

    uintmax_t    file_size(const path& p);
    uintmax_t    file_size(const path& p, error_code& ec) noexcept;

    uintmax_t    hard_link_count(const path& p);
    uintmax_t    hard_link_count(const path& p, error_code& ec) noexcept;

    bool is_block_file(file_status s) noexcept;
    bool is_block_file(const path& p);
    bool is_block_file(const path& p, error_code& ec) noexcept;

    bool is_character_file(file_status s) noexcept;
    bool is_character_file(const path& p);
    bool is_character_file(const path& p, error_code& ec) noexcept;

    bool is_directory(file_status s) noexcept;
    bool is_directory(const path& p);
    bool is_directory(const path& p, error_code& ec) noexcept;

    bool is_empty(const path& p);
    bool is_empty(const path& p, error_code& ec) noexcept;

    bool is_fifo(file_status s) noexcept;
    bool is_fifo(const path& p);
    bool is_fifo(const path& p, error_code& ec) noexcept;

    bool is_other(file_status s) noexcept;
    bool is_other(const path& p);
    bool is_other(const path& p, error_code& ec) noexcept;

    bool is_regular_file(file_status s) noexcept;
    bool is_regular_file(const path& p);
    bool is_regular_file(const path& p, error_code& ec) noexcept;

    bool is_socket(file_status s) noexcept;
    bool is_socket(const path& p);
    bool is_socket(const path& p, error_code& ec) noexcept;

    bool is_symlink(file_status s) noexcept;
    bool is_symlink(const path& p);
    bool is_symlink(const path& p, error_code& ec) noexcept;

    file_time_type  last_write_time(const path& p);
    file_time_type  last_write_time(const path& p, error_code& ec) noexcept;
    void last_write_time(const path& p, file_time_type new_time);
    void last_write_time(const path& p, file_time_type new_time,
                                 error_code& ec) noexcept;

    void permissions(const path& p, perms prms,
                     perm_options opts=perm_options::replace);
    void permissions(const path& p, perms prms, error_code& ec) noexcept;
    void permissions(const path& p, perms prms, perm_options opts,
                     error_code& ec);

    path proximate(const path& p, error_code& ec);
    path proximate(const path& p, const path& base = current_path());
    path proximate(const path& p, const path& base, error_code &ec);

    path read_symlink(const path& p);
    path read_symlink(const path& p, error_code& ec);

    path relative(const path& p, error_code& ec);
    path relative(const path& p, const path& base=current_path());
    path relative(const path& p, const path& base, error_code& ec);

    bool remove(const path& p);
    bool remove(const path& p, error_code& ec) noexcept;

    uintmax_t    remove_all(const path& p);
    uintmax_t    remove_all(const path& p, error_code& ec);

    void rename(const path& from, const path& to);
    void rename(const path& from, const path& to, error_code& ec) noexcept;

    void resize_file(const path& p, uintmax_t size);
    void resize_file(const path& p, uintmax_t size, error_code& ec) noexcept;

    space_info   space(const path& p);
    space_info   space(const path& p, error_code& ec) noexcept;

    file_status  status(const path& p);
    file_status  status(const path& p, error_code& ec) noexcept;

    bool status_known(file_status s) noexcept;

    file_status  symlink_status(const path& p);
    file_status  symlink_status(const path& p, error_code& ec) noexcept;

    path temp_directory_path();
    path temp_directory_path(error_code& ec);

    path weakly_canonical(path const& p);
    path weakly_canonical(path const& p, error_code& ec);

} // namespace std::filesystem

template <>
inline constexpr bool std::ranges::enable_borrowed_range<std::filesystem::directory_iterator> = true;
template <>
inline constexpr bool std::ranges::enable_borrowed_range<std::filesystem::recursive_directory_iterator> = true;

template <>
inline constexpr bool std::ranges::enable_view<std::filesystem::directory_iterator> = true;
template <>
inline constexpr bool std::ranges::enable_view<std::filesystem::recursive_directory_iterator> = true;

*/

#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
#  include <__cxx03/filesystem>
#else
#  include <__config>

#  if _LIBCPP_STD_VER >= 17
#    include <__filesystem/copy_options.h>
#    include <__filesystem/directory_entry.h>
#    include <__filesystem/directory_iterator.h>
#    include <__filesystem/directory_options.h>
#    include <__filesystem/file_status.h>
#    include <__filesystem/file_time_type.h>
#    include <__filesystem/file_type.h>
#    include <__filesystem/filesystem_error.h>
#    include <__filesystem/operations.h>
#    include <__filesystem/path.h>
#    include <__filesystem/path_iterator.h>
#    include <__filesystem/perm_options.h>
#    include <__filesystem/perms.h>
#    include <__filesystem/recursive_directory_iterator.h>
#    include <__filesystem/space_info.h>
#    include <__filesystem/u8path.h>
#  endif

#  include <version>

// standard-mandated includes

// [fs.filesystem.syn]
#  include <compare>

#  if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#    pragma GCC system_header
#  endif

#  if !defined(_LIBCPP_REMOVE_TRANSITIVE_INCLUDES) && _LIBCPP_STD_VER <= 20
#    include <concepts>
#    include <cstdlib>
#    include <cstring>
#    include <iosfwd>
#    include <new>
#    include <system_error>
#  endif
#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)

#endif // _LIBCPP_FILESYSTEM
