Files
harbour-core/harbour/contrib/hbziparch/ZipStorage.cpp
Viktor Szakats 40338a87d6 2008-06-24 00:35 UTC+0200 Viktor Szakats (harbour.01 syenar hu)
* contrib/hbziparch/_features.h
   * contrib/hbziparch/_platform.h
   * contrib/hbziparch/Aes.cpp
   * contrib/hbziparch/Aes.h
   * contrib/hbziparch/BaseLibCompressor.cpp
   * contrib/hbziparch/BaseLibCompressor.h
   * contrib/hbziparch/BytesWriter.h
   * contrib/hbziparch/Bzip2Compressor.cpp
   * contrib/hbziparch/Bzip2Compressor.h
   * contrib/hbziparch/DeflateCompressor.cpp
   * contrib/hbziparch/DeflateCompressor.h
   * contrib/hbziparch/DirEnumerator.cpp
   * contrib/hbziparch/DirEnumerator.h
   * contrib/hbziparch/FileFilter.cpp
   * contrib/hbziparch/FileFilter.h
   * contrib/hbziparch/FileInfo.h
   * contrib/hbziparch/Hmac.cpp
   * contrib/hbziparch/Hmac.h
   * contrib/hbziparch/RandomPool.cpp
   * contrib/hbziparch/RandomPool.h
   * contrib/hbziparch/Sha1.cpp
   * contrib/hbziparch/Sha1.h
   * contrib/hbziparch/std_mfc.h
   * contrib/hbziparch/std_stl.h
   * contrib/hbziparch/Wildcard.cpp
   * contrib/hbziparch/Wildcard.h
   * contrib/hbziparch/ZipAbstractFile.h
   * contrib/hbziparch/ZipAesCryptograph.cpp
   * contrib/hbziparch/ZipAesCryptograph.h
   * contrib/hbziparch/ZipArchive.cpp
   * contrib/hbziparch/ZipArchive.h
   * contrib/hbziparch/ZipAutoBuffer.cpp
   * contrib/hbziparch/ZipAutoBuffer.h
   * contrib/hbziparch/ZipBaseException.h
   * contrib/hbziparch/ZipCallback.h
   * contrib/hbziparch/ZipCallbackProvider.h
   * contrib/hbziparch/ZipCentralDir.cpp
   * contrib/hbziparch/ZipCentralDir.h
   * contrib/hbziparch/ZipCollections.h
   * contrib/hbziparch/ZipCollections_mfc.h
   * contrib/hbziparch/ZipCollections_stl.h
   * contrib/hbziparch/ZipCompatibility.cpp
   * contrib/hbziparch/ZipCompatibility.h
   * contrib/hbziparch/ZipCompressor.cpp
   * contrib/hbziparch/ZipCompressor.h
   * contrib/hbziparch/ZipCrc32Cryptograph.cpp
   * contrib/hbziparch/ZipCrc32Cryptograph.h
   * contrib/hbziparch/ZipCryptograph.cpp
   * contrib/hbziparch/ZipCryptograph.h
   * contrib/hbziparch/ZipException.cpp
   * contrib/hbziparch/ZipException.h
   * contrib/hbziparch/ZipExtraData.cpp
   * contrib/hbziparch/ZipExtraData.h
   * contrib/hbziparch/ZipExtraField.cpp
   * contrib/hbziparch/ZipExtraField.h
   * contrib/hbziparch/ZipFile.h
   * contrib/hbziparch/ZipFile_mfc.cpp
   * contrib/hbziparch/ZipFile_mfc.h
   * contrib/hbziparch/ZipFile_stl.cpp
   * contrib/hbziparch/ZipFile_stl.h
   * contrib/hbziparch/ZipFileHeader.cpp
   * contrib/hbziparch/ZipFileHeader.h
   * contrib/hbziparch/ZipFileMapping.h
   * contrib/hbziparch/ZipFileMapping_lnx.h
   * contrib/hbziparch/ZipFileMapping_win.h
   * contrib/hbziparch/ZipMemFile.cpp
   * contrib/hbziparch/ZipMemFile.h
   * contrib/hbziparch/ZipMutex.h
   * contrib/hbziparch/ZipMutex_lnx.h
   * contrib/hbziparch/ZipMutex_win.h
   * contrib/hbziparch/ZipPathComponent.h
   * contrib/hbziparch/ZipPlatform.h
   * contrib/hbziparch/ZipPlatformComm.cpp
   * contrib/hbziparch/ZipStorage.cpp
   * contrib/hbziparch/ZipStorage.h
   * contrib/hbziparch/ZipString.h
   * contrib/hbziparch/ZipString_mfc.h
   * contrib/hbziparch/ZipString_stl.h
   * contrib/hbziparch/ZipStringStoreSettings.h
     + ZipArchive lib update finished.
     ; Pass 2/2
     ; Please test.
