File/dir dialog styles and other changes (patch 1488371):
[wxWidgets.git] / src / common / zipstrm.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/common/zipstrm.cpp
3 // Purpose: Streams for Zip files
4 // Author: Mike Wetherell
5 // RCS-ID: $Id$
6 // Copyright: (c) Mike Wetherell
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12
13 #ifdef __BORLANDC__
14 #pragma hdrstop
15 #endif
16
17 #if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
18
19 #ifndef WX_PRECOMP
20 #include "wx/intl.h"
21 #include "wx/log.h"
22 #include "wx/utils.h"
23 #endif
24
25 #include "wx/zipstrm.h"
26 #include "wx/datstrm.h"
27 #include "wx/zstream.h"
28 #include "wx/mstream.h"
29 #include "wx/buffer.h"
30 #include "wx/ptr_scpd.h"
31 #include "wx/wfstream.h"
32 #include "wx/link.h"
33 #include "zlib.h"
34
35 // value for the 'version needed to extract' field (20 means 2.0)
36 enum {
37 VERSION_NEEDED_TO_EXTRACT = 20
38 };
39
40 // signatures for the various records (PKxx)
41 enum {
42 CENTRAL_MAGIC = 0x02014b50, // central directory record
43 LOCAL_MAGIC = 0x04034b50, // local header
44 END_MAGIC = 0x06054b50, // end of central directory record
45 SUMS_MAGIC = 0x08074b50 // data descriptor (info-zip)
46 };
47
48 // unix file attributes. zip stores them in the high 16 bits of the
49 // 'external attributes' field, hence the extra zeros.
50 enum {
51 wxZIP_S_IFMT = 0xF0000000,
52 wxZIP_S_IFDIR = 0x40000000,
53 wxZIP_S_IFREG = 0x80000000
54 };
55
56 // minimum sizes for the various records
57 enum {
58 CENTRAL_SIZE = 46,
59 LOCAL_SIZE = 30,
60 END_SIZE = 22,
61 SUMS_SIZE = 12
62 };
63
64 // The number of bytes that must be written to an wxZipOutputStream before
65 // a zip entry is created. The purpose of this latency is so that
66 // OpenCompressor() can see a little data before deciding which compressor
67 // it should use.
68 enum {
69 OUTPUT_LATENCY = 4096
70 };
71
72 // Some offsets into the local header
73 enum {
74 SUMS_OFFSET = 14
75 };
76
77 IMPLEMENT_DYNAMIC_CLASS(wxZipEntry, wxArchiveEntry)
78 IMPLEMENT_DYNAMIC_CLASS(wxZipClassFactory, wxArchiveClassFactory)
79
80 wxFORCE_LINK_THIS_MODULE(zipstrm)
81
82
83 /////////////////////////////////////////////////////////////////////////////
84 // Helpers
85
86 // read a string of a given length
87 //
88 static wxString ReadString(wxInputStream& stream, wxUint16 len, wxMBConv& conv)
89 {
90 #if wxUSE_UNICODE
91 wxCharBuffer buf(len);
92 stream.Read(buf.data(), len);
93 wxString str(buf, conv);
94 #else
95 wxString str;
96 (void)conv;
97 {
98 wxStringBuffer buf(str, len);
99 stream.Read(buf, len);
100 }
101 #endif
102
103 return str;
104 }
105
106 // Decode a little endian wxUint32 number from a character array
107 //
108 static inline wxUint32 CrackUint32(const char *m)
109 {
110 const unsigned char *n = (const unsigned char*)m;
111 return (n[3] << 24) | (n[2] << 16) | (n[1] << 8) | n[0];
112 }
113
114 // Temporarily lower the logging level in debug mode to avoid a warning
115 // from SeekI about seeking on a stream with data written back to it.
116 //
117 static wxFileOffset QuietSeek(wxInputStream& stream, wxFileOffset pos)
118 {
119 #if defined(__WXDEBUG__) && wxUSE_LOG
120 wxLogLevel level = wxLog::GetLogLevel();
121 wxLog::SetLogLevel(wxLOG_Debug - 1);
122 wxFileOffset result = stream.SeekI(pos);
123 wxLog::SetLogLevel(level);
124 return result;
125 #else
126 return stream.SeekI(pos);
127 #endif
128 }
129
130
131 /////////////////////////////////////////////////////////////////////////////
132 // Read a zip header
133
134 class wxZipHeader
135 {
136 public:
137 wxZipHeader(wxInputStream& stream, size_t size);
138
139 inline wxUint8 Read8();
140 inline wxUint16 Read16();
141 inline wxUint32 Read32();
142
143 const char *GetData() const { return m_data; }
144 size_t GetSize() const { return m_size; }
145 operator bool() const { return m_ok; }
146
147 size_t Seek(size_t pos) { m_pos = pos; return m_pos; }
148 size_t Skip(size_t size) { m_pos += size; return m_pos; }
149
150 wxZipHeader& operator>>(wxUint8& n) { n = Read8(); return *this; }
151 wxZipHeader& operator>>(wxUint16& n) { n = Read16(); return *this; }
152 wxZipHeader& operator>>(wxUint32& n) { n = Read32(); return *this; }
153
154 private:
155 char m_data[64];
156 size_t m_size;
157 size_t m_pos;
158 bool m_ok;
159 };
160
161 wxZipHeader::wxZipHeader(wxInputStream& stream, size_t size)
162 : m_size(0),
163 m_pos(0),
164 m_ok(false)
165 {
166 wxCHECK_RET(size <= sizeof(m_data), _T("buffer too small"));
167 m_size = stream.Read(m_data, size).LastRead();
168 m_ok = m_size == size;
169 }
170
171 wxUint8 wxZipHeader::Read8()
172 {
173 wxASSERT(m_pos < m_size);
174 return *wx_reinterpret_cast(wxUint8*, m_data + m_pos++);
175 }
176
177 wxUint16 wxZipHeader::Read16()
178 {
179 wxASSERT(m_pos + 2 <= m_size);
180 wxUint16 n = *wx_reinterpret_cast(wxUint16*, m_data + m_pos);
181 m_pos += 2;
182 return wxUINT16_SWAP_ON_BE(n);
183 }
184
185 wxUint32 wxZipHeader::Read32()
186 {
187 wxASSERT(m_pos + 4 <= m_size);
188 wxUint32 n = *wx_reinterpret_cast(wxUint32*, m_data + m_pos);
189 m_pos += 4;
190 return wxUINT32_SWAP_ON_BE(n);
191 }
192
193
194 /////////////////////////////////////////////////////////////////////////////
195 // Stored input stream
196 // Trival decompressor for files which are 'stored' in the zip file.
197
198 class wxStoredInputStream : public wxFilterInputStream
199 {
200 public:
201 wxStoredInputStream(wxInputStream& stream);
202
203 void Open(wxFileOffset len) { Close(); m_len = len; }
204 void Close() { m_pos = 0; m_lasterror = wxSTREAM_NO_ERROR; }
205
206 virtual char Peek() { return wxInputStream::Peek(); }
207 virtual wxFileOffset GetLength() const { return m_len; }
208
209 protected:
210 virtual size_t OnSysRead(void *buffer, size_t size);
211 virtual wxFileOffset OnSysTell() const { return m_pos; }
212
213 private:
214 wxFileOffset m_pos;
215 wxFileOffset m_len;
216
217 DECLARE_NO_COPY_CLASS(wxStoredInputStream)
218 };
219
220 wxStoredInputStream::wxStoredInputStream(wxInputStream& stream)
221 : wxFilterInputStream(stream),
222 m_pos(0),
223 m_len(0)
224 {
225 }
226
227 size_t wxStoredInputStream::OnSysRead(void *buffer, size_t size)
228 {
229 size_t count = wx_truncate_cast(size_t,
230 wxMin(size + wxFileOffset(0), m_len - m_pos + size_t(0)));
231 count = m_parent_i_stream->Read(buffer, count).LastRead();
232 m_pos += count;
233
234 if (count < size)
235 m_lasterror = m_pos == m_len ? wxSTREAM_EOF : wxSTREAM_READ_ERROR;
236
237 return count;
238 }
239
240
241 /////////////////////////////////////////////////////////////////////////////
242 // Stored output stream
243 // Trival compressor for files which are 'stored' in the zip file.
244
245 class wxStoredOutputStream : public wxFilterOutputStream
246 {
247 public:
248 wxStoredOutputStream(wxOutputStream& stream) :
249 wxFilterOutputStream(stream), m_pos(0) { }
250
251 bool Close() {
252 m_pos = 0;
253 m_lasterror = wxSTREAM_NO_ERROR;
254 return true;
255 }
256
257 protected:
258 virtual size_t OnSysWrite(const void *buffer, size_t size);
259 virtual wxFileOffset OnSysTell() const { return m_pos; }
260
261 private:
262 wxFileOffset m_pos;
263 DECLARE_NO_COPY_CLASS(wxStoredOutputStream)
264 };
265
266 size_t wxStoredOutputStream::OnSysWrite(const void *buffer, size_t size)
267 {
268 if (!IsOk() || !size)
269 return 0;
270 size_t count = m_parent_o_stream->Write(buffer, size).LastWrite();
271 if (count != size)
272 m_lasterror = wxSTREAM_WRITE_ERROR;
273 m_pos += count;
274 return count;
275 }
276
277
278 /////////////////////////////////////////////////////////////////////////////
279 // wxRawInputStream
280 //
281 // Used to handle the unusal case of raw copying an entry of unknown
282 // length. This can only happen when the zip being copied from is being
283 // read from a non-seekable stream, and also was original written to a
284 // non-seekable stream.
285 //
286 // In this case there's no option but to decompress the stream to find
287 // it's length, but we can still write the raw compressed data to avoid the
288 // compression overhead (which is the greater one).
289 //
290 // Usage is like this:
291 // m_rawin = new wxRawInputStream(*m_parent_i_stream);
292 // m_decomp = m_rawin->Open(OpenDecompressor(m_rawin->GetTee()));
293 //
294 // The wxRawInputStream owns a wxTeeInputStream object, the role of which
295 // is something like the unix 'tee' command; it is a transparent filter, but
296 // allows the data read to be read a second time via an extra method 'GetData'.
297 //
298 // The wxRawInputStream then draws data through the tee using a decompressor
299 // then instead of returning the decompressed data, retuns the raw data
300 // from wxTeeInputStream::GetData().
301
302 class wxTeeInputStream : public wxFilterInputStream
303 {
304 public:
305 wxTeeInputStream(wxInputStream& stream);
306
307 size_t GetCount() const { return m_end - m_start; }
308 size_t GetData(char *buffer, size_t size);
309
310 void Open();
311 bool Final();
312
313 wxInputStream& Read(void *buffer, size_t size);
314
315 protected:
316 virtual size_t OnSysRead(void *buffer, size_t size);
317 virtual wxFileOffset OnSysTell() const { return m_pos; }
318
319 private:
320 wxFileOffset m_pos;
321 wxMemoryBuffer m_buf;
322 size_t m_start;
323 size_t m_end;
324
325 DECLARE_NO_COPY_CLASS(wxTeeInputStream)
326 };
327
328 wxTeeInputStream::wxTeeInputStream(wxInputStream& stream)
329 : wxFilterInputStream(stream),
330 m_pos(0), m_buf(8192), m_start(0), m_end(0)
331 {
332 }
333
334 void wxTeeInputStream::Open()
335 {
336 m_pos = m_start = m_end = 0;
337 m_lasterror = wxSTREAM_NO_ERROR;
338 }
339
340 bool wxTeeInputStream::Final()
341 {
342 bool final = m_end == m_buf.GetDataLen();
343 m_end = m_buf.GetDataLen();
344 return final;
345 }
346
347 wxInputStream& wxTeeInputStream::Read(void *buffer, size_t size)
348 {
349 size_t count = wxInputStream::Read(buffer, size).LastRead();
350 m_end = m_buf.GetDataLen();
351 m_buf.AppendData(buffer, count);
352 return *this;
353 }
354
355 size_t wxTeeInputStream::OnSysRead(void *buffer, size_t size)
356 {
357 size_t count = m_parent_i_stream->Read(buffer, size).LastRead();
358 if (count < size)
359 m_lasterror = m_parent_i_stream->GetLastError();
360 return count;
361 }
362
363 size_t wxTeeInputStream::GetData(char *buffer, size_t size)
364 {
365 if (m_wbacksize) {
366 size_t len = m_buf.GetDataLen();
367 len = len > m_wbacksize ? len - m_wbacksize : 0;
368 m_buf.SetDataLen(len);
369 if (m_end > len) {
370 wxFAIL; // we've already returned data that's now being ungot
371 m_end = len;
372 }
373 m_parent_i_stream->Reset();
374 m_parent_i_stream->Ungetch(m_wback, m_wbacksize);
375 free(m_wback);
376 m_wback = NULL;
377 m_wbacksize = 0;
378 m_wbackcur = 0;
379 }
380
381 if (size > GetCount())
382 size = GetCount();
383 if (size) {
384 memcpy(buffer, m_buf + m_start, size);
385 m_start += size;
386 wxASSERT(m_start <= m_end);
387 }
388
389 if (m_start == m_end && m_start > 0 && m_buf.GetDataLen() > 0) {
390 size_t len = m_buf.GetDataLen();
391 char *buf = (char*)m_buf.GetWriteBuf(len);
392 len -= m_end;
393 memmove(buf, buf + m_end, len);
394 m_buf.UngetWriteBuf(len);
395 m_start = m_end = 0;
396 }
397
398 return size;
399 }
400
401 class wxRawInputStream : public wxFilterInputStream
402 {
403 public:
404 wxRawInputStream(wxInputStream& stream);
405 virtual ~wxRawInputStream() { delete m_tee; }
406
407 wxInputStream* Open(wxInputStream *decomp);
408 wxInputStream& GetTee() const { return *m_tee; }
409
410 protected:
411 virtual size_t OnSysRead(void *buffer, size_t size);
412 virtual wxFileOffset OnSysTell() const { return m_pos; }
413
414 private:
415 wxFileOffset m_pos;
416 wxTeeInputStream *m_tee;
417
418 enum { BUFSIZE = 8192 };
419 wxCharBuffer m_dummy;
420
421 DECLARE_NO_COPY_CLASS(wxRawInputStream)
422 };
423
424 wxRawInputStream::wxRawInputStream(wxInputStream& stream)
425 : wxFilterInputStream(stream),
426 m_pos(0),
427 m_tee(new wxTeeInputStream(stream)),
428 m_dummy(BUFSIZE)
429 {
430 }
431
432 wxInputStream *wxRawInputStream::Open(wxInputStream *decomp)
433 {
434 if (decomp) {
435 m_parent_i_stream = decomp;
436 m_pos = 0;
437 m_lasterror = wxSTREAM_NO_ERROR;
438 m_tee->Open();
439 return this;
440 } else {
441 return NULL;
442 }
443 }
444
445 size_t wxRawInputStream::OnSysRead(void *buffer, size_t size)
446 {
447 char *buf = (char*)buffer;
448 size_t count = 0;
449
450 while (count < size && IsOk())
451 {
452 while (m_parent_i_stream->IsOk() && m_tee->GetCount() == 0)
453 m_parent_i_stream->Read(m_dummy.data(), BUFSIZE);
454
455 size_t n = m_tee->GetData(buf + count, size - count);
456 count += n;
457
458 if (n == 0 && m_tee->Final())
459 m_lasterror = m_parent_i_stream->GetLastError();
460 }
461
462 m_pos += count;
463 return count;
464 }
465
466
467 /////////////////////////////////////////////////////////////////////////////
468 // Zlib streams than can be reused without recreating.
469
470 class wxZlibOutputStream2 : public wxZlibOutputStream
471 {
472 public:
473 wxZlibOutputStream2(wxOutputStream& stream, int level) :
474 wxZlibOutputStream(stream, level, wxZLIB_NO_HEADER) { }
475
476 bool Open(wxOutputStream& stream);
477 bool Close() { DoFlush(true); m_pos = wxInvalidOffset; return IsOk(); }
478 };
479
480 bool wxZlibOutputStream2::Open(wxOutputStream& stream)
481 {
482 wxCHECK(m_pos == wxInvalidOffset, false);
483
484 m_deflate->next_out = m_z_buffer;
485 m_deflate->avail_out = m_z_size;
486 m_pos = 0;
487 m_lasterror = wxSTREAM_NO_ERROR;
488 m_parent_o_stream = &stream;
489
490 if (deflateReset(m_deflate) != Z_OK) {
491 wxLogError(_("can't re-initialize zlib deflate stream"));
492 m_lasterror = wxSTREAM_WRITE_ERROR;
493 return false;
494 }
495
496 return true;
497 }
498
499 class wxZlibInputStream2 : public wxZlibInputStream
500 {
501 public:
502 wxZlibInputStream2(wxInputStream& stream) :
503 wxZlibInputStream(stream, wxZLIB_NO_HEADER) { }
504
505 bool Open(wxInputStream& stream);
506 };
507
508 bool wxZlibInputStream2::Open(wxInputStream& stream)
509 {
510 m_inflate->avail_in = 0;
511 m_pos = 0;
512 m_lasterror = wxSTREAM_NO_ERROR;
513 m_parent_i_stream = &stream;
514
515 if (inflateReset(m_inflate) != Z_OK) {
516 wxLogError(_("can't re-initialize zlib inflate stream"));
517 m_lasterror = wxSTREAM_READ_ERROR;
518 return false;
519 }
520
521 return true;
522 }
523
524
525 /////////////////////////////////////////////////////////////////////////////
526 // Class to hold wxZipEntry's Extra and LocalExtra fields
527
528 class wxZipMemory
529 {
530 public:
531 wxZipMemory() : m_data(NULL), m_size(0), m_capacity(0), m_ref(1) { }
532
533 wxZipMemory *AddRef() { m_ref++; return this; }
534 void Release() { if (--m_ref == 0) delete this; }
535
536 char *GetData() const { return m_data; }
537 size_t GetSize() const { return m_size; }
538 size_t GetCapacity() const { return m_capacity; }
539
540 wxZipMemory *Unique(size_t size);
541
542 private:
543 ~wxZipMemory() { delete [] m_data; }
544
545 char *m_data;
546 size_t m_size;
547 size_t m_capacity;
548 int m_ref;
549
550 wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipMemory)
551 };
552
553 wxZipMemory *wxZipMemory::Unique(size_t size)
554 {
555 wxZipMemory *zm;
556
557 if (m_ref > 1) {
558 --m_ref;
559 zm = new wxZipMemory;
560 } else {
561 zm = this;
562 }
563
564 if (zm->m_capacity < size) {
565 delete [] zm->m_data;
566 zm->m_data = new char[size];
567 zm->m_capacity = size;
568 }
569
570 zm->m_size = size;
571 return zm;
572 }
573
574 static inline wxZipMemory *AddRef(wxZipMemory *zm)
575 {
576 if (zm)
577 zm->AddRef();
578 return zm;
579 }
580
581 static inline void Release(wxZipMemory *zm)
582 {
583 if (zm)
584 zm->Release();
585 }
586
587 static void Copy(wxZipMemory*& dest, wxZipMemory *src)
588 {
589 Release(dest);
590 dest = AddRef(src);
591 }
592
593 static void Unique(wxZipMemory*& zm, size_t size)
594 {
595 if (!zm && size)
596 zm = new wxZipMemory;
597 if (zm)
598 zm = zm->Unique(size);
599 }
600
601
602 /////////////////////////////////////////////////////////////////////////////
603 // Collection of weak references to entries
604
605 WX_DECLARE_HASH_MAP(long, wxZipEntry*, wxIntegerHash,
606 wxIntegerEqual, wx__OffsetZipEntryMap);
607
608 class wxZipWeakLinks
609 {
610 public:
611 wxZipWeakLinks() : m_ref(1) { }
612
613 void Release(const wxZipInputStream* WXUNUSED(x))
614 { if (--m_ref == 0) delete this; }
615 void Release(wxFileOffset key)
616 { RemoveEntry(key); if (--m_ref == 0) delete this; }
617
618 wxZipWeakLinks *AddEntry(wxZipEntry *entry, wxFileOffset key);
619 void RemoveEntry(wxFileOffset key)
620 { m_entries.erase(wx_truncate_cast(key_type, key)); }
621 wxZipEntry *GetEntry(wxFileOffset key) const;
622 bool IsEmpty() const { return m_entries.empty(); }
623
624 private:
625 ~wxZipWeakLinks() { wxASSERT(IsEmpty()); }
626
627 typedef wx__OffsetZipEntryMap::key_type key_type;
628
629 int m_ref;
630 wx__OffsetZipEntryMap m_entries;
631
632 wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipWeakLinks)
633 };
634
635 wxZipWeakLinks *wxZipWeakLinks::AddEntry(wxZipEntry *entry, wxFileOffset key)
636 {
637 m_entries[wx_truncate_cast(key_type, key)] = entry;
638 m_ref++;
639 return this;
640 }
641
642 wxZipEntry *wxZipWeakLinks::GetEntry(wxFileOffset key) const
643 {
644 wx__OffsetZipEntryMap::const_iterator it =
645 m_entries.find(wx_truncate_cast(key_type, key));
646 return it != m_entries.end() ? it->second : NULL;
647 }
648
649
650 /////////////////////////////////////////////////////////////////////////////
651 // ZipEntry
652
653 wxZipEntry::wxZipEntry(
654 const wxString& name /*=wxEmptyString*/,
655 const wxDateTime& dt /*=wxDateTime::Now()*/,
656 wxFileOffset size /*=wxInvalidOffset*/)
657 :
658 m_SystemMadeBy(wxZIP_SYSTEM_MSDOS),
659 m_VersionMadeBy(wxMAJOR_VERSION * 10 + wxMINOR_VERSION),
660 m_VersionNeeded(VERSION_NEEDED_TO_EXTRACT),
661 m_Flags(0),
662 m_Method(wxZIP_METHOD_DEFAULT),
663 m_DateTime(dt),
664 m_Crc(0),
665 m_CompressedSize(wxInvalidOffset),
666 m_Size(size),
667 m_Key(wxInvalidOffset),
668 m_Offset(wxInvalidOffset),
669 m_DiskStart(0),
670 m_InternalAttributes(0),
671 m_ExternalAttributes(0),
672 m_Extra(NULL),
673 m_LocalExtra(NULL),
674 m_zipnotifier(NULL),
675 m_backlink(NULL)
676 {
677 if (!name.empty())
678 SetName(name);
679 }
680
681 wxZipEntry::~wxZipEntry()
682 {
683 if (m_backlink)
684 m_backlink->Release(m_Key);
685 Release(m_Extra);
686 Release(m_LocalExtra);
687 }
688
689 wxZipEntry::wxZipEntry(const wxZipEntry& e)
690 : wxArchiveEntry(e),
691 m_SystemMadeBy(e.m_SystemMadeBy),
692 m_VersionMadeBy(e.m_VersionMadeBy),
693 m_VersionNeeded(e.m_VersionNeeded),
694 m_Flags(e.m_Flags),
695 m_Method(e.m_Method),
696 m_DateTime(e.m_DateTime),
697 m_Crc(e.m_Crc),
698 m_CompressedSize(e.m_CompressedSize),
699 m_Size(e.m_Size),
700 m_Name(e.m_Name),
701 m_Key(e.m_Key),
702 m_Offset(e.m_Offset),
703 m_Comment(e.m_Comment),
704 m_DiskStart(e.m_DiskStart),
705 m_InternalAttributes(e.m_InternalAttributes),
706 m_ExternalAttributes(e.m_ExternalAttributes),
707 m_Extra(AddRef(e.m_Extra)),
708 m_LocalExtra(AddRef(e.m_LocalExtra)),
709 m_zipnotifier(NULL),
710 m_backlink(NULL)
711 {
712 }
713
714 wxZipEntry& wxZipEntry::operator=(const wxZipEntry& e)
715 {
716 if (&e != this) {
717 m_SystemMadeBy = e.m_SystemMadeBy;
718 m_VersionMadeBy = e.m_VersionMadeBy;
719 m_VersionNeeded = e.m_VersionNeeded;
720 m_Flags = e.m_Flags;
721 m_Method = e.m_Method;
722 m_DateTime = e.m_DateTime;
723 m_Crc = e.m_Crc;
724 m_CompressedSize = e.m_CompressedSize;
725 m_Size = e.m_Size;
726 m_Name = e.m_Name;
727 m_Key = e.m_Key;
728 m_Offset = e.m_Offset;
729 m_Comment = e.m_Comment;
730 m_DiskStart = e.m_DiskStart;
731 m_InternalAttributes = e.m_InternalAttributes;
732 m_ExternalAttributes = e.m_ExternalAttributes;
733 Copy(m_Extra, e.m_Extra);
734 Copy(m_LocalExtra, e.m_LocalExtra);
735 m_zipnotifier = NULL;
736 if (m_backlink) {
737 m_backlink->Release(m_Key);
738 m_backlink = NULL;
739 }
740 }
741 return *this;
742 }
743
744 wxString wxZipEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const
745 {
746 bool isDir = IsDir() && !m_Name.empty();
747
748 // optimisations for common (and easy) cases
749 switch (wxFileName::GetFormat(format)) {
750 case wxPATH_DOS:
751 {
752 wxString name(isDir ? m_Name + _T("\\") : m_Name);
753 for (size_t i = name.length() - 1; i > 0; --i)
754 if (name[i] == _T('/'))
755 name[i] = _T('\\');
756 return name;
757 }
758
759 case wxPATH_UNIX:
760 return isDir ? m_Name + _T("/") : m_Name;
761
762 default:
763 ;
764 }
765
766 wxFileName fn;
767
768 if (isDir)
769 fn.AssignDir(m_Name, wxPATH_UNIX);
770 else
771 fn.Assign(m_Name, wxPATH_UNIX);
772
773 return fn.GetFullPath(format);
774 }
775
776 // Static - Internally tars and zips use forward slashes for the path
777 // separator, absolute paths aren't allowed, and directory names have a
778 // trailing slash. This function converts a path into this internal format,
779 // but without a trailing slash for a directory.
780 //
781 wxString wxZipEntry::GetInternalName(const wxString& name,
782 wxPathFormat format /*=wxPATH_NATIVE*/,
783 bool *pIsDir /*=NULL*/)
784 {
785 wxString internal;
786
787 if (wxFileName::GetFormat(format) != wxPATH_UNIX)
788 internal = wxFileName(name, format).GetFullPath(wxPATH_UNIX);
789 else
790 internal = name;
791
792 bool isDir = !internal.empty() && internal.Last() == '/';
793 if (pIsDir)
794 *pIsDir = isDir;
795 if (isDir)
796 internal.erase(internal.length() - 1);
797
798 while (!internal.empty() && *internal.begin() == '/')
799 internal.erase(0, 1);
800 while (!internal.empty() && internal.compare(0, 2, _T("./")) == 0)
801 internal.erase(0, 2);
802 if (internal == _T(".") || internal == _T(".."))
803 internal = wxEmptyString;
804
805 return internal;
806 }
807
808 void wxZipEntry::SetSystemMadeBy(int system)
809 {
810 int mode = GetMode();
811 bool wasUnix = IsMadeByUnix();
812
813 m_SystemMadeBy = (wxUint8)system;
814
815 if (!wasUnix && IsMadeByUnix()) {
816 SetIsDir(IsDir());
817 SetMode(mode);
818 } else if (wasUnix && !IsMadeByUnix()) {
819 m_ExternalAttributes &= 0xffff;
820 }
821 }
822
823 void wxZipEntry::SetIsDir(bool isDir /*=true*/)
824 {
825 if (isDir)
826 m_ExternalAttributes |= wxZIP_A_SUBDIR;
827 else
828 m_ExternalAttributes &= ~wxZIP_A_SUBDIR;
829
830 if (IsMadeByUnix()) {
831 m_ExternalAttributes &= ~wxZIP_S_IFMT;
832 if (isDir)
833 m_ExternalAttributes |= wxZIP_S_IFDIR;
834 else
835 m_ExternalAttributes |= wxZIP_S_IFREG;
836 }
837 }
838
839 // Return unix style permission bits
840 //
841 int wxZipEntry::GetMode() const
842 {
843 // return unix permissions if present
844 if (IsMadeByUnix())
845 return (m_ExternalAttributes >> 16) & 0777;
846
847 // otherwise synthesize from the dos attribs
848 int mode = 0644;
849 if (m_ExternalAttributes & wxZIP_A_RDONLY)
850 mode &= ~0200;
851 if (m_ExternalAttributes & wxZIP_A_SUBDIR)
852 mode |= 0111;
853
854 return mode;
855 }
856
857 // Set unix permissions
858 //
859 void wxZipEntry::SetMode(int mode)
860 {
861 // Set dos attrib bits to be compatible
862 if (mode & 0222)
863 m_ExternalAttributes &= ~wxZIP_A_RDONLY;
864 else
865 m_ExternalAttributes |= wxZIP_A_RDONLY;
866
867 // set the actual unix permission bits if the system type allows
868 if (IsMadeByUnix()) {
869 m_ExternalAttributes &= ~(0777L << 16);
870 m_ExternalAttributes |= (mode & 0777L) << 16;
871 }
872 }
873
874 const char *wxZipEntry::GetExtra() const
875 {
876 return m_Extra ? m_Extra->GetData() : NULL;
877 }
878
879 size_t wxZipEntry::GetExtraLen() const
880 {
881 return m_Extra ? m_Extra->GetSize() : 0;
882 }
883
884 void wxZipEntry::SetExtra(const char *extra, size_t len)
885 {
886 Unique(m_Extra, len);
887 if (len)
888 memcpy(m_Extra->GetData(), extra, len);
889 }
890
891 const char *wxZipEntry::GetLocalExtra() const
892 {
893 return m_LocalExtra ? m_LocalExtra->GetData() : NULL;
894 }
895
896 size_t wxZipEntry::GetLocalExtraLen() const
897 {
898 return m_LocalExtra ? m_LocalExtra->GetSize() : 0;
899 }
900
901 void wxZipEntry::SetLocalExtra(const char *extra, size_t len)
902 {
903 Unique(m_LocalExtra, len);
904 if (len)
905 memcpy(m_LocalExtra->GetData(), extra, len);
906 }
907
908 void wxZipEntry::SetNotifier(wxZipNotifier& notifier)
909 {
910 wxArchiveEntry::UnsetNotifier();
911 m_zipnotifier = &notifier;
912 m_zipnotifier->OnEntryUpdated(*this);
913 }
914
915 void wxZipEntry::Notify()
916 {
917 if (m_zipnotifier)
918 m_zipnotifier->OnEntryUpdated(*this);
919 else if (GetNotifier())
920 GetNotifier()->OnEntryUpdated(*this);
921 }
922
923 void wxZipEntry::UnsetNotifier()
924 {
925 wxArchiveEntry::UnsetNotifier();
926 m_zipnotifier = NULL;
927 }
928
929 size_t wxZipEntry::ReadLocal(wxInputStream& stream, wxMBConv& conv)
930 {
931 wxUint16 nameLen, extraLen;
932 wxUint32 compressedSize, size, crc;
933
934 wxZipHeader ds(stream, LOCAL_SIZE - 4);
935 if (!ds)
936 return 0;
937
938 ds >> m_VersionNeeded >> m_Flags >> m_Method;
939 SetDateTime(wxDateTime().SetFromDOS(ds.Read32()));
940 ds >> crc >> compressedSize >> size >> nameLen >> extraLen;
941
942 bool sumsValid = (m_Flags & wxZIP_SUMS_FOLLOW) == 0;
943
944 if (sumsValid || crc)
945 m_Crc = crc;
946 if ((sumsValid || compressedSize) || m_Method == wxZIP_METHOD_STORE)
947 m_CompressedSize = compressedSize;
948 if ((sumsValid || size) || m_Method == wxZIP_METHOD_STORE)
949 m_Size = size;
950
951 SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
952 if (stream.LastRead() != nameLen + 0u)
953 return 0;
954
955 if (extraLen || GetLocalExtraLen()) {
956 Unique(m_LocalExtra, extraLen);
957 if (extraLen) {
958 stream.Read(m_LocalExtra->GetData(), extraLen);
959 if (stream.LastRead() != extraLen + 0u)
960 return 0;
961 }
962 }
963
964 return LOCAL_SIZE + nameLen + extraLen;
965 }
966
967 size_t wxZipEntry::WriteLocal(wxOutputStream& stream, wxMBConv& conv) const
968 {
969 wxString unixName = GetName(wxPATH_UNIX);
970 const wxWX2MBbuf name_buf = conv.cWX2MB(unixName);
971 const char *name = name_buf;
972 if (!name) name = "";
973 wxUint16 nameLen = wx_truncate_cast(wxUint16, strlen(name));
974
975 wxDataOutputStream ds(stream);
976
977 ds << m_VersionNeeded << m_Flags << m_Method;
978 ds.Write32(GetDateTime().GetAsDOS());
979
980 ds.Write32(m_Crc);
981 ds.Write32(m_CompressedSize != wxInvalidOffset ?
982 wx_truncate_cast(wxUint32, m_CompressedSize) : 0);
983 ds.Write32(m_Size != wxInvalidOffset ?
984 wx_truncate_cast(wxUint32, m_Size) : 0);
985
986 ds << nameLen;
987 wxUint16 extraLen = wx_truncate_cast(wxUint16, GetLocalExtraLen());
988 ds.Write16(extraLen);
989
990 stream.Write(name, nameLen);
991 if (extraLen)
992 stream.Write(m_LocalExtra->GetData(), extraLen);
993
994 return LOCAL_SIZE + nameLen + extraLen;
995 }
996
997 size_t wxZipEntry::ReadCentral(wxInputStream& stream, wxMBConv& conv)
998 {
999 wxUint16 nameLen, extraLen, commentLen;
1000
1001 wxZipHeader ds(stream, CENTRAL_SIZE - 4);
1002 if (!ds)
1003 return 0;
1004
1005 ds >> m_VersionMadeBy >> m_SystemMadeBy;
1006
1007 SetVersionNeeded(ds.Read16());
1008 SetFlags(ds.Read16());
1009 SetMethod(ds.Read16());
1010 SetDateTime(wxDateTime().SetFromDOS(ds.Read32()));
1011 SetCrc(ds.Read32());
1012 SetCompressedSize(ds.Read32());
1013 SetSize(ds.Read32());
1014
1015 ds >> nameLen >> extraLen >> commentLen
1016 >> m_DiskStart >> m_InternalAttributes >> m_ExternalAttributes;
1017 SetOffset(ds.Read32());
1018
1019 SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
1020 if (stream.LastRead() != nameLen + 0u)
1021 return 0;
1022
1023 if (extraLen || GetExtraLen()) {
1024 Unique(m_Extra, extraLen);
1025 if (extraLen) {
1026 stream.Read(m_Extra->GetData(), extraLen);
1027 if (stream.LastRead() != extraLen + 0u)
1028 return 0;
1029 }
1030 }
1031
1032 if (commentLen) {
1033 m_Comment = ReadString(stream, commentLen, conv);
1034 if (stream.LastRead() != commentLen + 0u)
1035 return 0;
1036 } else {
1037 m_Comment.clear();
1038 }
1039
1040 return CENTRAL_SIZE + nameLen + extraLen + commentLen;
1041 }
1042
1043 size_t wxZipEntry::WriteCentral(wxOutputStream& stream, wxMBConv& conv) const
1044 {
1045 wxString unixName = GetName(wxPATH_UNIX);
1046 const wxWX2MBbuf name_buf = conv.cWX2MB(unixName);
1047 const char *name = name_buf;
1048 if (!name) name = "";
1049 wxUint16 nameLen = wx_truncate_cast(wxUint16, strlen(name));
1050
1051 const wxWX2MBbuf comment_buf = conv.cWX2MB(m_Comment);
1052 const char *comment = comment_buf;
1053 if (!comment) comment = "";
1054 wxUint16 commentLen = wx_truncate_cast(wxUint16, strlen(comment));
1055
1056 wxUint16 extraLen = wx_truncate_cast(wxUint16, GetExtraLen());
1057
1058 wxDataOutputStream ds(stream);
1059
1060 ds << CENTRAL_MAGIC << m_VersionMadeBy << m_SystemMadeBy;
1061
1062 ds.Write16(wx_truncate_cast(wxUint16, GetVersionNeeded()));
1063 ds.Write16(wx_truncate_cast(wxUint16, GetFlags()));
1064 ds.Write16(wx_truncate_cast(wxUint16, GetMethod()));
1065 ds.Write32(GetDateTime().GetAsDOS());
1066 ds.Write32(GetCrc());
1067 ds.Write32(wx_truncate_cast(wxUint32, GetCompressedSize()));
1068 ds.Write32(wx_truncate_cast(wxUint32, GetSize()));
1069 ds.Write16(nameLen);
1070 ds.Write16(extraLen);
1071
1072 ds << commentLen << m_DiskStart << m_InternalAttributes
1073 << m_ExternalAttributes << wx_truncate_cast(wxUint32, GetOffset());
1074
1075 stream.Write(name, nameLen);
1076 if (extraLen)
1077 stream.Write(GetExtra(), extraLen);
1078 stream.Write(comment, commentLen);
1079
1080 return CENTRAL_SIZE + nameLen + extraLen + commentLen;
1081 }
1082
1083 // Info-zip prefixes this record with a signature, but pkzip doesn't. So if
1084 // the 1st value is the signature then it is probably an info-zip record,
1085 // though there is a small chance that it is in fact a pkzip record which
1086 // happens to have the signature as it's CRC.
1087 //
1088 size_t wxZipEntry::ReadDescriptor(wxInputStream& stream)
1089 {
1090 wxZipHeader ds(stream, SUMS_SIZE);
1091 if (!ds)
1092 return 0;
1093
1094 m_Crc = ds.Read32();
1095 m_CompressedSize = ds.Read32();
1096 m_Size = ds.Read32();
1097
1098 // if 1st value is the signature then this is probably an info-zip record
1099 if (m_Crc == SUMS_MAGIC)
1100 {
1101 wxZipHeader buf(stream, 8);
1102 wxUint32 u1 = buf.GetSize() >= 4 ? buf.Read32() : (wxUint32)LOCAL_MAGIC;
1103 wxUint32 u2 = buf.GetSize() == 8 ? buf.Read32() : 0;
1104
1105 // look for the signature of the following record to decide which
1106 if ((u1 == LOCAL_MAGIC || u1 == CENTRAL_MAGIC) &&
1107 (u2 != LOCAL_MAGIC && u2 != CENTRAL_MAGIC))
1108 {
1109 // it's a pkzip style record after all!
1110 if (buf.GetSize() > 0)
1111 stream.Ungetch(buf.GetData(), buf.GetSize());
1112 }
1113 else
1114 {
1115 // it's an info-zip record as expected
1116 if (buf.GetSize() > 4)
1117 stream.Ungetch(buf.GetData() + 4, buf.GetSize() - 4);
1118 m_Crc = wx_truncate_cast(wxUint32, m_CompressedSize);
1119 m_CompressedSize = m_Size;
1120 m_Size = u1;
1121 return SUMS_SIZE + 4;
1122 }
1123 }
1124
1125 return SUMS_SIZE;
1126 }
1127
1128 size_t wxZipEntry::WriteDescriptor(wxOutputStream& stream, wxUint32 crc,
1129 wxFileOffset compressedSize, wxFileOffset size)
1130 {
1131 m_Crc = crc;
1132 m_CompressedSize = compressedSize;
1133 m_Size = size;
1134
1135 wxDataOutputStream ds(stream);
1136
1137 ds.Write32(crc);
1138 ds.Write32(wx_truncate_cast(wxUint32, compressedSize));
1139 ds.Write32(wx_truncate_cast(wxUint32, size));
1140
1141 return SUMS_SIZE;
1142 }
1143
1144
1145 /////////////////////////////////////////////////////////////////////////////
1146 // wxZipEndRec - holds the end of central directory record
1147
1148 class wxZipEndRec
1149 {
1150 public:
1151 wxZipEndRec();
1152
1153 int GetDiskNumber() const { return m_DiskNumber; }
1154 int GetStartDisk() const { return m_StartDisk; }
1155 int GetEntriesHere() const { return m_EntriesHere; }
1156 int GetTotalEntries() const { return m_TotalEntries; }
1157 wxFileOffset GetSize() const { return m_Size; }
1158 wxFileOffset GetOffset() const { return m_Offset; }
1159 wxString GetComment() const { return m_Comment; }
1160
1161 void SetDiskNumber(int num)
1162 { m_DiskNumber = wx_truncate_cast(wxUint16, num); }
1163 void SetStartDisk(int num)
1164 { m_StartDisk = wx_truncate_cast(wxUint16, num); }
1165 void SetEntriesHere(int num)
1166 { m_EntriesHere = wx_truncate_cast(wxUint16, num); }
1167 void SetTotalEntries(int num)
1168 { m_TotalEntries = wx_truncate_cast(wxUint16, num); }
1169 void SetSize(wxFileOffset size)
1170 { m_Size = wx_truncate_cast(wxUint32, size); }
1171 void SetOffset(wxFileOffset offset)
1172 { m_Offset = wx_truncate_cast(wxUint32, offset); }
1173 void SetComment(const wxString& comment)
1174 { m_Comment = comment; }
1175
1176 bool Read(wxInputStream& stream, wxMBConv& conv);
1177 bool Write(wxOutputStream& stream, wxMBConv& conv) const;
1178
1179 private:
1180 wxUint16 m_DiskNumber;
1181 wxUint16 m_StartDisk;
1182 wxUint16 m_EntriesHere;
1183 wxUint16 m_TotalEntries;
1184 wxUint32 m_Size;
1185 wxUint32 m_Offset;
1186 wxString m_Comment;
1187 };
1188
1189 wxZipEndRec::wxZipEndRec()
1190 : m_DiskNumber(0),
1191 m_StartDisk(0),
1192 m_EntriesHere(0),
1193 m_TotalEntries(0),
1194 m_Size(0),
1195 m_Offset(0)
1196 {
1197 }
1198
1199 bool wxZipEndRec::Write(wxOutputStream& stream, wxMBConv& conv) const
1200 {
1201 const wxWX2MBbuf comment_buf = conv.cWX2MB(m_Comment);
1202 const char *comment = comment_buf;
1203 if (!comment) comment = "";
1204 wxUint16 commentLen = (wxUint16)strlen(comment);
1205
1206 wxDataOutputStream ds(stream);
1207
1208 ds << END_MAGIC << m_DiskNumber << m_StartDisk << m_EntriesHere
1209 << m_TotalEntries << m_Size << m_Offset << commentLen;
1210
1211 stream.Write(comment, commentLen);
1212
1213 return stream.IsOk();
1214 }
1215
1216 bool wxZipEndRec::Read(wxInputStream& stream, wxMBConv& conv)
1217 {
1218 wxZipHeader ds(stream, END_SIZE - 4);
1219 if (!ds)
1220 return false;
1221
1222 wxUint16 commentLen;
1223
1224 ds >> m_DiskNumber >> m_StartDisk >> m_EntriesHere
1225 >> m_TotalEntries >> m_Size >> m_Offset >> commentLen;
1226
1227 if (commentLen) {
1228 m_Comment = ReadString(stream, commentLen, conv);
1229 if (stream.LastRead() != commentLen + 0u)
1230 return false;
1231 }
1232
1233 if (m_DiskNumber != 0 || m_StartDisk != 0 ||
1234 m_EntriesHere != m_TotalEntries)
1235 wxLogWarning(_("assuming this is a multi-part zip concatenated"));
1236
1237 return true;
1238 }
1239
1240
1241 /////////////////////////////////////////////////////////////////////////////
1242 // A weak link from an input stream to an output stream
1243
1244 class wxZipStreamLink
1245 {
1246 public:
1247 wxZipStreamLink(wxZipOutputStream *stream) : m_ref(1), m_stream(stream) { }
1248
1249 wxZipStreamLink *AddRef() { m_ref++; return this; }
1250 wxZipOutputStream *GetOutputStream() const { return m_stream; }
1251
1252 void Release(class wxZipInputStream *WXUNUSED(s))
1253 { if (--m_ref == 0) delete this; }
1254 void Release(class wxZipOutputStream *WXUNUSED(s))
1255 { m_stream = NULL; if (--m_ref == 0) delete this; }
1256
1257 private:
1258 ~wxZipStreamLink() { }
1259
1260 int m_ref;
1261 wxZipOutputStream *m_stream;
1262
1263 wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipStreamLink)
1264 };
1265
1266
1267 /////////////////////////////////////////////////////////////////////////////
1268 // Input stream
1269
1270 // leave the default wxZipEntryPtr free for users
1271 wxDECLARE_SCOPED_PTR(wxZipEntry, wx__ZipEntryPtr)
1272 wxDEFINE_SCOPED_PTR (wxZipEntry, wx__ZipEntryPtr)
1273
1274 // constructor
1275 //
1276 wxZipInputStream::wxZipInputStream(wxInputStream& stream,
1277 wxMBConv& conv /*=wxConvLocal*/)
1278 : wxArchiveInputStream(stream, conv)
1279 {
1280 Init();
1281 }
1282
1283 #if 1 //WXWIN_COMPATIBILITY_2_6
1284
1285 // Part of the compatibility constructor, which has been made inline to
1286 // avoid a problem with it not being exported by mingw 3.2.3
1287 //
1288 void wxZipInputStream::Init(const wxString& file)
1289 {
1290 // no error messages
1291 wxLogNull nolog;
1292 Init();
1293 m_allowSeeking = true;
1294 m_ffile = wx_static_cast(wxFFileInputStream*, m_parent_i_stream);
1295 wx__ZipEntryPtr entry;
1296
1297 if (m_ffile->Ok()) {
1298 do {
1299 entry.reset(GetNextEntry());
1300 }
1301 while (entry.get() != NULL && entry->GetInternalName() != file);
1302 }
1303
1304 if (entry.get() == NULL)
1305 m_lasterror = wxSTREAM_READ_ERROR;
1306 }
1307
1308 wxInputStream& wxZipInputStream::OpenFile(const wxString& archive)
1309 {
1310 wxLogNull nolog;
1311 return *new wxFFileInputStream(archive);
1312 }
1313
1314 #endif // WXWIN_COMPATIBILITY_2_6
1315
1316 void wxZipInputStream::Init()
1317 {
1318 m_store = new wxStoredInputStream(*m_parent_i_stream);
1319 m_inflate = NULL;
1320 m_rawin = NULL;
1321 m_raw = false;
1322 m_headerSize = 0;
1323 m_decomp = NULL;
1324 m_parentSeekable = false;
1325 m_weaklinks = new wxZipWeakLinks;
1326 m_streamlink = NULL;
1327 m_offsetAdjustment = 0;
1328 m_position = wxInvalidOffset;
1329 m_signature = 0;
1330 m_TotalEntries = 0;
1331 m_lasterror = m_parent_i_stream->GetLastError();
1332 m_ffile = NULL;
1333 #if 1 //WXWIN_COMPATIBILITY_2_6
1334 m_allowSeeking = false;
1335 #endif
1336 }
1337
1338 wxZipInputStream::~wxZipInputStream()
1339 {
1340 CloseDecompressor(m_decomp);
1341
1342 delete m_store;
1343 delete m_inflate;
1344 delete m_rawin;
1345 delete m_ffile;
1346
1347 m_weaklinks->Release(this);
1348
1349 if (m_streamlink)
1350 m_streamlink->Release(this);
1351 }
1352
1353 wxString wxZipInputStream::GetComment()
1354 {
1355 if (m_position == wxInvalidOffset)
1356 if (!LoadEndRecord())
1357 return wxEmptyString;
1358
1359 if (!m_parentSeekable && Eof() && m_signature) {
1360 m_lasterror = wxSTREAM_NO_ERROR;
1361 m_lasterror = ReadLocal(true);
1362 }
1363
1364 return m_Comment;
1365 }
1366
1367 int wxZipInputStream::GetTotalEntries()
1368 {
1369 if (m_position == wxInvalidOffset)
1370 LoadEndRecord();
1371 return m_TotalEntries;
1372 }
1373
1374 wxZipStreamLink *wxZipInputStream::MakeLink(wxZipOutputStream *out)
1375 {
1376 wxZipStreamLink *link = NULL;
1377
1378 if (!m_parentSeekable && (IsOpened() || !Eof())) {
1379 link = new wxZipStreamLink(out);
1380 if (m_streamlink)
1381 m_streamlink->Release(this);
1382 m_streamlink = link->AddRef();
1383 }
1384
1385 return link;
1386 }
1387
1388 bool wxZipInputStream::LoadEndRecord()
1389 {
1390 wxCHECK(m_position == wxInvalidOffset, false);
1391 if (!IsOk())
1392 return false;
1393
1394 m_position = 0;
1395
1396 // First find the end-of-central-directory record.
1397 if (!FindEndRecord()) {
1398 // failed, so either this is a non-seekable stream (ok), or not a zip
1399 if (m_parentSeekable) {
1400 m_lasterror = wxSTREAM_READ_ERROR;
1401 wxLogError(_("invalid zip file"));
1402 return false;
1403 }
1404 else {
1405 wxLogNull nolog;
1406 wxFileOffset pos = m_parent_i_stream->TellI();
1407 if (pos != wxInvalidOffset)
1408 m_offsetAdjustment = m_position = pos;
1409 return true;
1410 }
1411 }
1412
1413 wxZipEndRec endrec;
1414
1415 // Read in the end record
1416 wxFileOffset endPos = m_parent_i_stream->TellI() - 4;
1417 if (!endrec.Read(*m_parent_i_stream, GetConv()))
1418 return false;
1419
1420 m_TotalEntries = endrec.GetTotalEntries();
1421 m_Comment = endrec.GetComment();
1422
1423 // Now find the central-directory. we have the file offset of
1424 // the CD, so look there first.
1425 if (m_parent_i_stream->SeekI(endrec.GetOffset()) != wxInvalidOffset &&
1426 ReadSignature() == CENTRAL_MAGIC) {
1427 m_signature = CENTRAL_MAGIC;
1428 m_position = endrec.GetOffset();
1429 m_offsetAdjustment = 0;
1430 return true;
1431 }
1432
1433 // If it's not there, then it could be that the zip has been appended
1434 // to a self extractor, so take the CD size (also in endrec), subtract
1435 // it from the file offset of the end-central-directory and look there.
1436 if (m_parent_i_stream->SeekI(endPos - endrec.GetSize())
1437 != wxInvalidOffset && ReadSignature() == CENTRAL_MAGIC) {
1438 m_signature = CENTRAL_MAGIC;
1439 m_position = endPos - endrec.GetSize();
1440 m_offsetAdjustment = m_position - endrec.GetOffset();
1441 return true;
1442 }
1443
1444 wxLogError(_("can't find central directory in zip"));
1445 m_lasterror = wxSTREAM_READ_ERROR;
1446 return false;
1447 }
1448
1449 // Find the end-of-central-directory record.
1450 // If found the stream will be positioned just past the 4 signature bytes.
1451 //
1452 bool wxZipInputStream::FindEndRecord()
1453 {
1454 if (!m_parent_i_stream->IsSeekable())
1455 return false;
1456
1457 // usually it's 22 bytes in size and the last thing in the file
1458 {
1459 wxLogNull nolog;
1460 if (m_parent_i_stream->SeekI(-END_SIZE, wxFromEnd) == wxInvalidOffset)
1461 return false;
1462 }
1463
1464 m_parentSeekable = true;
1465 m_signature = 0;
1466 char magic[4];
1467 if (m_parent_i_stream->Read(magic, 4).LastRead() != 4)
1468 return false;
1469 if ((m_signature = CrackUint32(magic)) == END_MAGIC)
1470 return true;
1471
1472 // unfortunately, the record has a comment field that can be up to 65535
1473 // bytes in length, so if the signature not found then search backwards.
1474 wxFileOffset pos = m_parent_i_stream->TellI();
1475 const int BUFSIZE = 1024;
1476 wxCharBuffer buf(BUFSIZE);
1477
1478 memcpy(buf.data(), magic, 3);
1479 wxFileOffset minpos = wxMax(pos - 65535L, 0);
1480
1481 while (pos > minpos) {
1482 size_t len = wx_truncate_cast(size_t,
1483 pos - wxMax(pos - (BUFSIZE - 3), minpos));
1484 memcpy(buf.data() + len, buf, 3);
1485 pos -= len;
1486
1487 if (m_parent_i_stream->SeekI(pos, wxFromStart) == wxInvalidOffset ||
1488 m_parent_i_stream->Read(buf.data(), len).LastRead() != len)
1489 return false;
1490
1491 char *p = buf.data() + len;
1492
1493 while (p-- > buf.data()) {
1494 if ((m_signature = CrackUint32(p)) == END_MAGIC) {
1495 size_t remainder = buf.data() + len - p;
1496 if (remainder > 4)
1497 m_parent_i_stream->Ungetch(p + 4, remainder - 4);
1498 return true;
1499 }
1500 }
1501 }
1502
1503 return false;
1504 }
1505
1506 wxZipEntry *wxZipInputStream::GetNextEntry()
1507 {
1508 if (m_position == wxInvalidOffset)
1509 if (!LoadEndRecord())
1510 return NULL;
1511
1512 m_lasterror = m_parentSeekable ? ReadCentral() : ReadLocal();
1513 if (!IsOk())
1514 return NULL;
1515
1516 wx__ZipEntryPtr entry(new wxZipEntry(m_entry));
1517 entry->m_backlink = m_weaklinks->AddEntry(entry.get(), entry->GetKey());
1518 return entry.release();
1519 }
1520
1521 wxStreamError wxZipInputStream::ReadCentral()
1522 {
1523 if (!AtHeader())
1524 CloseEntry();
1525
1526 if (m_signature == END_MAGIC)
1527 return wxSTREAM_EOF;
1528
1529 if (m_signature != CENTRAL_MAGIC) {
1530 wxLogError(_("error reading zip central directory"));
1531 return wxSTREAM_READ_ERROR;
1532 }
1533
1534 if (QuietSeek(*m_parent_i_stream, m_position + 4) == wxInvalidOffset)
1535 return wxSTREAM_READ_ERROR;
1536
1537 size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv());
1538 if (!size) {
1539 m_signature = 0;
1540 return wxSTREAM_READ_ERROR;
1541 }
1542
1543 m_position += size;
1544 m_signature = ReadSignature();
1545
1546 if (m_offsetAdjustment)
1547 m_entry.SetOffset(m_entry.GetOffset() + m_offsetAdjustment);
1548 m_entry.SetKey(m_entry.GetOffset());
1549
1550 return wxSTREAM_NO_ERROR;
1551 }
1552
1553 wxStreamError wxZipInputStream::ReadLocal(bool readEndRec /*=false*/)
1554 {
1555 if (!AtHeader())
1556 CloseEntry();
1557
1558 if (!m_signature)
1559 m_signature = ReadSignature();
1560
1561 if (m_signature == CENTRAL_MAGIC || m_signature == END_MAGIC) {
1562 if (m_streamlink && !m_streamlink->GetOutputStream()) {
1563 m_streamlink->Release(this);
1564 m_streamlink = NULL;
1565 }
1566 }
1567
1568 while (m_signature == CENTRAL_MAGIC) {
1569 if (m_weaklinks->IsEmpty() && m_streamlink == NULL)
1570 return wxSTREAM_EOF;
1571
1572 size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv());
1573 m_position += size;
1574 m_signature = 0;
1575 if (!size)
1576 return wxSTREAM_READ_ERROR;
1577
1578 wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetOffset());
1579 if (entry) {
1580 entry->SetSystemMadeBy(m_entry.GetSystemMadeBy());
1581 entry->SetVersionMadeBy(m_entry.GetVersionMadeBy());
1582 entry->SetComment(m_entry.GetComment());
1583 entry->SetDiskStart(m_entry.GetDiskStart());
1584 entry->SetInternalAttributes(m_entry.GetInternalAttributes());
1585 entry->SetExternalAttributes(m_entry.GetExternalAttributes());
1586 Copy(entry->m_Extra, m_entry.m_Extra);
1587 entry->Notify();
1588 m_weaklinks->RemoveEntry(entry->GetOffset());
1589 }
1590
1591 m_signature = ReadSignature();
1592 }
1593
1594 if (m_signature == END_MAGIC) {
1595 if (readEndRec || m_streamlink) {
1596 wxZipEndRec endrec;
1597 endrec.Read(*m_parent_i_stream, GetConv());
1598 m_Comment = endrec.GetComment();
1599 m_signature = 0;
1600 if (m_streamlink) {
1601 m_streamlink->GetOutputStream()->SetComment(endrec.GetComment());
1602 m_streamlink->Release(this);
1603 m_streamlink = NULL;
1604 }
1605 }
1606 return wxSTREAM_EOF;
1607 }
1608
1609 if (m_signature != LOCAL_MAGIC) {
1610 wxLogError(_("error reading zip local header"));
1611 return wxSTREAM_READ_ERROR;
1612 }
1613
1614 m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
1615 m_signature = 0;
1616 m_entry.SetOffset(m_position);
1617 m_entry.SetKey(m_position);
1618
1619 if (!m_headerSize) {
1620 return wxSTREAM_READ_ERROR;
1621 } else {
1622 m_TotalEntries++;
1623 return wxSTREAM_NO_ERROR;
1624 }
1625 }
1626
1627 wxUint32 wxZipInputStream::ReadSignature()
1628 {
1629 char magic[4];
1630 m_parent_i_stream->Read(magic, 4);
1631 return m_parent_i_stream->LastRead() == 4 ? CrackUint32(magic) : 0;
1632 }
1633
1634 bool wxZipInputStream::OpenEntry(wxArchiveEntry& entry)
1635 {
1636 wxZipEntry *zipEntry = wxStaticCast(&entry, wxZipEntry);
1637 return zipEntry ? OpenEntry(*zipEntry) : false;
1638 }
1639
1640 // Open an entry
1641 //
1642 bool wxZipInputStream::DoOpen(wxZipEntry *entry, bool raw)
1643 {
1644 if (m_position == wxInvalidOffset)
1645 if (!LoadEndRecord())
1646 return false;
1647 if (m_lasterror == wxSTREAM_READ_ERROR)
1648 return false;
1649 if (IsOpened())
1650 CloseEntry();
1651
1652 m_raw = raw;
1653
1654 if (entry) {
1655 if (AfterHeader() && entry->GetKey() == m_entry.GetOffset())
1656 return true;
1657 // can only open the current entry on a non-seekable stream
1658 wxCHECK(m_parentSeekable, false);
1659 }
1660
1661 m_lasterror = wxSTREAM_READ_ERROR;
1662
1663 if (entry)
1664 m_entry = *entry;
1665
1666 if (m_parentSeekable) {
1667 if (QuietSeek(*m_parent_i_stream, m_entry.GetOffset())
1668 == wxInvalidOffset)
1669 return false;
1670 if (ReadSignature() != LOCAL_MAGIC) {
1671 wxLogError(_("bad zipfile offset to entry"));
1672 return false;
1673 }
1674 }
1675
1676 if (m_parentSeekable || AtHeader()) {
1677 m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
1678 if (m_headerSize && m_parentSeekable) {
1679 wxZipEntry *ref = m_weaklinks->GetEntry(m_entry.GetKey());
1680 if (ref) {
1681 Copy(ref->m_LocalExtra, m_entry.m_LocalExtra);
1682 ref->Notify();
1683 m_weaklinks->RemoveEntry(ref->GetKey());
1684 }
1685 if (entry && entry != ref) {
1686 Copy(entry->m_LocalExtra, m_entry.m_LocalExtra);
1687 entry->Notify();
1688 }
1689 }
1690 }
1691
1692 if (m_headerSize)
1693 m_lasterror = wxSTREAM_NO_ERROR;
1694 return IsOk();
1695 }
1696
1697 bool wxZipInputStream::OpenDecompressor(bool raw /*=false*/)
1698 {
1699 wxASSERT(AfterHeader());
1700
1701 wxFileOffset compressedSize = m_entry.GetCompressedSize();
1702
1703 if (raw)
1704 m_raw = true;
1705
1706 if (m_raw) {
1707 if (compressedSize != wxInvalidOffset) {
1708 m_store->Open(compressedSize);
1709 m_decomp = m_store;
1710 } else {
1711 if (!m_rawin)
1712 m_rawin = new wxRawInputStream(*m_parent_i_stream);
1713 m_decomp = m_rawin->Open(OpenDecompressor(m_rawin->GetTee()));
1714 }
1715 } else {
1716 if (compressedSize != wxInvalidOffset &&
1717 (m_entry.GetMethod() != wxZIP_METHOD_DEFLATE ||
1718 wxZlibInputStream::CanHandleGZip())) {
1719 m_store->Open(compressedSize);
1720 m_decomp = OpenDecompressor(*m_store);
1721 } else {
1722 m_decomp = OpenDecompressor(*m_parent_i_stream);
1723 }
1724 }
1725
1726 m_crcAccumulator = crc32(0, Z_NULL, 0);
1727 m_lasterror = m_decomp ? m_decomp->GetLastError() : wxSTREAM_READ_ERROR;
1728 return IsOk();
1729 }
1730
1731 // Can be overriden to add support for additional decompression methods
1732 //
1733 wxInputStream *wxZipInputStream::OpenDecompressor(wxInputStream& stream)
1734 {
1735 switch (m_entry.GetMethod()) {
1736 case wxZIP_METHOD_STORE:
1737 if (m_entry.GetSize() == wxInvalidOffset) {
1738 wxLogError(_("stored file length not in Zip header"));
1739 break;
1740 }
1741 m_store->Open(m_entry.GetSize());
1742 return m_store;
1743
1744 case wxZIP_METHOD_DEFLATE:
1745 if (!m_inflate)
1746 m_inflate = new wxZlibInputStream2(stream);
1747 else
1748 m_inflate->Open(stream);
1749 return m_inflate;
1750
1751 default:
1752 wxLogError(_("unsupported Zip compression method"));
1753 }
1754
1755 return NULL;
1756 }
1757
1758 bool wxZipInputStream::CloseDecompressor(wxInputStream *decomp)
1759 {
1760 if (decomp && decomp == m_rawin)
1761 return CloseDecompressor(m_rawin->GetFilterInputStream());
1762 if (decomp != m_store && decomp != m_inflate)
1763 delete decomp;
1764 return true;
1765 }
1766
1767 // Closes the current entry and positions the underlying stream at the start
1768 // of the next entry
1769 //
1770 bool wxZipInputStream::CloseEntry()
1771 {
1772 if (AtHeader())
1773 return true;
1774 if (m_lasterror == wxSTREAM_READ_ERROR)
1775 return false;
1776
1777 if (!m_parentSeekable) {
1778 if (!IsOpened() && !OpenDecompressor(true))
1779 return false;
1780
1781 const int BUFSIZE = 8192;
1782 wxCharBuffer buf(BUFSIZE);
1783 while (IsOk())
1784 Read(buf.data(), BUFSIZE);
1785
1786 m_position += m_headerSize + m_entry.GetCompressedSize();
1787 }
1788
1789 if (m_lasterror == wxSTREAM_EOF)
1790 m_lasterror = wxSTREAM_NO_ERROR;
1791
1792 CloseDecompressor(m_decomp);
1793 m_decomp = NULL;
1794 m_entry = wxZipEntry();
1795 m_headerSize = 0;
1796 m_raw = false;
1797
1798 return IsOk();
1799 }
1800
1801 size_t wxZipInputStream::OnSysRead(void *buffer, size_t size)
1802 {
1803 if (!IsOpened())
1804 if ((AtHeader() && !DoOpen()) || !OpenDecompressor())
1805 m_lasterror = wxSTREAM_READ_ERROR;
1806 if (!IsOk() || !size)
1807 return 0;
1808
1809 size_t count = m_decomp->Read(buffer, size).LastRead();
1810 if (!m_raw)
1811 m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, count);
1812 if (count < size)
1813 m_lasterror = m_decomp->GetLastError();
1814
1815 if (Eof()) {
1816 if ((m_entry.GetFlags() & wxZIP_SUMS_FOLLOW) != 0) {
1817 m_headerSize += m_entry.ReadDescriptor(*m_parent_i_stream);
1818 wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetKey());
1819
1820 if (entry) {
1821 entry->SetCrc(m_entry.GetCrc());
1822 entry->SetCompressedSize(m_entry.GetCompressedSize());
1823 entry->SetSize(m_entry.GetSize());
1824 entry->Notify();
1825 }
1826 }
1827
1828 if (!m_raw) {
1829 m_lasterror = wxSTREAM_READ_ERROR;
1830
1831 if (m_entry.GetSize() != TellI())
1832 wxLogError(_("reading zip stream (entry %s): bad length"),
1833 m_entry.GetName().c_str());
1834 else if (m_crcAccumulator != m_entry.GetCrc())
1835 wxLogError(_("reading zip stream (entry %s): bad crc"),
1836 m_entry.GetName().c_str());
1837 else
1838 m_lasterror = wxSTREAM_EOF;
1839 }
1840 }
1841
1842 return count;
1843 }
1844
1845 #if 1 //WXWIN_COMPATIBILITY_2_6
1846
1847 // Borrowed from VS's zip stream (c) 1999 Vaclav Slavik
1848 //
1849 wxFileOffset wxZipInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
1850 {
1851 // seeking works when the stream is created with the compatibility
1852 // constructor
1853 if (!m_allowSeeking)
1854 return wxInvalidOffset;
1855 if (!IsOpened())
1856 if ((AtHeader() && !DoOpen()) || !OpenDecompressor())
1857 m_lasterror = wxSTREAM_READ_ERROR;
1858 if (!IsOk())
1859 return wxInvalidOffset;
1860
1861 // NB: since ZIP files don't natively support seeking, we have to
1862 // implement a brute force workaround -- reading all the data
1863 // between current and the new position (or between beginning of
1864 // the file and new position...)
1865
1866 wxFileOffset nextpos;
1867 wxFileOffset pos = TellI();
1868
1869 switch ( mode )
1870 {
1871 case wxFromCurrent : nextpos = seek + pos; break;
1872 case wxFromStart : nextpos = seek; break;
1873 case wxFromEnd : nextpos = GetLength() + seek; break;
1874 default : nextpos = pos; break; /* just to fool compiler, never happens */
1875 }
1876
1877 wxFileOffset toskip wxDUMMY_INITIALIZE(0);
1878 if ( nextpos >= pos )
1879 {
1880 toskip = nextpos - pos;
1881 }
1882 else
1883 {
1884 wxZipEntry current(m_entry);
1885 if (!OpenEntry(current))
1886 {
1887 m_lasterror = wxSTREAM_READ_ERROR;
1888 return pos;
1889 }
1890 toskip = nextpos;
1891 }
1892
1893 if ( toskip > 0 )
1894 {
1895 const int BUFSIZE = 4096;
1896 size_t sz;
1897 char buffer[BUFSIZE];
1898 while ( toskip > 0 )
1899 {
1900 sz = wx_truncate_cast(size_t, wxMin(toskip, BUFSIZE));
1901 Read(buffer, sz);
1902 toskip -= sz;
1903 }
1904 }
1905
1906 pos = nextpos;
1907 return pos;
1908 }
1909
1910 #endif // WXWIN_COMPATIBILITY_2_6
1911
1912
1913 /////////////////////////////////////////////////////////////////////////////
1914 // Output stream
1915
1916 #include "wx/listimpl.cpp"
1917 WX_DEFINE_LIST(wx__ZipEntryList)
1918
1919 wxZipOutputStream::wxZipOutputStream(wxOutputStream& stream,
1920 int level /*=-1*/,
1921 wxMBConv& conv /*=wxConvLocal*/)
1922 : wxArchiveOutputStream(stream, conv),
1923 m_store(new wxStoredOutputStream(stream)),
1924 m_deflate(NULL),
1925 m_backlink(NULL),
1926 m_initialData(new char[OUTPUT_LATENCY]),
1927 m_initialSize(0),
1928 m_pending(NULL),
1929 m_raw(false),
1930 m_headerOffset(0),
1931 m_headerSize(0),
1932 m_entrySize(0),
1933 m_comp(NULL),
1934 m_level(level),
1935 m_offsetAdjustment(wxInvalidOffset)
1936 {
1937 }
1938
1939 wxZipOutputStream::~wxZipOutputStream()
1940 {
1941 Close();
1942 WX_CLEAR_LIST(wx__ZipEntryList, m_entries);
1943 delete m_store;
1944 delete m_deflate;
1945 delete m_pending;
1946 delete [] m_initialData;
1947 if (m_backlink)
1948 m_backlink->Release(this);
1949 }
1950
1951 bool wxZipOutputStream::PutNextEntry(
1952 const wxString& name,
1953 const wxDateTime& dt /*=wxDateTime::Now()*/,
1954 wxFileOffset size /*=wxInvalidOffset*/)
1955 {
1956 return PutNextEntry(new wxZipEntry(name, dt, size));
1957 }
1958
1959 bool wxZipOutputStream::PutNextDirEntry(
1960 const wxString& name,
1961 const wxDateTime& dt /*=wxDateTime::Now()*/)
1962 {
1963 wxZipEntry *entry = new wxZipEntry(name, dt);
1964 entry->SetIsDir();
1965 return PutNextEntry(entry);
1966 }
1967
1968 bool wxZipOutputStream::CopyEntry(wxZipEntry *entry,
1969 wxZipInputStream& inputStream)
1970 {
1971 wx__ZipEntryPtr e(entry);
1972
1973 return
1974 inputStream.DoOpen(e.get(), true) &&
1975 DoCreate(e.release(), true) &&
1976 Write(inputStream).IsOk() && inputStream.Eof();
1977 }
1978
1979 bool wxZipOutputStream::PutNextEntry(wxArchiveEntry *entry)
1980 {
1981 wxZipEntry *zipEntry = wxStaticCast(entry, wxZipEntry);
1982 if (!zipEntry)
1983 delete entry;
1984 return PutNextEntry(zipEntry);
1985 }
1986
1987 bool wxZipOutputStream::CopyEntry(wxArchiveEntry *entry,
1988 wxArchiveInputStream& stream)
1989 {
1990 wxZipEntry *zipEntry = wxStaticCast(entry, wxZipEntry);
1991
1992 if (!zipEntry || !stream.OpenEntry(*zipEntry)) {
1993 delete entry;
1994 return false;
1995 }
1996
1997 return CopyEntry(zipEntry, wx_static_cast(wxZipInputStream&, stream));
1998 }
1999
2000 bool wxZipOutputStream::CopyArchiveMetaData(wxZipInputStream& inputStream)
2001 {
2002 m_Comment = inputStream.GetComment();
2003 if (m_backlink)
2004 m_backlink->Release(this);
2005 m_backlink = inputStream.MakeLink(this);
2006 return true;
2007 }
2008
2009 bool wxZipOutputStream::CopyArchiveMetaData(wxArchiveInputStream& stream)
2010 {
2011 return CopyArchiveMetaData(wx_static_cast(wxZipInputStream&, stream));
2012 }
2013
2014 void wxZipOutputStream::SetLevel(int level)
2015 {
2016 if (level != m_level) {
2017 if (m_comp != m_deflate)
2018 delete m_deflate;
2019 m_deflate = NULL;
2020 m_level = level;
2021 }
2022 }
2023
2024 bool wxZipOutputStream::DoCreate(wxZipEntry *entry, bool raw /*=false*/)
2025 {
2026 CloseEntry();
2027
2028 m_pending = entry;
2029 if (!m_pending)
2030 return false;
2031
2032 // write the signature bytes right away
2033 wxDataOutputStream ds(*m_parent_o_stream);
2034 ds << LOCAL_MAGIC;
2035
2036 // and if this is the first entry test for seekability
2037 if (m_headerOffset == 0 && m_parent_o_stream->IsSeekable()) {
2038 #if wxUSE_LOG
2039 bool logging = wxLog::IsEnabled();
2040 wxLogNull nolog;
2041 #endif // wxUSE_LOG
2042 wxFileOffset here = m_parent_o_stream->TellO();
2043
2044 if (here != wxInvalidOffset && here >= 4) {
2045 if (m_parent_o_stream->SeekO(here - 4) == here - 4) {
2046 m_offsetAdjustment = here - 4;
2047 #if wxUSE_LOG
2048 wxLog::EnableLogging(logging);
2049 #endif // wxUSE_LOG
2050 m_parent_o_stream->SeekO(here);
2051 }
2052 }
2053 }
2054
2055 m_pending->SetOffset(m_headerOffset);
2056
2057 m_crcAccumulator = crc32(0, Z_NULL, 0);
2058
2059 if (raw)
2060 m_raw = true;
2061
2062 m_lasterror = wxSTREAM_NO_ERROR;
2063 return true;
2064 }
2065
2066 // Can be overriden to add support for additional compression methods
2067 //
2068 wxOutputStream *wxZipOutputStream::OpenCompressor(
2069 wxOutputStream& stream,
2070 wxZipEntry& entry,
2071 const Buffer bufs[])
2072 {
2073 if (entry.GetMethod() == wxZIP_METHOD_DEFAULT) {
2074 if (GetLevel() == 0
2075 && (IsParentSeekable()
2076 || entry.GetCompressedSize() != wxInvalidOffset
2077 || entry.GetSize() != wxInvalidOffset)) {
2078 entry.SetMethod(wxZIP_METHOD_STORE);
2079 } else {
2080 int size = 0;
2081 for (int i = 0; bufs[i].m_data; ++i)
2082 size += bufs[i].m_size;
2083 entry.SetMethod(size <= 6 ?
2084 wxZIP_METHOD_STORE : wxZIP_METHOD_DEFLATE);
2085 }
2086 }
2087
2088 switch (entry.GetMethod()) {
2089 case wxZIP_METHOD_STORE:
2090 if (entry.GetCompressedSize() == wxInvalidOffset)
2091 entry.SetCompressedSize(entry.GetSize());
2092 return m_store;
2093
2094 case wxZIP_METHOD_DEFLATE:
2095 {
2096 int defbits = wxZIP_DEFLATE_NORMAL;
2097 switch (GetLevel()) {
2098 case 0: case 1:
2099 defbits = wxZIP_DEFLATE_SUPERFAST;
2100 break;
2101 case 2: case 3: case 4:
2102 defbits = wxZIP_DEFLATE_FAST;
2103 break;
2104 case 8: case 9:
2105 defbits = wxZIP_DEFLATE_EXTRA;
2106 break;
2107 }
2108 entry.SetFlags((entry.GetFlags() & ~wxZIP_DEFLATE_MASK) |
2109 defbits | wxZIP_SUMS_FOLLOW);
2110
2111 if (!m_deflate)
2112 m_deflate = new wxZlibOutputStream2(stream, GetLevel());
2113 else
2114 m_deflate->Open(stream);
2115
2116 return m_deflate;
2117 }
2118
2119 default:
2120 wxLogError(_("unsupported Zip compression method"));
2121 }
2122
2123 return NULL;
2124 }
2125
2126 bool wxZipOutputStream::CloseCompressor(wxOutputStream *comp)
2127 {
2128 if (comp == m_deflate)
2129 m_deflate->Close();
2130 else if (comp != m_store)
2131 delete comp;
2132 return true;
2133 }
2134
2135 // This is called when OUPUT_LATENCY bytes has been written to the
2136 // wxZipOutputStream to actually create the zip entry.
2137 //
2138 void wxZipOutputStream::CreatePendingEntry(const void *buffer, size_t size)
2139 {
2140 wxASSERT(IsOk() && m_pending && !m_comp);
2141 wx__ZipEntryPtr spPending(m_pending);
2142 m_pending = NULL;
2143
2144 Buffer bufs[] = {
2145 { m_initialData, m_initialSize },
2146 { (const char*)buffer, size },
2147 { NULL, 0 }
2148 };
2149
2150 if (m_raw)
2151 m_comp = m_store;
2152 else
2153 m_comp = OpenCompressor(*m_store, *spPending,
2154 m_initialSize ? bufs : bufs + 1);
2155
2156 if (IsParentSeekable()
2157 || (spPending->m_Crc
2158 && spPending->m_CompressedSize != wxInvalidOffset
2159 && spPending->m_Size != wxInvalidOffset))
2160 spPending->m_Flags &= ~wxZIP_SUMS_FOLLOW;
2161 else
2162 if (spPending->m_CompressedSize != wxInvalidOffset)
2163 spPending->m_Flags |= wxZIP_SUMS_FOLLOW;
2164
2165 m_headerSize = spPending->WriteLocal(*m_parent_o_stream, GetConv());
2166 m_lasterror = m_parent_o_stream->GetLastError();
2167
2168 if (IsOk()) {
2169 m_entries.push_back(spPending.release());
2170 OnSysWrite(m_initialData, m_initialSize);
2171 }
2172
2173 m_initialSize = 0;
2174 }
2175
2176 // This is called to write out the zip entry when Close has been called
2177 // before OUTPUT_LATENCY bytes has been written to the wxZipOutputStream.
2178 //
2179 void wxZipOutputStream::CreatePendingEntry()
2180 {
2181 wxASSERT(IsOk() && m_pending && !m_comp);
2182 wx__ZipEntryPtr spPending(m_pending);
2183 m_pending = NULL;
2184 m_lasterror = wxSTREAM_WRITE_ERROR;
2185
2186 if (!m_raw) {
2187 // Initially compresses the data to memory, then fall back to 'store'
2188 // if the compressor makes the data larger rather than smaller.
2189 wxMemoryOutputStream mem;
2190 Buffer bufs[] = { { m_initialData, m_initialSize }, { NULL, 0 } };
2191 wxOutputStream *comp = OpenCompressor(mem, *spPending, bufs);
2192
2193 if (!comp)
2194 return;
2195 if (comp != m_store) {
2196 bool ok = comp->Write(m_initialData, m_initialSize).IsOk();
2197 CloseCompressor(comp);
2198 if (!ok)
2199 return;
2200 }
2201
2202 m_entrySize = m_initialSize;
2203 m_crcAccumulator = crc32(0, (Byte*)m_initialData, m_initialSize);
2204
2205 if (mem.GetSize() > 0 && mem.GetSize() < m_initialSize) {
2206 m_initialSize = mem.GetSize();
2207 mem.CopyTo(m_initialData, m_initialSize);
2208 } else {
2209 spPending->SetMethod(wxZIP_METHOD_STORE);
2210 }
2211
2212 spPending->SetSize(m_entrySize);
2213 spPending->SetCrc(m_crcAccumulator);
2214 spPending->SetCompressedSize(m_initialSize);
2215 }
2216
2217 spPending->m_Flags &= ~wxZIP_SUMS_FOLLOW;
2218 m_headerSize = spPending->WriteLocal(*m_parent_o_stream, GetConv());
2219
2220 if (m_parent_o_stream->IsOk()) {
2221 m_entries.push_back(spPending.release());
2222 m_comp = m_store;
2223 m_store->Write(m_initialData, m_initialSize);
2224 }
2225
2226 m_initialSize = 0;
2227 m_lasterror = m_parent_o_stream->GetLastError();
2228 }
2229
2230 // Write the 'central directory' and the 'end-central-directory' records.
2231 //
2232 bool wxZipOutputStream::Close()
2233 {
2234 CloseEntry();
2235
2236 if (m_lasterror == wxSTREAM_WRITE_ERROR || m_entries.size() == 0)
2237 return false;
2238
2239 wxZipEndRec endrec;
2240
2241 endrec.SetEntriesHere(m_entries.size());
2242 endrec.SetTotalEntries(m_entries.size());
2243 endrec.SetOffset(m_headerOffset);
2244 endrec.SetComment(m_Comment);
2245
2246 wx__ZipEntryList::iterator it;
2247 wxFileOffset size = 0;
2248
2249 for (it = m_entries.begin(); it != m_entries.end(); ++it) {
2250 size += (*it)->WriteCentral(*m_parent_o_stream, GetConv());
2251 delete *it;
2252 }
2253 m_entries.clear();
2254
2255 endrec.SetSize(size);
2256 endrec.Write(*m_parent_o_stream, GetConv());
2257
2258 m_lasterror = m_parent_o_stream->GetLastError();
2259 if (!IsOk())
2260 return false;
2261 m_lasterror = wxSTREAM_EOF;
2262 return true;
2263 }
2264
2265 // Finish writing the current entry
2266 //
2267 bool wxZipOutputStream::CloseEntry()
2268 {
2269 if (IsOk() && m_pending)
2270 CreatePendingEntry();
2271 if (!IsOk())
2272 return false;
2273 if (!m_comp)
2274 return true;
2275
2276 CloseCompressor(m_comp);
2277 m_comp = NULL;
2278
2279 wxFileOffset compressedSize = m_store->TellO();
2280
2281 wxZipEntry& entry = *m_entries.back();
2282
2283 // When writing raw the crc and size can't be checked
2284 if (m_raw) {
2285 m_crcAccumulator = entry.GetCrc();
2286 m_entrySize = entry.GetSize();
2287 }
2288
2289 // Write the sums in the trailing 'data descriptor' if necessary
2290 if (entry.m_Flags & wxZIP_SUMS_FOLLOW) {
2291 wxASSERT(!IsParentSeekable());
2292 m_headerOffset +=
2293 entry.WriteDescriptor(*m_parent_o_stream, m_crcAccumulator,
2294 compressedSize, m_entrySize);
2295 m_lasterror = m_parent_o_stream->GetLastError();
2296 }
2297
2298 // If the local header didn't have the correct crc and size written to
2299 // it then seek back and fix it
2300 else if (m_crcAccumulator != entry.GetCrc()
2301 || m_entrySize != entry.GetSize()
2302 || compressedSize != entry.GetCompressedSize())
2303 {
2304 if (IsParentSeekable()) {
2305 wxFileOffset here = m_parent_o_stream->TellO();
2306 wxFileOffset headerOffset = m_headerOffset + m_offsetAdjustment;
2307 m_parent_o_stream->SeekO(headerOffset + SUMS_OFFSET);
2308 entry.WriteDescriptor(*m_parent_o_stream, m_crcAccumulator,
2309 compressedSize, m_entrySize);
2310 m_parent_o_stream->SeekO(here);
2311 m_lasterror = m_parent_o_stream->GetLastError();
2312 } else {
2313 m_lasterror = wxSTREAM_WRITE_ERROR;
2314 }
2315 }
2316
2317 m_headerOffset += m_headerSize + compressedSize;
2318 m_headerSize = 0;
2319 m_entrySize = 0;
2320 m_store->Close();
2321 m_raw = false;
2322
2323 if (IsOk())
2324 m_lasterror = m_parent_o_stream->GetLastError();
2325 else
2326 wxLogError(_("error writing zip entry '%s': bad crc or length"),
2327 entry.GetName().c_str());
2328 return IsOk();
2329 }
2330
2331 void wxZipOutputStream::Sync()
2332 {
2333 if (IsOk() && m_pending)
2334 CreatePendingEntry(NULL, 0);
2335 if (!m_comp)
2336 m_lasterror = wxSTREAM_WRITE_ERROR;
2337 if (IsOk()) {
2338 m_comp->Sync();
2339 m_lasterror = m_comp->GetLastError();
2340 }
2341 }
2342
2343 size_t wxZipOutputStream::OnSysWrite(const void *buffer, size_t size)
2344 {
2345 if (IsOk() && m_pending) {
2346 if (m_initialSize + size < OUTPUT_LATENCY) {
2347 memcpy(m_initialData + m_initialSize, buffer, size);
2348 m_initialSize += size;
2349 return size;
2350 } else {
2351 CreatePendingEntry(buffer, size);
2352 }
2353 }
2354
2355 if (!m_comp)
2356 m_lasterror = wxSTREAM_WRITE_ERROR;
2357 if (!IsOk() || !size)
2358 return 0;
2359
2360 if (m_comp->Write(buffer, size).LastWrite() != size)
2361 m_lasterror = wxSTREAM_WRITE_ERROR;
2362 m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, size);
2363 m_entrySize += m_comp->LastWrite();
2364
2365 return m_comp->LastWrite();
2366 }
2367
2368 #endif // wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM