From 02d6107c97b48888362e7c6a70dcac323c89d741 Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Sun, 17 Dec 2023 21:23:58 +0100 Subject: ZipArchive: Update to the 4.6.9 release --- zip/ZipArchive/ZipStorage.h | 472 +++++++++++++++++++++++++++++--------------- 1 file changed, 316 insertions(+), 156 deletions(-) (limited to 'zip/ZipArchive/ZipStorage.h') diff --git a/zip/ZipArchive/ZipStorage.h b/zip/ZipArchive/ZipStorage.h index 686751c..cc1569e 100644 --- a/zip/ZipArchive/ZipStorage.h +++ b/zip/ZipArchive/ZipStorage.h @@ -1,6 +1,6 @@ //////////////////////////////////////////////////////////////////////////////// -// This source file is part of the ZipArchive library source distribution and -// is Copyrighted 2000 - 2007 by Artpol Software - Tadeusz Dracz +// This source file is part of the ZipArchive Library Open Source distribution +// and is Copyrighted 2000 - 2022 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 @@ -9,7 +9,7 @@ // // For the licensing details refer to the License.txt file. // -// Web Site: http://www.artpol-software.com +// Web Site: https://www.artpol-software.com //////////////////////////////////////////////////////////////////////////////// /** @@ -35,48 +35,36 @@ #include "ZipMemFile.h" #include "ZipExport.h" #include "ZipCallback.h" - - +#include "BitFlag.h" +#include "ZipSplitNamesHandler.h" +#include "ZipException.h" +#include "ZipCollections.h" /** Represents the storage layer for an archive. */ -class ZIP_API CZipStorage +class ZIP_API CZipStorage { friend class CZipArchive; friend class CZipCentralDir; public: - /** - The type of the segmentation of the archive. - - \see - 0610051553 - \see - CZipArchive::GetSegmMode + Storage state. */ - enum ZipSegmentationMode + enum State { - noSegments, ///< No archive segmentation. - spannedArchive, ///< A spanned archive. - splitArchive, ///< A split archive. - - /** - The archive segmentation type will be auto-detected. - If the archive is on the removable device, - assume a spanned archive, otherwise assume a split archive. - */ - suggestedAuto, - - /** - If a segmented archive is on a removable device, assume a split archive. - Normally you create spanned archives on removable devices. - */ - suggestedSplit - }; + stateOpened = 0x0001, ///< The storage file is opened. + stateReadOnly = 0x0002, ///< The storage file is opened as read-only. + stateAutoClose = 0x0004, ///< The storage file will be closed when the storage is closed. + stateExisting = 0x0008, ///< The storage file existed before opening. + stateSegmented = 0x0010, ///< The current archive is segmented. + stateSplit = stateSegmented | 0x0020, ///< The current archive is split. + stateBinarySplit = stateSplit | 0x0040, ///< The current archive is binary split. + stateSpan = stateSegmented | 0x0080 ///< The current archive is spanned. + }; /** - The direction of seeking operation. + The direction of the seeking operation. \see CZipStorage::Seek @@ -86,20 +74,23 @@ public: seekFromBeginning, ///< Start seeking from the beginning of a file. seekFromEnd, ///< Start seeking from the end of a file. /** - Start seeking from the current position in an archive. + Start seeking from the current position in the archive. This value can cause a volume change when a segmented archive is opened for reading. */ seekCurrent }; + + static const ZIP_FILE_USIZE SignatureNotFound; + CZipStorage(); virtual ~CZipStorage(); void Initialize(); /** Opens a new or existing archive in memory. - The meaning for the parameters is the same as in the CZipArchive::Open(CZipAbstractFile& , int) method. + The meaning for the parameters is the same as in the CZipArchive::Open(CZipAbstractFile& , int, bool) method. */ - void Open(CZipAbstractFile& af, int iMode); + void Open(CZipAbstractFile& af, int iMode, bool bAutoClose); /** Opens or creates an archive. @@ -108,9 +99,35 @@ public: */ void Open(LPCTSTR lpszPathName, int iMode, ZIP_SIZE_TYPE uVolumeSize); + + /** + Gets the expected path of a volume name in a split archive. + + \param uVolume + The volume's zero-based index. + + \param bLast + If \c true, the volume is expected to be the last volume; \c false otherwise. + + \return + The expected path of a volume name in a split archive. + */ + CZipString GetSplitVolumeName(ZIP_VOLUME_TYPE uVolume, bool bLast) + { + if (m_pSplitNames == NULL) + { + ThrowError(CZipException::genericError); + return _T(""); // for Code Analysis + } + int flags = bLast ? CZipSplitNamesHandler::flLast : CZipSplitNamesHandler::flNone; + if (IsExisting()) + flags |= CZipSplitNamesHandler::flExisting; + return m_pSplitNames->GetVolumeName(m_szArchiveName, (ZIP_VOLUME_TYPE)(uVolume + 1), flags); + } /** - Closes a segmented archive in creation and reopens it as an existing segmented archive (no modifications allowed). + Closes a segmented archive in creation and reopens it as an existing segmented archive. + No modifications are allowed afterwards. The archive may also turn out to be a not segmented archive. */ void FinalizeSegm(); @@ -120,10 +137,7 @@ public: Called only by CZipCentralDir::Read when opening an existing archive. \param uLastVolume - The number of the volme the central directory is on. - - \note Throws exceptions. - + The number of the volume the central directory is on. */ void UpdateSegmMode(ZIP_VOLUME_TYPE uLastVolume); @@ -136,8 +150,6 @@ public: \return The number of free bytes on the current volume. - \note - Throws exceptions. */ ZIP_SIZE_TYPE AssureFree(ZIP_SIZE_TYPE uNeeded); @@ -154,13 +166,11 @@ public: If \c true, the whole chunk must fit in the current volume. If there is not enough free space, a volume change is performed. - \note - Throws exceptions. */ void Write(const void *pBuf, DWORD iSize, bool bAtOnce); /** - Gets the total size currently occupied by the archive. + Returns the total size currently occupied by the archive. \return The length of the current archive file increased by the number of bytes in the write buffer. @@ -176,9 +186,10 @@ public: bool IsClosed(bool bArchive) const { if (bArchive) - return GetCurrentVolume() == ZIP_VOLUME_NUMBER_UNSPECIFIED; + return !m_state.IsSetAny(stateOpened); else - return !m_pFile || !m_bInMemory && m_pFile->IsClosed(); + // assume not auto-close files always opened + return !m_pFile || (m_state.IsSetAny(stateAutoClose) && m_pFile->IsClosed()); } /** @@ -191,29 +202,38 @@ public: The number of bytes to read. \param bAtOnce - If \c true, the specified number of bytes must be read - from the same volume (no volume change is allowed). + If \c true, no volume change is allowed during reading. + If the requested number of bytes cannot be read from a single volume, an exception is thrown. - \note - Throws exceptions. */ DWORD Read(void* pBuf, DWORD iSize, bool bAtOnce); /** - Gets the position in the file, taking into account the number of bytes in the write buffer - and the number of bytes before the archive. + Returns the position in the file, taking into account the number of bytes in the write buffer + and the number of bytes before the archive. \return The position in the file. \note - Throws exceptions. + For binary split archives, it returns the position from the beginning of the first part. */ ZIP_SIZE_TYPE GetPosition() const { ZIP_SIZE_TYPE uPos = (ZIP_SIZE_TYPE)(m_pFile->GetPosition()) + m_uBytesInWriteBuffer; if (m_uCurrentVolume == 0) uPos -= m_uBytesBeforeZip; + else if (IsBinarySplit()) // not for the first volume + { + ZIP_VOLUME_TYPE uVolume = m_uCurrentVolume; + ASSERT(m_pCachedSizes->GetSize() > (ZIP_ARRAY_SIZE_TYPE)(uVolume - 1)); + do + { + uVolume--; + uPos += (ZIP_SIZE_TYPE)m_pCachedSizes->GetAt((ZIP_ARRAY_SIZE_TYPE)uVolume); + } + while (uVolume > 0); + } return uPos; } @@ -221,8 +241,6 @@ public: /** Flushes the data from the read buffer to the disk. - \note - Throws exceptions. */ void Flush(); @@ -232,7 +250,7 @@ public: */ void FlushFile() { - if (!m_bInMemory && !IsReadOnly()) + if (!IsReadOnly()) m_pFile->Flush(); } @@ -248,14 +266,12 @@ public: \param uNeeded The number of bytes needed in the volume. - \note - Throws exceptions. */ void NextVolume(ZIP_SIZE_TYPE uNeeded); /** - Gets a zero-based number of the current volume. + Returns a zero-based number of the current volume. */ ZIP_VOLUME_TYPE GetCurrentVolume() const {return m_uCurrentVolume;} @@ -277,46 +293,156 @@ public: } /** - Detects the segmentation mode. - - \return - - \c -1 : An existing segmented archive is opened. - - \c 0 : The archive is not segmented. - - \c 1 : A segmented archive in creation. + Changes the current volume to the previous volume during extract operations. */ - int IsSegmented() const + void ChangeVolumeDec() { - return m_iSegmMode == noSegments ? 0 : (m_bNewSegm ? 1 : -1); + if (m_uCurrentVolume == 0) + ThrowError(CZipException::badZipFile); + ChangeVolume((ZIP_VOLUME_TYPE)(m_uCurrentVolume - 1)); } - + /** - Checks, if the archive is a split archive. + Returns the value indicating whether the archive is a split archive (binary or regular). \return \c true, if the archive is a split archive; \c false otherwise. */ bool IsSplit() const { - return m_iSegmMode == splitArchive; + return m_state.IsSetAll(stateSplit); + } + + /** + Returns the value indicating whether the archive is a binary split archive. + + \return + \c true, if the archive is a binary split archive; \c false otherwise. + */ + bool IsBinarySplit() const + { + return m_state.IsSetAll(stateBinarySplit); + } + + /** + Returns the value indicating whether the archive is a regular split archive (not binary). + + \return + \c true, if the archive is a regular split archive; \c false otherwise. + */ + bool IsRegularSplit() const + { + return m_state.IsSetAll(stateSplit) && !m_state.IsSetAll(stateBinarySplit); } /** - Checks, if the archive is a spanned archive. + Returns the value indicating whether the archive is a spanned archive. \return \c true, if the archive is a spanned archive; \c false otherwise. */ bool IsSpanned() const { - return m_iSegmMode == spannedArchive; + return m_state.IsSetAll(stateSpan); } /** The same as the CZipArchive::IsReadOnly method. */ - bool IsReadOnly() + bool IsReadOnly() const { - return m_bReadOnly || IsSegmented() < 0; + return m_state.IsSetAny(stateReadOnly) || IsExistingSegmented(); + } + + /** + Returns the value indicating whether the archive is an existing segmented archive. + + \return + \c true, if the archive is an existing segmented archive; \c false otherwise. + */ + bool IsExistingSegmented() const + { + return m_state.IsSetAll(stateSegmented | stateExisting); + } + + /** + Returns the value indicating whether the archive is a new segmented archive. + + \return + \c true, if the archive is a new segmented archive; \c false otherwise. + */ + bool IsNewSegmented() const + { + return m_state.IsSetAny(stateSegmented) && !IsExisting(); + } + + /** + Returns the value indicating whether the archive is a segmented archive. + + \return + \c true, if the archive is a segmented archive; \c false otherwise. + */ + bool IsSegmented() const + { + return m_state.IsSetAny(stateSegmented); + } + + /** + Returns the value indicating whether the archive is an existing archive. + + \return + \c true, if the archive is an existing archive; \c false, if the archive is a new archive. + */ + bool IsExisting() const + { + return m_state.IsSetAny(stateExisting); + } + + /** + Sets the split names handler. + + \see + CZipArchive::SetSplitNamesHandler(CZipSplitNamesHandler*, bool) + \see + CZipSplitNamesHandler + */ + bool SetSplitNamesHandler(CZipSplitNamesHandler* pNames, bool bAutoDelete) + { + if (m_state != 0) + { + ZIPTRACE("%s(%i) : The archive is already opened.\n"); + return false; + } + ClearSplitNames(); + m_pSplitNames = pNames; + m_bAutoDeleteSplitNames = bAutoDelete; + return true; + } + + /** + Returns the current split names handler. + + \return + The current split names handler. + \see + CZipSplitNamesHandler + */ + CZipSplitNamesHandler* GetSplitNamesHandler() + { + return m_pSplitNames; + } + + /** + Returns the current split names handler (const). + + \return + The current split names handler. + \see + CZipSplitNamesHandler + */ + const CZipSplitNamesHandler* GetSplitNamesHandler() const + { + return m_pSplitNames; } /** @@ -332,7 +458,19 @@ public: ULONGLONG Seek(ULONGLONG lOff, SeekType iSeekType = seekFromBeginning); /** - Gets the number of free bytes on the current volume. + Performs the seeking operation in a binary split archive. + + \param lOff + The offset to move the file pointer. + + \param bSeekToBegin + If \c true, the file pointer is moved to the beginning before seeking. + If \c false, the file pointer is moved relatively to the current position. + */ + void SeekInBinary(ZIP_FILE_SIZE lOff, bool bSeekToBegin = false); + + /** + Returns the number of free bytes on the current volume. \return The number of free bytes on the current volume. @@ -342,19 +480,20 @@ public: /** Closes the storage. - \param bAfterException - Set to \c true, if an exception was thrown before. + \param bWrite + Set to \c false, if the storage should not perform any write operations. + \param bGetLastVolumeName + Set to \c true, if the storage should return the path. \return - The file path of the archive. + The file path of the archive or of the last volume in the archive. + Only if \a bGetLastVolumeName is set to \c true. - \note - Throws exceptions. */ - CZipString Close(bool bAfterException); + CZipString Close(bool bWrite, bool bGetLastVolumeName = false); /** - Represents the physical storage of the current archive segment. + Represents the physical storage for the archive (or the current archive segment in segmented archives). */ CZipAbstractFile* m_pFile; @@ -363,6 +502,26 @@ public: */ static char m_gszExtHeaderSignat[]; + ZipArchiveLib::CBitFlag& GetState() + { + return m_state; + } + + /** + Reverse-finds the location of the given signature starting from the current position in file. + + \param szSignature + The signature to locate. + + \param uMaxDepth + The maximum number of bytes to search for \a szSignature. + + \return + The location of the signature. + + */ + ZIP_FILE_USIZE LocateSignature(char* szSignature, ZIP_SIZE_TYPE uMaxDepth); + protected: /** @@ -375,27 +534,9 @@ protected: { return (ZIP_SIZE_TYPE)m_pFile->GetLength() - m_uBytesBeforeZip; } - - /** - Reverse-finds the location of the given signature starting from the current position in file. - - \param szSignature - The signature to locate. - - \param uMaxDepth - The maximum number of bytes to search for \a szSignature. - - \return - The location of the signature. - - \note - Throws exceptions. - */ - ZIP_FILE_USIZE LocateSignature(char* szSignature, ZIP_SIZE_TYPE uMaxDepth); - /** - Flushes without writing. Can be used only on not segmented archives. + Flushes without writing. It can be used only on not segmented archives. */ void EmptyWriteBuffer() { @@ -420,7 +561,7 @@ protected: bool OpenFile(LPCTSTR lpszName, UINT uFlags, bool bThrow = true); /** - Renames the last segment file in a split archive when finalizing the whole archive. + Renames the last segment file in a split archive when finalizing the archive. \return The name of the last segment. @@ -436,13 +577,11 @@ protected: \param uSize The number of bytes to write. - \note - Throws exceptions. */ void WriteInternalBuffer(const char *pBuf, DWORD uSize); /** - Gets the free space size on the current removable disk. + Returns the free space size on the current removable disk. \return The free space in bytes. @@ -450,7 +589,7 @@ protected: ZIP_SIZE_TYPE GetFreeVolumeSpace() const; /** - Notifies the callback object. + Calls the segmented callback object. Throws an exception if the callback method returns \c false. \param uNeeded @@ -463,24 +602,11 @@ protected: The string to be used as a filename (as an argument in the CZipException::Throw method) when an exception must be thrown. - \note - Throws exceptions. - \see + \see CZipArchive::SetSegmCallback */ void CallCallback(ZIP_SIZE_TYPE uNeeded, int iCode, CZipString szTemp); - /** - Constructs the name of a segment in a split archive. - - \param bLast - Set it to \c true, if constructing the last volume name. - - \return - The segment name. - */ - CZipString GetSplitVolumeName(bool bLast) const; - /** Changes a file when processing a split archive. */ @@ -492,27 +618,22 @@ protected: CZipString ChangeSpannedRead(); /** - Gets the free space left in the write buffer. + Returns the free space left in the write buffer. \return - The free space left in the write buffer in bytes. + The free space in bytes. */ DWORD GetFreeInBuffer() const {return m_pWriteBuffer.GetSize() - m_uBytesInWriteBuffer;} /** The value it holds, depends on the current mode: - - An opened existing split archive - stores the number of the last volume ( the one with "zip" extension). - - A split archive in creation - the size of the volume. + - An opened existing split archive: the number of the last volume ( usually the one with the "zip" extension). + - A split archive in creation: the size of the volume. This method is used only when processing split archives. */ ZIP_SIZE_TYPE m_uSplitData; - /** - The extension of the last segment. - */ - CZipString m_szSplitExtension; - /** The number of bytes available in the write buffer. */ @@ -520,7 +641,7 @@ protected: /** The value it holds depends on the segmentation mode: - - A split archive : the total size of the current volume. + - A split archive: the total size of the current volume. - A spanned archive: the free space on the current volume. */ ZIP_SIZE_TYPE m_uCurrentVolSize; @@ -534,29 +655,14 @@ protected: Stores the number of bytes that have been written physically to the current segment. Used only when processing a segmented archive in creation. */ - ZIP_SIZE_TYPE m_uBytesWritten; - - /** - \c true, if the current archive is a new segmented archive; \c false otherwise. - */ - bool m_bNewSegm; + ZIP_SIZE_TYPE m_uBytesWritten; /** The current volume number in a segmented archive. The value is zero-based. */ ZIP_VOLUME_TYPE m_uCurrentVolume; - - /** - \c true when the archive is created in memory; \c false otherwise. - */ - bool m_bInMemory; - - /** - \c true if OpenMode::zipOpenReadOnly was specified when opening the archive. - */ - bool m_bReadOnly; - + /** The number of bytes before the actual zip archive in a file. \see @@ -579,12 +685,7 @@ protected: \see CZipArchive::SetAdvanced */ - int m_iLocateBufferSize; - - /** - Takes one of the #ZipSegmentationMode values. - */ - int m_iSegmMode; + int m_iLocateBufferSize; /** A callback object called when there is a need for a volume change @@ -603,12 +704,71 @@ protected: CZipArchive::SetSegmCallback */ CZipSegmCallback* m_pSplitChangeVolumeFunc; + private: + ZIP_FILE_USIZE LocateSignature(char* szSignature, ZIP_SIZE_TYPE uMaxDepth, int& leftToFind, bool& found, ZIP_FILE_USIZE uFileLength); + + CZipString GetSplitVolumeName(bool bLast) + { + return GetSplitVolumeName(m_uCurrentVolume, bLast); + } + + void ClearSplitNames() + { + if (m_pSplitNames) + { + if (m_bAutoDeleteSplitNames) + delete m_pSplitNames; + m_pSplitNames = NULL; + m_bAutoDeleteSplitNames = false; + } + } + + void ClearCachedSizes() + { + if (m_pCachedSizes) + { + delete m_pCachedSizes; + m_pCachedSizes = NULL; + } + } + + void EnsureSplitNames() + { + if (IsSplit()) + { + if (m_pSplitNames == NULL) + { + m_bAutoDeleteSplitNames = true; + if (m_state.IsSetAll(stateBinarySplit)) + m_pSplitNames = new CZipBinSplitNamesHandler(); + else + m_pSplitNames = new CZipRegularSplitNamesHandler(); + } + m_pSplitNames->Initialize(m_szArchiveName); + } + } + + ZIP_FILE_USIZE GetCachedSize(ZIP_VOLUME_TYPE uVolume) + { + ASSERT(m_pCachedSizes); + if (m_pCachedSizes->GetSize() > (ZIP_ARRAY_SIZE_TYPE)uVolume) + return m_pCachedSizes->GetAt((ZIP_ARRAY_SIZE_TYPE)uVolume); + ThrowError(CZipException::genericError); + // for a compiler + return 0; + } + + void CacheSizes(); + + ZipArchiveLib::CBitFlag m_state; CZipSegmCallback* m_pChangeVolumeFunc; CZipString m_szArchiveName; CZipFile m_internalfile; - static const ZIP_FILE_USIZE SignatureNotFound; - void ThrowError(int err); + CZipSplitNamesHandler* m_pSplitNames; + CZipArray* m_pCachedSizes; + bool m_bAutoDeleteSplitNames; + void ThrowError(int err) const; }; #if (_MSC_VER > 1000) && (defined ZIP_HAS_DLL) -- cgit v1.2.3