diff options
-rw-r--r-- | butl/fdstream | 50 | ||||
-rw-r--r-- | butl/fdstream.cxx | 68 |
2 files changed, 102 insertions, 16 deletions
diff --git a/butl/fdstream b/butl/fdstream index 01a19b7..2814d4b 100644 --- a/butl/fdstream +++ b/butl/fdstream @@ -6,18 +6,19 @@ #define BUTL_FDSTREAM #include <istream> +#include <ostream> namespace butl { - // An input stream that is initialized with a file descriptor - // rather than a file name. + // An iostream that is initialized with a file descriptor rather than + // a file name. // // Notes and limitations: // // - char only - // - input only + // - input or output but not both // - no support for put back - // - throws std::system_error in case of a read() error + // - throws std::system_error in case of a read()/write() error // - not movable, though can be easily supported // class fdbuf: public std::basic_streambuf<char> @@ -35,17 +36,18 @@ namespace butl close (); void - open (int fd) {close (); fd_ = fd; setg (buf_, buf_, buf_);} + open (int fd); bool is_open () const {return fd_ != -1;} - // basic_streambuf input interface. - // public: using int_type = std::basic_streambuf<char>::int_type; using traits_type = std::basic_streambuf<char>::traits_type; + // basic_streambuf input interface. + // + public: virtual std::streamsize showmanyc (); @@ -56,26 +58,50 @@ namespace butl bool load (); + // basic_streambuf output interface. + // + public: + virtual int_type + overflow (int_type); + + virtual int + sync (); + + private: + bool + save (); + private: int fd_ = -1; char buf_[2048]; }; - class ifdstream_base + class fdstream_base { protected: - ifdstream_base () = default; - ifdstream_base (int fd): buf_ (fd) {} + fdstream_base () = default; + fdstream_base (int fd): buf_ (fd) {} protected: fdbuf buf_; }; - class ifdstream: ifdstream_base, public std::istream + class ifdstream: fdstream_base, public std::istream { public: ifdstream (): std::istream (&buf_) {} - ifdstream (int fd): ifdstream_base (fd), std::istream (&buf_) {} + ifdstream (int fd): fdstream_base (fd), std::istream (&buf_) {} + + void close () {buf_.close ();} + void open (int fd) {buf_.open (fd);} + bool is_open () const {return buf_.is_open ();} + }; + + class ofdstream: fdstream_base, public std::ostream + { + public: + ofdstream (): std::ostream (&buf_) {} + ofdstream (int fd): fdstream_base (fd), std::ostream (&buf_) {} void close () {buf_.close ();} void open (int fd) {buf_.open (fd);} diff --git a/butl/fdstream.cxx b/butl/fdstream.cxx index 6d7532a..0386f49 100644 --- a/butl/fdstream.cxx +++ b/butl/fdstream.cxx @@ -5,9 +5,9 @@ #include <butl/fdstream> #ifndef _WIN32 -# include <unistd.h> // close, read +# include <unistd.h> // close(), read(), write() #else -# include <io.h> // _close, _read +# include <io.h> // _close(), _read(), _write() #endif #include <system_error> @@ -20,9 +20,18 @@ namespace butl ~fdbuf () {close ();} void fdbuf:: + open (int fd) + { + close (); + fd_ = fd; + setg (buf_, buf_, buf_); + setp (buf_, buf_ + sizeof (buf_) - 1); // Keep space for overflow's char. + } + + void fdbuf:: close () { - if (fd_ != -1) + if (is_open ()) { #ifndef _WIN32 ::close (fd_); @@ -42,7 +51,7 @@ namespace butl fdbuf::int_type fdbuf:: underflow () { - int_type r = traits_type::eof (); + int_type r (traits_type::eof ()); if (is_open ()) { @@ -68,4 +77,55 @@ namespace butl setg (buf_, buf_, buf_ + n); return n != 0; } + + fdbuf::int_type fdbuf:: + overflow (int_type c) + { + int_type r (traits_type::eof ()); + + if (is_open () && c != traits_type::eof ()) + { + // Store last character in the space we reserved in open(). Note + // that pbump() doesn't do any checks. + // + *pptr () = traits_type::to_char_type (c); + pbump (1); + + if (save ()) + r = c; + } + + return r; + } + + int fdbuf:: + sync () + { + return is_open () && save () ? 0 : -1; + } + + bool fdbuf:: + save () + { + size_t n (pptr () - pbase ()); + + if (n != 0) + { +#ifndef _WIN32 + ssize_t m (::write (fd_, buf_, n)); +#else + int m (_write (fd_, buf_, static_cast<unsigned int> (sizeof (buf_)))); +#endif + + if (m == -1) + throw system_error (errno, system_category ()); + + if (n != static_cast<size_t> (m)) + return false; + + setp (buf_, buf_ + sizeof (buf_) - 1); + } + + return true; + } } |