| @@ -3,4 +3,5 @@ | |||||
| #include <cppfs/config.h> | #include <cppfs/config.h> | ||||
| #include <cppfs/directory.h> | #include <cppfs/directory.h> | ||||
| #include <cppfs/file.h> | #include <cppfs/file.h> | ||||
| #include <cppfs/file_events.h> | |||||
| #include <cppfs/path.h> | #include <cppfs/path.h> | ||||
| @@ -19,8 +19,6 @@ namespace cppfs | |||||
| static constexpr char path_delimiter = '/'; | static constexpr char path_delimiter = '/'; | ||||
| static constexpr const char * temp_dir = "/tmp"; | static constexpr const char * temp_dir = "/tmp"; | ||||
| }; | }; | ||||
| using fd_handle = int; | |||||
| #endif | #endif | ||||
| } | } | ||||
| @@ -66,12 +66,6 @@ namespace cppfs | |||||
| #endif | #endif | ||||
| }; | }; | ||||
| public: | |||||
| /** | |||||
| * @brief Get the default file permissions for directories. | |||||
| */ | |||||
| static inline const file_permissions& default_permissions(); | |||||
| private: | private: | ||||
| cppfs::path _path; | cppfs::path _path; | ||||
| @@ -97,7 +91,7 @@ namespace cppfs | |||||
| */ | */ | ||||
| inline const directory& create( | inline const directory& create( | ||||
| bool p_parent, | bool p_parent, | ||||
| const file_permissions& p_perm = default_permissions()) const; | |||||
| const file_permissions& p_perm = file_permissions::defaults()) const; | |||||
| /** | /** | ||||
| * @brief Remove the directory (and all it's content if the parameter is set to true) | * @brief Remove the directory (and all it's content if the parameter is set to true) | ||||
| @@ -9,24 +9,6 @@ namespace cppfs | |||||
| /* directory */ | /* directory */ | ||||
| const file_permissions& directory::default_permissions() | |||||
| { | |||||
| static const file_permissions value({ | |||||
| file_permission::user_read, | |||||
| file_permission::user_write, | |||||
| file_permission::user_execute, | |||||
| file_permission::group_read, | |||||
| file_permission::group_write, | |||||
| file_permission::group_execute, | |||||
| file_permission::others_read, | |||||
| file_permission::others_execute, | |||||
| }); | |||||
| return value; | |||||
| } | |||||
| template<typename... T_args> | template<typename... T_args> | ||||
| directory::directory(T_args&&... p_args) | directory::directory(T_args&&... p_args) | ||||
| : _path(std::forward<T_args>(p_args)...) | : _path(std::forward<T_args>(p_args)...) | ||||
| @@ -2,13 +2,37 @@ | |||||
| #include <string> | #include <string> | ||||
| #include <cppcore/misc/flags.h> | |||||
| #include <cppfs/config.h> | |||||
| #include "../path.h" | #include "../path.h" | ||||
| #include "../misc.h" | #include "../misc.h" | ||||
| namespace cppfs | namespace cppfs | ||||
| { | { | ||||
| enum class access_mode | |||||
| { | |||||
| unknown = 0, | |||||
| read_only, | |||||
| write_only, | |||||
| read_write, | |||||
| }; | |||||
| enum class open_flag | |||||
| { | |||||
| unknown = 0, | |||||
| create, | |||||
| append, | |||||
| truncate, | |||||
| non_blocking, | |||||
| }; | |||||
| using open_flags = cppcore::shifted_flags<open_flag>; | |||||
| struct file | struct file | ||||
| : public raw_handle | |||||
| { | { | ||||
| private: | private: | ||||
| cppfs::path _path; | cppfs::path _path; | ||||
| @@ -20,6 +44,11 @@ namespace cppfs | |||||
| template<typename... T_args> | template<typename... T_args> | ||||
| inline file(T_args&&... p_args); | inline file(T_args&&... p_args); | ||||
| /** | |||||
| * @brief Destrutor. | |||||
| */ | |||||
| inline ~file(); | |||||
| /** | /** | ||||
| * @brief Get the path of the file. | * @brief Get the path of the file. | ||||
| */ | */ | ||||
| @@ -30,10 +59,42 @@ namespace cppfs | |||||
| */ | */ | ||||
| inline bool exists() const; | inline bool exists() const; | ||||
| /** | |||||
| * @brief Get the size of the file. | |||||
| */ | |||||
| inline size_t size() const; | |||||
| /** | /** | ||||
| * @brief Remove the file. | * @brief Remove the file. | ||||
| */ | */ | ||||
| inline const file& remove() const; | inline const file& remove() const; | ||||
| /** | |||||
| * @brief Open the file for reading or writing. | |||||
| */ | |||||
| inline void open( | |||||
| const access_mode& p_access, | |||||
| const open_flags& p_flags = open_flags::empty(), | |||||
| const file_permissions& p_permissions = file_permissions::defaults()); | |||||
| /** | |||||
| * @brief Read data from the file and store it in the passed buffer. | |||||
| */ | |||||
| inline size_t read( | |||||
| void * p_buffer, | |||||
| size_t p_count); | |||||
| /** | |||||
| * @brief Write data from the passed buffer to the file. | |||||
| */ | |||||
| inline size_t write( | |||||
| const void * p_buffer, | |||||
| size_t p_count); | |||||
| /** | |||||
| * @brief Close the file. | |||||
| */ | |||||
| inline void close(); | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -12,6 +12,9 @@ namespace cppfs | |||||
| : _path(std::forward<T_args>(p_args)...) | : _path(std::forward<T_args>(p_args)...) | ||||
| { } | { } | ||||
| file::~file() | |||||
| { close(); } | |||||
| const cppfs::path& file::path() const | const cppfs::path& file::path() const | ||||
| { return _path; } | { return _path; } | ||||
| @@ -1,5 +1,8 @@ | |||||
| #pragma once | #pragma once | ||||
| #include <fcntl.h> | |||||
| #include <sys/stat.h> | |||||
| #include "file.h" | #include "file.h" | ||||
| namespace cppfs | namespace cppfs | ||||
| @@ -7,6 +10,20 @@ namespace cppfs | |||||
| /* file */ | /* file */ | ||||
| size_t file::size() const | |||||
| { | |||||
| struct stat st; | |||||
| auto& s = _path.str(); | |||||
| if (stat(s.c_str(), &st)) | |||||
| { | |||||
| using namespace std::string_literals; | |||||
| throw cppcore::error_exception("Unable to get file size: "s + s, errno); | |||||
| } | |||||
| return cppcore::convert_cast<size_t>(st.st_size); | |||||
| } | |||||
| const file& file::remove() const | const file& file::remove() const | ||||
| { | { | ||||
| using namespace std::string_literals; | using namespace std::string_literals; | ||||
| @@ -18,4 +35,89 @@ namespace cppfs | |||||
| return *this; | return *this; | ||||
| } | } | ||||
| void file::open( | |||||
| const access_mode& p_access, | |||||
| const open_flags& p_flags, | |||||
| const file_permissions& p_permissions) | |||||
| { | |||||
| close(); | |||||
| int flags = 0; | |||||
| switch (p_access) | |||||
| { | |||||
| case access_mode::read_only: | |||||
| flags |= O_RDONLY; | |||||
| break; | |||||
| case access_mode::write_only: | |||||
| flags |= O_WRONLY; | |||||
| break; | |||||
| case access_mode::read_write: | |||||
| flags |= O_RDWR; | |||||
| break; | |||||
| case access_mode::unknown: | |||||
| throw cppcore::exception("Invalid access mode: unknown"); | |||||
| } | |||||
| if (p_flags.is_set(open_flag::create)) flags |= O_CREAT; | |||||
| if (p_flags.is_set(open_flag::append)) flags |= O_APPEND; | |||||
| if (p_flags.is_set(open_flag::truncate)) flags |= O_TRUNC; | |||||
| if (p_flags.is_set(open_flag::non_blocking)) flags |= O_NONBLOCK; | |||||
| auto& s = _path.str(); | |||||
| _handle = ::open(s.c_str(), flags, file_permissions::to_unix(p_permissions)); | |||||
| if (_handle < 0) | |||||
| { | |||||
| using namespace std::string_literals; | |||||
| throw cppcore::error_exception("Unable to open file: "s + s, errno); | |||||
| } | |||||
| } | |||||
| size_t file::read( | |||||
| void * p_buffer, | |||||
| size_t p_count) | |||||
| { | |||||
| if (_handle < 0) | |||||
| open(access_mode::read_only, open_flags::empty(), file_permissions::defaults()); | |||||
| ssize_t ret = ::read(_handle, p_buffer, p_count); | |||||
| if (ret < 0) | |||||
| { | |||||
| using namespace std::string_literals; | |||||
| throw cppcore::error_exception("Unable to read from file: "s + _path.str(), errno); | |||||
| } | |||||
| return static_cast<size_t>(ret); | |||||
| } | |||||
| size_t file::write( | |||||
| const void * p_buffer, | |||||
| size_t p_count) | |||||
| { | |||||
| if (_handle < 0) | |||||
| open(access_mode::write_only, open_flag::append, file_permissions::defaults()); | |||||
| ssize_t ret = ::write(_handle, p_buffer, p_count); | |||||
| if (ret < 0) | |||||
| { | |||||
| using namespace std::string_literals; | |||||
| throw cppcore::error_exception("Unable to write to file: "s + _path.str(), errno); | |||||
| } | |||||
| return static_cast<size_t>(ret); | |||||
| } | |||||
| void file::close() | |||||
| { | |||||
| if (_handle >= 0) | |||||
| { | |||||
| if (::close(_handle)) | |||||
| { | |||||
| using namespace std::string_literals; | |||||
| throw cppcore::error_exception("Unable to close file: "s + _path.str(), errno); | |||||
| } | |||||
| _handle = -1; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -0,0 +1,15 @@ | |||||
| #pragma once | |||||
| #include <cppfs/config.h> | |||||
| #include "file_events/file_events.h" | |||||
| #if cppfs_os == cppfs_os_linux | |||||
| #include "file_events/select.h" | |||||
| #endif | |||||
| #include "file_events/file_events.inl" | |||||
| #if cppfs_os == cppfs_os_linux | |||||
| #include "file_events/select.inl" | |||||
| #endif | |||||
| @@ -0,0 +1,59 @@ | |||||
| #pragma once | |||||
| #include <sys/select.h> | |||||
| namespace cppfs | |||||
| { | |||||
| struct fdset final | |||||
| : public fd_set | |||||
| { | |||||
| public: | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| inline fdset(); | |||||
| /** | |||||
| * @brief Check if any file descriptor is set in this set. | |||||
| * | |||||
| * @param[in] max_fd Largest file descriptor stored. | |||||
| * | |||||
| * @retval true If any file descriptor is set. | |||||
| * @retval false If no file descriptor is set. | |||||
| */ | |||||
| inline bool is_any(int max_fd) const; | |||||
| /** | |||||
| * @brief Check if the passed file descriptor is set in this set. | |||||
| * | |||||
| * @param[in] fd File descriptor to check for. | |||||
| * | |||||
| * @retval true If the passed file descriptor is set. | |||||
| * @retval false If the passed file descriptor is not set. | |||||
| */ | |||||
| inline bool is_set(int fd) const; | |||||
| /** | |||||
| * @brief Set the passed file descriptor in the set. | |||||
| * | |||||
| * @param[in] fd File descriptor to set. | |||||
| */ | |||||
| inline void set(int fd); | |||||
| /** | |||||
| * @brief Clear the passed file descriptor in the set. | |||||
| * | |||||
| * @param[in] fd File descriptor to clear. | |||||
| */ | |||||
| inline void clear(int fd); | |||||
| /** | |||||
| * @brief Clear the complete file descriptor set. | |||||
| */ | |||||
| inline void reset(); | |||||
| }; | |||||
| } | |||||
| #include "fdset.inl" | |||||
| @@ -0,0 +1,63 @@ | |||||
| #pragma once | |||||
| #include "fdset.h" | |||||
| namespace cppfs | |||||
| { | |||||
| /* fdset */ | |||||
| static_assert( | |||||
| sizeof(fd_set) == sizeof(fdset), | |||||
| "Size of fd set implementation does not match."); | |||||
| fdset::fdset() | |||||
| { | |||||
| FD_ZERO(this); | |||||
| } | |||||
| bool fdset::is_any(int max_fd) const | |||||
| { | |||||
| static constexpr int field_count = sizeof(fds_bits) / sizeof(fds_bits[0]); | |||||
| if (max_fd >= FD_SETSIZE) | |||||
| throw cppcore::exception("Size of fd set exceeded!"); | |||||
| if (max_fd < 0) | |||||
| return false; | |||||
| auto cnt = (max_fd / field_count) + 1; | |||||
| for (int i = 0; i < cnt; ++i) | |||||
| { | |||||
| if (fds_bits[i]) | |||||
| return true; | |||||
| } | |||||
| return false; | |||||
| } | |||||
| bool fdset::is_set(int fd) const | |||||
| { | |||||
| if (fd >= FD_SETSIZE) | |||||
| throw cppcore::exception("Size of fd set exceeded!"); | |||||
| return FD_ISSET(fd, this); | |||||
| } | |||||
| void fdset::set(int fd) | |||||
| { | |||||
| if (fd >= FD_SETSIZE) | |||||
| throw cppcore::exception("Size of fd set exceeded!"); | |||||
| FD_SET(fd, this); | |||||
| } | |||||
| void fdset::clear(int fd) | |||||
| { | |||||
| if (fd >= FD_SETSIZE) | |||||
| throw cppcore::exception("Size of fd set exceeded!"); | |||||
| FD_CLR(fd, this); | |||||
| } | |||||
| void fdset::reset() | |||||
| { FD_ZERO(this); } | |||||
| } | |||||
| @@ -0,0 +1,96 @@ | |||||
| #pragma once | |||||
| #include <cppfs/misc.h> | |||||
| #include "types.h" | |||||
| namespace cppfs | |||||
| { | |||||
| template<typename T_impl> | |||||
| struct file_events | |||||
| { | |||||
| public: | |||||
| using impl_type = T_impl; | |||||
| using result_impl_type = typename impl_type::result_type; | |||||
| struct result | |||||
| { | |||||
| private: | |||||
| result_impl_type _impl; | |||||
| public: | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| template<typename X_impl> | |||||
| inline result(X_impl&& p_impl); | |||||
| /** | |||||
| * @brief Check if any event has occured in any handle. | |||||
| */ | |||||
| bool is_set() const; | |||||
| /** | |||||
| * @brief Check if any event has occured in the passed handle. | |||||
| */ | |||||
| bool is_set( | |||||
| const raw_handle& p_handle) const; | |||||
| /** | |||||
| * @brief Check if any of the passed event has occured in any handle. | |||||
| */ | |||||
| bool is_set( | |||||
| const event_types& p_events) const; | |||||
| /** | |||||
| * @brief Check if any of the passed event has occured in the passed handle. | |||||
| */ | |||||
| bool is_set( | |||||
| const event_types& p_events, | |||||
| const raw_handle& p_handle) const; | |||||
| }; | |||||
| private: | |||||
| impl_type _impl; | |||||
| public: | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| inline file_events(); | |||||
| /** | |||||
| * @brief Add or change a event listener for the passed file. | |||||
| */ | |||||
| inline void add( | |||||
| const raw_handle& p_handle, | |||||
| const event_types& p_events); | |||||
| /** | |||||
| * @brief Remove the event listener for the passed file. | |||||
| */ | |||||
| inline void erase( | |||||
| const raw_handle& p_handle); | |||||
| /** | |||||
| * @brief Reset all event listeners. | |||||
| */ | |||||
| inline void reset(); | |||||
| /** | |||||
| * @brief Wait for file events and return the result. | |||||
| */ | |||||
| inline result wait( | |||||
| const event_timeout * p_timeout) const; | |||||
| /** | |||||
| * @brief Wait for file events and call the passed callback for each event. | |||||
| */ | |||||
| template<typename T_lambda> | |||||
| inline void wait( | |||||
| T_lambda&& p_lambda, | |||||
| const event_timeout * p_timeout) const; | |||||
| }; | |||||
| } | |||||
| @@ -0,0 +1,89 @@ | |||||
| #pragma once | |||||
| #include "file_events.h" | |||||
| namespace cppfs | |||||
| { | |||||
| /* file_events::result */ | |||||
| template<typename T_impl> | |||||
| template<typename X_impl> | |||||
| file_events<T_impl>::result::result(X_impl&& p_impl) | |||||
| : _impl(std::forward<X_impl>(p_impl)) | |||||
| { } | |||||
| template<typename T_impl> | |||||
| bool file_events<T_impl>::result::is_set() const | |||||
| { | |||||
| static const event_types all_types({ | |||||
| event_type::read, | |||||
| event_type::write, | |||||
| event_type::except, | |||||
| }); | |||||
| return _impl.is_set(all_types, nullptr); | |||||
| } | |||||
| template<typename T_impl> | |||||
| bool file_events<T_impl>::result::is_set( | |||||
| const raw_handle& p_handle) const | |||||
| { | |||||
| static const event_types all_types({ | |||||
| event_type::read, | |||||
| event_type::write, | |||||
| event_type::except, | |||||
| }); | |||||
| return _impl.is_set(all_types, &p_handle); | |||||
| } | |||||
| template<typename T_impl> | |||||
| bool file_events<T_impl>::result::is_set( | |||||
| const event_types& p_events) const | |||||
| { | |||||
| return _impl.is_set(p_events, nullptr); | |||||
| } | |||||
| template<typename T_impl> | |||||
| bool file_events<T_impl>::result::is_set( | |||||
| const event_types& p_events, | |||||
| const raw_handle& p_handle) const | |||||
| { | |||||
| return _impl.is_set(p_events, &p_handle); | |||||
| } | |||||
| /* file_events */ | |||||
| template<typename T_impl> | |||||
| file_events<T_impl>::file_events() | |||||
| : _impl() | |||||
| { } | |||||
| template<typename T_impl> | |||||
| void file_events<T_impl>::add( | |||||
| const raw_handle& p_handle, | |||||
| const event_types& p_events) | |||||
| { _impl.add(p_handle, p_events); } | |||||
| template<typename T_impl> | |||||
| void file_events<T_impl>::erase( | |||||
| const raw_handle& p_handle) | |||||
| { _impl.erase(p_handle); } | |||||
| template<typename T_impl> | |||||
| void file_events<T_impl>::reset() | |||||
| { _impl = impl_type(); } | |||||
| template<typename T_impl> | |||||
| typename file_events<T_impl>::result | |||||
| file_events<T_impl>::wait( | |||||
| const event_timeout * p_timeout) const | |||||
| { return result(_impl.wait(p_timeout)); } | |||||
| template<typename T_impl> | |||||
| template<typename T_lambda> | |||||
| void file_events<T_impl>::wait( | |||||
| T_lambda&& p_lambda, | |||||
| const event_timeout * p_timeout) const | |||||
| { _impl.wait(std::forward<T_lambda>(p_lambda), p_timeout); } | |||||
| } | |||||
| @@ -0,0 +1,68 @@ | |||||
| #pragma once | |||||
| #include <sys/select.h> | |||||
| #include <cppfs/file/file.h> | |||||
| #include "types.h" | |||||
| #include "fdset.h" | |||||
| namespace cppfs | |||||
| { | |||||
| struct fe_select | |||||
| { | |||||
| public: | |||||
| struct storage | |||||
| { | |||||
| int max_fd { -1 }; | |||||
| fdset read; | |||||
| fdset write; | |||||
| fdset except; | |||||
| inline bool is_set( | |||||
| const event_types& p_events, | |||||
| const raw_handle* p_handle) const; | |||||
| }; | |||||
| using result_type = storage; | |||||
| private: | |||||
| storage _storage; | |||||
| public: | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| inline fe_select() = default; | |||||
| public: /* file_events implementation */ | |||||
| /** | |||||
| * @brief Add or change a event listener for the passed file. | |||||
| */ | |||||
| inline void add( | |||||
| const raw_handle& p_handle, | |||||
| const event_types& p_events); | |||||
| /** | |||||
| * @brief Remove the event listener for the passed handle. | |||||
| */ | |||||
| inline void erase( | |||||
| const raw_handle& p_handle); | |||||
| /** | |||||
| * @brief Wait for file events and return the result. | |||||
| */ | |||||
| inline result_type wait( | |||||
| const event_timeout * p_timeout) const; | |||||
| /** | |||||
| * @brief Wait for file events and call the passed callback for each event. | |||||
| */ | |||||
| template<typename T_lambda> | |||||
| inline void wait( | |||||
| T_lambda&& p_lambda, | |||||
| const event_timeout * p_timeout) const; | |||||
| }; | |||||
| } | |||||
| @@ -0,0 +1,114 @@ | |||||
| #pragma once | |||||
| #include <cppcore/conversion/time.h> | |||||
| #include "select.h" | |||||
| namespace cppfs | |||||
| { | |||||
| /* fe_select::storage */ | |||||
| bool fe_select::storage::is_set( | |||||
| const event_types& p_events, | |||||
| const raw_handle* p_handle) const | |||||
| { | |||||
| if (p_handle) | |||||
| { | |||||
| auto fd = p_handle->handle(); | |||||
| return (p_events.is_set(event_type::read) && read.is_set(fd)) | |||||
| || (p_events.is_set(event_type::write) && write.is_set(fd)) | |||||
| || (p_events.is_set(event_type::except) && except.is_set(fd)); | |||||
| } | |||||
| else | |||||
| { | |||||
| return (p_events.is_set(event_type::read) && read.is_any(max_fd)) | |||||
| || (p_events.is_set(event_type::write) && write.is_any(max_fd)) | |||||
| || (p_events.is_set(event_type::except) && except.is_any(max_fd)); | |||||
| } | |||||
| } | |||||
| /* fe_select */ | |||||
| void fe_select::add( | |||||
| const raw_handle& p_handle, | |||||
| const event_types& p_events) | |||||
| { | |||||
| int fd = p_handle.handle(); | |||||
| if (fd < 0) | |||||
| throw cppcore::exception("The passed file is not opened!"); | |||||
| if (p_events.is_set(event_type::read)) _storage.read.set(fd); | |||||
| if (p_events.is_set(event_type::write)) _storage.write.set(fd); | |||||
| if (p_events.is_set(event_type::except)) _storage.except.set(fd); | |||||
| if ( !_storage.read.is_set(fd) | |||||
| && !_storage.write.is_set(fd) | |||||
| && !_storage.except.is_set(fd)) | |||||
| throw cppcore::exception("No or unkown events passed!"); | |||||
| _storage.max_fd = std::max(fd, _storage.max_fd); | |||||
| } | |||||
| void fe_select::erase(const raw_handle& p_handle) | |||||
| { | |||||
| int fd = p_handle.handle(); | |||||
| if (fd < 0) | |||||
| throw cppcore::exception("The passed file is not opened!"); | |||||
| _storage.read.clear(fd); | |||||
| _storage.write.clear(fd); | |||||
| _storage.except.clear(fd); | |||||
| if (fd == _storage.max_fd) | |||||
| { | |||||
| while (_storage.max_fd >= 0 | |||||
| && !_storage.read.is_set(_storage.max_fd) | |||||
| && !_storage.write.is_set(_storage.max_fd) | |||||
| && !_storage.except.is_set(_storage.max_fd)) | |||||
| --_storage.max_fd; | |||||
| } | |||||
| } | |||||
| fe_select::result_type fe_select::wait(const event_timeout * p_timeout) const | |||||
| { | |||||
| auto ret = _storage; | |||||
| struct timespec timeout { 0, 0 }; | |||||
| if (p_timeout) | |||||
| { | |||||
| auto now = std::chrono::steady_clock::now(); | |||||
| if (now < *p_timeout) | |||||
| { | |||||
| auto diff = *p_timeout - now; | |||||
| timeout = cppcore::duration_cast<timespec>(diff); | |||||
| } | |||||
| } | |||||
| if (::pselect(ret.max_fd + 1, &ret.read, &ret.write, &ret.except, p_timeout ? &timeout : nullptr, nullptr) < 0) | |||||
| { | |||||
| auto err = errno; | |||||
| if (err != EINTR) | |||||
| throw cppcore::error_exception("Error while fetching file events with 'select'", err); | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| template<typename T_lambda> | |||||
| void fe_select::wait(T_lambda&& p_lambda, const event_timeout * p_timeout) const | |||||
| { | |||||
| auto ret = wait(p_timeout); | |||||
| for (auto fd = 0; fd <= ret.max_fd; ++fd) | |||||
| { | |||||
| event_types ev; | |||||
| if (ret.read.is_set(fd)) ev.set(event_type::read); | |||||
| if (ret.write.is_set(fd)) ev.set(event_type::write); | |||||
| if (ret.except.is_set(fd)) ev.set(event_type::except); | |||||
| if (ev) | |||||
| p_lambda(fd, ev); | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,23 @@ | |||||
| #pragma once | |||||
| #include <chrono> | |||||
| #include <cppcore/misc/flags.h> | |||||
| namespace cppfs | |||||
| { | |||||
| enum class event_type | |||||
| { | |||||
| unknown = 0, | |||||
| read, | |||||
| write, | |||||
| except, | |||||
| }; | |||||
| using event_types = cppcore::shifted_flags<event_type>; | |||||
| using event_clock = std::chrono::steady_clock; | |||||
| using event_timeout = event_clock::time_point; | |||||
| } | |||||
| @@ -2,6 +2,8 @@ | |||||
| #include "misc/misc.h" | #include "misc/misc.h" | ||||
| #include "misc/misc.inl" | |||||
| #if cppfs_os == cppfs_os_linux | #if cppfs_os == cppfs_os_linux | ||||
| #include "misc/misc.linux.inl" | #include "misc/misc.linux.inl" | ||||
| #endif | #endif | ||||
| @@ -2,10 +2,6 @@ | |||||
| #include <cppcore/misc/flags.h> | #include <cppcore/misc/flags.h> | ||||
| #if cppfs_os == cppfs_os_linux | |||||
| #include <sys/stat.h> | |||||
| #endif | |||||
| namespace cppfs | namespace cppfs | ||||
| { | { | ||||
| @@ -39,6 +35,20 @@ namespace cppfs | |||||
| others_execute, | others_execute, | ||||
| }; | }; | ||||
| struct raw_handle | |||||
| { | |||||
| #if cppfs_os == cppfs_os_linux | |||||
| protected: | |||||
| int _handle { -1 }; | |||||
| public: | |||||
| /** | |||||
| * @brief Get the raw handle. | |||||
| */ | |||||
| inline int handle() const; | |||||
| #endif | |||||
| }; | |||||
| struct file_permissions | struct file_permissions | ||||
| : public cppcore::shifted_flags<file_permission> | : public cppcore::shifted_flags<file_permission> | ||||
| { | { | ||||
| @@ -48,6 +58,12 @@ namespace cppfs | |||||
| public: | public: | ||||
| using base_type::base_type; | using base_type::base_type; | ||||
| public: | |||||
| /** | |||||
| * @brief Get the default file permissions. | |||||
| */ | |||||
| inline static const file_permissions& defaults(); | |||||
| #if cppfs_os == cppfs_os_linux | #if cppfs_os == cppfs_os_linux | ||||
| public: | public: | ||||
| /** | /** | ||||
| @@ -0,0 +1,26 @@ | |||||
| #pragma once | |||||
| #include "misc.h" | |||||
| namespace cppfs | |||||
| { | |||||
| const file_permissions& file_permissions::defaults() | |||||
| { | |||||
| static const file_permissions value({ | |||||
| file_permission::user_read, | |||||
| file_permission::user_write, | |||||
| file_permission::user_execute, | |||||
| file_permission::group_read, | |||||
| file_permission::group_write, | |||||
| file_permission::group_execute, | |||||
| file_permission::others_read, | |||||
| file_permission::others_execute, | |||||
| }); | |||||
| return value; | |||||
| } | |||||
| } | |||||
| @@ -5,6 +5,8 @@ | |||||
| namespace cppfs | namespace cppfs | ||||
| { | { | ||||
| /* file_permissions */ | |||||
| mode_t file_permissions::to_unix(const file_permissions& p_perm) | mode_t file_permissions::to_unix(const file_permissions& p_perm) | ||||
| { | { | ||||
| mode_t perm = 0; | mode_t perm = 0; | ||||
| @@ -43,4 +45,9 @@ namespace cppfs | |||||
| return perm; | return perm; | ||||
| } | } | ||||
| /* raw_handle */ | |||||
| int raw_handle::handle() const | |||||
| { return _handle; } | |||||
| } | } | ||||
| @@ -31,6 +31,8 @@ TEST(directory_tests, iterator) | |||||
| files, | files, | ||||
| std::vector<std::string>({ | std::vector<std::string>({ | ||||
| "directory_tests.cpp", | "directory_tests.cpp", | ||||
| "file_events_tests.cpp", | |||||
| "file_tests.cpp", | |||||
| "path_tests.cpp", | "path_tests.cpp", | ||||
| })); | })); | ||||
| } | } | ||||
| @@ -0,0 +1,108 @@ | |||||
| #include <gtest/gtest.h> | |||||
| #include <fstream> | |||||
| #include <cppfs.h> | |||||
| using namespace ::testing; | |||||
| using namespace ::cppfs; | |||||
| struct pipe_handle | |||||
| : public raw_handle | |||||
| { | |||||
| pipe_handle(int p_handle) | |||||
| { | |||||
| _handle = p_handle; | |||||
| } | |||||
| ~pipe_handle() | |||||
| { | |||||
| close(_handle); | |||||
| } | |||||
| }; | |||||
| TEST(file_events_tests, select_with_callback) | |||||
| { | |||||
| int fds[2]; | |||||
| ASSERT_EQ(0, pipe(fds)); | |||||
| pipe_handle read_handle(fds[0]); | |||||
| pipe_handle write_handle(fds[1]); | |||||
| file_events<fe_select> fe; | |||||
| fe.add(read_handle, event_types::all()); | |||||
| fe.add(write_handle, event_types::all()); | |||||
| auto timeout = event_clock::now(); | |||||
| fe.wait( | |||||
| [&](auto fd, auto events) { | |||||
| EXPECT_EQ(fd, write_handle.handle()); | |||||
| }, | |||||
| &timeout); | |||||
| write(write_handle.handle(), "fuuu", 4); | |||||
| auto i = 0; | |||||
| fe.wait( | |||||
| [&](auto fd, auto events) { | |||||
| switch (i) | |||||
| { | |||||
| case 0: | |||||
| EXPECT_EQ(fd, read_handle.handle()); | |||||
| EXPECT_EQ(events, event_types(event_type::read)); | |||||
| break; | |||||
| case 1: | |||||
| EXPECT_EQ(fd, write_handle.handle()); | |||||
| EXPECT_EQ(events, event_types(event_type::write)); | |||||
| break; | |||||
| } | |||||
| ++i; | |||||
| }, | |||||
| &timeout); | |||||
| } | |||||
| TEST(file_events_tests, select_with_result) | |||||
| { | |||||
| int fds[2]; | |||||
| ASSERT_EQ(0, pipe(fds)); | |||||
| pipe_handle read_handle(fds[0]); | |||||
| pipe_handle write_handle(fds[1]); | |||||
| file_events<fe_select> fe; | |||||
| fe.add(read_handle, event_types::all()); | |||||
| fe.add(write_handle, event_types::all()); | |||||
| auto timeout = event_clock::now(); | |||||
| auto ret = fe.wait(&timeout); | |||||
| EXPECT_TRUE (ret.is_set()); | |||||
| EXPECT_FALSE(ret.is_set(event_type::read)); | |||||
| EXPECT_TRUE (ret.is_set(event_type::write)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::except)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::read, read_handle)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::write, read_handle)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::except, read_handle)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::read, write_handle)); | |||||
| EXPECT_TRUE (ret.is_set(event_type::write, write_handle)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::except, write_handle)); | |||||
| write(write_handle.handle(), "fuuu", 4); | |||||
| ret = fe.wait(&timeout); | |||||
| EXPECT_TRUE (ret.is_set()); | |||||
| EXPECT_TRUE (ret.is_set(event_type::read)); | |||||
| EXPECT_TRUE (ret.is_set(event_type::write)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::except)); | |||||
| EXPECT_TRUE (ret.is_set(event_type::read, read_handle)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::write, read_handle)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::except, read_handle)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::read, write_handle)); | |||||
| EXPECT_TRUE (ret.is_set(event_type::write, write_handle)); | |||||
| EXPECT_FALSE(ret.is_set(event_type::except, write_handle)); | |||||
| } | |||||
| @@ -0,0 +1,73 @@ | |||||
| #include <gtest/gtest.h> | |||||
| #include <fstream> | |||||
| #include <cppfs.h> | |||||
| using namespace ::testing; | |||||
| using namespace ::cppfs; | |||||
| TEST(file_tests, exists) | |||||
| { | |||||
| EXPECT_FALSE(file("./cppfs").exists()); | |||||
| EXPECT_TRUE (file("./cppfs/file_tests.cpp").exists()); | |||||
| EXPECT_FALSE(file("./cppfs/asdf.txt").exists()); | |||||
| } | |||||
| TEST(file_tests, size) | |||||
| { | |||||
| EXPECT_EQ(1619, file("./cppfs/file_tests.cpp").size()); | |||||
| } | |||||
| TEST(file_tests, open_close_and_remove) | |||||
| { | |||||
| EXPECT_ANY_THROW(file("/tmp/cppfs/file_tests/test.file").open(access_mode::read_write)); | |||||
| file f("/tmp/cppfs_file_tests.test_file"); | |||||
| f.open(access_mode::read_write, open_flag::create); | |||||
| f.close(); | |||||
| EXPECT_TRUE(f.exists()); | |||||
| f.remove(); | |||||
| EXPECT_FALSE(f.exists()); | |||||
| } | |||||
| TEST(file_tests, open_write_close_remove) | |||||
| { | |||||
| file f("/tmp/cppfs_file_tests.write_file"); | |||||
| f.open(access_mode::write_only, open_flag::create); | |||||
| EXPECT_EQ(15, f.write("Hello World :)\n", 15)); | |||||
| f.close(); | |||||
| std::string s; | |||||
| std::getline(std::ifstream("/tmp/cppfs_file_tests.write_file"), s); | |||||
| EXPECT_EQ(s, "Hello World :)"); | |||||
| EXPECT_TRUE(f.exists()); | |||||
| f.remove(); | |||||
| EXPECT_FALSE(f.exists()); | |||||
| } | |||||
| TEST(file_tests, open_read_close_remove) | |||||
| { | |||||
| std::ofstream("/tmp/cppfs_file_tests.read_file") << "Hello World :)" << std::endl; | |||||
| char buffer[100] = { }; | |||||
| file f("/tmp/cppfs_file_tests.read_file"); | |||||
| f.open(access_mode::read_only); | |||||
| EXPECT_EQ(15, f.read(&buffer[0], sizeof(buffer))); | |||||
| f.close(); | |||||
| EXPECT_EQ(std::string(&buffer[0]), std::string("Hello World :)\n")); | |||||
| EXPECT_TRUE(f.exists()); | |||||
| f.remove(); | |||||
| EXPECT_FALSE(f.exists()); | |||||
| } | |||||