From f80d8ebf5be8f70dd6ac8044f84fd839cd574bda Mon Sep 17 00:00:00 2001 From: Michael Carlberg Date: Mon, 26 Dec 2016 10:29:32 +0100 Subject: [PATCH] file_util: File descriptor streams --- include/utils/file.hpp | 71 +++++++++++++++++-- src/utils/file.cpp | 156 ++++++++++++++++++++++++++++++++--------- 2 files changed, 188 insertions(+), 39 deletions(-) diff --git a/include/utils/file.hpp b/include/utils/file.hpp index bfe30de4..3b90d87b 100644 --- a/include/utils/file.hpp +++ b/include/utils/file.hpp @@ -10,14 +10,16 @@ class file_ptr { explicit file_ptr(const string& path, const string& mode = "a+"); ~file_ptr(); - file_ptr(const file_ptr& o) = delete; - file_ptr& operator=(const file_ptr& o) = delete; + explicit operator bool(); + operator bool() const; - operator bool(); + explicit operator FILE*(); + operator FILE*() const; - FILE* operator()(); + explicit operator int(); + operator int() const; - protected: + private: FILE* m_ptr = nullptr; string m_path; string m_mode; @@ -28,10 +30,65 @@ class file_descriptor { explicit file_descriptor(const string& path, int flags = 0); explicit file_descriptor(int fd); ~file_descriptor(); - operator int(); + + file_descriptor& operator=(const int); + + explicit operator bool(); + operator bool() const; + + explicit operator int(); + operator int() const; protected: - int m_fd{0}; + void close(); + + private: + int m_fd{-1}; +}; + +class fd_streambuf : public std::streambuf { + public: + using traits_type = std::streambuf::traits_type; + + explicit fd_streambuf(int fd); + ~fd_streambuf(); + + explicit operator int(); + operator int() const; + + void open(int fd); + void close(); + + protected: + int sync(); + int overflow(int c); + int underflow(); + + private: + file_descriptor m_fd; + char m_out[BUFSIZ]; + char m_in[BUFSIZ - 1]; +}; + +template +class fd_stream : public StreamType { + public: + using type = fd_stream; + + explicit fd_stream(int fd) : m_buf(fd) { + StreamType::rdbuf(&m_buf); + } + + explicit operator int() { + return static_cast(*this); + } + + operator int() const { + return m_buf; + } + + protected: + fd_streambuf m_buf; }; namespace file_util { diff --git a/src/utils/file.cpp b/src/utils/file.cpp index 99549a21..0387ab62 100644 --- a/src/utils/file.cpp +++ b/src/utils/file.cpp @@ -2,72 +2,164 @@ #include #include #include +#include #include "errors.hpp" #include "utils/file.hpp" POLYBAR_NS -/** - * Deconstruct file wrapper - */ +// implementation of file_ptr {{{ + file_ptr::file_ptr(const string& path, const string& mode) : m_path(string(path)), m_mode(string(mode)) { m_ptr = fopen(m_path.c_str(), m_mode.c_str()); } -/** - * Deconstruct file wrapper - */ file_ptr::~file_ptr() { if (m_ptr != nullptr) { fclose(m_ptr); } } -/** - * Logical operator testing if the file handler was created - */ file_ptr::operator bool() { + return static_cast(*this); +} +file_ptr::operator bool() const { return m_ptr != nullptr; } -/** - * Call operator returning a pointer to the file handler - */ -FILE* file_ptr::operator()() { +file_ptr::operator FILE*() { + return static_cast(*this); +} +file_ptr::operator FILE*() const { return m_ptr; } -/** - * Construct file descriptor wrapper - */ +file_ptr::operator int() { + return static_cast(*this); +} +file_ptr::operator int() const { + return fileno(*this); +} + +// }}} +// implementation of file_descriptor {{{ + file_descriptor::file_descriptor(const string& path, int flags) { if ((m_fd = open(path.c_str(), flags)) == -1) { throw system_error("Failed to open file descriptor"); } } -/** - * Construct file descriptor wrapper from an existing handle - */ -file_descriptor::file_descriptor(int fd) : m_fd(fd) {} - -/** - * Deconstruct file descriptor wrapper - */ -file_descriptor::~file_descriptor() { - if (m_fd > 0) { - close(m_fd); +file_descriptor::file_descriptor(int fd) : m_fd(fd) { + if (!*this) { + throw system_error("Given file descriptor is not valid"); } } -/** - * Conversion operator returning the fd handle - */ +file_descriptor::~file_descriptor() { + close(); +} + +file_descriptor& file_descriptor::operator=(const int fd) { + close(); + m_fd = fd; + return *this; +} + file_descriptor::operator int() { + return static_cast(*this); +} +file_descriptor::operator int() const { return m_fd; } +file_descriptor::operator bool() { + return static_cast(*this); +} +file_descriptor::operator bool() const { + errno = 0; // reset since fcntl only changes it on error + if ((fcntl(m_fd, F_GETFD) == -1) || errno == EBADF) { + errno = EBADF; + return false; + } + return true; +} + +void file_descriptor::close() { + if (m_fd == -1) { + return; + } else if (::close(m_fd) == -1) { + throw system_error("Failed to close file descriptor"); + } + m_fd = -1; +} + +// }}} +// implementation of file_streambuf {{{ + +fd_streambuf::fd_streambuf(int fd) : m_fd(fd) {} + +fd_streambuf::~fd_streambuf() { + sync(); +} + +fd_streambuf::operator int() { + return static_cast(*this); +} +fd_streambuf::operator int() const { + return m_fd; +} + +void fd_streambuf::open(int fd) { + if (m_fd) { + sync(); + } + m_fd = fd; + setg(m_in, m_in, m_in); + setp(m_out, m_out + sizeof(m_in)); +} + +void fd_streambuf::close() { + if (m_fd) { + sync(); + m_fd = -1; + } +} + +int fd_streambuf::sync() { + if (pbase() != pptr()) { + auto size = pptr() - pbase(); + auto bytes = ::write(m_fd, m_out, size); + if (bytes > 0) { + std::copy(pbase() + bytes, pptr(), pbase()); + setp(pbase(), epptr()); + pbump(size - bytes); + } + } + return pptr() != epptr() ? 0 : -1; +} + +int fd_streambuf::overflow(int c) { + if (!traits_type::eq_int_type(c, traits_type::eof())) { + *pptr() = traits_type::to_char_type(c); + pbump(1); + } + return sync() == -1 ? traits_type::eof() : traits_type::not_eof(c); +} + +int fd_streambuf::underflow() { + if (gptr() == egptr()) { + auto pback = std::min(gptr() - eback(), std::ptrdiff_t(m_in)); + std::copy(egptr() - pback, egptr(), eback()); + auto bytes = ::read(m_fd, eback() + pback, BUFSIZ); + setg(eback(), eback() + pback, eback() + pback + std::max(0L, bytes)); + } + return gptr() == egptr() ? traits_type::eof() : traits_type::to_int_type(*gptr()); +} + +// }}} + namespace file_util { /** * Checks if the given file exist @@ -86,7 +178,7 @@ namespace file_util { string contents((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); return contents; } catch (std::ios_base::failure& e) { - return string{""}; + return ""; } } @@ -95,7 +187,7 @@ namespace file_util { */ bool is_fifo(string filename) { auto fileptr = factory_util::unique(filename); - int fd = fileno((*fileptr)()); + int fd = fileno(*fileptr); struct stat statbuf {}; fstat(fd, &statbuf); return S_ISFIFO(statbuf.st_mode);