diff options
| author | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2008-06-08 11:04:43 +0200 |
|---|---|---|
| committer | Tomas Bzatek <tbzatek@users.sourceforge.net> | 2008-06-08 11:04:43 +0200 |
| commit | 16f738ecee689c6feb2acb7e4ef4d9bb4144ae7d (patch) | |
| tree | 3d22f54f7298f81b18ed66d05a62fa8bfab359ab /zip/ZipArchive/ZipStorage.cpp | |
| download | tuxcmd-modules-release-0.6.36-dev.tar.xz | |
Initial commitv0.6.36release-0.6.36-dev
Diffstat (limited to 'zip/ZipArchive/ZipStorage.cpp')
| -rw-r--r-- | zip/ZipArchive/ZipStorage.cpp | 615 |
1 files changed, 615 insertions, 0 deletions
diff --git a/zip/ZipArchive/ZipStorage.cpp b/zip/ZipArchive/ZipStorage.cpp new file mode 100644 index 0000000..d416fd6 --- /dev/null +++ b/zip/ZipArchive/ZipStorage.cpp @@ -0,0 +1,615 @@ +////////////////////////////////////////////////////////////////////////////////
+// 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 = ⁡
+ 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));
+}
+
|
