Add canvas-note.cc that probably shouldn't exist anyway :)
[ardour.git] / libs / glibmm2 / examples / iochannel_stream / fdstream.cc
1 /* Copyright (C) 2004 The glibmm Development Team
2  *
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Library General Public
5  * License as published by the Free Software Foundation; either
6  * version 2 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  * Library General Public License for more details.
12  *
13  * You should have received a copy of the GNU Library General Public
14  * License along with this library; if not, write to the Free
15  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16  */
17
18 #include "fdstream.h"
19
20 #include <glibmm/main.h>
21
22 fdstreambuf::fdstreambuf()
23 {
24   reset();
25 }
26
27 fdstreambuf::fdstreambuf(int fd, bool manage)
28 {
29   create_iochannel(fd, manage);
30 }
31
32 fdstreambuf::~fdstreambuf()
33 {
34   sync();
35 }
36
37 void fdstreambuf::reset()
38 {
39   setg(putback_buffer + 1, putback_buffer + 1, putback_buffer + 1);
40   error_condition.error = false;
41 }
42
43 void fdstreambuf::create_iochannel(int fd, bool manage)
44 {
45   sync();
46   reset();
47
48   if(fd >= 0)
49   {
50     iochannel_ = Glib::IOChannel::create_from_fd(fd);
51
52     #ifdef GLIBMM_EXCEPTIONS_ENABLED
53     iochannel_->set_encoding("");
54     #else
55     std::auto_ptr<Glib::Error> ex;
56     iochannel_->set_encoding("", ex);
57     #endif //GLIBMM_EXCEPTIONS_ENABLED
58    
59     iochannel_->set_buffered(true);
60     iochannel_->set_close_on_unref(manage);
61   }  
62 }
63
64 void fdstreambuf::detach_fd()
65 {
66   iochannel_->set_close_on_unref(false);
67 }
68
69 void fdstreambuf::connect(const sigc::slot<bool, Glib::IOCondition>& callback,
70                           Glib::IOCondition condition)
71 {
72   Glib::signal_io().connect(callback, iochannel_, condition);
73 }
74
75 fdstream_error fdstreambuf::get_error() const
76 {
77   return error_condition;
78 }
79
80 // the standard requires sync to return 0 for success and -1 for error
81 int fdstreambuf::sync()
82 {
83   if (!iochannel_)
84     return -1;
85
86   #ifdef GLIBMM_EXCEPTIONS_ENABLED
87   try
88   {
89     iochannel_->flush();
90   }
91   catch(Glib::IOChannelError& io_error)
92   {
93     error_condition.error = true;
94     error_condition.code = io_error.code();
95     return -1;
96   }
97   #else
98   std::auto_ptr<Glib::Error> io_error;
99   iochannel_->flush(io_error);
100   if(io_error.get())
101   {
102     error_condition.error = true;
103     error_condition.code = (Glib::IOChannelError::Code)io_error->code();
104     return -1;
105   }
106   #endif //GLIBMM_EXCEPTIONS_ENABLED
107
108   return 0;
109 }
110
111 void fdstreambuf::close_iochannel()
112 {
113   iochannel_->set_close_on_unref(false);
114   reset();
115
116   #ifdef GLIBMM_EXCEPTIONS_ENABLED
117   try
118   {
119     iochannel_->close(true);
120   }
121   catch(Glib::IOChannelError& io_error)
122   {
123     error_condition.error = true;
124     error_condition.code = io_error.code();
125   }
126   #else
127   std::auto_ptr<Glib::Error> io_error;
128   iochannel_->close(true, io_error);
129   if(io_error.get())
130   {
131     error_condition.error = true;
132     error_condition.code = (Glib::IOChannelError::Code)io_error->code();
133   }
134   #endif //GLIBMM_EXCEPTIONS_ENABLED
135
136 }
137
138 // the standard requires this to return either the character
139 // written on overflow or traits_type::eof() (= EOF with char_type == char)
140 fdstreambuf::traits_type::int_type fdstreambuf::overflow(int_type c)
141 {
142   if(!traits_type::eq_int_type(c, traits_type::eof()))
143   {
144     #ifdef GLIBMM_EXCEPTIONS_ENABLED
145     try
146     {
147       gsize result = 0;
148       char write_char = c;
149       iochannel_->write(&write_char, 1, result);
150     }
151     catch(Glib::IOChannelError& io_error)
152     {
153       error_condition.error = true;
154       error_condition.code = io_error.code();
155       return traits_type::eof();
156     }
157     #else
158     std::auto_ptr<Glib::Error> io_error;
159     gsize result = 0;
160     char write_char = c;
161     iochannel_->write(&write_char, 1, result, io_error);
162     if(io_error.get())
163     {
164       error_condition.error = true;
165       error_condition.code = (Glib::IOChannelError::Code)io_error->code();
166       return traits_type::eof();;
167     }
168     #endif //GLIBMM_EXCEPTIONS_ENABLED
169   }
170   return traits_type::not_eof(c);
171 }
172
173 // the standard requires this to return the number of characters written
174 // (which will be 0 for stream failure - it is not correct to return EOF)
175 std::streamsize fdstreambuf::xsputn(const char* source, std::streamsize num)
176 {
177   gsize result = 0;
178
179   // the documentation for Glib::IOChannel indicates that Glib::IOChannel::write()
180   // will only do a short write in the event of stream failure, so there is no
181   // need to check result and have a second bite (byte) at it as would be
182   // necessary with Unix write()
183   #ifdef GLIBMM_EXCEPTIONS_ENABLED
184   try
185   {
186     iochannel_->write(source, num, result);
187   }
188   catch(Glib::IOChannelError& io_error)
189   {
190     error_condition.error = true;
191     error_condition.code = io_error.code();
192     result = 0;
193   }
194   #else
195   std::auto_ptr<Glib::Error> io_error;
196   iochannel_->write(source, num, result, io_error);
197   if(io_error.get())
198   {
199     error_condition.error = true;
200     error_condition.code = (Glib::IOChannelError::Code)io_error->code();
201     result = 0;
202   }
203   #endif //GLIBMM_EXCEPTIONS_ENABLED
204
205   return result;
206 }
207
208 // the standard requires this to return the first character available
209 // on underflow or traits_type::eof() (= EOF with char_type == char)
210 fdstreambuf::traits_type::int_type fdstreambuf::underflow()
211 {
212   if(gptr() < egptr())
213     return traits_type::to_int_type(*gptr());
214
215   // copy the character in bump position (if any) to putback position
216   if(gptr() - eback())
217     *putback_buffer = *(gptr() - 1);
218
219   // now insert a character into the bump position
220   gsize result = 0;
221   #ifdef GLIBMM_EXCEPTIONS_ENABLED
222   try
223   {
224     iochannel_->read(putback_buffer + 1, 1, result);
225   }
226   catch(Glib::IOChannelError& io_error)
227   {
228     error_condition.error = true;
229     error_condition.code = io_error.code();
230     return traits_type::eof();
231   }
232   #else
233   std::auto_ptr<Glib::Error> io_error;
234   iochannel_->read(putback_buffer + 1, 1, result, io_error);
235   if(io_error.get())
236   {
237     error_condition.error = true;
238     error_condition.code = (Glib::IOChannelError::Code)io_error->code();
239     return traits_type::eof();
240   }
241   #endif //GLIBMM_EXCEPTIONS_ENABLED
242
243   // some other error - is this possible?  In case it is, cater for it
244   if (result == 0)
245     return traits_type::eof();
246   
247   // reset buffer pointers
248   setg(putback_buffer,
249        putback_buffer + 1,
250        putback_buffer + 2);
251
252   // return character in bump/peek position
253   return traits_type::to_int_type(*gptr()); // == *(putback_buffer + 1)
254 }
255
256 // the standard requires this to return the number of characters fetched
257 // (which will be 0 for stream failure - it is not correct to return EOF)
258 std::streamsize fdstreambuf::xsgetn(char* dest, std::streamsize num)
259 {
260   std::streamsize chars_read = 0;
261
262   // available would normally be 0, but could be up to 2 if there
263   // have been putbacks or a peek and a putback
264   std::streamsize available = egptr() - gptr();
265
266   // if num is less than or equal to the characters already in the
267   // putback buffer, extract from buffer
268   if (num <= available)
269   {
270     traits_type::copy(dest, gptr(), num);
271     gbump(num);
272     chars_read = num;
273   }
274   else
275   {
276     // first copy out putback buffer
277     if (available)
278     {
279       traits_type::copy(dest, gptr(), available);
280       chars_read = available;
281     }
282
283     // read up to everything else we need with Glib::IOChannel::read()
284     gsize result = 0;
285     #ifdef GLIBMM_EXCEPTIONS_ENABLED
286     try
287     {
288     #else
289     std::auto_ptr<Glib::Error> io_error;
290     #endif //GLIBMM_EXCEPTIONS_ENABLED
291       do
292       {
293         #ifdef GLIBMM_EXCEPTIONS_ENABLED
294         iochannel_->read(dest + chars_read,
295                          num - chars_read,
296                          result);
297         #else
298         iochannel_->read(dest + chars_read,
299                          num - chars_read,
300                          result, io_error);
301         #endif //GLIBMM_EXCEPTIONS_ENABLED
302
303         if (result > 0)
304           chars_read += result;
305       }
306       while (result > 0 && result < static_cast<gsize>(num - chars_read));
307     #ifdef GLIBMM_EXCEPTIONS_ENABLED
308     }
309     catch(Glib::IOChannelError& io_error)
310     #else
311     if(io_error.get())
312     #endif //GLIBMM_EXCEPTIONS_ENABLED
313     {
314       error_condition.error = true;
315   
316       #ifdef GLIBMM_EXCEPTIONS_ENABLED
317       error_condition.code = io_error.code();
318       #else
319       error_condition.code = (Glib::IOChannelError::Code)io_error->code();
320       #endif //GLIBMM_EXCEPTIONS_ENABLED
321       return chars_read;
322     }
323
324     if(chars_read)
325     {
326       // now mimic extraction of all characters by sbumpc() by putting
327       // two characters into the buffer (if available) and resetting the
328       // buffer pointers
329       int putback_count = 0;
330       if(chars_read >= 2)
331       {
332         *putback_buffer = *(dest + (chars_read - 2));
333         putback_count = 2;
334       }
335       else
336       {      // if we have reached here then we have only fetched
337              // one character and it must have been read with
338              // Glib::IOChannel::read() and not taken from the
339              // putback buffer - otherwise we would have ended
340              // at the first if block in this method
341              // - and this also means that gptr() == egptr()
342         if(gptr() - eback())
343         {
344           *putback_buffer = *(gptr() - 1);
345           putback_count = 2;
346         }
347         else putback_count = 1;
348       }
349
350       *(putback_buffer + 1) = *(dest + (chars_read - 1));
351
352       // reset buffer pointers
353       this->setg(putback_buffer + (2 - putback_count),
354                  putback_buffer + 2,
355                  putback_buffer + 2);
356     }
357   }
358   return chars_read;
359 }
360
361 fdstream::fdstream(int fd, bool manage)
362 : std::istream(0),
363   std::ostream(0),
364   buf(fd, manage)
365 {
366   std::istream::rdbuf(&buf);
367   std::ostream::rdbuf(&buf);
368 }
369
370 fdstream::fdstream()
371 : std::istream(0),
372   std::ostream(0)
373 {
374   std::istream::rdbuf(&buf);
375   std::ostream::rdbuf(&buf);
376 }
377
378 void fdstream::attach(int fd, bool manage)
379 {
380   buf.create_iochannel(fd, manage);
381 }
382
383 void fdstream::detach()
384 {
385   buf.detach_fd();
386 }
387
388 void fdstream::close()
389 {
390   buf.close_iochannel();
391 }
392
393 void fdstream::connect(const sigc::slot<bool, Glib::IOCondition>& callback,
394              Glib::IOCondition condition)
395 {
396   buf.connect(callback, condition);
397 }
398
399 fdstream_error fdstream::get_error() const
400 {
401   return buf.get_error();
402 }