1 /* Copyright (C) 2002 The gtkmm Development Team
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2.1 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
15  */
16 
17 #include <glibmm/streamiochannel.h>
18 #include <glibmm/main.h> //For Source
19 #include <glib.h>
20 #include <fstream>
21 #include <iostream>
22 
23 namespace Glib
24 {
25 
26 #ifndef GLIBMM_DISABLE_DEPRECATED
27 
28 // static
29 Glib::RefPtr<StreamIOChannel>
create(std::istream & stream)30 StreamIOChannel::create(std::istream& stream)
31 {
32   return Glib::RefPtr<StreamIOChannel>(new StreamIOChannel(&stream, nullptr));
33 }
34 
35 // static
36 Glib::RefPtr<StreamIOChannel>
create(std::ostream & stream)37 StreamIOChannel::create(std::ostream& stream)
38 {
39   return Glib::RefPtr<StreamIOChannel>(new StreamIOChannel(nullptr, &stream));
40 }
41 
42 // static
43 Glib::RefPtr<StreamIOChannel>
create(std::iostream & stream)44 StreamIOChannel::create(std::iostream& stream)
45 {
46   return Glib::RefPtr<StreamIOChannel>(new StreamIOChannel(&stream, &stream));
47 }
48 
StreamIOChannel(std::istream * stream_in,std::ostream * stream_out)49 StreamIOChannel::StreamIOChannel(std::istream* stream_in, std::ostream* stream_out)
50 : stream_in_(stream_in), stream_out_(stream_out)
51 {
52   get_flags_vfunc(); // initialize GIOChannel flag bits
53 }
54 
~StreamIOChannel()55 StreamIOChannel::~StreamIOChannel() noexcept
56 {
57 }
58 
59 IOStatus
read_vfunc(char * buf,gsize count,gsize & bytes_read)60 StreamIOChannel::read_vfunc(char* buf, gsize count, gsize& bytes_read)
61 {
62   g_return_val_if_fail(stream_in_ != nullptr, IO_STATUS_ERROR);
63 
64   stream_in_->clear();
65   stream_in_->read(buf, count);
66   bytes_read = stream_in_->gcount();
67 
68   if (stream_in_->eof())
69     return IO_STATUS_EOF;
70 
71   if (stream_in_->fail())
72   {
73     throw Glib::Error(G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Reading from stream failed");
74   }
75 
76   return IO_STATUS_NORMAL;
77 }
78 
79 IOStatus
write_vfunc(const char * buf,gsize count,gsize & bytes_written)80 StreamIOChannel::write_vfunc(const char* buf, gsize count, gsize& bytes_written)
81 {
82   g_return_val_if_fail(stream_out_ != nullptr, IO_STATUS_ERROR);
83 
84   bytes_written = 0;
85 
86   stream_out_->clear();
87   stream_out_->write(buf, count);
88 
89   if (stream_out_->fail())
90   {
91     throw Glib::Error(G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Writing to stream failed");
92   }
93 
94   bytes_written = count; // all or nothing ;)
95 
96   return IO_STATUS_NORMAL;
97 }
98 
99 IOStatus
seek_vfunc(gint64 offset,SeekType type)100 StreamIOChannel::seek_vfunc(gint64 offset, SeekType type)
101 {
102   std::ios::seekdir direction = std::ios::beg;
103 
104   switch (type)
105   {
106   case SEEK_TYPE_SET:
107     direction = std::ios::beg;
108     break;
109   case SEEK_TYPE_CUR:
110     direction = std::ios::cur;
111     break;
112   case SEEK_TYPE_END:
113     direction = std::ios::end;
114     break;
115   }
116 
117   bool failed = false;
118 
119   if (stream_in_)
120   {
121     stream_in_->clear();
122     stream_in_->seekg(offset, direction);
123     failed = stream_in_->fail();
124   }
125   if (stream_out_)
126   {
127     stream_out_->clear();
128     stream_out_->seekp(offset, direction);
129     failed = (failed || stream_out_->fail());
130   }
131 
132   if (failed)
133   {
134     throw Glib::Error(G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Seeking into stream failed");
135   }
136 
137   return Glib::IO_STATUS_NORMAL;
138 }
139 
140 IOStatus
close_vfunc()141 StreamIOChannel::close_vfunc()
142 {
143   bool failed = false;
144 
145   if (std::fstream* const fstream = dynamic_cast<std::fstream*>(stream_in_))
146   {
147     fstream->clear();
148     fstream->close();
149     failed = fstream->fail();
150   }
151   else if (std::ifstream* const ifstream = dynamic_cast<std::ifstream*>(stream_in_))
152   {
153     ifstream->clear();
154     ifstream->close();
155     failed = ifstream->fail();
156   }
157   else if (std::ofstream* const ofstream = dynamic_cast<std::ofstream*>(stream_out_))
158   {
159     ofstream->clear();
160     ofstream->close();
161     failed = ofstream->fail();
162   }
163   else
164   {
165     throw Glib::Error(
166       G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Attempt to close non-file stream");
167   }
168 
169   if (failed)
170   {
171     throw Glib::Error(G_IO_CHANNEL_ERROR, G_IO_CHANNEL_ERROR_FAILED, "Failed to close stream");
172   }
173 
174   return IO_STATUS_NORMAL;
175 }
176 
set_flags_vfunc(IOFlags)177 IOStatus StreamIOChannel::set_flags_vfunc(IOFlags)
178 {
179   return IO_STATUS_NORMAL;
180 }
181 
182 IOFlags
get_flags_vfunc()183 StreamIOChannel::get_flags_vfunc()
184 {
185   gobj()->is_seekable = 1;
186   gobj()->is_readable = (stream_in_ != nullptr);
187   gobj()->is_writeable = (stream_out_ != nullptr);
188 
189   IOFlags flags = IO_FLAG_IS_SEEKABLE;
190 
191   if (stream_in_)
192     flags |= IO_FLAG_IS_READABLE;
193   if (stream_out_)
194     flags |= IO_FLAG_IS_WRITEABLE;
195 
196   return flags;
197 }
198 
create_watch_vfunc(IOCondition)199 Glib::RefPtr<Glib::Source> StreamIOChannel::create_watch_vfunc(IOCondition)
200 {
201   g_warning("Glib::StreamIOChannel::create_watch_vfunc() not implemented");
202   return Glib::RefPtr<Glib::Source>();
203 }
204 
205 #endif // GLIBMM_DISABLE_DEPRECATED
206 
207 } // namespace Glib
208