/////////////////////////////////////////////////////////////////////////////
-// Name: zipstrm.cpp
+// Name: src/common/zipstrm.cpp
// Purpose: Streams for Zip files
// Author: Mike Wetherell
// RCS-ID: $Id$
#include "wx/wxprec.h"
#ifdef __BORLANDC__
- #pragma hdrstop
+ #pragma hdrstop
#endif
+#if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
+
#ifndef WX_PRECOMP
- #include "wx/defs.h"
+ #include "wx/intl.h"
+ #include "wx/log.h"
+ #include "wx/utils.h"
#endif
-#if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
-
#include "wx/zipstrm.h"
-#include "wx/log.h"
-#include "wx/intl.h"
#include "wx/datstrm.h"
#include "wx/zstream.h"
#include "wx/mstream.h"
-#include "wx/utils.h"
#include "wx/buffer.h"
#include "wx/ptr_scpd.h"
#include "wx/wfstream.h"
//
static wxString ReadString(wxInputStream& stream, wxUint16 len, wxMBConv& conv)
{
+ if (len == 0)
+ return wxEmptyString;
+
#if wxUSE_UNICODE
wxCharBuffer buf(len);
stream.Read(buf.data(), len);
return (n[3] << 24) | (n[2] << 16) | (n[1] << 8) | n[0];
}
+// Decode a little endian wxUint16 number from a character array
+//
+static inline wxUint16 CrackUint16(const char *m)
+{
+ const unsigned char *n = (const unsigned char*)m;
+ return (n[1] << 8) | n[0];
+}
+
// Temporarily lower the logging level in debug mode to avoid a warning
// from SeekI about seeking on a stream with data written back to it.
//
static wxFileOffset QuietSeek(wxInputStream& stream, wxFileOffset pos)
{
-#ifdef __WXDEBUG__
+#if defined(__WXDEBUG__) && wxUSE_LOG
wxLogLevel level = wxLog::GetLogLevel();
wxLog::SetLogLevel(wxLOG_Debug - 1);
wxFileOffset result = stream.SeekI(pos);
}
+/////////////////////////////////////////////////////////////////////////////
+// Read a zip header
+
+class wxZipHeader
+{
+public:
+ wxZipHeader(wxInputStream& stream, size_t size);
+
+ inline wxUint8 Read8();
+ inline wxUint16 Read16();
+ inline wxUint32 Read32();
+
+ const char *GetData() const { return m_data; }
+ size_t GetSize() const { return m_size; }
+ operator bool() const { return m_ok; }
+
+ size_t Seek(size_t pos) { m_pos = pos; return m_pos; }
+ size_t Skip(size_t size) { m_pos += size; return m_pos; }
+
+ wxZipHeader& operator>>(wxUint8& n) { n = Read8(); return *this; }
+ wxZipHeader& operator>>(wxUint16& n) { n = Read16(); return *this; }
+ wxZipHeader& operator>>(wxUint32& n) { n = Read32(); return *this; }
+
+private:
+ char m_data[64];
+ size_t m_size;
+ size_t m_pos;
+ bool m_ok;
+};
+
+wxZipHeader::wxZipHeader(wxInputStream& stream, size_t size)
+ : m_size(0),
+ m_pos(0),
+ m_ok(false)
+{
+ wxCHECK_RET(size <= sizeof(m_data), _T("buffer too small"));
+ m_size = stream.Read(m_data, size).LastRead();
+ m_ok = m_size == size;
+}
+
+wxUint8 wxZipHeader::Read8()
+{
+ wxASSERT(m_pos < m_size);
+ return m_data[m_pos++];
+}
+
+wxUint16 wxZipHeader::Read16()
+{
+ wxASSERT(m_pos + 2 <= m_size);
+ wxUint16 n = CrackUint16(m_data + m_pos);
+ m_pos += 2;
+ return n;
+}
+
+wxUint32 wxZipHeader::Read32()
+{
+ wxASSERT(m_pos + 4 <= m_size);
+ wxUint32 n = CrackUint32(m_data + m_pos);
+ m_pos += 4;
+ return n;
+}
+
+
/////////////////////////////////////////////////////////////////////////////
// Stored input stream
// Trival decompressor for files which are 'stored' in the zip file.
count = m_parent_i_stream->Read(buffer, count).LastRead();
m_pos += count;
- if (m_pos == m_len && count < size)
- m_lasterror = wxSTREAM_EOF;
- else if (!*m_parent_i_stream)
- m_lasterror = wxSTREAM_READ_ERROR;
+ if (count < size)
+ m_lasterror = m_pos == m_len ? wxSTREAM_EOF : wxSTREAM_READ_ERROR;
return count;
}
size_t wxTeeInputStream::OnSysRead(void *buffer, size_t size)
{
size_t count = m_parent_i_stream->Read(buffer, size).LastRead();
- m_lasterror = m_parent_i_stream->GetLastError();
+ if (count < size)
+ m_lasterror = m_parent_i_stream->GetLastError();
return count;
}
wxFAIL; // we've already returned data that's now being ungot
m_end = len;
}
+ m_parent_i_stream->Reset();
m_parent_i_stream->Ungetch(m_wback, m_wbacksize);
free(m_wback);
m_wback = NULL;
size_t m_size;
size_t m_capacity;
int m_ref;
+
+ wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipMemory)
};
wxZipMemory *wxZipMemory::Unique(size_t size)
bool IsEmpty() const { return m_entries.empty(); }
private:
- typedef wx__OffsetZipEntryMap::key_type key_type;
-
~wxZipWeakLinks() { wxASSERT(IsEmpty()); }
+ typedef wx__OffsetZipEntryMap::key_type key_type;
+
int m_ref;
wx__OffsetZipEntryMap m_entries;
+
+ wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipWeakLinks)
};
wxZipWeakLinks *wxZipWeakLinks::AddEntry(wxZipEntry *entry, wxFileOffset key)
case wxPATH_DOS:
{
wxString name(isDir ? m_Name + _T("\\") : m_Name);
- for (size_t i = name.length() - 1; i > 0; --i)
+ for (size_t i = 0; i < name.length(); i++)
if (name[i] == _T('/'))
name[i] = _T('\\');
return name;
wxUint16 nameLen, extraLen;
wxUint32 compressedSize, size, crc;
- wxDataInputStream ds(stream);
+ wxZipHeader ds(stream, LOCAL_SIZE - 4);
+ if (!ds)
+ return 0;
ds >> m_VersionNeeded >> m_Flags >> m_Method;
SetDateTime(wxDateTime().SetFromDOS(ds.Read32()));
m_Size = size;
SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
+ if (stream.LastRead() != nameLen + 0u)
+ return 0;
if (extraLen || GetLocalExtraLen()) {
Unique(m_LocalExtra, extraLen);
- if (extraLen)
+ if (extraLen) {
stream.Read(m_LocalExtra->GetData(), extraLen);
+ if (stream.LastRead() != extraLen + 0u)
+ return 0;
+ }
}
return LOCAL_SIZE + nameLen + extraLen;
{
wxUint16 nameLen, extraLen, commentLen;
- wxDataInputStream ds(stream);
+ wxZipHeader ds(stream, CENTRAL_SIZE - 4);
+ if (!ds)
+ return 0;
ds >> m_VersionMadeBy >> m_SystemMadeBy;
SetOffset(ds.Read32());
SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
+ if (stream.LastRead() != nameLen + 0u)
+ return 0;
if (extraLen || GetExtraLen()) {
Unique(m_Extra, extraLen);
- if (extraLen)
+ if (extraLen) {
stream.Read(m_Extra->GetData(), extraLen);
+ if (stream.LastRead() != extraLen + 0u)
+ return 0;
+ }
}
- if (commentLen)
+ if (commentLen) {
m_Comment = ReadString(stream, commentLen, conv);
- else
+ if (stream.LastRead() != commentLen + 0u)
+ return 0;
+ } else {
m_Comment.clear();
+ }
return CENTRAL_SIZE + nameLen + extraLen + commentLen;
}
//
size_t wxZipEntry::ReadDescriptor(wxInputStream& stream)
{
- wxDataInputStream ds(stream);
+ wxZipHeader ds(stream, SUMS_SIZE);
+ if (!ds)
+ return 0;
m_Crc = ds.Read32();
m_CompressedSize = ds.Read32();
// if 1st value is the signature then this is probably an info-zip record
if (m_Crc == SUMS_MAGIC)
{
- char buf[8];
- stream.Read(buf, sizeof(buf));
- wxUint32 u1 = CrackUint32(buf);
- wxUint32 u2 = CrackUint32(buf + 4);
+ wxZipHeader buf(stream, 8);
+ wxUint32 u1 = buf.GetSize() >= 4 ? buf.Read32() : (wxUint32)LOCAL_MAGIC;
+ wxUint32 u2 = buf.GetSize() == 8 ? buf.Read32() : 0;
// look for the signature of the following record to decide which
if ((u1 == LOCAL_MAGIC || u1 == CENTRAL_MAGIC) &&
(u2 != LOCAL_MAGIC && u2 != CENTRAL_MAGIC))
{
// it's a pkzip style record after all!
- stream.Ungetch(buf, sizeof(buf));
+ if (buf.GetSize() > 0)
+ stream.Ungetch(buf.GetData(), buf.GetSize());
}
else
{
// it's an info-zip record as expected
- stream.Ungetch(buf + 4, sizeof(buf) - 4);
+ if (buf.GetSize() > 4)
+ stream.Ungetch(buf.GetData() + 4, buf.GetSize() - 4);
m_Crc = wx_truncate_cast(wxUint32, m_CompressedSize);
m_CompressedSize = m_Size;
m_Size = u1;
bool wxZipEndRec::Read(wxInputStream& stream, wxMBConv& conv)
{
- wxDataInputStream ds(stream);
+ wxZipHeader ds(stream, END_SIZE - 4);
+ if (!ds)
+ return false;
+
wxUint16 commentLen;
ds >> m_DiskNumber >> m_StartDisk >> m_EntriesHere
>> m_TotalEntries >> m_Size >> m_Offset >> commentLen;
- if (commentLen)
+ if (commentLen) {
m_Comment = ReadString(stream, commentLen, conv);
+ if (stream.LastRead() != commentLen + 0u)
+ return false;
+ }
- if (stream.IsOk())
- if (m_DiskNumber == 0 && m_StartDisk == 0 &&
- m_EntriesHere == m_TotalEntries)
- return true;
- else
- wxLogError(_("unsupported zip archive"));
+ if (m_DiskNumber != 0 || m_StartDisk != 0 ||
+ m_EntriesHere != m_TotalEntries)
+ wxLogWarning(_("assuming this is a multi-part zip concatenated"));
- return false;
+ return true;
}
int m_ref;
wxZipOutputStream *m_stream;
+
+ wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipStreamLink)
};
else {
wxLogNull nolog;
wxFileOffset pos = m_parent_i_stream->TellI();
- // FIXME
- //if (pos != wxInvalidOffset)
- if (pos >= 0 && pos <= LONG_MAX)
+ if (pos != wxInvalidOffset)
m_offsetAdjustment = m_position = pos;
return true;
}
// Read in the end record
wxFileOffset endPos = m_parent_i_stream->TellI() - 4;
- if (!endrec.Read(*m_parent_i_stream, GetConv())) {
- if (!*m_parent_i_stream) {
- m_lasterror = wxSTREAM_READ_ERROR;
- return false;
- }
- // TODO: try this out
- wxLogWarning(_("assuming this is a multi-part zip concatenated"));
- }
+ if (!endrec.Read(*m_parent_i_stream, GetConv()))
+ return false;
m_TotalEntries = endrec.GetTotalEntries();
m_Comment = endrec.GetComment();
if (QuietSeek(*m_parent_i_stream, m_position + 4) == wxInvalidOffset)
return wxSTREAM_READ_ERROR;
- m_position += m_entry.ReadCentral(*m_parent_i_stream, GetConv());
- if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR) {
+ size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv());
+ if (!size) {
m_signature = 0;
return wxSTREAM_READ_ERROR;
}
+ m_position += size;
m_signature = ReadSignature();
if (m_offsetAdjustment)
if (m_weaklinks->IsEmpty() && m_streamlink == NULL)
return wxSTREAM_EOF;
- m_position += m_entry.ReadCentral(*m_parent_i_stream, GetConv());
+ size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv());
+ m_position += size;
m_signature = 0;
- if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR)
+ if (!size)
return wxSTREAM_READ_ERROR;
wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetOffset());
return wxSTREAM_EOF;
}
- if (m_signature != LOCAL_MAGIC) {
- wxLogError(_("error reading zip local header"));
- return wxSTREAM_READ_ERROR;
- }
-
- m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
- m_signature = 0;
- m_entry.SetOffset(m_position);
- m_entry.SetKey(m_position);
+ if (m_signature == LOCAL_MAGIC) {
+ m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
+ m_signature = 0;
+ m_entry.SetOffset(m_position);
+ m_entry.SetKey(m_position);
- if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR) {
- return wxSTREAM_READ_ERROR;
- } else {
- m_TotalEntries++;
- return wxSTREAM_NO_ERROR;
+ if (m_headerSize) {
+ m_TotalEntries++;
+ return wxSTREAM_NO_ERROR;
+ }
}
+
+ wxLogError(_("error reading zip local header"));
+ return wxSTREAM_READ_ERROR;
}
wxUint32 wxZipInputStream::ReadSignature()
if (m_parentSeekable || AtHeader()) {
m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
- if (m_parentSeekable) {
+ if (m_headerSize && m_parentSeekable) {
wxZipEntry *ref = m_weaklinks->GetEntry(m_entry.GetKey());
if (ref) {
Copy(ref->m_LocalExtra, m_entry.m_LocalExtra);
}
}
- m_lasterror = m_parent_i_stream->GetLastError();
+ if (m_headerSize)
+ m_lasterror = wxSTREAM_NO_ERROR;
return IsOk();
}
size_t count = m_decomp->Read(buffer, size).LastRead();
if (!m_raw)
m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, count);
- m_lasterror = m_decomp->GetLastError();
+ if (count < size)
+ m_lasterror = m_decomp->GetLastError();
if (Eof()) {
if ((m_entry.GetFlags() & wxZIP_SUMS_FOLLOW) != 0) {
if (!m_raw) {
m_lasterror = wxSTREAM_READ_ERROR;
- if (m_parent_i_stream->IsOk()) {
- if (m_entry.GetSize() != TellI())
- wxLogError(_("reading zip stream (entry %s): bad length"),
- m_entry.GetName().c_str());
- else if (m_crcAccumulator != m_entry.GetCrc())
- wxLogError(_("reading zip stream (entry %s): bad crc"),
- m_entry.GetName().c_str());
- else
- m_lasterror = wxSTREAM_EOF;
- }
+ if (m_entry.GetSize() != TellI())
+ wxLogError(_("reading zip stream (entry %s): bad length"),
+ m_entry.GetName().c_str());
+ else if (m_crcAccumulator != m_entry.GetCrc())
+ wxLogError(_("reading zip stream (entry %s): bad crc"),
+ m_entry.GetName().c_str());
+ else
+ m_lasterror = wxSTREAM_EOF;
}
}