2008-06-23 22:41:33 +00:00

616 lines
16 KiB
C++

////////////////////////////////////////////////////////////////////////////////
// This source file is part of the ZipArchive library source distribution and
// is Copyrighted 2000 - 2007 by Artpol Software - Tadeusz Dracz
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// For the licensing details refer to the License.txt file.
//
// Web Site: http://www.artpol-software.com
////////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "ZipStorage.h"
#include "ZipArchive.h"
#include "ZipPlatform.h"
char CZipStorage::m_gszExtHeaderSignat[] = {0x50, 0x4b, 0x07, 0x08};
const ZIP_FILE_USIZE CZipStorage::SignatureNotFound = ZIP_FILE_USIZE(-1);
CZipStorage::CZipStorage()
{
Initialize();
}
void CZipStorage::Initialize()
{
m_pSplitChangeVolumeFunc = m_pSpanChangeVolumeFunc = m_pChangeVolumeFunc = NULL;
m_iWriteBufferSize = 65536;
m_pFile = NULL;
m_szSplitExtension = _T("zip");
m_iLocateBufferSize = 32768;
m_uBytesBeforeZip = 0;
m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED;
m_szArchiveName.Empty();
}
CZipStorage::~CZipStorage()
{
}
DWORD CZipStorage::Read(void *pBuf, DWORD iSize, bool bAtOnce)
{
if (iSize == 0)
return 0;
DWORD iRead;
for(;;)
{
iRead = m_pFile->Read(pBuf, iSize);
if (!iRead)
{
if (IsSegmented())
ChangeVolume();
else
ThrowError(CZipException::badZipFile);
}
else
break;
}
if (iRead == iSize)
return iRead;
else if (bAtOnce || !IsSegmented())
ThrowError(CZipException::badZipFile);
while (iRead < iSize)
{
ChangeVolume();
UINT iNewRead = m_pFile->Read((char*)pBuf + iRead, iSize - iRead);
if (!iNewRead && iRead < iSize)
ThrowError(CZipException::badZipFile);
iRead += iNewRead;
}
return iRead;
}
void CZipStorage::Open(LPCTSTR lpszPathName, int iMode, ZIP_SIZE_TYPE uVolumeSize)
{
m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED;
m_pWriteBuffer.Allocate(m_iWriteBufferSize);
m_uBytesInWriteBuffer = 0;
m_bNewSegm = false;
m_pFile = &m_internalfile;
m_bInMemory = false;
m_szArchiveName = lpszPathName;
m_pChangeVolumeFunc = NULL;
if (iMode == CZipArchive::zipCreate || iMode == CZipArchive::zipCreateSegm
|| iMode == CZipArchive::zipCreateAppend) // create new archive
{
m_bReadOnly = false;
m_uCurrentVolume = 0;
if (iMode == CZipArchive::zipCreate || iMode == CZipArchive::zipCreateAppend)
{
m_iSegmMode = noSegments;
OpenFile(lpszPathName, (iMode == CZipArchive::zipCreate ? CZipFile::modeCreate : CZipFile::modeNoTruncate) | CZipFile::modeReadWrite);
}
else // create a segmented archive
{
m_bNewSegm = true;
m_uBytesWritten = 0;
if (uVolumeSize == ZIP_AUTODETECT_VOLUME_SIZE) // spanned archive
{
if (!m_pSpanChangeVolumeFunc)
ThrowError(CZipException::noCallback);
if (!ZipPlatform::IsDriveRemovable(lpszPathName))
ThrowError(CZipException::nonRemovable);
m_iSegmMode = spannedArchive;
m_pChangeVolumeFunc = m_pSpanChangeVolumeFunc;
}
else
{
m_uSplitData = uVolumeSize;
m_iSegmMode = splitArchive;
m_pChangeVolumeFunc = m_pSplitChangeVolumeFunc;
}
NextVolume(4);
Write(m_gszExtHeaderSignat, 4, true);
}
}
else // open existing
{
m_bReadOnly = iMode == CZipArchive::zipOpenReadOnly;
OpenFile(lpszPathName, CZipFile::modeNoTruncate |
(m_bReadOnly ? CZipFile::modeRead : CZipFile::modeReadWrite));
// m_uData and m_iSegmMode are automatically set during reading the central dir
m_iSegmMode = uVolumeSize == 0 ? suggestedAuto : suggestedSplit;
}
}
void CZipStorage::Open(CZipAbstractFile& af, int iMode)
{
m_pWriteBuffer.Allocate(m_iWriteBufferSize);
m_uBytesInWriteBuffer = 0;
m_bNewSegm = false;
m_pFile = &af;
m_bInMemory = true;
if (iMode == CZipArchive::zipCreate || iMode == CZipArchive::zipCreateAppend)
{
m_uCurrentVolume = 0;
m_iSegmMode = noSegments;
if (iMode == CZipArchive::zipCreate)
af.SetLength(0);
else
af.SeekToEnd();
}
else // open existing
{
af.SeekToBegin();
m_iSegmMode = suggestedAuto;
}
}
void CZipStorage::ChangeVolume(ZIP_VOLUME_TYPE uNumber)
{
if (uNumber == m_uCurrentVolume || m_iSegmMode == noSegments) // the second condition may happen in some bad archives
return;
m_uCurrentVolume = uNumber;
OpenFile(IsSpanned() ? ChangeSpannedRead() : ChangeSplitRead(),
CZipFile::modeNoTruncate | CZipFile::modeRead);
}
void CZipStorage::ThrowError(int err)
{
CZipException::Throw(err, m_pFile->GetFilePath());
}
bool CZipStorage::OpenFile(LPCTSTR lpszName, UINT uFlags, bool bThrow)
{
return m_pFile->Open(lpszName, uFlags | CZipFile::shareDenyWrite, bThrow);
}
CZipString CZipStorage::ChangeSpannedRead()
{
CZipString szTemp = m_pFile->GetFilePath();
m_pFile->Close();
CallCallback(0, CZipSegmCallback::scVolumeNeededForRead, szTemp);
return szTemp;
}
CZipString CZipStorage::ChangeSplitRead()
{
bool lastPart = (ZIP_SIZE_TYPE)m_uCurrentVolume == m_uSplitData;
CZipString szTemp = GetSplitVolumeName(lastPart);
if (m_pChangeVolumeFunc)
{
int iCode = CZipSegmCallback::scVolumeNeededForRead;
for(;;)
{
CallCallback(lastPart ? ZIP_SPLIT_LAST_VOLUME : 0, iCode, szTemp);
if (ZipPlatform::FileExists(m_pChangeVolumeFunc->m_szExternalFile))
{
szTemp = m_pChangeVolumeFunc->m_szExternalFile;
break;
}
else
iCode = CZipSegmCallback::scFileNotFound;
}
}
m_pFile->Close();
return szTemp;
}
CZipString CZipStorage::RenameLastFileInSplitArchive()
{
ASSERT(IsSplit());
// give to the last volume the zip extension
CZipString szFileName = m_pFile->GetFilePath();
CZipString szNewFileName = GetSplitVolumeName(true);
if (m_pChangeVolumeFunc)
{
int code = CZipSegmCallback::scVolumeNeededForWrite;
for(;;)
{
CallCallback(ZIP_SPLIT_LAST_VOLUME, code, szNewFileName);
szNewFileName = m_pChangeVolumeFunc->m_szExternalFile;
if (ZipPlatform::FileExists(szNewFileName))
code = CZipSegmCallback::scFileNameDuplicated;
else
break;
}
}
if (!m_bInMemory)
{
m_pFile->Flush();
m_pFile->Close();
}
if (!m_pChangeVolumeFunc && ZipPlatform::FileExists(szNewFileName))
ZipPlatform::RemoveFile(szNewFileName);
ZipPlatform::RenameFile(szFileName, szNewFileName);
return szNewFileName;
}
CZipString CZipStorage::Close(bool bAfterException)
{
bool bClose = true;
CZipString sz;
if (!bAfterException)
{
Flush();
if (IsSplit() && m_bNewSegm)
{
sz = RenameLastFileInSplitArchive();
bClose = false;// already closed in RenameLastFileInSplitArchive
}
}
if (sz.IsEmpty())
sz = m_pFile->GetFilePath();
if (bClose && !m_bInMemory)
{
if (!bAfterException)
FlushFile();
m_pFile->Close();
}
m_pWriteBuffer.Release();
m_uCurrentVolume = ZIP_VOLUME_NUMBER_UNSPECIFIED;
m_iSegmMode = noSegments;
m_pFile = NULL;
m_uBytesBeforeZip = 0;
return sz;
}
CZipString CZipStorage::GetSplitVolumeName(bool bLast) const
{
CZipString szFilePath = m_szArchiveName;
CZipPathComponent zpc(szFilePath);
CZipString szExt;
if (bLast)
szExt = m_szSplitExtension;
else
{
DWORD vol = m_uCurrentVolume + 1;
if (vol < 100)
szExt.Format(_T("z%.2u"), vol);
else
szExt.Format(_T("z%u"), vol);
}
zpc.SetExtension(szExt);
return zpc.GetFullPath();
}
void CZipStorage::NextVolume(ZIP_SIZE_TYPE uNeeded)
{
Flush();
ASSERT(m_iSegmMode != noSegments);
bool bSpan = IsSpanned();
if (m_uBytesWritten)
{
m_uBytesWritten = 0;
m_uCurrentVolume++;
ZIP_VOLUME_TYPE uMaxVolumes = (ZIP_VOLUME_TYPE)(bSpan ? 999 : 0xFFFF);
if (m_uCurrentVolume >= uMaxVolumes) // m_uCurrentVolume is a zero-based index
ThrowError(CZipException::tooManyVolumes);
}
CZipString szFileName;
if (bSpan)
szFileName = m_szArchiveName;
else
szFileName = GetSplitVolumeName(false);
if (!m_pFile->IsClosed())
{
m_pFile->Flush();
m_pFile->Close();
}
if (m_pChangeVolumeFunc)
{
int iCode = CZipSegmCallback::scVolumeNeededForWrite;
for(;;)
{
CallCallback(uNeeded, iCode, szFileName);
if (!bSpan)
// allow the user to change the filename
szFileName = m_pChangeVolumeFunc->m_szExternalFile;
if (ZipPlatform::FileExists(szFileName))
iCode = CZipSegmCallback::scFileNameDuplicated;
else
{
if (bSpan)
{
CZipString label;
label.Format(_T("pkback# %.3d"), m_uCurrentVolume + 1);
if (!ZipPlatform::SetVolLabel(szFileName, label))
{
iCode = CZipSegmCallback::scCannotSetVolLabel;
continue;
}
}
if (OpenFile(szFileName, CZipFile::modeCreate | CZipFile::modeReadWrite, false))
break;
else
iCode = CZipSegmCallback::scFileCreationFailure;
}
}
m_uCurrentVolSize = bSpan ? GetFreeVolumeSpace() : m_uSplitData;
}
else
{
if (bSpan)
ThrowError(CZipException::internalError);
m_uCurrentVolSize = m_uSplitData;
OpenFile(szFileName, CZipFile::modeCreate | CZipFile::modeReadWrite);
}
}
void CZipStorage::CallCallback(ZIP_SIZE_TYPE uNeeded, int iCode, CZipString szTemp)
{
if (!m_pChangeVolumeFunc)
ThrowError(CZipException::internalError);
m_pChangeVolumeFunc->m_szExternalFile = szTemp;
m_pChangeVolumeFunc->m_uVolumeNeeded = (ZIP_VOLUME_TYPE)(m_uCurrentVolume + 1);
m_pChangeVolumeFunc->m_iCode = iCode;
if (!m_pChangeVolumeFunc->Callback(uNeeded))
CZipException::Throw(CZipException::aborted, szTemp);
}
ZIP_SIZE_TYPE CZipStorage::GetFreeVolumeSpace() const
{
ASSERT (IsSpanned());
CZipString szTemp = m_pFile->GetFilePath();
if (szTemp.IsEmpty()) // called once when creating a segmented archive
return 0;
else
{
CZipPathComponent zpc(szTemp);
ULONGLONG ret = ZipPlatform::GetDeviceFreeSpace(zpc.GetFilePath());
if (ret > (ZIP_SIZE_TYPE)(-1))
return (ZIP_SIZE_TYPE)(-1);
else
return (ZIP_SIZE_TYPE)ret;
}
}
void CZipStorage::UpdateSegmMode(ZIP_VOLUME_TYPE uLastDisk)
{
m_uCurrentVolume = uLastDisk;
if (uLastDisk)
{
// segmentation detected
CZipString szFilePath = m_pFile->GetFilePath();
if (m_iSegmMode == suggestedAuto)
m_iSegmMode = ZipPlatform::IsDriveRemovable(szFilePath) ?
spannedArchive : splitArchive;
else
{
ASSERT(m_iSegmMode == suggestedSplit);
m_iSegmMode = splitArchive;
}
if (IsSpanned())
{
if (!m_pSpanChangeVolumeFunc)
ThrowError(CZipException::noCallback);
m_pChangeVolumeFunc = m_pSpanChangeVolumeFunc;
}
else /*if (IsSplit())*/
{
m_uSplitData = uLastDisk; // volume with .zip extension ( the last one)
m_pChangeVolumeFunc = m_pSplitChangeVolumeFunc;
}
CZipPathComponent zpc(szFilePath);
m_szSplitExtension = zpc.GetFileExt();
m_pWriteBuffer.Release(); // no need for this in this case
}
else
m_iSegmMode = noSegments;
}
ZIP_SIZE_TYPE CZipStorage::AssureFree(ZIP_SIZE_TYPE uNeeded)
{
ZIP_SIZE_TYPE uFree;
while ((uFree = VolumeLeft()) < uNeeded)
{
if (IsSplit() && !m_uBytesWritten && !m_uBytesInWriteBuffer)
// in the splitArchive mode, if the size of the archive is less
// than the size of the packet to be written at once,
// increase once the size of the volume
m_uCurrentVolSize = uNeeded;
else
NextVolume(uNeeded);
}
return uFree;
}
void CZipStorage::Write(const void *pBuf, DWORD iSize, bool bAtOnce)
{
if (!IsSegmented())
WriteInternalBuffer((char*)pBuf, iSize);
else
{
// if not at once, one byte is enough free space
DWORD iNeeded = bAtOnce ? iSize : 1;
DWORD uTotal = 0;
while (uTotal < iSize)
{
ZIP_SIZE_TYPE uFree = AssureFree(iNeeded);
DWORD uLeftToWrite = iSize - uTotal;
DWORD uToWrite = uFree < uLeftToWrite ? (DWORD)uFree : uLeftToWrite;
WriteInternalBuffer((char*)pBuf + uTotal, uToWrite);
if (bAtOnce)
return;
else
uTotal += uToWrite;
}
}
}
void CZipStorage::WriteInternalBuffer(const char *pBuf, DWORD uSize)
{
DWORD uWritten = 0;
while (uWritten < uSize)
{
DWORD uFreeInBuffer = GetFreeInBuffer();
if (uFreeInBuffer == 0)
{
Flush();
uFreeInBuffer = m_pWriteBuffer.GetSize();
}
DWORD uLeftToWrite = uSize - uWritten;
DWORD uToCopy = uLeftToWrite < uFreeInBuffer ? uLeftToWrite : uFreeInBuffer;
memcpy((char*)m_pWriteBuffer + m_uBytesInWriteBuffer, pBuf + uWritten, uToCopy);
uWritten += uToCopy;
m_uBytesInWriteBuffer += uToCopy;
}
}
ZIP_SIZE_TYPE CZipStorage::VolumeLeft() const
{
// for spanned archives m_uCurrentVolSize is updated after each flush()
ZIP_SIZE_TYPE uBytes = m_uBytesInWriteBuffer + (IsSpanned() ? 0 : m_uBytesWritten);
return uBytes > m_uCurrentVolSize ? 0 : m_uCurrentVolSize - uBytes;
}
void CZipStorage::Flush()
{
if (m_uBytesInWriteBuffer)
{
m_pFile->Write(m_pWriteBuffer, m_uBytesInWriteBuffer);
if (m_iSegmMode != noSegments)
m_uBytesWritten += m_uBytesInWriteBuffer;
m_uBytesInWriteBuffer = 0;
}
if (IsSpanned())
// after writing it is difficult to predict the free space due to
// not completly written clusters, write operation may start from a new cluster
m_uCurrentVolSize = GetFreeVolumeSpace();
}
ZIP_FILE_USIZE CZipStorage::LocateSignature(char* szSignature, ZIP_SIZE_TYPE uMaxDepth)
{
const int recordSize = 4;
CZipAutoBuffer buffer(m_iLocateBufferSize);
ZIP_FILE_USIZE uFileLength = m_pFile->GetLength();
ZIP_SIZE_TYPE max = (ZIP_SIZE_TYPE)(uFileLength < uMaxDepth ? uFileLength : uMaxDepth);
ZIP_SIZE_TYPE position = (ZIP_SIZE_TYPE)(uFileLength - m_pFile->GetPosition());
int offset = 0;
int leftToFind = recordSize - 1;
int toRead = m_iLocateBufferSize;
bool found = false; // for fast checking if leftToFind needs resetting
while ( position < max )
{
position += toRead;
if ( position > max )
{
int diff = (int) ( position - max );
toRead -= diff;
offset = diff;
position = max;
}
Seek(position, seekFromEnd);
int actuallyRead = m_pFile->Read((char*)buffer + offset, toRead);
if (actuallyRead != toRead)
ThrowError(CZipException::badZipFile);
int pos = m_iLocateBufferSize - 1;
while ( pos >= offset )
{
if ( buffer[pos] == szSignature[leftToFind] )
{
if ( leftToFind == 0 )
return (ZIP_FILE_USIZE)(uFileLength - ( position - ( pos - offset ) ));
if ( !found )
found = true;
leftToFind--;
pos--;
}
else if ( found )
{
leftToFind = recordSize - 1;
found = false;
// do not decrease position, the current pos may be the first to find
}
else
pos--;
}
}
return SignatureNotFound;
}
ULONGLONG CZipStorage::Seek(ULONGLONG lOff, SeekType iSeekType)
{
if (iSeekType == seekCurrent)
{
ZIP_SIZE_TYPE uPosition = (ZIP_SIZE_TYPE)m_pFile->GetPosition();
if (IsSegmented() == -1)
{
ZIP_FILE_USIZE uLength = m_pFile->GetLength();
while (uPosition + lOff >= uLength)
{
ZIP_SIZE_TYPE uCanSeek = (ZIP_SIZE_TYPE)(uLength - uPosition);
lOff -= uCanSeek;
ChangeVolume();
uPosition = 0;
uLength = m_pFile->GetLength();
}
return lOff > 0 ? m_pFile->Seek((ZIP_FILE_USIZE)lOff) : 0;
}
else
return m_pFile->Seek((ZIP_FILE_SIZE)lOff, CZipAbstractFile::current);
}
else
{
if (m_uCurrentVolume == 0 && m_uBytesBeforeZip > 0)
lOff += m_uBytesBeforeZip;
return m_pFile->Seek((ZIP_FILE_USIZE)lOff, iSeekType == seekFromBeginning);
}
}
void CZipStorage::FinalizeSegm()
{
ASSERT(IsSegmented() == 1); // spanned archive in creation
ASSERT(!m_bInMemory);
CZipString szFileName;
if (IsSplit() && m_bNewSegm)
szFileName = RenameLastFileInSplitArchive();
else
{
szFileName = m_pFile->GetFilePath();
// the file is already closed
m_pFile->Close();
}
m_bNewSegm = false;
if (m_uCurrentVolume == 0) // one-volume segmented archive was converted to normal archive
m_iSegmMode = noSegments;
else
m_uSplitData = m_uCurrentVolume;
OpenFile(szFileName, CZipFile::modeNoTruncate | (m_iSegmMode == noSegments ? CZipFile::modeReadWrite : CZipFile::modeRead));
}