/* Copyright (C) 2004 The glibmm Development Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "fdstream.h" #include fdstreambuf::fdstreambuf() { reset(); } fdstreambuf::fdstreambuf(int fd, bool manage) { create_iochannel(fd, manage); } fdstreambuf::~fdstreambuf() { sync(); } void fdstreambuf::reset() { setg(putback_buffer + 1, putback_buffer + 1, putback_buffer + 1); error_condition.error = false; } void fdstreambuf::create_iochannel(int fd, bool manage) { sync(); reset(); if (fd >= 0) { iochannel_ = Glib::IOChannel::create_from_fd(fd); iochannel_->set_encoding(""); iochannel_->set_buffered(true); iochannel_->set_close_on_unref(manage); } } void fdstreambuf::detach_fd() { iochannel_->set_close_on_unref(false); } void fdstreambuf::connect( const sigc::slot& callback, Glib::IOCondition condition) { Glib::signal_io().connect(callback, iochannel_, condition); } fdstream_error fdstreambuf::get_error() const { return error_condition; } // the standard requires sync to return 0 for success and -1 for error int fdstreambuf::sync() { if (!iochannel_) return -1; try { iochannel_->flush(); } catch (Glib::IOChannelError& io_error) { error_condition.error = true; error_condition.code = io_error.code(); return -1; } return 0; } void fdstreambuf::close_iochannel() { iochannel_->set_close_on_unref(false); reset(); try { iochannel_->close(true); } catch (Glib::IOChannelError& io_error) { error_condition.error = true; error_condition.code = io_error.code(); } } // the standard requires this to return either the character // written on overflow or traits_type::eof() (= EOF with char_type == char) fdstreambuf::traits_type::int_type fdstreambuf::overflow(int_type c) { if (!traits_type::eq_int_type(c, traits_type::eof())) { try { gsize result = 0; char write_char = c; iochannel_->write(&write_char, 1, result); } catch (Glib::IOChannelError& io_error) { error_condition.error = true; error_condition.code = io_error.code(); return traits_type::eof(); } } return traits_type::not_eof(c); } // the standard requires this to return the number of characters written // (which will be 0 for stream failure - it is not correct to return EOF) std::streamsize fdstreambuf::xsputn(const char* source, std::streamsize num) { gsize result = 0; // the documentation for Glib::IOChannel indicates that Glib::IOChannel::write() // will only do a short write in the event of stream failure, so there is no // need to check result and have a second bite (byte) at it as would be // necessary with Unix write() try { iochannel_->write(source, num, result); } catch (Glib::IOChannelError& io_error) { error_condition.error = true; error_condition.code = io_error.code(); result = 0; } return result; } // the standard requires this to return the first character available // on underflow or traits_type::eof() (= EOF with char_type == char) fdstreambuf::traits_type::int_type fdstreambuf::underflow() { if (gptr() < egptr()) return traits_type::to_int_type(*gptr()); // copy the character in bump position (if any) to putback position if (gptr() - eback()) *putback_buffer = *(gptr() - 1); // now insert a character into the bump position gsize result = 0; try { iochannel_->read(putback_buffer + 1, 1, result); } catch (Glib::IOChannelError& io_error) { error_condition.error = true; error_condition.code = io_error.code(); return traits_type::eof(); } // some other error - is this possible? In case it is, cater for it if (result == 0) return traits_type::eof(); // reset buffer pointers setg(putback_buffer, putback_buffer + 1, putback_buffer + 2); // return character in bump/peek position return traits_type::to_int_type(*gptr()); // == *(putback_buffer + 1) } // the standard requires this to return the number of characters fetched // (which will be 0 for stream failure - it is not correct to return EOF) std::streamsize fdstreambuf::xsgetn(char* dest, std::streamsize num) { std::streamsize chars_read = 0; // available would normally be 0, but could be up to 2 if there // have been putbacks or a peek and a putback std::streamsize available = egptr() - gptr(); // if num is less than or equal to the characters already in the // putback buffer, extract from buffer if (num <= available) { traits_type::copy(dest, gptr(), num); gbump(num); chars_read = num; } else { // first copy out putback buffer if (available) { traits_type::copy(dest, gptr(), available); chars_read = available; } // read up to everything else we need with Glib::IOChannel::read() gsize result = 0; try { do { iochannel_->read(dest + chars_read, num - chars_read, result); if (result > 0) chars_read += result; } while (result > 0 && result < static_cast(num - chars_read)); } catch (Glib::IOChannelError& io_error) { error_condition.error = true; error_condition.code = io_error.code(); return chars_read; } if (chars_read) { // now mimic extraction of all characters by sbumpc() by putting // two characters into the buffer (if available) and resetting the // buffer pointers int putback_count = 0; if (chars_read >= 2) { *putback_buffer = *(dest + (chars_read - 2)); putback_count = 2; } else { // if we have reached here then we have only fetched // one character and it must have been read with // Glib::IOChannel::read() and not taken from the // putback buffer - otherwise we would have ended // at the first if block in this method // - and this also means that gptr() == egptr() if (gptr() - eback()) { *putback_buffer = *(gptr() - 1); putback_count = 2; } else putback_count = 1; } *(putback_buffer + 1) = *(dest + (chars_read - 1)); // reset buffer pointers this->setg(putback_buffer + (2 - putback_count), putback_buffer + 2, putback_buffer + 2); } } return chars_read; } fdstream::fdstream(int fd, bool manage) : std::istream(nullptr), std::ostream(nullptr), buf(fd, manage) { std::istream::rdbuf(&buf); std::ostream::rdbuf(&buf); } fdstream::fdstream() : std::istream(nullptr), std::ostream(nullptr) { std::istream::rdbuf(&buf); std::ostream::rdbuf(&buf); } void fdstream::attach(int fd, bool manage) { buf.create_iochannel(fd, manage); } void fdstream::detach() { buf.detach_fd(); } void fdstream::close() { buf.close_iochannel(); } void fdstream::connect(const sigc::slot& callback, Glib::IOCondition condition) { buf.connect(callback, condition); } fdstream_error fdstream::get_error() const { return buf.get_error(); }