// -*- C++ -*- /*************************************************************************** * * fstream.cc - Definition for the Standard Library file streams * * $Id: fstream.cc 172106 2011-11-02 17:04:12Z statham $ * *************************************************************************** * * Copyright (c) 1994-2001 Rogue Wave Software, Inc. All Rights Reserved. * * This computer software is owned by Rogue Wave Software, Inc. and is * protected by U.S. copyright laws and other laws and by international * treaties. This computer software is furnished by Rogue Wave Software, * Inc. pursuant to a written license agreement and may be used, copied, * transmitted, and stored only in accordance with the terms of such * license and with the inclusion of the above copyright notice. This * computer software or any other copies thereof may not be provided or * otherwise made available to any other person. * * U.S. Government Restricted Rights. This computer software is provided * with Restricted Rights. Use, duplication, or disclosure by the * Government is subject to restrictions as set forth in subparagraph (c) * (1) (ii) of The Rights in Technical Data and Computer Software clause * at DFARS 252.227-7013 or subparagraphs (c) (1) and (2) of the * Commercial Computer Software--Restricted Rights at 48 CFR 52.227-19, * as applicable. Manufacturer is Rogue Wave Software, Inc., 5500 * Flatiron Parkway, Boulder, Colorado 80301 USA. * **************************************************************************/ _RWSTD_NAMESPACE_BEGIN (std) template basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>:: open (const char *__name, ios_base::openmode __mode, long __prot) { _RWSTD_ASSERT (0 != __name); _RWSTD_ASSERT (this->_C_is_valid ()); if (is_open ()) return 0; this->_C_iomode = __mode; _C_file._C_open (__name, __mode & ~ios_base::ate, __prot); if (!is_open ()) return 0; this->setg (0, 0, 0); this->setp (0, 0); if (__mode & ios_base::ate) { if ((_C_cur_pos = _C_beg_pos = _C_file_seek (0, ios_base::end)) == _RWSTD_INVALID_FPOS) { close (); return 0; } } else _C_cur_pos = _C_beg_pos = pos_type(); return this; } template basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::close () { _RWSTD_ASSERT (this->_C_is_valid ()); if (!is_open ()) return 0; // failure // avoid expensive call to overflow() unless necessary if (this->pptr () != this->pbase () && this->_C_is_eof (overflow ())) return 0; // failure // write out any unshift sequence if necessary // (applies to multibyte, state dependent encodings only) if (this->_C_out_last () && !_C_unshift ()) return 0; // failure _C_cur_pos = _C_beg_pos = _RWSTD_INVALID_FPOS; this->setg (0, 0, 0); this->setp (0, 0); if (!_C_file._C_is_std () && !_C_file._C_close ()) return 0; // failure _C_file = _RWSTD_INVALID_FILE; return this; } template streamsize basic_filebuf<_CharT, _Traits>::showmanyc () { _RWSTD_ASSERT (this->_C_is_valid ()); if (!this->_C_is_in () || !is_open () || _C_cur_pos == _RWSTD_INVALID_FPOS) return -1; // start with the number of chars in get area streamsize __retval = this->egptr () - this->gptr (); // no prob if this fails for non-seekable devices pos_type __end_pos = _C_file_seek (0, ios_base::end); if (__end_pos != _RWSTD_INVALID_FPOS) { // restore position within file only if seek succeeded _C_file_seek (_C_cur_pos, ios_base::beg); const _C_codecvt_t &__cvt = _USE_FACET (_C_codecvt_t, this->getloc ()); if (__cvt.always_noconv ()) __retval += __end_pos - _C_cur_pos; else // make most pessimistic conversion estimate __retval += (__end_pos - _C_cur_pos) / __cvt.max_length (); } return __retval > 0 ? __retval : 0; } template _TYPENAME basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::uflow () { int_type __c = underflow (); if (!this->_C_is_eof(__c)) this->gbump (1); return __c; } template _TYPENAME basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::underflow () { _RWSTD_ASSERT (this->_C_is_valid ()); this->setp (0, 0); // invalidate put area this->_C_out_last (false); // needed by close () if (!this->_C_is_in () || !is_open()) return traits_type::eof (); char_type* const __to_end = this->_C_buffer + this->_C_bufsize; // fill the buffer if it's empty if (this->gptr () == this->egptr()) { // N.B.: gptr() could be null here // determine the maximum possible size of putback area (if any) // make sure putback area isn't too big - try to honor // _RWSTD_PBACK_SIZE if possible, otherwise shrink _C_pbacksize = min (this->_C_putback_avail (), _RWSTD_PBACK_SIZE); _RWSTD_ASSERT (0 != this->_C_bufsize); if (_C_pbacksize == this->_C_bufsize) _C_pbacksize = this->_C_bufsize - 1; traits_type::move (this->eback(), this->gptr () - _C_pbacksize, _C_pbacksize); // fill the get area from the file, performing code conversion if // necessary streamsize __nread = 0; // number of bytes read from file _C_beg_pos = _C_cur_pos; const _C_codecvt_t &__cvt = _USE_FACET (_C_codecvt_t, this->getloc ()); if (__cvt.always_noconv ()) { // no conversion required __nread = (__to_end - this->_C_buffer) - _C_pbacksize; __nread = _C_file._C_read (this->_C_buffer + _C_pbacksize, sizeof (char_type), __nread); if (__nread < 0) return traits_type::eof (); // error while reading this->setg (this->_C_buffer, this->_C_buffer + _C_pbacksize, this->_C_buffer + _C_pbacksize + __nread); // adjust the current position in the file, // taking into account CR/LF conversion on windows __nread += _C_crlf_intern_count (this->gptr (), this->gptr () + __nread); } else { // conversion required char __xbuf[_RWSTD_DEFAULT_BUFSIZE]; char* __from_base = __xbuf; const char* __from_next = 0; char_type* __to_base = this->_C_buffer + _C_pbacksize; char_type* __to_next = __to_base; state_type __state = _C_cur_pos.state (); codecvt_base::result __res = codecvt_base::ok; while (__to_next != __to_end) { // read only as many characters as we have positions left in // internal buffer - guarantees we won't read more characters // than we can put into the internal buffer after conversion // and ending file position isn't in the middle of a shift // sequence // N.B.: area between __xbuf and __from_base contains partially // converted sequences left from previous read streamsize __n = min (ptrdiff_t (__to_end - __to_next), ptrdiff_t (sizeof __xbuf - (__from_base - __xbuf))); __n = _C_file._C_read (__from_base, sizeof (char), __n); if (0 > __n) return traits_type::eof (); // error while reading if (0 == __n) break; // reached eof // take into account CR/LF conversion on Win32 __nread += __n + _C_crlf_extern_count (__from_base, __from_base + __n); // adjust `n' to hold the number of external chars in buffer __n += __from_base - __xbuf; // convert any partially converted sequence from the previous // iteration (possibly empty) plus what we just read in __res = __cvt.in (__state, __xbuf, __xbuf + __n, __from_next, __to_base, __to_end, __to_next); if (codecvt_base::partial == __res) { // compute the length of partially converted sequence __n -= __from_next - __xbuf; // copy the sequence to beginning of xbuf memmove (__xbuf, __from_next, __n); // will append external chars to end of the sequence __from_base = __xbuf + __n; } else if (codecvt_base::ok == __res) __from_base = __xbuf; else // codecvt_base::error return traits_type::eof (); __to_base = __to_next; // continue at end of converted seq } _RWSTD_ASSERT (codecvt_base::ok == __res); _C_cur_pos.state (__state); this->setg (this->_C_buffer, this->_C_buffer + _C_pbacksize, __to_next); } // end conversion block if (__nread == 0) return traits_type::eof (); _C_cur_pos += __nread; } return traits_type::to_int_type (*this->gptr ()); } template _TYPENAME basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::overflow (int_type __c /* = eof () */) { _RWSTD_ASSERT (this->_C_is_valid ()); this->setg (0, 0, 0); // invalidate the get area if (!this->_C_is_out () || !is_open()) return traits_type::eof (); char_type* __from_beg; // pointer to start of sequence to write char_type* __from_end; // pointer to end of sequence to write char_type __c_to_char; // input arg converted to char_type // (used only in unbuffered mode) if (this->_C_is_unbuffered ()) { __c_to_char = traits_type::to_char_type(__c); __from_beg = &__c_to_char; __from_end = __from_beg + 1; } else { __from_beg = this->_C_buffer; __from_end = this->pptr (); } if (this->pptr () == 0 && !this->_C_is_unbuffered ()) { // put area not valid yet - just need to initialize it this->setp (this->_C_buffer, this->_C_buf_end ()); } else if ( this->pptr () == this->epptr () || this->_C_is_eof (__c) || this->_C_is_unbuffered ()) { // flush put area to file, // performing code conversion if necessary streamsize __nwrote = 0; // num chars to write state_type __state = _C_cur_pos.state (); // state of stream // N.B. the sentry constructed by the ostream currently // seeks to the end of the file when in append mode // but we're going to seek anyway because the user // can manipulate the buffer directly if (this->_C_iomode & ios_base::app) _C_cur_pos = _C_file_seek (0, ios_base::end); _C_beg_pos = _C_cur_pos; const _C_codecvt_t &__cvt = _USE_FACET (_C_codecvt_t, this->getloc ()); if (__cvt.always_noconv ()) { // no conversion __nwrote = __from_end - __from_beg;; if (!_C_file._C_write (__from_beg, sizeof (char_type), __nwrote)) return traits_type::eof (); // error while writing } else { // conversion required: we do this a chunk at a time // to avoid dynamic allocation of memory char __xbuf[_RWSTD_DEFAULT_BUFSIZE]; char* __xbuf_end = __xbuf + sizeof __xbuf; char* __to_next = 0; const char_type* __from_next = 0; const char_type* __base = __from_beg; while (__from_next != __from_end) { codecvt_base::result __res = __cvt.out (__state, __base, __from_end, __from_next, __xbuf, __xbuf_end, __to_next); if ( __res == codecvt_base::error || ! _C_file._C_write (__xbuf, sizeof (char), __to_next - __xbuf)) return traits_type::eof (); // N.B. a partial conversion will result if there's // not enough space in the conversion buffer // to hold the converted sequence, but we're o.k. // since we'll be passing any remaining unconverted // characters (starting at __from_next) in the next iteration // __res is ok or partial __nwrote += __to_next - __xbuf + _C_crlf_extern_count (__xbuf, __to_next); __base = __from_next; // do the next chunk } } // end conversion block // adjust the current position in the file _C_cur_pos += __nwrote; _C_cur_pos.state (__state); // reset the put area if (!this->_C_is_unbuffered ()) this->setp (this->_C_buffer, this->_C_buf_end ()); } // now that there's room in the buffer, call sputc() recursively // to actually place the character in the buffer (unless we're // in unbuffered mode because we just wrote it out) if (!this->_C_is_eof (__c) && !this->_C_is_unbuffered ()) this->sputc (__c); this->_C_out_last (true); // needed by close () return traits_type::not_eof (__c); } template _TYPENAME basic_filebuf<_CharT, _Traits>::int_type basic_filebuf<_CharT, _Traits>::pbackfail (int_type __c) { _RWSTD_ASSERT (this->_C_is_valid ()); if (!is_open ()) return traits_type::eof (); // we could get here if gptr = eback or if __c != *(gptr-1) if (!this->_C_putback_avail ()) { // try to make a putback area available if (this->seekoff (-1, ios_base::cur) == _RWSTD_INVALID_FPOS) return traits_type::eof (); if (this->_C_is_eof (underflow ())) return traits_type::eof (); this->gbump (1); } if (traits_type::eq (traits_type::to_char_type (__c), *(this->gptr () - 1)) || this->_C_is_eof (__c)) { // "put back" original value this->gbump (-1); return traits_type::not_eof (__c); } else if (this->_C_is_out ()) { // overwrite existing value with new value this->gbump (-1); *this->gptr () = traits_type::to_char_type (__c); return __c; } else return traits_type::eof (); } template basic_streambuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::setbuf (char_type *__buf, streamsize __len) { _RWSTD_ASSERT (this->_C_is_valid ()); // sync the buffer to the external file so it can be deallocated if (sync () != 0) return 0; bool __own_old_buf = this->_C_own_buf (); if (__len > 0) { // replace the buffer... if (__buf == 0) { // ...newly allocated buffer... __buf = new char_type[__len]; // try to allocate before deleting this->_C_own_buf (true); } else // ...or with supplied buffer this->_C_own_buf (false); if (__own_old_buf) delete [] this->_C_buffer; this->_C_buffer = __buf; this->_C_bufsize = __len; this->_C_set_unbuffered (false); } else { // unbuffer this stream if (__buf != 0) // invalid input (__len == 0 => __buf == 0) return 0; // character buffer is preserved (used as get area only), // streambuf object unbuffered for writing // to put a streambuf object into an unbuffered mode (see 27.8.1.4, // p10) and affect the size of the get area, setbuf() should first // be called with the desired (non-zero) size and then again with // both arguments being 0 this->_C_set_unbuffered (true); } this->setg (0, 0, 0); this->setp (0, 0); // a character buffer of nonzero size must exist // even in unbuffered mode _RWSTD_ASSERT (0 != this->_C_buffer); _RWSTD_ASSERT (0 != this->_C_bufsize); return this; } // 27.8.1.4, p 11 template _TYPENAME basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>:: seekoff (off_type __off, ios_base::seekdir __way, ios_base::openmode) { _RWSTD_ASSERT (this->_C_is_valid ()); if (!is_open ()) return _RWSTD_INVALID_FPOS; int __width = _USE_FACET (_C_codecvt_t, this->getloc ()).encoding (); // offset must be zero with multibyte encoding if (0 != __off && __width <= 0) return _RWSTD_INVALID_FPOS; // sync the buffer... (this also invalidates the get/put area) if (sync () != 0) return _RWSTD_INVALID_FPOS; // ...and, if last op was output, append an unshift sequence if (this->_C_out_last ()) _C_unshift (); // perform the seek pos_type __new_pos; if (__width > 0) __new_pos = _C_file_seek (__width * __off, __way); else __new_pos = _C_file_seek (0, __way); if (__new_pos != _RWSTD_INVALID_FPOS) _C_cur_pos = _C_beg_pos = __new_pos; this->_C_out_last (false); // needed by close() return __new_pos; } template _TYPENAME basic_filebuf<_CharT, _Traits>::pos_type basic_filebuf<_CharT, _Traits>::seekpos (pos_type __pos, ios_base::openmode) { _RWSTD_ASSERT (this->_C_is_valid ()); if (!is_open ()) return _RWSTD_INVALID_FPOS; // flush the output area if it exists if (this->pptr () != 0) { if (this->_C_is_eof (this->overflow (traits_type::eof ()))) return _RWSTD_INVALID_FPOS; } if (_C_file_seek (__pos, ios_base::beg) == _RWSTD_INVALID_FPOS) return _RWSTD_INVALID_FPOS; _C_cur_pos = _C_beg_pos = __pos; this->setg (0, 0, 0); this->setp (0, 0); this->_C_out_last (false); // needed by close() return __pos; } template int basic_filebuf<_CharT, _Traits>::sync () { _RWSTD_ASSERT (this->_C_is_valid ()); // put area active if (this->pptr () != 0) { // flush the buffer to the file if (this->_C_is_eof (overflow (traits_type::eof ()))) return -1; } // get area active if (this->gptr () != 0) { // get the number of chars consumed in the buffer streamoff __consumed = (this->gptr () - this->eback ()) - _C_pbacksize; const _C_codecvt_t &__cvt = _USE_FACET (_C_codecvt_t, this->getloc ()); int __width = __cvt.encoding (); if (__width > 0) { // constant width conversion: // adjust the current position/state in the file, // taking into account CR/LF conversion on Win32 _C_cur_pos = _C_beg_pos; _C_cur_pos += (__width * __consumed) + _C_crlf_intern_count (this->eback () + _C_pbacksize, this->gptr()); } else { // This gets a bit tricky here because we don't know the external // file position corresponding to the position in the // internal buffer. To figure this out, we'll use the known // file position/state corresponding to the start of the buffer // (which we have carefully saved in _C_beg_pos) and // convert the characters up to the current position in the // buffer, counting how many external chars result. We can // then use the offset from _C_beg_pos and the state // returned from codecvt::out() to construct the current file // position streamoff __ext_chars = 0; // converted external chars char __xbuf[_RWSTD_DEFAULT_BUFSIZE]; char* __xbuf_end = __xbuf + sizeof __xbuf; char* __to_next = 0; const char_type* __from_next = 0; const char_type* __from_end = this->gptr (); state_type __state = _C_beg_pos.state (); const char_type* __base = this->eback () + _C_pbacksize; while (__from_next != __from_end) { codecvt_base::result __res = __cvt.out (__state, __base, __from_end, __from_next, __xbuf, __xbuf_end, __to_next); if (__res == codecvt_base::error) return -1; // take into account CR/LF conversion on Win32 __ext_chars += (__to_next - __xbuf) + _C_crlf_extern_count (__xbuf, __to_next); // repeat with next chunk __base = __from_next; } _C_cur_pos = _C_beg_pos + __ext_chars; _C_cur_pos.state (__state); } if (_C_file_seek (_C_cur_pos, ios_base::beg) == _RWSTD_INVALID_FPOS) return -1; traits_type::move (this->eback(), this->gptr () - _C_pbacksize, _C_pbacksize); this->setg (this->eback(), this->eback() + _C_pbacksize, this->eback() + _C_pbacksize); } _C_beg_pos = _C_cur_pos; this->setp (0, 0); return 0; } template streamsize basic_filebuf<_CharT, _Traits>::xsputn (const char_type* __s, streamsize __len) { _RWSTD_ASSERT (this->_C_is_valid ()); if (!__s || (__len == 0) || !this->_C_is_out ()) return 0; if (this->pptr () != 0 && (this->pptr () + __len) < this->epptr ()) { traits_type::copy (this->pptr (), __s, __len); this->pbump (int(__len)); } else { for (streamsize __n = 0; __n < __len; ++__n) { if (this->_C_is_eof (this->sputc (*__s++))) return __n; } } return __len; } template basic_filebuf<_CharT, _Traits>* basic_filebuf<_CharT, _Traits>::_C_open (_RW::__rw_file_t __frep, char_type *__buf, streamsize __n) { if (is_open () || !setbuf (__buf,__n) || !__frep._C_is_open ()) return 0; _C_file = __frep; this->_C_iomode = _C_file._C_get_mode (); this->setg (0, 0, 0); this->setp (0, 0); _C_cur_pos = _C_beg_pos = pos_type(); // offset == 0, state = state() return this; } // write an unshift sequence if not in initial state // N.B.: this is a no-op unless the stream is imbued with user-defined // locale/facet because the default implementations of unshift() // always return noconv // returns: true on success template bool basic_filebuf<_CharT, _Traits>::_C_unshift () { // unshifting isn't necessary if encoding isn't state dependent or // if state is equivalent to initial state (determined by mbsinit()) state_type __state = _C_cur_pos.state (); if ( _USE_FACET (_C_codecvt_t, this->getloc ()).encoding () >= 0 || _MBSINIT (&__state) != 0) return true; // buf to hold unshift sequence - assumes that the shift // sequence will be less than 64 chars (we can't safely // use dynamic allocation because _C_unshift // could be called as a result of memory alloc exception) char __useq[64]; char* __useq_end = 0; codecvt_base::result __res = _USE_FACET (_C_codecvt_t, this->getloc ()).unshift ( __state, __useq, __useq + sizeof __useq, __useq_end); streamoff __nbytes = __useq_end - __useq; // in the unlikely event that the buffer isn't big enough, assert _RWSTD_ASSERT(__res != codecvt_base::partial); if (__res == codecvt_base::error) return false; if (__res == codecvt_base::noconv) return true; if (_C_file._C_write (__useq, sizeof (char), __nbytes)) return false; _C_cur_pos += __nbytes; // CR/LF conversion not an issue here return true; } _RWSTD_NAMESPACE_END // std