]> git.saurik.com Git - wxWidgets.git/blame - src/common/stream.cpp
Add wxUSE_ARCSTREAM so that other archive classes can be used without wxZip
[wxWidgets.git] / src / common / stream.cpp
CommitLineData
3d4c6a21 1/////////////////////////////////////////////////////////////////////////////
49e399d8 2// Name: src/common/stream.cpp
3d4c6a21
GL
3// Purpose: wxStream base classes
4// Author: Guilhem Lavaux
49e399d8
VZ
5// Modified by: VZ (23.11.00) to fix realloc()ing new[]ed memory,
6// general code review
3d4c6a21
GL
7// Created: 11/07/98
8// RCS-ID: $Id$
9// Copyright: (c) Guilhem Lavaux
65571936 10// Licence: wxWindows licence
3d4c6a21
GL
11/////////////////////////////////////////////////////////////////////////////
12
49e399d8
VZ
13// ============================================================================
14// declarations
15// ============================================================================
16
17// ----------------------------------------------------------------------------
18// headers
19// ----------------------------------------------------------------------------
47fc03d2 20
14f355c2 21#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
47fc03d2 22 #pragma implementation "stream.h"
3d4c6a21
GL
23#endif
24
db138a4c
JS
25// For compilers that support precompilation, includes "wx.h".
26#include "wx/wxprec.h"
ce4169a4
RR
27
28#ifdef __BORLANDC__
29 #pragma hdrstop
30#endif
31
32#ifndef WX_PRECOMP
33 #include "wx/defs.h"
34#endif
35
36#if wxUSE_STREAMS
37
6d44bf31 38#include <ctype.h>
3096bd2f
VZ
39#include "wx/stream.h"
40#include "wx/datstrm.h"
733b8ed3 41#include "wx/textfile.h"
adc35078 42#include "wx/log.h"
db138a4c 43
49e399d8
VZ
44// ----------------------------------------------------------------------------
45// constants
46// ----------------------------------------------------------------------------
47
48// the temporary buffer size used when copying from stream to stream
2b5f62a0 49#define BUF_TEMP_SIZE 4096
8ef6a930 50
49e399d8
VZ
51// ============================================================================
52// implementation
53// ============================================================================
54
6d44bf31
GL
55// ----------------------------------------------------------------------------
56// wxStreamBuffer
57// ----------------------------------------------------------------------------
58
49e399d8
VZ
59void wxStreamBuffer::SetError(wxStreamError err)
60{
2b5f62a0 61 if ( m_stream && m_stream->m_lasterror == wxSTREAM_NO_ERROR )
49e399d8
VZ
62 m_stream->m_lasterror = err;
63}
64
65void wxStreamBuffer::InitBuffer()
66{
67 m_buffer_start =
68 m_buffer_end =
69 m_buffer_pos = NULL;
70 m_buffer_size = 0;
71
d4b45f48 72 // if we are going to allocate the buffer, we should free it later as well
d775fa82 73 m_destroybuf = true;
49e399d8
VZ
74}
75
76void wxStreamBuffer::Init()
77{
78 InitBuffer();
79
d775fa82 80 m_fixed = true;
49e399d8 81}
926c550d 82
2b5f62a0 83wxStreamBuffer::wxStreamBuffer(BufMode mode)
6d44bf31 84{
a2bd1fbb
VZ
85 Init();
86
2b5f62a0 87 m_stream = NULL;
49e399d8
VZ
88 m_mode = mode;
89
d775fa82 90 m_flushable = false;
6d44bf31
GL
91}
92
2b5f62a0 93wxStreamBuffer::wxStreamBuffer(wxStreamBase& stream, BufMode mode)
84b46c35 94{
a2bd1fbb
VZ
95 Init();
96
2b5f62a0 97 m_stream = &stream;
49e399d8
VZ
98 m_mode = mode;
99
d775fa82 100 m_flushable = true;
84b46c35
GL
101}
102
103wxStreamBuffer::wxStreamBuffer(const wxStreamBuffer& buffer)
104{
2b5f62a0 105 // doing this has big chances to lead to a crash when the source buffer is
49e399d8 106 // destroyed (otherwise assume the caller knows what he does)
2b5f62a0 107 wxASSERT_MSG( !buffer.m_destroybuf,
49e399d8
VZ
108 _T("it's a bad idea to copy this buffer") );
109
110 m_buffer_start = buffer.m_buffer_start;
111 m_buffer_end = buffer.m_buffer_end;
112 m_buffer_pos = buffer.m_buffer_pos;
113 m_buffer_size = buffer.m_buffer_size;
114 m_fixed = buffer.m_fixed;
115 m_flushable = buffer.m_flushable;
116 m_stream = buffer.m_stream;
117 m_mode = buffer.m_mode;
d775fa82 118 m_destroybuf = false;
84b46c35
GL
119}
120
49e399d8 121void wxStreamBuffer::FreeBuffer()
6d44bf31 122{
49e399d8
VZ
123 if ( m_destroybuf )
124 free(m_buffer_start);
6d44bf31
GL
125}
126
49e399d8 127wxStreamBuffer::~wxStreamBuffer()
6d44bf31 128{
49e399d8 129 FreeBuffer();
6d44bf31
GL
130}
131
47fc03d2
VZ
132wxInputStream *wxStreamBuffer::GetInputStream() const
133{
134 return m_mode == write ? NULL : (wxInputStream *)m_stream;
135}
136
137wxOutputStream *wxStreamBuffer::GetOutputStream() const
138{
139 return m_mode == read ? NULL : (wxOutputStream *)m_stream;
140}
141
49e399d8
VZ
142void wxStreamBuffer::SetBufferIO(void *buffer_start,
143 void *buffer_end,
144 bool takeOwnership)
67c8c225
VZ
145{
146 SetBufferIO(buffer_start, (char *)buffer_end - (char *)buffer_start,
147 takeOwnership);
148}
149
150void wxStreamBuffer::SetBufferIO(void *start,
151 size_t len,
152 bool takeOwnership)
6d44bf31 153{
49e399d8
VZ
154 // start by freeing the old buffer
155 FreeBuffer();
885ee235 156
67c8c225
VZ
157 m_buffer_start = (char *)start;
158 m_buffer_end = m_buffer_start + len;
6d44bf31 159
67c8c225 160 m_buffer_size = len;
49e399d8
VZ
161
162 // if we own it, we free it
421db2ad 163 m_destroybuf = takeOwnership;
49e399d8
VZ
164
165 ResetBuffer();
166}
6d44bf31 167
49e399d8
VZ
168void wxStreamBuffer::SetBufferIO(size_t bufsize)
169{
170 // start by freeing the old buffer
171 FreeBuffer();
172
173 if ( bufsize )
174 {
d775fa82 175 SetBufferIO(malloc(bufsize), bufsize, true /* take ownership */);
49e399d8
VZ
176 }
177 else // no buffer size => no buffer
178 {
179 InitBuffer();
180 }
6d44bf31
GL
181}
182
183void wxStreamBuffer::ResetBuffer()
184{
2b5f62a0
VZ
185 if ( m_stream )
186 {
187 m_stream->Reset();
188 m_stream->m_lastcount = 0;
189 }
49e399d8 190
2b5f62a0
VZ
191 m_buffer_pos = m_mode == read && m_flushable
192 ? m_buffer_end
193 : m_buffer_start;
6d44bf31
GL
194}
195
49e399d8 196// fill the buffer with as much data as possible (only for read buffers)
75ed1d15
GL
197bool wxStreamBuffer::FillBuffer()
198{
47fc03d2
VZ
199 wxInputStream *inStream = GetInputStream();
200
d775fa82 201 // It's legal to have no stream, so we don't complain about it just return false
2b5f62a0 202 if ( !inStream )
d775fa82 203 return false;
75ed1d15 204
47fc03d2 205 size_t count = inStream->OnSysRead(m_buffer_start, m_buffer_size);
49e399d8 206 if ( !count )
d775fa82 207 return false;
75ed1d15 208
49e399d8
VZ
209 m_buffer_end = m_buffer_start + count;
210 m_buffer_pos = m_buffer_start;
211
d775fa82 212 return true;
75ed1d15
GL
213}
214
49e399d8 215// write the buffer contents to the stream (only for write buffers)
75ed1d15
GL
216bool wxStreamBuffer::FlushBuffer()
217{
d775fa82 218 wxCHECK_MSG( m_flushable, false, _T("can't flush this buffer") );
75ed1d15 219
49e399d8
VZ
220 // FIXME: what is this check for? (VZ)
221 if ( m_buffer_pos == m_buffer_start )
d775fa82 222 return false;
75ed1d15 223
47fc03d2
VZ
224 wxOutputStream *outStream = GetOutputStream();
225
d775fa82 226 wxCHECK_MSG( outStream, false, _T("should have a stream in wxStreamBuffer") );
49e399d8
VZ
227
228 size_t current = m_buffer_pos - m_buffer_start;
47fc03d2 229 size_t count = outStream->OnSysWrite(m_buffer_start, current);
49e399d8 230 if ( count != current )
d775fa82 231 return false;
49e399d8
VZ
232
233 m_buffer_pos = m_buffer_start;
75ed1d15 234
d775fa82 235 return true;
75ed1d15
GL
236}
237
49e399d8
VZ
238size_t wxStreamBuffer::GetDataLeft()
239{
240 /* Why is this done? RR. */
241 if ( m_buffer_pos == m_buffer_end && m_flushable)
242 FillBuffer();
243
244 return GetBytesLeft();
245}
246
247// copy up to size bytes from our buffer into the provided one
75ed1d15
GL
248void wxStreamBuffer::GetFromBuffer(void *buffer, size_t size)
249{
49e399d8
VZ
250 // don't get more bytes than left in the buffer
251 size_t left = GetBytesLeft();
75ed1d15 252
49e399d8
VZ
253 if ( size > left )
254 size = left;
75ed1d15 255
49e399d8
VZ
256 memcpy(buffer, m_buffer_pos, size);
257 m_buffer_pos += size;
75ed1d15
GL
258}
259
49e399d8 260// copy the contents of the provided buffer into this one
75ed1d15
GL
261void wxStreamBuffer::PutToBuffer(const void *buffer, size_t size)
262{
49e399d8 263 size_t left = GetBytesLeft();
2b5f62a0 264
49e399d8
VZ
265 if ( size > left )
266 {
267 if ( m_fixed )
268 {
269 // we can't realloc the buffer, so just copy what we can
270 size = left;
271 }
272 else // !m_fixed
273 {
274 // realloc the buffer to have enough space for the data
275 size_t delta = m_buffer_pos - m_buffer_start;
276
277 char *startOld = m_buffer_start;
278 m_buffer_size += size;
279 m_buffer_start = (char *)realloc(m_buffer_start, m_buffer_size);
280 if ( !m_buffer_start )
281 {
282 // don't leak memory if realloc() failed
283 m_buffer_start = startOld;
284 m_buffer_size -= size;
285
286 // what else can we do?
287 return;
288 }
289
290 // adjust the pointers invalidated by realloc()
291 m_buffer_pos = m_buffer_start + delta;
292 m_buffer_end = m_buffer_start + m_buffer_size;
293 }
56dc1ffd 294 }
49e399d8
VZ
295
296 memcpy(m_buffer_pos, buffer, size);
297 m_buffer_pos += size;
75ed1d15
GL
298}
299
84b46c35
GL
300void wxStreamBuffer::PutChar(char c)
301{
47fc03d2
VZ
302 wxOutputStream *outStream = GetOutputStream();
303
304 wxCHECK_RET( outStream, _T("should have a stream in wxStreamBuffer") );
84b46c35 305
49e399d8
VZ
306 // if we don't have buffer at all, just forward this call to the stream,
307 if ( !HasBuffer() )
308 {
2b5f62a0 309 outStream->OnSysWrite(&c, sizeof(c));
49e399d8
VZ
310 }
311 else
312 {
313 // otherwise check we have enough space left
314 if ( !GetDataLeft() && !FlushBuffer() )
315 {
316 // we don't
2b5f62a0 317 SetError(wxSTREAM_WRITE_ERROR);
49e399d8
VZ
318 }
319 else
320 {
2b5f62a0 321 PutToBuffer(&c, sizeof(c));
49e399d8
VZ
322 m_stream->m_lastcount = 1;
323 }
324 }
84b46c35
GL
325}
326
6319afe3
GL
327char wxStreamBuffer::Peek()
328{
49e399d8
VZ
329 wxCHECK_MSG( m_stream && HasBuffer(), 0,
330 _T("should have the stream and the buffer in wxStreamBuffer") );
6319afe3 331
49e399d8
VZ
332 if ( !GetDataLeft() )
333 {
2b5f62a0 334 SetError(wxSTREAM_READ_ERROR);
49e399d8
VZ
335 return 0;
336 }
6319afe3 337
49e399d8 338 char c;
2b5f62a0 339 GetFromBuffer(&c, sizeof(c));
49e399d8 340 m_buffer_pos--;
6319afe3 341
49e399d8 342 return c;
6319afe3
GL
343}
344
84b46c35
GL
345char wxStreamBuffer::GetChar()
346{
47fc03d2
VZ
347 wxInputStream *inStream = GetInputStream();
348
349 wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
84b46c35 350
49e399d8
VZ
351 char c;
352 if ( !HasBuffer() )
353 {
2b5f62a0 354 inStream->OnSysRead(&c, sizeof(c));
49e399d8
VZ
355 }
356 else
357 {
358 if ( !GetDataLeft() )
359 {
2b5f62a0 360 SetError(wxSTREAM_READ_ERROR);
49e399d8
VZ
361 c = 0;
362 }
363 else
364 {
2b5f62a0 365 GetFromBuffer(&c, sizeof(c));
49e399d8
VZ
366 m_stream->m_lastcount = 1;
367 }
368 }
84b46c35 369
84b46c35 370 return c;
84b46c35
GL
371}
372
8ef6a930 373size_t wxStreamBuffer::Read(void *buffer, size_t size)
6d44bf31 374{
49e399d8 375 // lasterror is reset before all new IO calls
2b5f62a0
VZ
376 if ( m_stream )
377 m_stream->Reset();
49e399d8 378
2b5f62a0 379 size_t read;
49e399d8
VZ
380 if ( !HasBuffer() )
381 {
2b5f62a0
VZ
382 wxInputStream *inStream = GetInputStream();
383
384 wxCHECK_MSG( inStream, 0, _T("should have a stream in wxStreamBuffer") );
385
386 read = inStream->OnSysRead(buffer, size);
49e399d8
VZ
387 }
388 else // we have a buffer, use it
389 {
390 size_t orig_size = size;
391
392 while ( size > 0 )
393 {
394 size_t left = GetDataLeft();
395
396 // if the requested number of bytes if greater than the buffer
397 // size, read data in chunks
398 if ( size > left )
399 {
400 GetFromBuffer(buffer, left);
401 size -= left;
402 buffer = (char *)buffer + left;
403
404 if ( !FillBuffer() )
405 {
2b5f62a0 406 SetError(wxSTREAM_EOF);
49e399d8
VZ
407 break;
408 }
409 }
410 else // otherwise just do it in one gulp
411 {
412 GetFromBuffer(buffer, size);
413 size = 0;
414 }
415 }
416
2b5f62a0 417 read = orig_size - size;
6d44bf31 418 }
49e399d8 419
2b5f62a0
VZ
420 if ( m_stream )
421 m_stream->m_lastcount = read;
422
423 return read;
6d44bf31
GL
424}
425
49e399d8
VZ
426// this should really be called "Copy()"
427size_t wxStreamBuffer::Read(wxStreamBuffer *dbuf)
8ef6a930 428{
49e399d8 429 wxCHECK_MSG( m_mode != write, 0, _T("can't read from this buffer") );
8ef6a930 430
49e399d8
VZ
431 char buf[BUF_TEMP_SIZE];
432 size_t nRead,
433 total = 0;
7f42cff1 434
49e399d8
VZ
435 do
436 {
437 nRead = Read(dbuf, WXSIZEOF(buf));
438 if ( nRead )
439 {
440 nRead = dbuf->Write(buf, nRead);
441 total += nRead;
442 }
443 }
444 while ( nRead );
445
446 return total;
8ef6a930
GL
447}
448
449size_t wxStreamBuffer::Write(const void *buffer, size_t size)
6d44bf31 450{
2b5f62a0
VZ
451 if (m_stream)
452 {
453 // lasterror is reset before all new IO calls
454 m_stream->Reset();
455 }
6d44bf31 456
999836aa 457 size_t ret;
6d44bf31 458
49e399d8
VZ
459 if ( !HasBuffer() && m_fixed )
460 {
2b5f62a0
VZ
461 wxOutputStream *outStream = GetOutputStream();
462
463 wxCHECK_MSG( outStream, 0, _T("should have a stream in wxStreamBuffer") );
464
49e399d8 465 // no buffer, just forward the call to the stream
2b5f62a0 466 ret = outStream->OnSysWrite(buffer, size);
49e399d8
VZ
467 }
468 else // we [may] have a buffer, use it
469 {
470 size_t orig_size = size;
471
472 while ( size > 0 )
473 {
474 size_t left = GetBytesLeft();
475
476 // if the buffer is too large to fit in the stream buffer, split
477 // it in smaller parts
478 //
479 // NB: If stream buffer isn't fixed (as for wxMemoryOutputStream),
480 // we always go to the second case.
481 //
482 // FIXME: fine, but if it fails we should (re)try writing it by
483 // chunks as this will (hopefully) always work (VZ)
2b5f62a0 484
49e399d8
VZ
485 if ( size > left && m_fixed )
486 {
487 PutToBuffer(buffer, left);
488 size -= left;
489 buffer = (char *)buffer + left;
490
491 if ( !FlushBuffer() )
492 {
2b5f62a0 493 SetError(wxSTREAM_WRITE_ERROR);
49e399d8
VZ
494
495 break;
496 }
497
498 m_buffer_pos = m_buffer_start;
499 }
500 else // we can do it in one gulp
501 {
502 PutToBuffer(buffer, size);
503 size = 0;
504 }
505 }
506
2b5f62a0 507 ret = orig_size - size;
6d44bf31 508 }
49e399d8 509
2b5f62a0
VZ
510 if (m_stream)
511 {
512 // i am not entirely sure what we do this for
513 m_stream->m_lastcount = ret;
514 }
515
516 return ret;
8ef6a930
GL
517}
518
519size_t wxStreamBuffer::Write(wxStreamBuffer *sbuf)
520{
49e399d8
VZ
521 wxCHECK_MSG( m_mode != read, 0, _T("can't write to this buffer") );
522 wxCHECK_MSG( sbuf->m_mode != write, 0, _T("can't read from that buffer") );
8ef6a930 523
49e399d8
VZ
524 char buf[BUF_TEMP_SIZE];
525 size_t nWrite,
526 total = 0;
7f42cff1 527
49e399d8
VZ
528 do
529 {
530 size_t nRead = sbuf->Read(buf, WXSIZEOF(buf));
531 if ( nRead )
532 {
533 nWrite = Write(buf, nRead);
534 if ( nWrite < nRead )
535 {
536 // put back data we couldn't copy
537 wxInputStream *in_stream = (wxInputStream *)sbuf->GetStream();
538
539 in_stream->Ungetch(buf + nWrite, nRead - nWrite);
540 }
541
542 total += nWrite;
543 }
544 else
545 {
546 nWrite = 0;
547 }
548 }
549 while ( nWrite == WXSIZEOF(buf) );
fae05df5 550
49e399d8 551 return total;
75ed1d15
GL
552}
553
4004775e 554wxFileOffset wxStreamBuffer::Seek(wxFileOffset pos, wxSeekMode mode)
75ed1d15 555{
4004775e 556 wxFileOffset ret_off, diff;
75ed1d15 557
4004775e 558 wxFileOffset last_access = GetLastAccess();
75ed1d15 559
49e399d8
VZ
560 if ( !m_flushable )
561 {
562 switch (mode)
563 {
564 case wxFromStart:
565 diff = pos;
566 break;
567
568 case wxFromCurrent:
569 diff = pos + GetIntPosition();
570 break;
571
572 case wxFromEnd:
573 diff = pos + last_access;
574 break;
575
576 default:
577 wxFAIL_MSG( _T("invalid seek mode") );
578
30984dea 579 return wxInvalidOffset;
49e399d8
VZ
580 }
581 if (diff < 0 || diff > last_access)
30984dea 582 return wxInvalidOffset;
49e399d8
VZ
583 SetIntPosition(diff);
584 return diff;
d984207c 585 }
49e399d8
VZ
586
587 switch ( mode )
588 {
589 case wxFromStart:
590 // We'll try to compute an internal position later ...
591 ret_off = m_stream->OnSysSeek(pos, wxFromStart);
592 ResetBuffer();
593 return ret_off;
594
595 case wxFromCurrent:
596 diff = pos + GetIntPosition();
597
598 if ( (diff > last_access) || (diff < 0) )
599 {
600 // We must take into account the fact that we have read
601 // something previously.
602 ret_off = m_stream->OnSysSeek(diff-last_access, wxFromCurrent);
603 ResetBuffer();
604 return ret_off;
605 }
606 else
607 {
608 SetIntPosition(diff);
609 return pos;
610 }
611
612 case wxFromEnd:
613 // Hard to compute: always seek to the requested position.
614 ret_off = m_stream->OnSysSeek(pos, wxFromEnd);
615 ResetBuffer();
616 return ret_off;
75ed1d15 617 }
49e399d8 618
30984dea 619 return wxInvalidOffset;
75ed1d15
GL
620}
621
4004775e 622wxFileOffset wxStreamBuffer::Tell() const
75ed1d15 623{
30984dea 624 wxFileOffset pos;
915a955c 625
2b5f62a0
VZ
626 // ask the stream for position if we have a real one
627 if ( m_stream )
915a955c
VZ
628 {
629 pos = m_stream->OnSysTell();
630 if ( pos == wxInvalidOffset )
30984dea 631 return wxInvalidOffset;
915a955c
VZ
632 }
633 else // no associated stream
634 {
635 pos = 0;
636 }
c7a9fa36
RR
637
638 pos += GetIntPosition();
cd6ce4a9 639
49e399d8 640 if ( m_mode == read && m_flushable )
c7a9fa36 641 pos -= GetLastAccess();
cd6ce4a9 642
c7a9fa36 643 return pos;
75ed1d15
GL
644}
645
75ed1d15
GL
646// ----------------------------------------------------------------------------
647// wxStreamBase
648// ----------------------------------------------------------------------------
649
650wxStreamBase::wxStreamBase()
651{
2b5f62a0 652 m_lasterror = wxSTREAM_NO_ERROR;
c7a9fa36 653 m_lastcount = 0;
75ed1d15
GL
654}
655
656wxStreamBase::~wxStreamBase()
657{
658}
659
588066b7
VZ
660size_t wxStreamBase::GetSize() const
661{
662 wxFileOffset length = GetLength();
663 return length == wxInvalidOffset ? 0 : (size_t)length;
664}
665
4004775e 666wxFileOffset wxStreamBase::OnSysSeek(wxFileOffset WXUNUSED(seek), wxSeekMode WXUNUSED(mode))
b9138710 667{
30984dea 668 return wxInvalidOffset;
b9138710
VZ
669}
670
4004775e 671wxFileOffset wxStreamBase::OnSysTell() const
b9138710 672{
30984dea 673 return wxInvalidOffset;
b9138710
VZ
674}
675
2d67974d
WS
676#if WXWIN_COMPATIBILITY_2_2
677
678wxStreamError wxStreamBase::LastError() const
679{
680 return m_lasterror;
681}
682
683size_t wxStreamBase::StreamSize() const
684{
685 return GetSize();
686}
687
688#endif // WXWIN_COMPATIBILITY_2_2
689
1678ad78
GL
690// ----------------------------------------------------------------------------
691// wxInputStream
692// ----------------------------------------------------------------------------
693
3d4c6a21 694wxInputStream::wxInputStream()
3d4c6a21 695{
49e399d8
VZ
696 m_wback = NULL;
697 m_wbacksize =
698 m_wbackcur = 0;
6d44bf31
GL
699}
700
fae05df5 701wxInputStream::~wxInputStream()
6d44bf31 702{
fae05df5 703 free(m_wback);
3d4c6a21
GL
704}
705
2b5f62a0 706bool wxInputStream::CanRead() const
47fc03d2 707{
2b5f62a0
VZ
708 // we don't know if there is anything to read or not and by default we
709 // prefer to be optimistic and try to read data unless we know for sure
710 // there is no more of it
711 return m_lasterror != wxSTREAM_EOF;
47fc03d2
VZ
712}
713
cd6ce4a9
VZ
714bool wxInputStream::Eof() const
715{
2b5f62a0
VZ
716 // the only way the base class can know we're at EOF is when we'd already
717 // tried to read beyond it in which case last error is set accordingly
718 return GetLastError() == wxSTREAM_EOF;
cd6ce4a9
VZ
719}
720
fae05df5 721char *wxInputStream::AllocSpaceWBack(size_t needed_size)
3d4c6a21 722{
49e399d8 723 // get number of bytes left from previous wback buffer
c7a9fa36 724 size_t toget = m_wbacksize - m_wbackcur;
fae05df5 725
49e399d8
VZ
726 // allocate a buffer large enough to hold prev + new data
727 char *temp_b = (char *)malloc(needed_size + toget);
fae05df5 728
c7a9fa36
RR
729 if (!temp_b)
730 return NULL;
fae05df5 731
adc35078 732 // copy previous data (and free old buffer) if needed
c7a9fa36
RR
733 if (m_wback)
734 {
735 memmove(temp_b + needed_size, m_wback + m_wbackcur, toget);
736 free(m_wback);
737 }
5ac8158a 738
adc35078 739 // done
c7a9fa36
RR
740 m_wback = temp_b;
741 m_wbackcur = 0;
742 m_wbacksize = needed_size + toget;
783ff666 743
49e399d8 744 return m_wback;
6d44bf31
GL
745}
746
2b5f62a0 747size_t wxInputStream::GetWBack(void *buf, size_t size)
6d44bf31 748{
c7a9fa36
RR
749 if (!m_wback)
750 return 0;
a324a7bc 751
e9f69291
VZ
752 // how many bytes do we have in the buffer?
753 size_t toget = m_wbacksize - m_wbackcur;
754
2b5f62a0 755 if ( size < toget )
e9f69291
VZ
756 {
757 // we won't read everything
2b5f62a0 758 toget = size;
e9f69291 759 }
fae05df5 760
2b5f62a0 761 // copy the data from the cache
e9f69291 762 memcpy(buf, m_wback + m_wbackcur, toget);
fae05df5 763
49e399d8 764 m_wbackcur += toget;
e9f69291 765 if ( m_wbackcur == m_wbacksize )
c7a9fa36 766 {
e9f69291 767 // TODO: should we really free it here all the time? maybe keep it?
c7a9fa36 768 free(m_wback);
49e399d8 769 m_wback = NULL;
c7a9fa36
RR
770 m_wbacksize = 0;
771 m_wbackcur = 0;
772 }
cd6ce4a9 773
e9f69291 774 // return the number of bytes copied
49e399d8 775 return toget;
6d44bf31
GL
776}
777
8f7173ab 778size_t wxInputStream::Ungetch(const void *buf, size_t bufsize)
fae05df5 779{
42a3aedb
VZ
780 if ( m_lasterror != wxSTREAM_NO_ERROR && m_lasterror != wxSTREAM_EOF )
781 {
782 // can't operate on this stream until the error is cleared
783 return 0;
784 }
785
c7a9fa36
RR
786 char *ptrback = AllocSpaceWBack(bufsize);
787 if (!ptrback)
788 return 0;
cd6ce4a9 789
d775fa82 790 // Eof() shouldn't return true any longer
42a3aedb
VZ
791 if ( m_lasterror == wxSTREAM_EOF )
792 m_lasterror = wxSTREAM_NO_ERROR;
793
c7a9fa36
RR
794 memcpy(ptrback, buf, bufsize);
795 return bufsize;
fae05df5
GL
796}
797
798bool wxInputStream::Ungetch(char c)
1e3eca9d 799{
2b5f62a0 800 return Ungetch(&c, sizeof(c)) != 0;
fae05df5
GL
801}
802
803char wxInputStream::GetC()
804{
c7a9fa36 805 char c;
2b5f62a0 806 Read(&c, sizeof(c));
c7a9fa36 807 return c;
1e3eca9d
GL
808}
809
49e399d8 810wxInputStream& wxInputStream::Read(void *buf, size_t size)
6d44bf31 811{
9a76510b
VZ
812 char *p = (char *)buf;
813 m_lastcount = 0;
814
815 size_t read = GetWBack(buf, size);
816 for ( ;; )
c7a9fa36 817 {
9a76510b
VZ
818 size -= read;
819 m_lastcount += read;
820 p += read;
821
822 if ( !size )
823 {
824 // we read the requested amount of data
825 break;
826 }
827
2b5f62a0
VZ
828 if ( p != buf && !CanRead() )
829 {
830 // we have already read something and we would block in OnSysRead()
831 // now: don't do it but return immediately
832 break;
833 }
834
9b11cb6e 835 read = OnSysRead(p, size);
9a76510b
VZ
836 if ( !read )
837 {
838 // no more data available
839 break;
840 }
c7a9fa36 841 }
fae05df5 842
c7a9fa36 843 return *this;
3d4c6a21
GL
844}
845
75ed1d15
GL
846char wxInputStream::Peek()
847{
c7a9fa36 848 char c;
2b5f62a0
VZ
849 Read(&c, sizeof(c));
850 if (m_lasterror == wxSTREAM_NO_ERROR)
c7a9fa36
RR
851 {
852 Ungetch(c);
853 return c;
854 }
cd6ce4a9 855
c7a9fa36 856 return 0;
75ed1d15
GL
857}
858
3d4c6a21
GL
859wxInputStream& wxInputStream::Read(wxOutputStream& stream_out)
860{
cd6ce4a9 861 char buf[BUF_TEMP_SIZE];
3d4c6a21 862
2b5f62a0 863 for ( ;; )
c7a9fa36 864 {
2b5f62a0
VZ
865 size_t bytes_read = Read(buf, WXSIZEOF(buf)).LastRead();
866 if ( !bytes_read )
867 break;
868
869 if ( stream_out.Write(buf, bytes_read).LastWrite() != bytes_read )
870 break;
c7a9fa36 871 }
2b5f62a0 872
c7a9fa36 873 return *this;
3d4c6a21
GL
874}
875
4004775e 876wxFileOffset wxInputStream::SeekI(wxFileOffset pos, wxSeekMode mode)
75ed1d15 877{
adc35078
RR
878 // RR: This code is duplicated in wxBufferedInputStream. This is
879 // not really a good design, but buffered stream are different
880 // from all other in that they handle two stream-related objects,
881 // the stream buffer and parent stream.
882
883 // I don't know whether it should be put as well in wxFileInputStream::OnSysSeek
cd6ce4a9 884 if (m_lasterror==wxSTREAM_EOF)
2b5f62a0 885 m_lasterror=wxSTREAM_NO_ERROR;
c7a9fa36 886
adc35078
RR
887 /* RR: A call to SeekI() will automatically invalidate any previous
888 call to Ungetch(), otherwise it would be possible to SeekI() to
c7a9fa36 889 one position, unread some bytes there, SeekI() to another position
19da7237
GRG
890 and the data would be corrupted.
891
892 GRG: Could add code here to try to navigate within the wback
893 buffer if possible, but is it really needed? It would only work
894 when seeking in wxFromCurrent mode, else it would invalidate
adc35078 895 anyway... */
2b5f62a0 896
cd6ce4a9 897 if (m_wback)
c7a9fa36 898 {
adc35078 899 wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
2b5f62a0 900
c7a9fa36 901 free(m_wback);
49e399d8 902 m_wback = NULL;
c7a9fa36
RR
903 m_wbacksize = 0;
904 m_wbackcur = 0;
905 }
fe8aa971 906
c7a9fa36 907 return OnSysSeek(pos, mode);
75ed1d15
GL
908}
909
4004775e 910wxFileOffset wxInputStream::TellI() const
75ed1d15 911{
30984dea 912 wxFileOffset pos = OnSysTell();
19da7237
GRG
913
914 if (pos != wxInvalidOffset)
915 pos -= (m_wbacksize - m_wbackcur);
916
917 return pos;
75ed1d15
GL
918}
919
1678ad78 920
fae05df5
GL
921// ----------------------------------------------------------------------------
922// wxOutputStream
923// ----------------------------------------------------------------------------
49e399d8 924
fae05df5 925wxOutputStream::wxOutputStream()
1678ad78 926{
1678ad78
GL
927}
928
fae05df5 929wxOutputStream::~wxOutputStream()
123a7fdd 930{
123a7fdd
GL
931}
932
47fc03d2
VZ
933size_t wxOutputStream::OnSysWrite(const void * WXUNUSED(buffer),
934 size_t WXUNUSED(bufsize))
935{
936 return 0;
937}
938
7513f9ff
GRG
939void wxOutputStream::PutC(char c)
940{
2b5f62a0 941 Write(&c, sizeof(c));
7513f9ff
GRG
942}
943
fae05df5 944wxOutputStream& wxOutputStream::Write(const void *buffer, size_t size)
1678ad78 945{
c7a9fa36
RR
946 m_lastcount = OnSysWrite(buffer, size);
947 return *this;
1678ad78
GL
948}
949
fae05df5 950wxOutputStream& wxOutputStream::Write(wxInputStream& stream_in)
38830220 951{
c7a9fa36
RR
952 stream_in.Read(*this);
953 return *this;
38830220
RR
954}
955
4004775e 956wxFileOffset wxOutputStream::TellO() const
38830220 957{
c7a9fa36 958 return OnSysTell();
38830220
RR
959}
960
4004775e 961wxFileOffset wxOutputStream::SeekO(wxFileOffset pos, wxSeekMode mode)
38830220 962{
c7a9fa36 963 return OnSysSeek(pos, mode);
38830220
RR
964}
965
fae05df5 966void wxOutputStream::Sync()
1678ad78 967{
1678ad78
GL
968}
969
123a7fdd 970
e2acb9ae
RR
971// ----------------------------------------------------------------------------
972// wxCountingOutputStream
973// ----------------------------------------------------------------------------
974
975wxCountingOutputStream::wxCountingOutputStream ()
e2acb9ae 976{
c7a9fa36 977 m_currentPos = 0;
e2acb9ae
RR
978}
979
588066b7 980wxFileOffset wxCountingOutputStream::GetLength() const
e2acb9ae 981{
c7a9fa36 982 return m_lastcount;
e2acb9ae
RR
983}
984
49e399d8
VZ
985size_t wxCountingOutputStream::OnSysWrite(const void *WXUNUSED(buffer),
986 size_t size)
e2acb9ae 987{
c7a9fa36 988 m_currentPos += size;
49e399d8
VZ
989 if (m_currentPos > m_lastcount)
990 m_lastcount = m_currentPos;
991
c7a9fa36 992 return m_currentPos;
e2acb9ae
RR
993}
994
4004775e 995wxFileOffset wxCountingOutputStream::OnSysSeek(wxFileOffset pos, wxSeekMode mode)
e2acb9ae 996{
49e399d8
VZ
997 switch ( mode )
998 {
999 case wxFromStart:
1000 m_currentPos = pos;
1001 break;
1002
1003 case wxFromEnd:
1004 m_currentPos = m_lastcount + pos;
1005 break;
1006
1007 case wxFromCurrent:
1008 m_currentPos += pos;
1009 break;
1010
1011 default:
1012 wxFAIL_MSG( _T("invalid seek mode") );
30984dea 1013 return wxInvalidOffset;
49e399d8 1014 }
cd6ce4a9 1015
49e399d8
VZ
1016 if (m_currentPos > m_lastcount)
1017 m_lastcount = m_currentPos;
cd6ce4a9 1018
c7a9fa36 1019 return m_currentPos;
e2acb9ae
RR
1020}
1021
4004775e 1022wxFileOffset wxCountingOutputStream::OnSysTell() const
e2acb9ae 1023{
c7a9fa36 1024 return m_currentPos;
e2acb9ae 1025}
cd6ce4a9 1026
1678ad78 1027// ----------------------------------------------------------------------------
fae05df5 1028// wxFilterInputStream
1678ad78 1029// ----------------------------------------------------------------------------
e2acb9ae 1030
fae05df5 1031wxFilterInputStream::wxFilterInputStream()
3d4c6a21 1032{
47fc03d2 1033 m_parent_i_stream = NULL;
6d44bf31
GL
1034}
1035
fae05df5 1036wxFilterInputStream::wxFilterInputStream(wxInputStream& stream)
6d44bf31 1037{
c7a9fa36 1038 m_parent_i_stream = &stream;
3d4c6a21
GL
1039}
1040
fae05df5 1041wxFilterInputStream::~wxFilterInputStream()
3d4c6a21 1042{
6d44bf31
GL
1043}
1044
fae05df5
GL
1045// ----------------------------------------------------------------------------
1046// wxFilterOutputStream
1047// ----------------------------------------------------------------------------
49e399d8 1048
fae05df5 1049wxFilterOutputStream::wxFilterOutputStream()
6d44bf31 1050{
47fc03d2 1051 m_parent_o_stream = NULL;
3d4c6a21
GL
1052}
1053
fae05df5 1054wxFilterOutputStream::wxFilterOutputStream(wxOutputStream& stream)
3d4c6a21 1055{
c7a9fa36 1056 m_parent_o_stream = &stream;
6d44bf31
GL
1057}
1058
fae05df5 1059wxFilterOutputStream::~wxFilterOutputStream()
6d44bf31 1060{
6d44bf31
GL
1061}
1062
fae05df5
GL
1063// ----------------------------------------------------------------------------
1064// wxBufferedInputStream
1065// ----------------------------------------------------------------------------
49e399d8 1066
47fc03d2
VZ
1067wxBufferedInputStream::wxBufferedInputStream(wxInputStream& s,
1068 wxStreamBuffer *buffer)
49e399d8 1069 : wxFilterInputStream(s)
6d44bf31 1070{
47fc03d2
VZ
1071 if ( buffer )
1072 {
1073 // use the buffer provided by the user
1074 m_i_streambuf = buffer;
1075 }
1076 else // create a default buffer
1077 {
1078 m_i_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::read);
49e399d8 1079
47fc03d2
VZ
1080 m_i_streambuf->SetBufferIO(1024);
1081 }
6d44bf31
GL
1082}
1083
fae05df5 1084wxBufferedInputStream::~wxBufferedInputStream()
6d44bf31 1085{
4004775e 1086 m_parent_i_stream->SeekI(-(wxFileOffset)m_i_streambuf->GetBytesLeft(),
1fb45475 1087 wxFromCurrent);
672cedf8 1088
c7a9fa36 1089 delete m_i_streambuf;
6d44bf31
GL
1090}
1091
6319afe3
GL
1092char wxBufferedInputStream::Peek()
1093{
c7a9fa36 1094 return m_i_streambuf->Peek();
6319afe3
GL
1095}
1096
49e399d8 1097wxInputStream& wxBufferedInputStream::Read(void *buf, size_t size)
1e3eca9d 1098{
e9f69291 1099 // reset the error flag
2b5f62a0 1100 Reset();
1e3eca9d 1101
e9f69291
VZ
1102 // first read from the already cached data
1103 m_lastcount = GetWBack(buf, size);
1104
1105 // do we have to read anything more?
1106 if ( m_lastcount < size )
c7a9fa36 1107 {
e9f69291
VZ
1108 size -= m_lastcount;
1109 buf = (char *)buf + m_lastcount;
1e3eca9d 1110
e9f69291
VZ
1111 // the call to wxStreamBuffer::Read() below will reset our m_lastcount,
1112 // so save it
1113 size_t countOld = m_lastcount;
1114
1115 m_i_streambuf->Read(buf, size);
1116
1117 m_lastcount += countOld;
1118 }
6d44bf31 1119
c7a9fa36 1120 return *this;
6d44bf31
GL
1121}
1122
4004775e 1123wxFileOffset wxBufferedInputStream::SeekI(wxFileOffset pos, wxSeekMode mode)
6d44bf31 1124{
adc35078
RR
1125 // RR: Look at wxInputStream for comments.
1126
1127 if (m_lasterror==wxSTREAM_EOF)
2b5f62a0 1128 Reset();
adc35078
RR
1129
1130 if (m_wback)
1131 {
1132 wxLogDebug( wxT("Seeking in stream which has data written back to it.") );
2b5f62a0 1133
adc35078
RR
1134 free(m_wback);
1135 m_wback = NULL;
1136 m_wbacksize = 0;
1137 m_wbackcur = 0;
1138 }
2b5f62a0 1139
c7a9fa36 1140 return m_i_streambuf->Seek(pos, mode);
6d44bf31
GL
1141}
1142
4004775e 1143wxFileOffset wxBufferedInputStream::TellI() const
6d44bf31 1144{
30984dea 1145 wxFileOffset pos = m_i_streambuf->Tell();
adc35078
RR
1146
1147 if (pos != wxInvalidOffset)
1148 pos -= (m_wbacksize - m_wbackcur);
2b5f62a0 1149
adc35078 1150 return pos;
38830220 1151}
6d44bf31 1152
fae05df5 1153size_t wxBufferedInputStream::OnSysRead(void *buffer, size_t bufsize)
38830220 1154{
c7a9fa36 1155 return m_parent_i_stream->Read(buffer, bufsize).LastRead();
38830220
RR
1156}
1157
4004775e 1158wxFileOffset wxBufferedInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
38830220 1159{
c7a9fa36 1160 return m_parent_i_stream->SeekI(seek, mode);
6d44bf31
GL
1161}
1162
4004775e 1163wxFileOffset wxBufferedInputStream::OnSysTell() const
6d44bf31 1164{
c7a9fa36 1165 return m_parent_i_stream->TellI();
38830220 1166}
6d44bf31 1167
47fc03d2
VZ
1168void wxBufferedInputStream::SetInputStreamBuffer(wxStreamBuffer *buffer)
1169{
1170 wxCHECK_RET( buffer, _T("wxBufferedInputStream needs buffer") );
1171
1172 delete m_i_streambuf;
1173 m_i_streambuf = buffer;
1174}
1175
fae05df5
GL
1176// ----------------------------------------------------------------------------
1177// wxBufferedOutputStream
1178// ----------------------------------------------------------------------------
6d44bf31 1179
47fc03d2
VZ
1180wxBufferedOutputStream::wxBufferedOutputStream(wxOutputStream& s,
1181 wxStreamBuffer *buffer)
49e399d8 1182 : wxFilterOutputStream(s)
6d44bf31 1183{
47fc03d2
VZ
1184 if ( buffer )
1185 {
1186 m_o_streambuf = buffer;
1187 }
1188 else // create a default one
1189 {
1190 m_o_streambuf = new wxStreamBuffer(*this, wxStreamBuffer::write);
1191
1192 m_o_streambuf->SetBufferIO(1024);
1193 }
6d44bf31
GL
1194}
1195
fae05df5 1196wxBufferedOutputStream::~wxBufferedOutputStream()
6d44bf31 1197{
c7a9fa36
RR
1198 Sync();
1199 delete m_o_streambuf;
3d4c6a21
GL
1200}
1201
8f0ff178
RN
1202bool wxBufferedOutputStream::Close()
1203{
1204 Sync();
1205 return IsOk();
1206}
1207
1208
fae05df5 1209wxOutputStream& wxBufferedOutputStream::Write(const void *buffer, size_t size)
123a7fdd 1210{
c7a9fa36
RR
1211 m_lastcount = 0;
1212 m_o_streambuf->Write(buffer, size);
1213 return *this;
123a7fdd
GL
1214}
1215
4004775e 1216wxFileOffset wxBufferedOutputStream::SeekO(wxFileOffset pos, wxSeekMode mode)
f4ada568 1217{
c7a9fa36
RR
1218 Sync();
1219 return m_o_streambuf->Seek(pos, mode);
f4ada568
GL
1220}
1221
4004775e 1222wxFileOffset wxBufferedOutputStream::TellO() const
3d4c6a21 1223{
c7a9fa36 1224 return m_o_streambuf->Tell();
3d4c6a21
GL
1225}
1226
fae05df5 1227void wxBufferedOutputStream::Sync()
3d4c6a21 1228{
c7a9fa36
RR
1229 m_o_streambuf->FlushBuffer();
1230 m_parent_o_stream->Sync();
3d4c6a21 1231}
219f895a 1232
fae05df5 1233size_t wxBufferedOutputStream::OnSysWrite(const void *buffer, size_t bufsize)
f4ada568 1234{
c7a9fa36 1235 return m_parent_o_stream->Write(buffer, bufsize).LastWrite();
f4ada568
GL
1236}
1237
4004775e 1238wxFileOffset wxBufferedOutputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
219f895a 1239{
c7a9fa36 1240 return m_parent_o_stream->SeekO(seek, mode);
219f895a
RR
1241}
1242
4004775e 1243wxFileOffset wxBufferedOutputStream::OnSysTell() const
219f895a 1244{
c7a9fa36
RR
1245 return m_parent_o_stream->TellO();
1246}
1247
588066b7 1248wxFileOffset wxBufferedOutputStream::GetLength() const
c7a9fa36 1249{
588066b7 1250 return m_parent_o_stream->GetLength() + m_o_streambuf->GetIntPosition();
219f895a 1251}
6d44bf31 1252
47fc03d2
VZ
1253void wxBufferedOutputStream::SetOutputStreamBuffer(wxStreamBuffer *buffer)
1254{
1255 wxCHECK_RET( buffer, _T("wxBufferedOutputStream needs buffer") );
1256
1257 delete m_o_streambuf;
1258 m_o_streambuf = buffer;
1259}
1260
6d44bf31
GL
1261// ----------------------------------------------------------------------------
1262// Some IOManip function
1263// ----------------------------------------------------------------------------
1264
1265wxOutputStream& wxEndL(wxOutputStream& stream)
1266{
733b8ed3
VZ
1267 static const wxChar *eol = wxTextFile::GetEOL();
1268
1269 return stream.Write(eol, wxStrlen(eol));
6d44bf31 1270}
ce4169a4
RR
1271
1272#endif
1273 // wxUSE_STREAMS