OSDN Git Service

2003-06-05 Benjamin Kosnik <bkoz@redhat.com>
[pf3gnuchains/gcc-fork.git] / libstdc++-v3 / include / bits / fstream.tcc
1 // File based streams -*- C++ -*-
2
3 // Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003
4 // Free Software Foundation, Inc.
5 //
6 // This file is part of the GNU ISO C++ Library.  This library is free
7 // software; you can redistribute it and/or modify it under the
8 // terms of the GNU General Public License as published by the
9 // Free Software Foundation; either version 2, or (at your option)
10 // any later version.
11
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16
17 // You should have received a copy of the GNU General Public License along
18 // with this library; see the file COPYING.  If not, write to the Free
19 // Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 // USA.
21
22 // As a special exception, you may use this file as part of a free software
23 // library without restriction.  Specifically, if other files instantiate
24 // templates or use macros or inline functions from this file, or you compile
25 // this file and link it with other files to produce an executable, this
26 // file does not by itself cause the resulting executable to be covered by
27 // the GNU General Public License.  This exception does not however
28 // invalidate any other reasons why the executable file might be covered by
29 // the GNU General Public License.
30
31 //
32 // ISO C++ 14882: 27.8  File-based streams
33 //
34
35 #ifndef _CPP_BITS_FSTREAM_TCC
36 #define _CPP_BITS_FSTREAM_TCC 1
37
38 #pragma GCC system_header
39
40 namespace std
41 {
42   template<typename _CharT, typename _Traits>
43     void
44     basic_filebuf<_CharT, _Traits>::
45     _M_allocate_internal_buffer()
46     {
47       if (!_M_buf_allocated && this->_M_buf_size)
48         {
49           // Allocate internal buffer.
50           this->_M_buf = new char_type[this->_M_buf_size];
51           _M_buf_allocated = true;
52         }
53     }
54
55   // Both close and setbuf need to deallocate internal buffers, if it exists.
56   template<typename _CharT, typename _Traits>
57     void
58     basic_filebuf<_CharT, _Traits>::
59     _M_destroy_internal_buffer() throw()
60     {
61       if (_M_buf_allocated)
62         {
63           delete [] this->_M_buf;
64           this->_M_buf = NULL;
65           _M_buf_allocated = false;
66           this->setg(NULL, NULL, NULL);
67           this->setp(NULL, NULL);
68         }
69     }
70
71   template<typename _CharT, typename _Traits>
72     basic_filebuf<_CharT, _Traits>::
73     basic_filebuf() : __streambuf_type(), _M_file(&_M_lock), 
74     _M_state_cur(__state_type()), _M_state_beg(__state_type()), _M_buf(NULL), 
75     _M_buf_allocated(false),_M_last_overflowed(false), 
76     _M_filepos(0), _M_pback(char_type()), _M_pback_cur_save(0), 
77     _M_pback_end_save(0), _M_pback_init(false), _M_codecvt(0)
78     { 
79       this->_M_buf_unified = true;        
80       if (has_facet<__codecvt_type>(this->_M_buf_locale))
81         _M_codecvt = &use_facet<__codecvt_type>(this->_M_buf_locale);
82     }
83
84   template<typename _CharT, typename _Traits>
85     typename basic_filebuf<_CharT, _Traits>::__filebuf_type* 
86     basic_filebuf<_CharT, _Traits>::
87     open(const char* __s, ios_base::openmode __mode)
88     {
89       __filebuf_type *__ret = NULL;
90       if (!this->is_open())
91         {
92           _M_file.open(__s, __mode);
93           if (this->is_open())
94             {
95               _M_allocate_internal_buffer();
96               this->_M_mode = __mode;
97
98               // Setup initial position of buffer.
99               _M_set_buffer(0);
100
101               if ((__mode & ios_base::ate) 
102                   && this->seekoff(0, ios_base::end, __mode) < 0)
103                 // 27.8.1.3,4
104                 this->close();
105               else
106                 __ret = this;
107             }
108         }
109       return __ret;
110     }
111
112   template<typename _CharT, typename _Traits>
113     typename basic_filebuf<_CharT, _Traits>::__filebuf_type* 
114     basic_filebuf<_CharT, _Traits>::
115     close() throw()
116     {
117       __filebuf_type* __ret = NULL;
118       if (this->is_open())
119         {
120           bool __testfail = false;
121           try
122             {
123               const int_type __eof = traits_type::eof();
124               const bool __testput = this->_M_out_beg < this->_M_out_lim;
125
126               if (__testput 
127                   && traits_type::eq_int_type(this->overflow(), __eof))
128                 __testfail = true;
129
130 #if 0
131               // XXX not done
132               if (_M_last_overflowed)
133                 {
134                   _M_output_unshift();
135                   this->overflow();
136                 }
137 #endif
138             }
139           catch(...)
140             {
141               __testfail = true;
142             }
143               
144           // NB: Do this here so that re-opened filebufs will be cool...
145           this->_M_mode = ios_base::openmode(0);
146           this->_M_pback_init = false;
147           _M_destroy_internal_buffer();
148           
149           if (!_M_file.close())
150             __testfail = true;
151
152           if (!__testfail)
153             __ret = this;
154         }
155       _M_last_overflowed = false;
156       return __ret;
157     }
158
159   template<typename _CharT, typename _Traits>
160     streamsize 
161     basic_filebuf<_CharT, _Traits>::
162     showmanyc()
163     {
164       streamsize __ret = -1;
165       const bool __testin = this->_M_mode & ios_base::in;
166
167       if (__testin && this->is_open())
168         {
169           // For a stateful encoding (-1) the pending sequence might be just
170           // shift and unshift prefixes with no actual character.
171           __ret = this->_M_in_end - this->_M_in_cur;
172           if (__check_facet(_M_codecvt).encoding() >= 0)
173             __ret += _M_file.showmanyc() / _M_codecvt->max_length();
174         }
175
176       _M_last_overflowed = false;       
177       return __ret;
178     }
179   
180   template<typename _CharT, typename _Traits>
181     typename basic_filebuf<_CharT, _Traits>::int_type 
182     basic_filebuf<_CharT, _Traits>::
183     _M_underflow(bool __bump)
184     {
185       int_type __ret = traits_type::eof();
186       const bool __testin = this->_M_mode & ios_base::in;
187       const bool __testout = this->_M_mode & ios_base::out;
188
189       if (__testin)
190         {
191           // Check for pback madness, and if so swich back to the
192           // normal buffers and jet outta here before expensive
193           // fileops happen...
194           _M_destroy_pback();
195
196           const size_t __buflen = this->_M_buf_size 
197                                   ? this->_M_buf_size - 1 : 0;
198           if (__buflen)
199             {
200               if (this->_M_in_cur < this->_M_in_end)
201                 {
202                   __ret = traits_type::to_int_type(*this->_M_in_cur);
203                   if (__bump)
204                     _M_move_in_cur(1);
205                   return __ret;
206                 }
207
208               // Sync internal and external buffers.
209               if (__testout && this->_M_out_beg < this->_M_out_lim)
210                 this->overflow();
211
212               // Get and convert input sequence.
213               streamsize __elen = 0;
214               streamsize __ilen = 0;
215               if (__check_facet(_M_codecvt).always_noconv())
216                 {
217                   __elen = _M_file.xsgetn(reinterpret_cast<char*>(this->_M_in_beg), __buflen);
218                   __ilen = __elen;
219                 }
220               else
221                 {
222                   char* __buf = static_cast<char*>(__builtin_alloca(__buflen));
223                   __elen = _M_file.xsgetn(__buf, __buflen);
224                   
225                   const char* __eend;
226                   char_type* __iend;
227                   codecvt_base::result __r;
228                   __r = _M_codecvt->in(_M_state_cur, __buf, __buf + __elen, 
229                                        __eend, this->_M_in_beg, 
230                                        this->_M_in_beg + __buflen, __iend);
231                   if (__r == codecvt_base::ok)
232                     __ilen = __iend - this->_M_in_beg;
233                   else if (__r == codecvt_base::noconv)
234                     {
235                       traits_type::copy(this->_M_in_beg,
236                                         reinterpret_cast<char_type*>(__buf), 
237                                         __elen);
238                       __ilen = __elen;
239                     }
240                   else 
241                     {
242                       // Unwind.
243                       __ilen = 0;
244                       _M_file.seekoff(-__elen, ios_base::cur, ios_base::in);
245                     }
246                 }
247
248               if (__ilen > 0)
249                 {
250                   _M_set_buffer(__ilen);
251                   __ret = traits_type::to_int_type(*this->_M_in_cur);
252                   if (__bump)
253                     _M_move_in_cur(1);
254                 }                   
255             }
256           else
257             {
258               // Unbuffered.
259               char __buf;
260               if (_M_file.xsgetn(&__buf, 1) > 0)
261                 {
262                   if (__check_facet(_M_codecvt).always_noconv())
263                     {
264                       char_type* __cp = reinterpret_cast<char_type*>(&__buf);
265                       __ret = traits_type::to_int_type(*__cp);
266                     }
267                   else
268                     {
269                       char_type __c;             
270                       const char* __eend;
271                       char_type* __iend;
272                       codecvt_base::result __r;
273                       __r = _M_codecvt->in(_M_state_cur, &__buf, &__buf + 1, 
274                                            __eend, &__c, &__c + 1, __iend);
275                       if (__r == codecvt_base::ok 
276                           || __r == codecvt_base::noconv)
277                         __ret = traits_type::to_int_type(__c);
278                     }
279
280                   // Need to put back this extracted character so that
281                   // sgetc will not advance the input stream iff
282                   // underflow, but cannot call pbackfail directly as
283                   // it calls underflow... which leads to a recursive
284                   // showdown.
285                   if (!__bump)
286                     {
287                       _M_create_pback();
288                       *this->_M_in_cur = traits_type::to_char_type(__ret); 
289                     }
290                 }
291             }
292         }
293       _M_last_overflowed = false;       
294       return __ret;
295     }
296
297   template<typename _CharT, typename _Traits>
298     typename basic_filebuf<_CharT, _Traits>::int_type 
299     basic_filebuf<_CharT, _Traits>::
300     pbackfail(int_type __i)
301     {
302       int_type __ret = traits_type::eof();
303       const bool __testin = this->_M_mode & ios_base::in;
304
305       if (__testin)
306         {
307           // Remember whether the pback buffer is active, otherwise below
308           // we may try to store in it a second char (libstdc++/9761).
309           const bool __testpb = this->_M_pback_init;       
310           const bool __testeof = traits_type::eq_int_type(__i, __ret);
311           
312           int_type __tmp;
313           if (this->_M_in_beg < this->_M_in_cur)
314             {
315               _M_move_in_cur(-1);
316               __tmp = traits_type::to_int_type(*this->_M_in_cur);
317             }
318           else if (this->seekoff(-1, ios_base::cur) >= 0)
319             {
320               __tmp = this->underflow();
321               if (traits_type::eq_int_type(__tmp, __ret))
322                 return __ret;
323             }
324           else
325             {
326               // At the beginning of the buffer, need to make a
327               // putback position available.  But the seek may fail
328               // (f.i., at the beginning of a file, see
329               // libstdc++/9439) and in that case we return
330               // traits_type::eof().
331               return __ret;
332             }
333
334           // Try to put back __i into input sequence in one of three ways.
335           // Order these tests done in is unspecified by the standard.
336           if (!__testeof && traits_type::eq_int_type(__i, __tmp))
337             __ret = __i;
338           else if (__testeof)
339             __ret = traits_type::not_eof(__i);
340           else if (!__testpb)
341             {
342               _M_create_pback();
343               *this->_M_in_cur = traits_type::to_char_type(__i); 
344               __ret = __i;
345             }
346         }
347       _M_last_overflowed = false;       
348       return __ret;
349     }
350
351   template<typename _CharT, typename _Traits>
352     typename basic_filebuf<_CharT, _Traits>::int_type 
353     basic_filebuf<_CharT, _Traits>::
354     overflow(int_type __c)
355     {
356       int_type __ret = traits_type::eof();
357       const bool __testeof = traits_type::eq_int_type(__c, __ret);
358       const bool __testout = this->_M_mode & ios_base::out;
359       
360       if (__testout)
361         {
362           if (this->_M_out_beg < this->_M_out_lim)
363             {
364               // Need to restore current position. The position of the
365               // external byte sequence (_M_file) corresponds to
366               // _M_filepos, and we need to move it to _M_out_beg for
367               // the write.
368               if (_M_filepos != this->_M_out_beg)
369                 _M_file.seekoff(this->_M_out_beg - _M_filepos, ios_base::cur);
370
371               // If appropriate, append the overflow char.
372               if (!__testeof)
373                 *this->_M_out_lim++ = traits_type::to_char_type(__c);
374               
375               // Convert pending sequence to external representation,
376               // output.
377               if (_M_convert_to_external(this->_M_out_beg,
378                                          this->_M_out_lim - this->_M_out_beg)
379                   && (!__testeof || (__testeof && !_M_file.sync())))
380                 {
381                   _M_set_buffer(0);
382                   __ret = traits_type::not_eof(__c);
383                 }
384             }
385           else
386             {
387               // Unbuffered.
388               char_type __conv = traits_type::to_char_type(__c);
389               if (!__testeof && _M_convert_to_external(&__conv, 1))
390                 __ret = __c;
391             }
392         }
393       _M_last_overflowed = true;        
394       return __ret;
395     }
396   
397   template<typename _CharT, typename _Traits>
398     bool
399     basic_filebuf<_CharT, _Traits>::
400     _M_convert_to_external(_CharT* __ibuf, streamsize __ilen)
401     {
402       // Sizes of external and pending output.
403       streamsize __elen = 0;
404       streamsize __plen = 0;
405
406       if (__check_facet(_M_codecvt).always_noconv())
407         {
408           __elen += _M_file.xsputn(reinterpret_cast<char*>(__ibuf), __ilen);
409           __plen += __ilen;
410         }
411       else
412         {
413           // Worst-case number of external bytes needed.
414           int __ext_multiplier = _M_codecvt->encoding();
415           if (__ext_multiplier ==  -1 || __ext_multiplier == 0)
416             __ext_multiplier = sizeof(char_type);
417           streamsize __blen = __ilen * __ext_multiplier;
418           char* __buf = static_cast<char*>(__builtin_alloca(__blen));
419           char* __bend;
420           const char_type* __iend;
421           codecvt_base::result __r;
422           __r = _M_codecvt->out(_M_state_cur, __ibuf, __ibuf + __ilen,
423                                 __iend, __buf, __buf + __blen, __bend);
424           
425           if (__r == codecvt_base::ok || __r == codecvt_base::partial)
426             __blen = __bend - __buf;
427           else if (__r == codecvt_base::noconv)
428             {
429               // Same as the always_noconv case above.
430               __buf = reinterpret_cast<char*>(__ibuf);
431               __blen = __ilen;
432             }
433           else
434             {
435               // Result == error .
436               __blen = 0;
437             }
438           
439           if (__blen)
440             {
441               __elen += _M_file.xsputn(__buf, __blen);
442               __plen += __blen;
443             }
444           
445           // Try once more for partial conversions.
446           if (__r == codecvt_base::partial)
447             {
448               const char_type* __iresume = __iend;
449               streamsize __rlen = this->_M_out_lim - __iend;
450               __r = _M_codecvt->out(_M_state_cur, __iresume,
451                                     __iresume + __rlen, __iend, __buf, 
452                                     __buf + __blen, __bend);
453               if (__r != codecvt_base::error)
454                 {
455                   __rlen = __bend - __buf;
456                   __elen += _M_file.xsputn(__buf, __rlen);
457                   __plen += __rlen;
458                 }
459             }
460         }
461       return __elen && __elen == __plen;
462     }
463
464   template<typename _CharT, typename _Traits>
465     typename basic_filebuf<_CharT, _Traits>::__streambuf_type* 
466     basic_filebuf<_CharT, _Traits>::
467     setbuf(char_type* __s, streamsize __n)
468     {
469       if (!this->is_open() && __s == 0 && __n == 0)
470         this->_M_buf_size = 0;
471       else if (__s && __n > 1)
472         {
473           // This is implementation-defined behavior, and assumes that
474           // an external char_type array of length (__s + __n) exists
475           // and has been pre-allocated. If this is not the case,
476           // things will quickly blow up. The length argument __n must
477           // be greater than 1 because __n - 1 positions will be used
478           // for the get and put areas, and 1 position is needed to
479           // host the overflow char of a full put area.
480
481           // Step 1: Destroy the current internal array.
482           _M_destroy_internal_buffer();
483           
484           // Step 2: Use the external array.
485           this->_M_buf = __s;
486           this->_M_buf_size = __n;
487           _M_set_buffer(0);
488         }
489       _M_last_overflowed = false;       
490       return this; 
491     }
492   
493   template<typename _CharT, typename _Traits>
494     typename basic_filebuf<_CharT, _Traits>::pos_type
495     basic_filebuf<_CharT, _Traits>::
496     seekoff(off_type __off, ios_base::seekdir __way, ios_base::openmode __mode)
497     {
498       pos_type __ret =  pos_type(off_type(-1)); 
499       const bool __testin = (ios_base::in & this->_M_mode & __mode) != 0;
500       const bool __testout = (ios_base::out & this->_M_mode & __mode) != 0;
501       
502       int __width = 0;
503       if (_M_codecvt)
504           __width = _M_codecvt->encoding();
505       if (__width < 0)
506         __width = 0;
507
508       const bool __testfail = __off != 0 && __width <= 0;      
509       if (this->is_open() && !__testfail && (__testin || __testout)) 
510         {
511           // Ditch any pback buffers to avoid confusion.
512           _M_destroy_pback();
513
514           if (__way != ios_base::cur || __off != 0)
515             { 
516               // Sync the internal and external streams.              
517               const bool __testget = this->_M_in_beg < this->_M_in_end;
518               const bool __testput = this->_M_out_beg < this->_M_out_lim;
519               off_type __computed_off = __width * __off;
520
521               if (__testput || _M_last_overflowed)
522                 {
523                   // Part one: update the output sequence.
524                   this->sync();
525
526                   // Part two: output unshift sequence.
527                   _M_output_unshift();
528                 }
529               else if (__testget && __way == ios_base::cur)
530                 __computed_off += this->_M_in_cur - _M_filepos;
531
532               // Return pos_type(off_type(-1)) in case of failure.
533               __ret = _M_file.seekoff(__computed_off, __way, __mode);
534               _M_set_buffer(0);
535             }
536           else
537             {
538               // NB: Need to do this in case _M_file in indeterminate
539               // state, ie _M_file._offset == -1
540               pos_type __tmp = _M_file.seekoff(__off, ios_base::cur, __mode);
541               if (__tmp >= 0)
542                 {
543                   // Seek successful.
544                   __ret = __tmp;
545                   __ret += std::max(this->_M_out_cur, this->_M_in_cur) 
546                            - _M_filepos;
547                 }
548             }
549         }
550       _M_last_overflowed = false;       
551       return __ret;
552     }
553
554   template<typename _CharT, typename _Traits>
555     typename basic_filebuf<_CharT, _Traits>::pos_type
556     basic_filebuf<_CharT, _Traits>::
557     seekpos(pos_type __pos, ios_base::openmode __mode)
558     {
559 #ifdef _GLIBCPP_RESOLVE_LIB_DEFECTS
560 // 171. Strange seekpos() semantics due to joint position
561       return this->seekoff(off_type(__pos), ios_base::beg, __mode);
562 #endif
563     }
564
565   template<typename _CharT, typename _Traits>
566     void 
567     basic_filebuf<_CharT, _Traits>::
568     _M_output_unshift()
569     { }
570
571   template<typename _CharT, typename _Traits>
572     void
573     basic_filebuf<_CharT, _Traits>::
574     imbue(const locale& __loc)
575     {
576       const bool __testbeg = !this->seekoff(0, ios_base::cur, this->_M_mode);
577       const bool __teststate = __check_facet(_M_codecvt).encoding() == -1;
578
579       if (this->_M_buf_locale != __loc 
580           && (!this->is_open() || (__testbeg && !__teststate)))
581         {
582           this->_M_buf_locale = __loc;
583           if (__builtin_expect(has_facet<__codecvt_type>(__loc), true))
584             _M_codecvt = &use_facet<__codecvt_type>(__loc);
585
586           // NB This may require the reconversion of previously
587           // converted chars. This in turn may cause the
588           // reconstruction of the original file. YIKES!!  This
589           // implementation interprets this requirement as requiring
590           // the file position be at the beginning, and a stateless
591           // encoding, or that the filebuf be closed. Opinions may differ.
592         }
593       _M_last_overflowed = false;       
594     }
595
596   // Inhibit implicit instantiations for required instantiations,
597   // which are defined via explicit instantiations elsewhere.  
598   // NB:  This syntax is a GNU extension.
599 #if _GLIBCPP_EXTERN_TEMPLATE
600   extern template class basic_filebuf<char>;
601   extern template class basic_ifstream<char>;
602   extern template class basic_ofstream<char>;
603   extern template class basic_fstream<char>;
604
605 #ifdef _GLIBCPP_USE_WCHAR_T
606   extern template class basic_filebuf<wchar_t>;
607   extern template class basic_ifstream<wchar_t>;
608   extern template class basic_ofstream<wchar_t>;
609   extern template class basic_fstream<wchar_t>;
610 #endif
611 #endif
612 } // namespace std
613
614 #endif