X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/f4a8c29f7a3ffa7eb60d9b7f055565eb2df368d9..af7e24c33e1e6e00a7687ee965b921dbe60cdb36:/src/common/stream.cpp?ds=sidebyside diff --git a/src/common/stream.cpp b/src/common/stream.cpp index f25f6381fb..1a03caa346 100644 --- a/src/common/stream.cpp +++ b/src/common/stream.cpp @@ -1,611 +1,1449 @@ ///////////////////////////////////////////////////////////////////////////// -// Name: stream.cpp +// Name: src/common/stream.cpp // Purpose: wxStream base classes // Author: Guilhem Lavaux -// Modified by: +// Modified by: VZ (23.11.00) to fix realloc()ing new[]ed memory, +// general code review // Created: 11/07/98 // RCS-ID: $Id$ // Copyright: (c) Guilhem Lavaux -// Licence: wxWindows license +// Licence: wxWindows licence ///////////////////////////////////////////////////////////////////////////// -#ifdef __GNUG__ -#pragma implementation "stream.h" -#endif +// ============================================================================ +// declarations +// ============================================================================ + +// ---------------------------------------------------------------------------- +// headers +// ---------------------------------------------------------------------------- // For compilers that support precompilation, includes "wx.h". #include "wx/wxprec.h" -#include -#include -#include -#include #ifdef __BORLANDC__ -#pragma hdrstop + #pragma hdrstop #endif +#if wxUSE_STREAMS + +#include "wx/stream.h" + +#ifndef WX_PRECOMP + #include "wx/log.h" +#endif + +#include +#include "wx/datstrm.h" +#include "wx/textfile.h" + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +// the temporary buffer size used when copying from stream to stream +#define BUF_TEMP_SIZE 4096 + +// ============================================================================ +// implementation +// ============================================================================ + // ---------------------------------------------------------------------------- // wxStreamBuffer // ---------------------------------------------------------------------------- -wxStreamBuffer::wxStreamBuffer(wxInputStream& i_stream) - : m_buffer_start(NULL), m_buffer_end(NULL), m_buffer_pos(NULL), - m_buffer_size(0), m_istream(&i_stream), m_ostream(NULL) +void wxStreamBuffer::SetError(wxStreamError err) { + if ( m_stream && m_stream->m_lasterror == wxSTREAM_NO_ERROR ) + m_stream->m_lasterror = err; } -wxStreamBuffer::wxStreamBuffer(wxOutputStream& o_stream) - : m_buffer_start(NULL), m_buffer_end(NULL), m_buffer_pos(NULL), - m_buffer_size(0), m_istream(NULL), m_ostream(&o_stream) +void wxStreamBuffer::InitBuffer() { + m_buffer_start = + m_buffer_end = + m_buffer_pos = NULL; + + // if we are going to allocate the buffer, we should free it later as well + m_destroybuf = true; } -wxStreamBuffer::~wxStreamBuffer() +void wxStreamBuffer::Init() +{ + InitBuffer(); + + m_fixed = true; +} + +void wxStreamBuffer::InitWithStream(wxStreamBase& stream, BufMode mode) { - wxDELETEA(m_buffer_start); + Init(); + + m_stream = &stream; + m_mode = mode; + + m_flushable = true; } -void wxStreamBuffer::WriteBack(char c) +wxStreamBuffer::wxStreamBuffer(BufMode mode) { - if (m_ostream) - return; + Init(); - // Assume that if we write "back" we have read a few bytes: so we have some - // space. - if (m_buffer_pos == m_buffer_start) - return; + m_stream = NULL; + m_mode = mode; - m_buffer_pos--; - *m_buffer_pos = c; + m_flushable = false; } -void wxStreamBuffer::SetBufferIO(char *buffer_start, char *buffer_end) +wxStreamBuffer::wxStreamBuffer(const wxStreamBuffer& buffer) { - size_t ret; + // doing this has big chances to lead to a crash when the source buffer is + // destroyed (otherwise assume the caller knows what he does) + wxASSERT_MSG( !buffer.m_destroybuf, + wxT("it's a bad idea to copy this buffer") ); + + m_buffer_start = buffer.m_buffer_start; + m_buffer_end = buffer.m_buffer_end; + m_buffer_pos = buffer.m_buffer_pos; + m_fixed = buffer.m_fixed; + m_flushable = buffer.m_flushable; + m_stream = buffer.m_stream; + m_mode = buffer.m_mode; + m_destroybuf = false; +} - m_buffer_start = buffer_start; - m_buffer_end = buffer_end; +void wxStreamBuffer::FreeBuffer() +{ + if ( m_destroybuf ) + { + free(m_buffer_start); + m_buffer_start = NULL; + } +} + +wxStreamBuffer::~wxStreamBuffer() +{ + FreeBuffer(); +} - m_buffer_size = m_buffer_end-m_buffer_start; +wxInputStream *wxStreamBuffer::GetInputStream() const +{ + return m_mode == write ? NULL : (wxInputStream *)m_stream; +} - if (m_istream) { - ret = m_istream->DoRead(m_buffer_start, m_buffer_size); - m_buffer_end = m_buffer_start + ret; - } - m_buffer_pos = m_buffer_start; +wxOutputStream *wxStreamBuffer::GetOutputStream() const +{ + return m_mode == read ? NULL : (wxOutputStream *)m_stream; } -void wxStreamBuffer::SetBufferIO(size_t bufsize) +void wxStreamBuffer::SetBufferIO(void *buffer_start, + void *buffer_end, + bool takeOwnership) { - char *b_start; + SetBufferIO(buffer_start, (char *)buffer_end - (char *)buffer_start, + takeOwnership); +} - wxDELETE(m_buffer_start); +void wxStreamBuffer::SetBufferIO(void *start, + size_t len, + bool takeOwnership) +{ + // start by freeing the old buffer + FreeBuffer(); - if (!bufsize) { - m_buffer_start = NULL; - m_buffer_end = NULL; - m_buffer_pos = NULL; - m_buffer_size = 0; - return; - } + m_buffer_start = (char *)start; + m_buffer_end = m_buffer_start + len; - b_start = new char[bufsize]; + // if we own it, we free it + m_destroybuf = takeOwnership; - SetBufferIO(b_start, b_start + bufsize); + ResetBuffer(); +} + +void wxStreamBuffer::SetBufferIO(size_t bufsize) +{ + if ( bufsize ) + { + // this will free the old buffer and allocate the new one + SetBufferIO(malloc(bufsize), bufsize, true /* take ownership */); + } + else // no buffer size => no buffer + { + // still free the old one + FreeBuffer(); + InitBuffer(); + } } void wxStreamBuffer::ResetBuffer() { - if (m_istream) + if ( m_stream ) + { + m_stream->Reset(); + m_stream->m_lastcount = 0; + } + + m_buffer_pos = m_mode == read && m_flushable + ? m_buffer_end + : m_buffer_start; +} + +void wxStreamBuffer::Truncate() +{ + size_t new_size = m_buffer_pos - m_buffer_start; + if ( m_buffer_pos == m_buffer_end ) + return; + + if ( !new_size ) + { + FreeBuffer(); + InitBuffer(); + return; + } + + char *new_start = (char *)realloc(m_buffer_start, new_size); + wxCHECK_RET( new_size, wxT("shrinking buffer shouldn't fail") ); + + m_buffer_start = new_start; + m_buffer_end = m_buffer_start + new_size; m_buffer_pos = m_buffer_end; - else +} + +// fill the buffer with as much data as possible (only for read buffers) +bool wxStreamBuffer::FillBuffer() +{ + wxInputStream *inStream = GetInputStream(); + + // It's legal to have no stream, so we don't complain about it just return false + if ( !inStream ) + return false; + + size_t count = inStream->OnSysRead(GetBufferStart(), GetBufferSize()); + if ( !count ) + return false; + + m_buffer_end = m_buffer_start + count; m_buffer_pos = m_buffer_start; + + return true; } -void wxStreamBuffer::Read(void *buffer, size_t size) +// write the buffer contents to the stream (only for write buffers) +bool wxStreamBuffer::FlushBuffer() { - wxASSERT(m_istream != NULL); + wxCHECK_MSG( m_flushable, false, wxT("can't flush this buffer") ); - // ------------------ - // Buffering disabled - // ------------------ + // FIXME: what is this check for? (VZ) + if ( m_buffer_pos == m_buffer_start ) + return false; - if (!m_buffer_size) { - m_istream->m_lastread = m_istream->DoRead(buffer, size); - return; - } + wxOutputStream *outStream = GetOutputStream(); - // ----------------- - // Buffering enabled - // ----------------- - size_t buf_left, orig_size = size; - size_t read_ret; + wxCHECK_MSG( outStream, false, wxT("should have a stream in wxStreamBuffer") ); - while (size > 0) { - buf_left = m_buffer_end - m_buffer_pos; + size_t current = m_buffer_pos - m_buffer_start; + size_t count = outStream->OnSysWrite(m_buffer_start, current); + if ( count != current ) + return false; - // First case: the requested buffer is larger than the stream buffer, - // we split - if (size > buf_left) { - memcpy(buffer, m_buffer_pos, buf_left); - size -= buf_left; - buffer = (char *)buffer + buf_left; // ANSI C++ violation. + m_buffer_pos = m_buffer_start; - read_ret = m_istream->DoRead(m_buffer_start, m_buffer_size); + return true; +} - // Read failed: EOF - if (read_ret == 0) { - m_istream->m_lastread = orig_size-size; - m_istream->m_eof = TRUE; - m_buffer_pos = m_buffer_end = m_buffer_start; - return; - } else { - m_buffer_end = m_buffer_start+read_ret; - m_buffer_pos = m_buffer_start; - } - } else { +size_t wxStreamBuffer::GetDataLeft() +{ + /* Why is this done? RR. */ + if ( m_buffer_pos == m_buffer_end && m_flushable) + FillBuffer(); + + return GetBytesLeft(); +} + +// copy up to size bytes from our buffer into the provided one +void wxStreamBuffer::GetFromBuffer(void *buffer, size_t size) +{ + // don't get more bytes than left in the buffer + size_t left = GetBytesLeft(); - // Second case: we just copy from the stream buffer. - memcpy(buffer, m_buffer_pos, size); - m_buffer_pos += size; - break; + if ( size > left ) + size = left; + + memcpy(buffer, m_buffer_pos, size); + m_buffer_pos += size; +} + +// copy the contents of the provided buffer into this one +void wxStreamBuffer::PutToBuffer(const void *buffer, size_t size) +{ + size_t left = GetBytesLeft(); + + if ( size > left ) + { + if ( m_fixed ) + { + // we can't realloc the buffer, so just copy what we can + size = left; + } + else // !m_fixed + { + // realloc the buffer to have enough space for the data + if ( m_buffer_pos + size > m_buffer_end ) + { + size_t delta = m_buffer_pos - m_buffer_start; + size_t new_size = delta + size; + + char *startOld = m_buffer_start; + m_buffer_start = (char *)realloc(m_buffer_start, new_size); + if ( !m_buffer_start ) + { + // don't leak memory if realloc() failed + m_buffer_start = startOld; + + // what else can we do? + return; + } + + // adjust the pointers invalidated by realloc() + m_buffer_pos = m_buffer_start + delta; + m_buffer_end = m_buffer_start + new_size; + } // else: the buffer is big enough + } } - } - m_istream->m_lastread = orig_size; + + memcpy(m_buffer_pos, buffer, size); + m_buffer_pos += size; } -void wxStreamBuffer::Write(const void *buffer, size_t size) +void wxStreamBuffer::PutChar(char c) { - wxASSERT(m_ostream != NULL); + wxOutputStream *outStream = GetOutputStream(); - // ------------------ - // Buffering disabled - // ------------------ + wxCHECK_RET( outStream, wxT("should have a stream in wxStreamBuffer") ); - if (!m_buffer_size) { - m_ostream->m_lastwrite = m_ostream->DoWrite(buffer, size); - return; - } + // if we don't have buffer at all, just forward this call to the stream, + if ( !HasBuffer() ) + { + outStream->OnSysWrite(&c, sizeof(c)); + } + else + { + // otherwise check we have enough space left + if ( !GetDataLeft() && !FlushBuffer() ) + { + // we don't + SetError(wxSTREAM_WRITE_ERROR); + } + else + { + PutToBuffer(&c, sizeof(c)); + m_stream->m_lastcount = 1; + } + } +} + +char wxStreamBuffer::Peek() +{ + wxCHECK_MSG( m_stream && HasBuffer(), 0, + wxT("should have the stream and the buffer in wxStreamBuffer") ); - // ------------------ - // Buffering enabled - // ------------------ + if ( !GetDataLeft() ) + { + SetError(wxSTREAM_READ_ERROR); + return 0; + } - size_t buf_left, orig_size = size; - size_t write_ret; + char c; + GetFromBuffer(&c, sizeof(c)); + m_buffer_pos--; - while (size > 0) { - buf_left = m_buffer_end - m_buffer_pos; + return c; +} - // First case: the buffer to write is larger than the stream buffer, - // we split it - if (size > buf_left) { - memcpy(m_buffer_pos, buffer, buf_left); - size -= buf_left; - buffer = (char *)buffer + buf_left; // ANSI C++ violation. +char wxStreamBuffer::GetChar() +{ + wxInputStream *inStream = GetInputStream(); - write_ret = m_ostream->DoWrite(m_buffer_start, m_buffer_size); - if (write_ret != m_buffer_size) { - m_ostream->m_bad = TRUE; - m_ostream->m_lastwrite = orig_size-size; - m_buffer_pos = m_buffer_end = m_buffer_start; - return; - } - m_buffer_pos = m_buffer_start; + wxCHECK_MSG( inStream, 0, wxT("should have a stream in wxStreamBuffer") ); + + char c; + if ( !HasBuffer() ) + { + inStream->OnSysRead(&c, sizeof(c)); + } + else + { + if ( !GetDataLeft() ) + { + SetError(wxSTREAM_READ_ERROR); + c = 0; + } + else + { + GetFromBuffer(&c, sizeof(c)); + m_stream->m_lastcount = 1; + } + } + + return c; +} + +size_t wxStreamBuffer::Read(void *buffer, size_t size) +{ + wxASSERT_MSG( buffer, wxT("Warning: Null pointer is about to be used") ); - } else { + /* Clear buffer first */ + memset(buffer, 0x00, size); - // Second case: just copy it in the stream buffer. + // lasterror is reset before all new IO calls + if ( m_stream ) + m_stream->Reset(); - memcpy(m_buffer_pos, buffer, size); - m_buffer_pos += size; - break; + size_t readBytes; + if ( !HasBuffer() ) + { + wxInputStream *inStream = GetInputStream(); + + wxCHECK_MSG( inStream, 0, wxT("should have a stream in wxStreamBuffer") ); + + readBytes = inStream->OnSysRead(buffer, size); + } + else // we have a buffer, use it + { + size_t orig_size = size; + + while ( size > 0 ) + { + size_t left = GetDataLeft(); + + // if the requested number of bytes if greater than the buffer + // size, read data in chunks + if ( size > left ) + { + GetFromBuffer(buffer, left); + size -= left; + buffer = (char *)buffer + left; + + if ( !FillBuffer() ) + { + SetError(wxSTREAM_EOF); + break; + } + } + else // otherwise just do it in one gulp + { + GetFromBuffer(buffer, size); + size = 0; + } + } + + readBytes = orig_size - size; } - } - m_ostream->m_lastwrite = orig_size; + + if ( m_stream ) + m_stream->m_lastcount = readBytes; + + return readBytes; } -// ---------------------------------------------------------------------------- -// wxInputStream -// ---------------------------------------------------------------------------- +// this should really be called "Copy()" +size_t wxStreamBuffer::Read(wxStreamBuffer *dbuf) +{ + wxCHECK_MSG( m_mode != write, 0, wxT("can't read from this buffer") ); + + char buf[BUF_TEMP_SIZE]; + size_t nRead, + total = 0; + + do + { + nRead = Read(buf, WXSIZEOF(buf)); + if ( nRead ) + { + nRead = dbuf->Write(buf, nRead); + total += nRead; + } + } + while ( nRead ); -wxInputStream::wxInputStream() + return total; +} + +size_t wxStreamBuffer::Write(const void *buffer, size_t size) { - m_i_destroybuf = TRUE; - m_i_streambuf = new wxStreamBuffer(*this); - m_eof = FALSE; - m_lastread = 0; + wxASSERT_MSG( buffer, wxT("Warning: Null pointer is about to be send") ); + + if (m_stream) + { + // lasterror is reset before all new IO calls + m_stream->Reset(); + } + + size_t ret; + + if ( !HasBuffer() && m_fixed ) + { + wxOutputStream *outStream = GetOutputStream(); + + wxCHECK_MSG( outStream, 0, wxT("should have a stream in wxStreamBuffer") ); + + // no buffer, just forward the call to the stream + ret = outStream->OnSysWrite(buffer, size); + } + else // we [may] have a buffer, use it + { + size_t orig_size = size; + + while ( size > 0 ) + { + size_t left = GetBytesLeft(); + + // if the buffer is too large to fit in the stream buffer, split + // it in smaller parts + // + // NB: If stream buffer isn't fixed (as for wxMemoryOutputStream), + // we always go to the second case. + // + // FIXME: fine, but if it fails we should (re)try writing it by + // chunks as this will (hopefully) always work (VZ) + + if ( size > left && m_fixed ) + { + PutToBuffer(buffer, left); + size -= left; + buffer = (char *)buffer + left; + + if ( !FlushBuffer() ) + { + SetError(wxSTREAM_WRITE_ERROR); + + break; + } + + m_buffer_pos = m_buffer_start; + } + else // we can do it in one gulp + { + PutToBuffer(buffer, size); + size = 0; + } + } + + ret = orig_size - size; + } + + if (m_stream) + { + // i am not entirely sure what we do this for + m_stream->m_lastcount = ret; + } + + return ret; } -wxInputStream::wxInputStream(wxStreamBuffer *buffer) +size_t wxStreamBuffer::Write(wxStreamBuffer *sbuf) { - m_i_destroybuf = FALSE; - m_i_streambuf = buffer; - m_eof = FALSE; - m_lastread = 0; + wxCHECK_MSG( m_mode != read, 0, wxT("can't write to this buffer") ); + wxCHECK_MSG( sbuf->m_mode != write, 0, wxT("can't read from that buffer") ); + + char buf[BUF_TEMP_SIZE]; + size_t nWrite, + total = 0; + + do + { + size_t nRead = sbuf->Read(buf, WXSIZEOF(buf)); + if ( nRead ) + { + nWrite = Write(buf, nRead); + if ( nWrite < nRead ) + { + // put back data we couldn't copy + wxInputStream *in_stream = (wxInputStream *)sbuf->GetStream(); + + in_stream->Ungetch(buf + nWrite, nRead - nWrite); + } + + total += nWrite; + } + else + { + nWrite = 0; + } + } + while ( nWrite == WXSIZEOF(buf) ); + + return total; } -wxInputStream::~wxInputStream() +wxFileOffset wxStreamBuffer::Seek(wxFileOffset pos, wxSeekMode mode) { - if (m_i_destroybuf) - delete m_i_streambuf; + wxFileOffset ret_off, diff; + + wxFileOffset last_access = GetLastAccess(); + + if ( !m_flushable ) + { + switch (mode) + { + case wxFromStart: + diff = pos; + break; + + case wxFromCurrent: + diff = pos + GetIntPosition(); + break; + + case wxFromEnd: + diff = pos + last_access; + break; + + default: + wxFAIL_MSG( wxT("invalid seek mode") ); + + return wxInvalidOffset; + } + if (diff < 0 || diff > last_access) + return wxInvalidOffset; + size_t int_diff = wx_truncate_cast(size_t, diff); + wxCHECK_MSG( (wxFileOffset)int_diff == diff, wxInvalidOffset, wxT("huge file not supported") ); + SetIntPosition(int_diff); + return diff; + } + + switch ( mode ) + { + case wxFromStart: + // We'll try to compute an internal position later ... + ret_off = m_stream->OnSysSeek(pos, wxFromStart); + ResetBuffer(); + return ret_off; + + case wxFromCurrent: + diff = pos + GetIntPosition(); + + if ( (diff > last_access) || (diff < 0) ) + { + // We must take into account the fact that we have read + // something previously. + ret_off = m_stream->OnSysSeek(diff-last_access, wxFromCurrent); + ResetBuffer(); + return ret_off; + } + else + { + size_t int_diff = wx_truncate_cast(size_t, diff); + wxCHECK_MSG( (wxFileOffset)int_diff == diff, wxInvalidOffset, wxT("huge file not supported") ); + SetIntPosition(int_diff); + return diff; + } + + case wxFromEnd: + // Hard to compute: always seek to the requested position. + ret_off = m_stream->OnSysSeek(pos, wxFromEnd); + ResetBuffer(); + return ret_off; + } + + return wxInvalidOffset; } -char wxInputStream::GetC() +wxFileOffset wxStreamBuffer::Tell() const { - char c; - m_i_streambuf->Read(&c, 1); - return c; + wxFileOffset pos; + + // ask the stream for position if we have a real one + if ( m_stream ) + { + pos = m_stream->OnSysTell(); + if ( pos == wxInvalidOffset ) + return wxInvalidOffset; + } + else // no associated stream + { + pos = 0; + } + + pos += GetIntPosition(); + + if ( m_mode == read && m_flushable ) + pos -= GetLastAccess(); + + return pos; } -wxInputStream& wxInputStream::Read(void *buffer, size_t size) +// ---------------------------------------------------------------------------- +// wxStreamBase +// ---------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(wxStreamBase, wxObject) + +wxStreamBase::wxStreamBase() { - m_i_streambuf->Read(buffer, size); - // wxStreamBuffer sets all variables for us - return *this; + m_lasterror = wxSTREAM_NO_ERROR; + m_lastcount = 0; } -#define BUF_TEMP_SIZE 10000 +wxStreamBase::~wxStreamBase() +{ +} -wxInputStream& wxInputStream::Read(wxOutputStream& stream_out) +size_t wxStreamBase::GetSize() const { - char buf[BUF_TEMP_SIZE]; - size_t bytes_read = BUF_TEMP_SIZE; + wxFileOffset length = GetLength(); + if ( length == (wxFileOffset)wxInvalidOffset ) + return 0; - while (bytes_read == BUF_TEMP_SIZE && !stream_out.Bad()) { - bytes_read = Read(buf, bytes_read).LastRead(); + const size_t len = wx_truncate_cast(size_t, length); + wxASSERT_MSG( len == length + size_t(0), wxT("large files not supported") ); - stream_out.Write(buf, bytes_read); - } - return *this; + return len; } -wxInputStream& wxInputStream::operator>>(wxString& line) +wxFileOffset wxStreamBase::OnSysSeek(wxFileOffset WXUNUSED(seek), wxSeekMode WXUNUSED(mode)) { - wxDataInputStream s(*this); + return wxInvalidOffset; +} + +wxFileOffset wxStreamBase::OnSysTell() const +{ + return wxInvalidOffset; +} + +// ---------------------------------------------------------------------------- +// wxInputStream +// ---------------------------------------------------------------------------- - line = s.ReadLine(); - return *this; +IMPLEMENT_ABSTRACT_CLASS(wxInputStream, wxStreamBase) + +wxInputStream::wxInputStream() +{ + m_wback = NULL; + m_wbacksize = + m_wbackcur = 0; } -wxInputStream& wxInputStream::operator>>(char& c) +wxInputStream::~wxInputStream() { - c = GetC(); - return *this; + free(m_wback); } -wxInputStream& wxInputStream::operator>>(short& i) +bool wxInputStream::CanRead() const { - long l; + // we don't know if there is anything to read or not and by default we + // prefer to be optimistic and try to read data unless we know for sure + // there is no more of it + return m_lasterror != wxSTREAM_EOF; +} - *this >> l; - i = (short)l; - return *this; +bool wxInputStream::Eof() const +{ + // the only way the base class can know we're at EOF is when we'd already + // tried to read beyond it in which case last error is set accordingly + return GetLastError() == wxSTREAM_EOF; } -wxInputStream& wxInputStream::operator>>(int& i) +char *wxInputStream::AllocSpaceWBack(size_t needed_size) { - long l; + // get number of bytes left from previous wback buffer + size_t toget = m_wbacksize - m_wbackcur; + + // allocate a buffer large enough to hold prev + new data + char *temp_b = (char *)malloc(needed_size + toget); + + if (!temp_b) + return NULL; - *this >> l; - i = (short)l; - return *this; + // copy previous data (and free old buffer) if needed + if (m_wback) + { + memmove(temp_b + needed_size, m_wback + m_wbackcur, toget); + free(m_wback); + } + + // done + m_wback = temp_b; + m_wbackcur = 0; + m_wbacksize = needed_size + toget; + + return m_wback; } -wxInputStream& wxInputStream::operator>>(long& i) +size_t wxInputStream::GetWBack(void *buf, size_t size) { - /* I only implemented a simple integer parser */ - int c, sign; + wxASSERT_MSG( buf, wxT("Warning: Null pointer is about to be used") ); - while (isspace( c = GetC() ) ) - /* Do nothing */ ; + /* Clear buffer first */ + memset(buf, 0x00, size); - i = 0; - if (! (c == '-' || isdigit(c)) ) { - InputStreamBuffer()->WriteBack(c); - return *this; - } + if (!m_wback) + return 0; - if (c == '-') { - sign = -1; - c = GetC(); - } else - sign = 1; + // how many bytes do we have in the buffer? + size_t toget = m_wbacksize - m_wbackcur; - while (isdigit(c)) { - i = i*10 + c; - c = GetC(); - } + if ( size < toget ) + { + // we won't read everything + toget = size; + } - i *= sign; + // copy the data from the cache + memcpy(buf, m_wback + m_wbackcur, toget); + + m_wbackcur += toget; + if ( m_wbackcur == m_wbacksize ) + { + // TODO: should we really free it here all the time? maybe keep it? + free(m_wback); + m_wback = NULL; + m_wbacksize = 0; + m_wbackcur = 0; + } - return *this; + // return the number of bytes copied + return toget; } -wxInputStream& wxInputStream::operator>>(float& f) +size_t wxInputStream::Ungetch(const void *buf, size_t bufsize) { - /* I only implemented a simple float parser */ - int c, sign; + wxASSERT_MSG( buf, wxT("Warning: Null pointer is about to be used in Ungetch()") ); - while (isspace( c = GetC() ) ) - /* Do nothing */ ; + if ( m_lasterror != wxSTREAM_NO_ERROR && m_lasterror != wxSTREAM_EOF ) + { + // can't operate on this stream until the error is cleared + return 0; + } - f = 0.0; - if (! (c == '-' || isdigit(c)) ) { - InputStreamBuffer()->WriteBack(c); - return *this; - } + char *ptrback = AllocSpaceWBack(bufsize); + if (!ptrback) + return 0; - if (c == '-') { - sign = -1; - c = GetC(); - } else - sign = 1; + // Eof() shouldn't return true any longer + if ( m_lasterror == wxSTREAM_EOF ) + m_lasterror = wxSTREAM_NO_ERROR; - while (isdigit(c)) { - f = f*10 + c; - c = GetC(); - } + memcpy(ptrback, buf, bufsize); + return bufsize; +} - if (c == '.') { - float f_multiplicator = 0.1; - c = GetC(); +bool wxInputStream::Ungetch(char c) +{ + return Ungetch(&c, sizeof(c)) != 0; +} - while (isdigit(c)) { - f += c*f_multiplicator; - f_multiplicator /= 10; - c = GetC(); - } - } +int wxInputStream::GetC() +{ + unsigned char c; + Read(&c, sizeof(c)); + return LastRead() ? c : wxEOF; +} - f *= sign; +wxInputStream& wxInputStream::Read(void *buf, size_t size) +{ + wxASSERT_MSG( buf, wxT("Warning: Null pointer is about to be read") ); + + char *p = (char *)buf; + m_lastcount = 0; + + size_t read = GetWBack(buf, size); + for ( ;; ) + { + size -= read; + m_lastcount += read; + p += read; + + if ( !size ) + { + // we read the requested amount of data + break; + } + + if ( p != buf && !CanRead() ) + { + // we have already read something and we would block in OnSysRead() + // now: don't do it but return immediately + break; + } + + read = OnSysRead(p, size); + if ( !read ) + { + // no more data available + break; + } + } - return *this; + return *this; } -wxInputStream& wxInputStream::operator>>(wxObject *& obj) +char wxInputStream::Peek() { - wxObjectInputStream obj_s(*this); - obj = obj_s.LoadObject(); - return *this; + char c; + Read(&c, sizeof(c)); + if (m_lasterror == wxSTREAM_NO_ERROR) + { + Ungetch(c); + return c; + } + + return 0; } -off_t wxInputStream::SeekI(off_t pos, wxSeekMode mode) +wxInputStream& wxInputStream::Read(wxOutputStream& stream_out) { - off_t ret_off, diff, last_access; + size_t lastcount = 0; + char buf[BUF_TEMP_SIZE]; + + for ( ;; ) + { + size_t bytes_read = Read(buf, WXSIZEOF(buf)).LastRead(); + if ( !bytes_read ) + break; - last_access = m_i_streambuf->GetLastAccess(); + if ( stream_out.Write(buf, bytes_read).LastWrite() != bytes_read ) + break; - switch (mode) { - case wxFromStart: - diff = DoTellInput() - pos; - if ( diff < 0 || diff > last_access ) { - ret_off = DoSeekInput(pos, wxFromStart); - m_i_streambuf->ResetBuffer(); - return ret_off; - } else { - m_i_streambuf->SetIntPosition(last_access - diff); - return pos; + lastcount += bytes_read; } - case wxFromCurrent: - diff = pos + m_i_streambuf->GetIntPosition(); - if ( (diff > last_access) || (diff < 0) ) { - ret_off = DoSeekInput(pos, wxFromCurrent); - m_i_streambuf->ResetBuffer(); - return ret_off; - } else { - m_i_streambuf->SetIntPosition(diff); - return pos; + m_lastcount = lastcount; + + return *this; +} + +wxFileOffset wxInputStream::SeekI(wxFileOffset pos, wxSeekMode mode) +{ + // RR: This code is duplicated in wxBufferedInputStream. This is + // not really a good design, but buffered stream are different + // from all others in that they handle two stream-related objects: + // the stream buffer and parent stream. + + // I don't know whether it should be put as well in wxFileInputStream::OnSysSeek + if (m_lasterror==wxSTREAM_EOF) + m_lasterror=wxSTREAM_NO_ERROR; + + // avoid unnecessary seek operations (optimization) + wxFileOffset currentPos = TellI(), size = GetLength(); + if ((mode == wxFromStart && currentPos == pos) || + (mode == wxFromCurrent && pos == 0) || + (mode == wxFromEnd && size != wxInvalidOffset && currentPos == size-pos)) + return currentPos; + + if (!IsSeekable() && mode == wxFromCurrent && pos > 0) + { + // rather than seeking, we can just read data and discard it; + // this allows to forward-seek also non-seekable streams! + char buf[BUF_TEMP_SIZE]; + size_t bytes_read; + + // read chunks of BUF_TEMP_SIZE bytes until we reach the new position + for ( ; pos >= BUF_TEMP_SIZE; pos -= bytes_read) + { + bytes_read = Read(buf, WXSIZEOF(buf)).LastRead(); + if ( m_lasterror != wxSTREAM_NO_ERROR ) + return wxInvalidOffset; + + wxASSERT(bytes_read == WXSIZEOF(buf)); + } + + // read the last 'pos' bytes + bytes_read = Read(buf, (size_t)pos).LastRead(); + if ( m_lasterror != wxSTREAM_NO_ERROR ) + return wxInvalidOffset; + + wxASSERT(bytes_read == (size_t)pos); + + // we should now have seeked to the right position... + return TellI(); } - case wxFromEnd: - // Hard to compute: always seek to the requested position. - ret_off = DoSeekInput(pos, wxFromEnd); - m_i_streambuf->ResetBuffer(); - return ret_off; - } - return wxInvalidOffset; + + /* RR: A call to SeekI() will automatically invalidate any previous + call to Ungetch(), otherwise it would be possible to SeekI() to + one position, unread some bytes there, SeekI() to another position + and the data would be corrupted. + + GRG: Could add code here to try to navigate within the wback + buffer if possible, but is it really needed? It would only work + when seeking in wxFromCurrent mode, else it would invalidate + anyway... */ + + if (m_wback) + { + wxLogDebug( wxT("Seeking in stream which has data written back to it.") ); + + free(m_wback); + m_wback = NULL; + m_wbacksize = 0; + m_wbackcur = 0; + } + + return OnSysSeek(pos, mode); } -off_t wxInputStream::TellI() const +wxFileOffset wxInputStream::TellI() const { - return DoTellInput() - m_i_streambuf->GetLastAccess() + - m_i_streambuf->GetIntPosition(); + wxFileOffset pos = OnSysTell(); + + if (pos != wxInvalidOffset) + pos -= (m_wbacksize - m_wbackcur); + + return pos; } + // ---------------------------------------------------------------------------- // wxOutputStream // ---------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(wxOutputStream, wxStreamBase) + wxOutputStream::wxOutputStream() { - m_o_destroybuf = TRUE; - m_o_streambuf = new wxStreamBuffer(*this); - m_bad = FALSE; - m_lastwrite = 0; } -wxOutputStream::wxOutputStream(wxStreamBuffer *buffer) +wxOutputStream::~wxOutputStream() { - m_o_destroybuf = FALSE; - m_o_streambuf = buffer; - m_bad = FALSE; - m_lastwrite = 0; } -wxOutputStream::~wxOutputStream() +size_t wxOutputStream::OnSysWrite(const void * WXUNUSED(buffer), + size_t WXUNUSED(bufsize)) { - if (m_o_destroybuf) - delete m_o_streambuf; + return 0; +} + +void wxOutputStream::PutC(char c) +{ + Write(&c, sizeof(c)); } wxOutputStream& wxOutputStream::Write(const void *buffer, size_t size) { - m_o_streambuf->Write(buffer, size); - return *this; + m_lastcount = OnSysWrite(buffer, size); + return *this; } wxOutputStream& wxOutputStream::Write(wxInputStream& stream_in) { - stream_in.Read(*this); - return *this; + stream_in.Read(*this); + return *this; } -off_t wxOutputStream::SeekO(off_t pos, wxSeekMode mode) +wxFileOffset wxOutputStream::TellO() const { - off_t ret_off; - - switch (mode) { - case wxFromStart: - if ( (unsigned)abs (DoTellOutput()-pos) > m_o_streambuf->GetLastAccess() ) { - ret_off = DoSeekOutput(pos, wxFromStart); - m_o_streambuf->ResetBuffer(); - return ret_off; - } else { - m_o_streambuf->SetIntPosition( DoTellOutput() - pos); - return pos; - } - case wxFromCurrent: - if ( ((unsigned)pos > m_o_streambuf->GetLastAccess()) || (pos < 0) ) { - ret_off = DoSeekOutput(pos, wxFromCurrent); - m_o_streambuf->ResetBuffer(); - return ret_off; - } else { - m_o_streambuf->SetIntPosition(pos); - return pos; - } - case wxFromEnd: - // Hard to compute: always seek to the requested position. - ret_off = DoSeekOutput(pos, wxFromEnd); - m_o_streambuf->ResetBuffer(); - return ret_off; - } - return wxInvalidOffset; + return OnSysTell(); } -off_t wxOutputStream::TellO() const +wxFileOffset wxOutputStream::SeekO(wxFileOffset pos, wxSeekMode mode) { - return DoTellOutput() - m_o_streambuf->GetLastAccess() - + m_o_streambuf->GetIntPosition(); + return OnSysSeek(pos, mode); } void wxOutputStream::Sync() { - DoWrite(m_o_streambuf->GetBufferStart(), m_o_streambuf->GetIntPosition()); +} + + +// ---------------------------------------------------------------------------- +// wxCountingOutputStream +// ---------------------------------------------------------------------------- + +IMPLEMENT_DYNAMIC_CLASS(wxCountingOutputStream, wxOutputStream) - m_o_streambuf->ResetBuffer(); +wxCountingOutputStream::wxCountingOutputStream () +{ + m_currentPos = 0; } -wxOutputStream& wxOutputStream::operator<<(const char *string) +wxFileOffset wxCountingOutputStream::GetLength() const { - return Write(string, strlen(string)); + return m_lastcount; } -wxOutputStream& wxOutputStream::operator<<(wxString& string) +size_t wxCountingOutputStream::OnSysWrite(const void *WXUNUSED(buffer), + size_t size) { - return Write(string, string.Len()); + m_currentPos += size; + if (m_currentPos > m_lastcount) + m_lastcount = m_currentPos; + + return m_currentPos; } -wxOutputStream& wxOutputStream::operator<<(char c) +wxFileOffset wxCountingOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode) { - return Write(&c, 1); + ssize_t new_pos = wx_truncate_cast(ssize_t, pos); + + switch ( mode ) + { + case wxFromStart: + wxCHECK_MSG( (wxFileOffset)new_pos == pos, wxInvalidOffset, wxT("huge position not supported") ); + break; + + case wxFromEnd: + new_pos = m_lastcount + new_pos; + wxCHECK_MSG( (wxFileOffset)new_pos == (wxFileOffset)(m_lastcount + pos), wxInvalidOffset, wxT("huge position not supported") ); + break; + + case wxFromCurrent: + new_pos = m_currentPos + new_pos; + wxCHECK_MSG( (wxFileOffset)new_pos == (wxFileOffset)(m_currentPos + pos), wxInvalidOffset, wxT("huge position not supported") ); + break; + + default: + wxFAIL_MSG( wxT("invalid seek mode") ); + return wxInvalidOffset; + } + + m_currentPos = new_pos; + + if (m_currentPos > m_lastcount) + m_lastcount = m_currentPos; + + return m_currentPos; } -wxOutputStream& wxOutputStream::operator<<(short i) +wxFileOffset wxCountingOutputStream::OnSysTell() const { - wxString strint; + return m_currentPos; +} + +// ---------------------------------------------------------------------------- +// wxFilterInputStream +// ---------------------------------------------------------------------------- - strint.Printf("%i", i); - return Write(strint, strint.Len()); +IMPLEMENT_ABSTRACT_CLASS(wxFilterInputStream, wxInputStream) + +wxFilterInputStream::wxFilterInputStream() + : m_parent_i_stream(NULL), + m_owns(false) +{ } -wxOutputStream& wxOutputStream::operator<<(int i) +wxFilterInputStream::wxFilterInputStream(wxInputStream& stream) + : m_parent_i_stream(&stream), + m_owns(false) { - wxString strint; +} - strint.Printf("%i", i); - return Write(strint, strint.Len()); +wxFilterInputStream::wxFilterInputStream(wxInputStream *stream) + : m_parent_i_stream(stream), + m_owns(true) +{ } -wxOutputStream& wxOutputStream::operator<<(long i) +wxFilterInputStream::~wxFilterInputStream() +{ + if (m_owns) + delete m_parent_i_stream; +} + +// ---------------------------------------------------------------------------- +// wxFilterOutputStream +// ---------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(wxFilterOutputStream, wxOutputStream) + +wxFilterOutputStream::wxFilterOutputStream() + : m_parent_o_stream(NULL), + m_owns(false) { - wxString strlong; +} - strlong.Printf("%i", i); - return Write((const char *)strlong, strlong.Len()); +wxFilterOutputStream::wxFilterOutputStream(wxOutputStream& stream) + : m_parent_o_stream(&stream), + m_owns(false) +{ } -wxOutputStream& wxOutputStream::operator<<(double f) +wxFilterOutputStream::wxFilterOutputStream(wxOutputStream *stream) + : m_parent_o_stream(stream), + m_owns(true) { - wxString strfloat; +} - strfloat.Printf("%f", f); - return Write(strfloat, strfloat.Len()); +bool wxFilterOutputStream::Close() +{ + if (m_parent_o_stream && m_owns) + return m_parent_o_stream->Close(); + else + return true; } -wxOutputStream& wxOutputStream::operator<<(wxObject& obj) +wxFilterOutputStream::~wxFilterOutputStream() { - wxObjectOutputStream obj_s(*this); - obj_s.SaveObject(obj); - return *this; + if (m_owns) + delete m_parent_o_stream; } // ---------------------------------------------------------------------------- -// wxFilterInputStream +// wxFilterClassFactoryBase // ---------------------------------------------------------------------------- -wxFilterInputStream::wxFilterInputStream(wxInputStream& stream) - : wxInputStream(NULL) + +IMPLEMENT_ABSTRACT_CLASS(wxFilterClassFactoryBase, wxObject) + +wxString wxFilterClassFactoryBase::PopExtension(const wxString& location) const { - m_parent_i_stream = &stream; - m_i_streambuf = stream.InputStreamBuffer(); + return location.substr(0, FindExtension(location)); } -wxFilterInputStream::~wxFilterInputStream() +wxString::size_type wxFilterClassFactoryBase::FindExtension( + const wxString& location) const { + for (const wxChar *const *p = GetProtocols(wxSTREAM_FILEEXT); *p; p++) + { + if ( location.EndsWith(*p) ) + return location.length() - wxStrlen(*p); + } + + return wxString::npos; } -size_t wxFilterInputStream::DoRead(void *buffer, size_t size) +bool wxFilterClassFactoryBase::CanHandle(const wxString& protocol, + wxStreamProtocolType type) const { - return m_parent_i_stream->Read(buffer, size).LastRead(); + if (type == wxSTREAM_FILEEXT) + return FindExtension(protocol) != wxString::npos; + else + for (const wxChar *const *p = GetProtocols(type); *p; p++) + if (protocol == *p) + return true; + + return false; } -off_t wxFilterInputStream::DoSeekInput(off_t pos, wxSeekMode mode) +// ---------------------------------------------------------------------------- +// wxFilterClassFactory +// ---------------------------------------------------------------------------- + +IMPLEMENT_ABSTRACT_CLASS(wxFilterClassFactory, wxFilterClassFactoryBase) + +wxFilterClassFactory *wxFilterClassFactory::sm_first = NULL; + +void wxFilterClassFactory::Remove() { - return m_parent_i_stream->SeekI(pos, mode); + if (m_next != this) + { + wxFilterClassFactory **pp = &sm_first; + + while (*pp != this) + pp = &(*pp)->m_next; + + *pp = m_next; + + m_next = this; + } } -off_t wxFilterInputStream::DoTellInput() const +// ---------------------------------------------------------------------------- +// wxBufferedInputStream +// ---------------------------------------------------------------------------- + +namespace +{ + +// helper function used for initializing the buffer used by +// wxBufferedInput/OutputStream: it simply returns the provided buffer if it's +// not NULL or creates a buffer of the given size otherwise +template +wxStreamBuffer * +CreateBufferIfNeeded(T& stream, wxStreamBuffer *buffer, size_t bufsize = 1024) { - return m_parent_i_stream->TellI(); + return buffer ? buffer : new wxStreamBuffer(bufsize, stream); } +} // anonymous namespace + +wxBufferedInputStream::wxBufferedInputStream(wxInputStream& stream, + wxStreamBuffer *buffer) + : wxFilterInputStream(stream) +{ + m_i_streambuf = CreateBufferIfNeeded(*this, buffer); +} + +wxBufferedInputStream::wxBufferedInputStream(wxInputStream& stream, + size_t bufsize) + : wxFilterInputStream(stream) +{ + m_i_streambuf = CreateBufferIfNeeded(*this, NULL, bufsize); +} + +wxBufferedInputStream::~wxBufferedInputStream() +{ + m_parent_i_stream->SeekI(-(wxFileOffset)m_i_streambuf->GetBytesLeft(), + wxFromCurrent); + + delete m_i_streambuf; +} + +char wxBufferedInputStream::Peek() +{ + return m_i_streambuf->Peek(); +} + +wxInputStream& wxBufferedInputStream::Read(void *buf, size_t size) +{ + // reset the error flag + Reset(); + + // first read from the already cached data + m_lastcount = GetWBack(buf, size); + + // do we have to read anything more? + if ( m_lastcount < size ) + { + size -= m_lastcount; + buf = (char *)buf + m_lastcount; + + // the call to wxStreamBuffer::Read() below may reset our m_lastcount + // (but it also may not do it if the buffer is associated to another + // existing stream and wasn't created by us), so save it + size_t countOld = m_lastcount; + + // the new count of the bytes read is the count of bytes read this time + m_lastcount = m_i_streambuf->Read(buf, size); + + // plus those we had read before + m_lastcount += countOld; + } + + return *this; +} + +wxFileOffset wxBufferedInputStream::SeekI(wxFileOffset pos, wxSeekMode mode) +{ + // RR: Look at wxInputStream for comments. + + if (m_lasterror==wxSTREAM_EOF) + Reset(); + + if (m_wback) + { + wxLogDebug( wxT("Seeking in stream which has data written back to it.") ); + + free(m_wback); + m_wback = NULL; + m_wbacksize = 0; + m_wbackcur = 0; + } + + return m_i_streambuf->Seek(pos, mode); +} + +wxFileOffset wxBufferedInputStream::TellI() const +{ + wxFileOffset pos = m_i_streambuf->Tell(); + + if (pos != wxInvalidOffset) + pos -= (m_wbacksize - m_wbackcur); + + return pos; +} + +size_t wxBufferedInputStream::OnSysRead(void *buffer, size_t bufsize) +{ + return m_parent_i_stream->Read(buffer, bufsize).LastRead(); +} + +wxFileOffset wxBufferedInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode) +{ + return m_parent_i_stream->SeekI(seek, mode); +} + +wxFileOffset wxBufferedInputStream::OnSysTell() const +{ + return m_parent_i_stream->TellI(); +} + +void wxBufferedInputStream::SetInputStreamBuffer(wxStreamBuffer *buffer) +{ + wxCHECK_RET( buffer, wxT("wxBufferedInputStream needs buffer") ); + + delete m_i_streambuf; + m_i_streambuf = buffer; +} // ---------------------------------------------------------------------------- -// wxFilterOutputStream +// wxBufferedOutputStream // ---------------------------------------------------------------------------- -wxFilterOutputStream::wxFilterOutputStream(wxOutputStream& stream) - : wxOutputStream(NULL) + +wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& stream, + wxStreamBuffer *buffer) + : wxFilterOutputStream(stream) { - m_parent_o_stream = &stream; - m_o_streambuf = stream.OutputStreamBuffer(); + m_o_streambuf = CreateBufferIfNeeded(*this, buffer); } -wxFilterOutputStream::~wxFilterOutputStream() +wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& stream, + size_t bufsize) + : wxFilterOutputStream(stream) { + m_o_streambuf = CreateBufferIfNeeded(*this, NULL, bufsize); } -size_t wxFilterOutputStream::DoWrite(const void *buffer, size_t size) +wxBufferedOutputStream::~wxBufferedOutputStream() { - return m_parent_o_stream->Write(buffer, size).LastWrite(); + Sync(); + delete m_o_streambuf; +} + +bool wxBufferedOutputStream::Close() +{ + Sync(); + return IsOk(); } -off_t wxFilterOutputStream::DoSeekOutput(off_t pos, wxSeekMode mode) + +wxOutputStream& wxBufferedOutputStream::Write(const void *buffer, size_t size) { - return m_parent_o_stream->SeekO(pos, mode); + m_lastcount = 0; + m_o_streambuf->Write(buffer, size); + return *this; } -off_t wxFilterOutputStream::DoTellOutput() const +wxFileOffset wxBufferedOutputStream::SeekO(wxFileOffset pos, wxSeekMode mode) { - return m_parent_o_stream->TellO(); + Sync(); + return m_o_streambuf->Seek(pos, mode); +} + +wxFileOffset wxBufferedOutputStream::TellO() const +{ + return m_o_streambuf->Tell(); +} + +void wxBufferedOutputStream::Sync() +{ + m_o_streambuf->FlushBuffer(); + m_parent_o_stream->Sync(); +} + +size_t wxBufferedOutputStream::OnSysWrite(const void *buffer, size_t bufsize) +{ + return m_parent_o_stream->Write(buffer, bufsize).LastWrite(); +} + +wxFileOffset wxBufferedOutputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode) +{ + return m_parent_o_stream->SeekO(seek, mode); +} + +wxFileOffset wxBufferedOutputStream::OnSysTell() const +{ + return m_parent_o_stream->TellO(); +} + +wxFileOffset wxBufferedOutputStream::GetLength() const +{ + return m_parent_o_stream->GetLength() + m_o_streambuf->GetIntPosition(); +} + +void wxBufferedOutputStream::SetOutputStreamBuffer(wxStreamBuffer *buffer) +{ + wxCHECK_RET( buffer, wxT("wxBufferedOutputStream needs buffer") ); + + delete m_o_streambuf; + m_o_streambuf = buffer; } // ---------------------------------------------------------------------------- @@ -614,9 +1452,9 @@ off_t wxFilterOutputStream::DoTellOutput() const wxOutputStream& wxEndL(wxOutputStream& stream) { -#ifdef __MSW__ - return stream.Write("\r\n", 2); -#else - return stream.Write("\n", 1); -#endif + static const wxChar *eol = wxTextFile::GetEOL(); + + return stream.Write(eol, wxStrlen(eol)); } + +#endif // wxUSE_STREAMS