celestia/src/celcompat/fs.h

480 lines
12 KiB
C++

#pragma once
#ifdef _WIN32
#include <windows.h>
#else
#include <dirent.h>
#endif
#include <system_error>
#include <string>
#include <iostream>
#include <memory>
#ifdef _WIN32
#include <celutil/winutil.h>
#endif
namespace celestia
{
namespace filesystem
{
class filesystem_error : public std::system_error
{
public:
filesystem_error(std::error_code ec, const char* msg) :
std::system_error(ec, msg)
{
}
}; // filesystem_error
class path
{
public:
#ifdef _WIN32
using string_type = std::wstring;
#else
using string_type = std::string;
#endif
using value_type = string_type::value_type;
#ifdef _WIN32
static constexpr value_type preferred_separator = L'\\';
#else
static constexpr value_type preferred_separator = '/';
#endif
enum format
{
native_format,
generic_format,
auto_format
};
path() noexcept
{
}
path(const path&) = default;
path(path&& p) noexcept :
m_path(std::move(p.m_path)),
m_fmt(p.m_fmt)
{
}
path(string_type&& p, format fmt = auto_format) :
m_path(std::move(p)),
m_fmt(fmt)
{
#ifdef _WIN32
fixup_separators();
#endif
}
template<typename T> path(const T& p, format fmt = auto_format ) :
m_path(encconv(p)),
m_fmt(fmt)
{
#ifdef _WIN32
fixup_separators();
#endif
}
~path() = default;
path& operator=(const path& p) = default;
path& operator=(path&&) = default;
template<typename T> path& append(const T& p)
{
if (empty())
m_path = p;
else
m_path.append(1, preferred_separator).append(p);
return *this;
}
template<typename T> path& operator/=(const T& p)
{
return append(p);
}
template<typename T> path& concat(const T& p)
{
m_path += p;
return *this;
}
template<typename T> path& operator+=(const T& p)
{
return concat(p);
}
bool operator==(const path& other) const noexcept
{
return m_path == other.m_path && m_fmt == other.m_fmt;
}
bool operator!=(const path& other) const noexcept
{
return !(*this == other);
}
bool operator<(const path& other) const noexcept
{
return m_path < other.m_path;
}
bool operator>(const path& other) const noexcept
{
return m_path > other.m_path;
}
bool operator<=(const path& other) const noexcept
{
return m_path <= other.m_path;
}
bool operator>=(const path& other) const noexcept
{
return m_path >= other.m_path;
}
std::string string() const noexcept
{
#ifdef _WIN32
return WideToCurrentCP(m_path);
#else
return m_path;
#endif
}
std::wstring wstring() const noexcept
{
#ifdef _WIN32
return m_path;
#else
return L""; // FIXME
#endif
}
std::string u8string() const noexcept
{
#ifdef _WIN32
return WideToUTF8(m_path);
#else
return m_path; // FIXME
#endif
}
const value_type* c_str() const noexcept
{
return m_path.c_str();
}
const string_type& native() const noexcept
{
return m_path;
}
operator string_type() const noexcept
{
return m_path;
}
bool empty() const noexcept
{
return m_path.empty();
}
path filename() const;
path stem() const;
path extension() const;
path parent_path() const;
path& replace_extension(const path& replacement = path());
bool is_relative() const
{
return !is_absolute();
}
bool is_absolute() const;
private:
inline string_type encconv(const std::string& p) const
{
#ifdef _WIN32
return CurrentCPToWide(p);
#else
return p;
#endif
}
inline string_type encconv(const std::wstring& p) const
{
#ifdef _WIN32
return p;
#else
(void)p;
return ""; // FIXME
#endif
}
#ifdef _WIN32
void fixup_separators();
#endif
string_type m_path;
format m_fmt { auto_format };
}; // path
path operator/(const path& lhs, const path& rhs);
std::ostream& operator<<(std::ostream& os, const path& p);
path u8path(const std::string& source);
enum class directory_options : unsigned char {
none = 0,
follow_directory_symlink = 1, // Not implemented
skip_permission_denied = 2
};
inline constexpr directory_options operator&(directory_options _LHS,
directory_options _RHS) {
return static_cast<directory_options>(static_cast<unsigned char>(_LHS) &
static_cast<unsigned char>(_RHS));
}
inline constexpr directory_options operator|(directory_options _LHS,
directory_options _RHS) {
return static_cast<directory_options>(static_cast<unsigned char>(_LHS) |
static_cast<unsigned char>(_RHS));
}
inline constexpr directory_options operator^(directory_options _LHS,
directory_options _RHS) {
return static_cast<directory_options>(static_cast<unsigned char>(_LHS) ^
static_cast<unsigned char>(_RHS));
}
inline constexpr directory_options operator~(directory_options _LHS) {
return static_cast<directory_options>(~static_cast<unsigned char>(_LHS));
}
inline directory_options& operator&=(directory_options& _LHS,
directory_options _RHS) {
return _LHS = _LHS & _RHS;
}
inline directory_options& operator|=(directory_options& _LHS,
directory_options _RHS) {
return _LHS = _LHS | _RHS;
}
inline directory_options& operator^=(directory_options& _LHS,
directory_options _RHS) {
return _LHS = _LHS ^ _RHS;
}
class directory_iterator;
class recursive_directory_iterator;
class directory_entry
{
public:
directory_entry() = default;
directory_entry(const directory_entry&) = default;
directory_entry(directory_entry&&) = default;
explicit directory_entry(const path& p) :
m_path(p)
{
};
directory_entry& operator=(const directory_entry&) = default;
directory_entry& operator=(directory_entry&&) = default;
const class path& path() const noexcept
{
return m_path;
}
operator const class path& () const noexcept
{
return m_path;
};
bool operator==(const directory_entry& other) const noexcept
{
return m_path == other.m_path;
}
bool operator!=(const directory_entry& other) const noexcept
{
return !(*this == other);
}
private:
friend class directory_iterator;
friend class recursive_directory_iterator;
class path m_path;
}; // directory_entry
class directory_iterator
{
public:
using value_type = directory_entry;
using difference_type = std::ptrdiff_t;
using pointer = const directory_entry*;
using reference = const directory_entry&;
using iterator_category = std::input_iterator_tag;
directory_iterator() = default;
explicit directory_iterator(const path& p);
directory_iterator(const path& p, directory_options options);
directory_iterator(const path& p, std::error_code& ec);
directory_iterator(const path& p, directory_options options, std::error_code& ec);
directory_iterator(const directory_iterator&) = default;
directory_iterator(directory_iterator&&) = default;
~directory_iterator() = default;
directory_iterator& operator=(const directory_iterator&) = default;
directory_iterator& operator=(directory_iterator&&) = default;
directory_iterator& operator++();
directory_iterator& increment(std::error_code&);
const directory_entry& operator*() const noexcept
{
return m_entry;
}
const directory_entry* operator->() const noexcept
{
return &m_entry;
}
bool operator==(const directory_iterator& other) const noexcept;
bool operator!=(const directory_iterator& other) const noexcept
{
return !(*this == other);
}
private:
friend class recursive_directory_iterator;
directory_iterator(const path& p, directory_options options, std::error_code* ec, bool advance = true);
directory_iterator& __increment(std::error_code* ec = nullptr);
struct SearchImpl;
void reset();
path m_path {};
directory_entry m_entry {};
std::shared_ptr<SearchImpl> m_search { nullptr };
}; // directory_iterator
inline directory_iterator begin(directory_iterator iter) noexcept
{
return iter;
}
inline directory_iterator end(directory_iterator) noexcept
{
return directory_iterator();
}
class recursive_directory_iterator
{
public:
using value_type = directory_entry;
using difference_type = std::ptrdiff_t;
using pointer = const directory_entry*;
using reference = const directory_entry&;
using iterator_category = std::input_iterator_tag;
recursive_directory_iterator() = default;
recursive_directory_iterator(const recursive_directory_iterator&) = default;
recursive_directory_iterator(recursive_directory_iterator&&) noexcept = default;
explicit recursive_directory_iterator(const path& p);
recursive_directory_iterator(const path& p, directory_options options);
recursive_directory_iterator(const path& p, std::error_code& ec);
recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec);
~recursive_directory_iterator() = default;
recursive_directory_iterator& operator=(const recursive_directory_iterator&) = default;
recursive_directory_iterator& operator=(recursive_directory_iterator&&) = default;
bool recursion_pending() const
{
return m_pending;
};
void disable_recursion_pending()
{
m_pending = false;
};
void pop();
void pop(std::error_code& ec);
recursive_directory_iterator& operator++();
recursive_directory_iterator& increment(std::error_code&);
const directory_entry& operator*() const noexcept
{
return *m_iter;
}
const directory_entry* operator->() const noexcept
{
return &*m_iter;
}
bool operator==(const recursive_directory_iterator& other) const noexcept;
bool operator!=(const recursive_directory_iterator& other) const noexcept
{
return !(*this == other);
}
private:
recursive_directory_iterator(const path& p, directory_options options, std::error_code* ec);
recursive_directory_iterator& __increment(std::error_code* ec = nullptr);
void reset();
directory_options m_options { directory_options::none };
std::error_code m_ec {};
int m_depth { 0 };
bool m_pending { true };
struct DirStack;
std::shared_ptr<DirStack> m_dirs { nullptr };
directory_iterator m_iter {};
}; // recursive_directory_iterator
inline recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept
{
return iter;
}
inline recursive_directory_iterator end(recursive_directory_iterator) noexcept
{
return recursive_directory_iterator();
}
uintmax_t file_size(const path& p, std::error_code& ec) noexcept;
uintmax_t file_size(const path& p);
bool exists(const path& p);
bool exists(const path& p, std::error_code& ec) noexcept;
bool is_directory(const path& p);
bool is_directory(const path& p, std::error_code& ec) noexcept;
bool create_directory(const path& p);
bool create_directory(const path& p, std::error_code& ec) noexcept;
path current_path();
path current_path(std::error_code& ec);
void current_path(const path& p);
void current_path(const path& p, std::error_code& ec) noexcept;
}
}