From ee8b50877ffd066078a3805bbadfda13894cd9ea Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 3 Nov 2024 08:50:42 +0100 Subject: [PATCH 01/31] Update project version in CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2eb86345..5378bb34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required( VERSION 3.14 ) project( bit7z - VERSION 4.0.8 + VERSION 4.0.9 DESCRIPTION "A C++ static library offering a clean and simple interface to the 7-zip/p7zip shared libraries" HOMEPAGE_URL "https://github.com/rikyoz/bit7z/" ) set( CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON" ) From 6e135d9b6a29934ef4b2250be4e7f44ad7345748 Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 3 Nov 2024 08:55:55 +0100 Subject: [PATCH 02/31] Update CPM.cmake to v0.40.2 --- cmake/Dependencies.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index 49498016..d853052e 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -1,6 +1,6 @@ # Downloading the CPM.cmake package manager -set( CPM_DOWNLOAD_VERSION 0.38.6 ) -set( CPM_DOWNLOAD_HASH 11c3fa5f1ba14f15d31c2fb63dbc8628ee133d81c8d764caad9a8db9e0bacb07 ) +set( CPM_DOWNLOAD_VERSION 0.40.2 ) +set( CPM_DOWNLOAD_HASH "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d" ) set( CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake" ) if( NOT ( EXISTS ${CPM_DOWNLOAD_LOCATION} ) ) message( STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}" ) From 919710f8371e607f33e0f2a764515b9272819e07 Mon Sep 17 00:00:00 2001 From: Oz Date: Wed, 21 Aug 2024 21:55:36 +0200 Subject: [PATCH 03/31] Update README badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3e6d9fc7..992f27f3 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@
- GitHub releaseC++14WindowsLinuxmacOSx86, x64, arm, arm64donatedocsBuild status + GitHub releaseC++14WindowsLinuxmacOSBSDx86, x64, arm, arm64donatedocsBuild status
- MSVC 2015+GCC 4.9+Clang 3.6+CodeFactor GradeLicense + MSVC 2015+GCC 4.9+Clang 3.6+CodeFactor GradeLicense
## ⚡️ Introduction From ffe0a94351c089682289ca72e725aa98db059dc1 Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 3 Nov 2024 09:53:26 +0100 Subject: [PATCH 04/31] Add support for checking archive start at the beginning of a file --- include/bit7z/bitarchivereader.hpp | 69 ++++++++++++++++++++++++++++-- include/bit7z/bitarchivewriter.hpp | 48 +++++++++++++++++++++ include/bit7z/bitinputarchive.hpp | 58 +++++++++++++++++-------- include/bit7z/bitoutputarchive.hpp | 16 +++++-- src/bitarchiveeditor.cpp | 4 +- src/bitarchivereader.cpp | 23 +++++++++- src/bitarchivewriter.cpp | 26 ++++++++++- src/bitinputarchive.cpp | 36 +++++++++++----- src/bitoutputarchive.cpp | 23 ++++++---- 9 files changed, 257 insertions(+), 46 deletions(-) diff --git a/include/bit7z/bitarchivereader.hpp b/include/bit7z/bitarchivereader.hpp index dcc7fe2a..f0b4bdcc 100644 --- a/include/bit7z/bitarchivereader.hpp +++ b/include/bit7z/bitarchivereader.hpp @@ -36,11 +36,53 @@ class BitArchiveReader final : public BitAbstractArchiveOpener, public BitInputA * * @param lib the 7z library used. * @param inArchive the path to the archive to be read. + * @param archiveStart whether to search for the archive's start throughout the entire file + * or only at the beginning. * @param format the format of the input archive. - * @param password the password needed for opening the input archive. + * @param password (optional) the password needed for opening the input archive. */ BitArchiveReader( const Bit7zLibrary& lib, const tstring& inArchive, + ArchiveStartOffset archiveStart, + const BitInFormat& format BIT7Z_DEFAULT_FORMAT, + const tstring& password = {} ); + + /** + * @brief Constructs a BitArchiveReader object, opening the input file archive. + * + * @note When bit7z is compiled using the `BIT7Z_AUTO_FORMAT` option, the format + * argument has the default value BitFormat::Auto (automatic format detection of the input archive). + * On the contrary, when `BIT7Z_AUTO_FORMAT` is not defined (i.e., no auto format detection available), + * the format argument must be specified. + * + * @param lib the 7z library used. + * @param inArchive the path to the archive to be read. + * @param format the format of the input archive. + * @param password (optional) the password needed for opening the input archive. + */ + BitArchiveReader( const Bit7zLibrary& lib, + const tstring& inArchive, + const BitInFormat& format BIT7Z_DEFAULT_FORMAT, + const tstring& password = {} ); + + /** + * @brief Constructs a BitArchiveReader object, opening the archive in the input buffer. + * + * @note When bit7z is compiled using the `BIT7Z_AUTO_FORMAT` option, the format + * argument has the default value BitFormat::Auto (automatic format detection of the input archive). + * On the contrary, when `BIT7Z_AUTO_FORMAT` is not defined (i.e., no auto format detection available), + * the format argument must be specified. + * + * @param lib the 7z library used. + * @param inArchive the input buffer containing the archive to be read. + * @param archiveStart whether to search for the archive's start throughout the entire file + * or only at the beginning. + * @param format the format of the input archive. + * @param password (optional) the password needed for opening the input archive. + */ + BitArchiveReader( const Bit7zLibrary& lib, + const buffer_t& inArchive, + ArchiveStartOffset archiveStart, const BitInFormat& format BIT7Z_DEFAULT_FORMAT, const tstring& password = {} ); @@ -55,13 +97,34 @@ class BitArchiveReader final : public BitAbstractArchiveOpener, public BitInputA * @param lib the 7z library used. * @param inArchive the input buffer containing the archive to be read. * @param format the format of the input archive. - * @param password the password needed for opening the input archive. + * @param password (optional) the password needed for opening the input archive. */ BitArchiveReader( const Bit7zLibrary& lib, const std::vector< byte_t >& inArchive, const BitInFormat& format BIT7Z_DEFAULT_FORMAT, const tstring& password = {} ); + /** + * @brief Constructs a BitArchiveReader object, opening the archive from the standard input stream. + * + * @note When bit7z is compiled using the `BIT7Z_AUTO_FORMAT` option, the format + * argument has the default value BitFormat::Auto (automatic format detection of the input archive). + * On the contrary, when `BIT7Z_AUTO_FORMAT` is not defined (i.e., no auto format detection available), + * the format argument must be specified. + * + * @param lib the 7z library used. + * @param inArchive the standard input stream of the archive to be read. + * @param archiveStart whether to search for the archive's start throughout the entire file + * or only at the beginning. + * @param format the format of the input archive. + * @param password (optional) the password needed for opening the input archive. + */ + BitArchiveReader( const Bit7zLibrary& lib, + std::istream& inArchive, + ArchiveStartOffset archiveStart, + const BitInFormat& format BIT7Z_DEFAULT_FORMAT, + const tstring& password = {} ); + /** * @brief Constructs a BitArchiveReader object, opening the archive from the standard input stream. * @@ -73,7 +136,7 @@ class BitArchiveReader final : public BitAbstractArchiveOpener, public BitInputA * @param lib the 7z library used. * @param inArchive the standard input stream of the archive to be read. * @param format the format of the input archive. - * @param password the password needed for opening the input archive. + * @param password (optional) the password needed for opening the input archive. */ BitArchiveReader( const Bit7zLibrary& lib, std::istream& inArchive, diff --git a/include/bit7z/bitarchivewriter.hpp b/include/bit7z/bitarchivewriter.hpp index 1b53ca80..080b62f6 100644 --- a/include/bit7z/bitarchivewriter.hpp +++ b/include/bit7z/bitarchivewriter.hpp @@ -32,11 +32,43 @@ class BitArchiveWriter : public BitAbstractArchiveCreator, public BitOutputArchi * * @param lib the 7z library to use. * @param inArchive the path to an input archive file. + * @param startOffset whether to search for the archive's start throughout the entire file + * or only at the beginning. * @param format the input/output archive format. * @param password (optional) the password needed to read the input archive. */ BitArchiveWriter( const Bit7zLibrary& lib, const tstring& inArchive, + ArchiveStartOffset startOffset, + const BitInOutFormat& format, + const tstring& password = {} ); + + /** + * @brief Constructs a BitArchiveWriter object, reading the given archive file path. + * + * @param lib the 7z library to use. + * @param inArchive the path to an input archive file. + * @param format the input/output archive format. + * @param password (optional) the password needed to read the input archive. + */ + BitArchiveWriter( const Bit7zLibrary& lib, + const tstring& inArchive, + const BitInOutFormat& format, + const tstring& password = {} ); + + /** + * @brief Constructs a BitArchiveWriter object, reading the archive in the given buffer. + * + * @param lib the 7z library to use. + * @param inArchive the buffer containing the input archive. + * @param startOffset whether to search for the archive's start throughout the entire file + * or only at the beginning. + * @param format the input/output archive format. + * @param password (optional) the password needed to read the input archive. + */ + BitArchiveWriter( const Bit7zLibrary& lib, + const buffer_t& inArchive, + ArchiveStartOffset startOffset, const BitInOutFormat& format, const tstring& password = {} ); @@ -53,6 +85,22 @@ class BitArchiveWriter : public BitAbstractArchiveCreator, public BitOutputArchi const BitInOutFormat& format, const tstring& password = {} ); + /** + * @brief Constructs a BitArchiveWriter object, reading the archive from the given standard input stream. + * + * @param lib the 7z library to use. + * @param inArchive the standard stream of the input archive. + * @param startOffset whether to search for the archive's start throughout the entire file + * or only at the beginning. + * @param format the input/output archive format. + * @param password (optional) the password needed to read the input archive. + */ + BitArchiveWriter( const Bit7zLibrary& lib, + std::istream& inArchive, + ArchiveStartOffset startOffset, + const BitInOutFormat& format, + const tstring& password = {} ); + /** * @brief Constructs a BitArchiveWriter object, reading the archive from the given standard input stream. * diff --git a/include/bit7z/bitinputarchive.hpp b/include/bit7z/bitinputarchive.hpp index e83545f5..9e630465 100644 --- a/include/bit7z/bitinputarchive.hpp +++ b/include/bit7z/bitinputarchive.hpp @@ -25,6 +25,12 @@ namespace bit7z { using std::vector; +enum struct ArchiveStartOffset : std::uint8_t { + None, ///< Don't specify an archive start offset. For some formats, like Zip archives, + ///< this means that the whole input file will be searched for the archive's start. + FileStart ///< Check only the file start for the archive's start. +}; + /** * @brief The BitInputArchive class, given a handler object, allows reading/extracting the content of archives. */ @@ -33,38 +39,54 @@ class BitInputArchive { /** * @brief Constructs a BitInputArchive object, opening the input file archive. * - * @param handler the reference to the BitAbstractArchiveHandler object containing all the settings to - * be used for reading the input archive - * @param inFile the path to the input archive file + * @param handler the reference to the BitAbstractArchiveHandler object containing all the settings to + * be used for reading the input archive + * @param inFile the path to the input archive file + * @param startOffset (optional) specifies whether to search for the archive's start throughout the + * entire file or only at the beginning. The default behavior is to search at the beginning. */ - BitInputArchive( const BitAbstractArchiveHandler& handler, const tstring& inFile ); + BitInputArchive( const BitAbstractArchiveHandler& handler, + const tstring& inFile, + ArchiveStartOffset startOffset = ArchiveStartOffset::None ); /** * @brief Constructs a BitInputArchive object, opening the input file archive. * - * @param handler the reference to the BitAbstractArchiveHandler object containing all the settings to - * be used for reading the input archive - * @param arcPath the path to the input archive file + * @param handler the reference to the BitAbstractArchiveHandler object containing all the settings to + * be used for reading the input archive + * @param arcPath the path to the input archive file + * @param startOffset (optional) whether to search for the archive's start throughout the entire file + * or only at the beginning. The default behavior is to search at the beginning. */ - BitInputArchive( const BitAbstractArchiveHandler& handler, const fs::path& arcPath ); + BitInputArchive( const BitAbstractArchiveHandler& handler, + const fs::path& arcPath, + ArchiveStartOffset startOffset = ArchiveStartOffset::None ); /** * @brief Constructs a BitInputArchive object, opening the archive given in the input buffer. * - * @param handler the reference to the BitAbstractArchiveHandler object containing all the settings to - * be used for reading the input archive - * @param inBuffer the buffer containing the input archive + * @param handler the reference to the BitAbstractArchiveHandler object containing all the settings to + * be used for reading the input archive + * @param inBuffer the buffer containing the input archive + * @param startOffset (optional) whether to search for the archive's start throughout the entire file + * or only at the beginning. The default behavior is to search at the beginning. */ - BitInputArchive( const BitAbstractArchiveHandler& handler, const std::vector< byte_t >& inBuffer ); + BitInputArchive( const BitAbstractArchiveHandler& handler, + const buffer_t& inBuffer, + ArchiveStartOffset startOffset = ArchiveStartOffset::None ); /** * @brief Constructs a BitInputArchive object, opening the archive by reading the given input stream. * - * @param handler the reference to the BitAbstractArchiveHandler object containing all the settings to - * be used for reading the input archive - * @param inStream the standard input stream of the input archive + * @param handler the reference to the BitAbstractArchiveHandler object containing all the settings to + * be used for reading the input archive + * @param inStream the standard input stream of the input archive + * @param startOffset (optional) whether to search for the archive's start throughout the entire file + * or only at the beginning. The default behavior is to search at the beginning. */ - BitInputArchive( const BitAbstractArchiveHandler& handler, std::istream& inStream ); + BitInputArchive( const BitAbstractArchiveHandler& handler, + std::istream& inStream, + ArchiveStartOffset startOffset = ArchiveStartOffset::None ); BitInputArchive( const BitInputArchive& ) = delete; @@ -275,7 +297,8 @@ class BitInputArchive { const BitAbstractArchiveHandler& mArchiveHandler; tstring mArchivePath; - auto openArchiveStream( const fs::path& name, IInStream* inStream ) -> IInArchive*; + BIT7Z_NODISCARD + auto openArchiveStream( const fs::path& name, IInStream* inStream, ArchiveStartOffset startOffset ) -> IInArchive*; public: /** @@ -397,6 +420,7 @@ class BitInputArchive { * @return the item at the given index within the archive. */ BIT7Z_NODISCARD auto itemAt( uint32_t index ) const -> BitArchiveItemOffset; + }; } // namespace bit7z diff --git a/include/bit7z/bitoutputarchive.hpp b/include/bit7z/bitoutputarchive.hpp index a9175d41..5f49d568 100644 --- a/include/bit7z/bitoutputarchive.hpp +++ b/include/bit7z/bitoutputarchive.hpp @@ -78,7 +78,9 @@ class BitOutputArchive { * be used for creating the new archive and reading the (optional) input archive. * @param inFile (optional) the path to an input archive file. */ - explicit BitOutputArchive( const BitAbstractArchiveCreator& creator, const tstring& inFile ); + explicit BitOutputArchive( const BitAbstractArchiveCreator& creator, + const tstring& inFile, + ArchiveStartOffset startOffset = ArchiveStartOffset::None ); /** * @brief Constructs a BitOutputArchive object, opening an input file archive from the given buffer. @@ -91,7 +93,9 @@ class BitOutputArchive { * be used for creating the new archive and reading the (optional) input archive. * @param inBuffer the buffer containing an input archive file. */ - BitOutputArchive( const BitAbstractArchiveCreator& creator, const std::vector< byte_t >& inBuffer ); + BitOutputArchive( const BitAbstractArchiveCreator& creator, + const buffer_t& inBuffer, + ArchiveStartOffset startOffset = ArchiveStartOffset::None ); /** * @brief Constructs a BitOutputArchive object, reading an input file archive from the given std::istream. @@ -100,7 +104,9 @@ class BitOutputArchive { * be used for creating the new archive and reading the (optional) input archive. * @param inStream the standard input stream of the input archive file. */ - BitOutputArchive( const BitAbstractArchiveCreator& creator, std::istream& inStream ); + BitOutputArchive( const BitAbstractArchiveCreator& creator, + std::istream& inStream, + ArchiveStartOffset startOffset = ArchiveStartOffset::None ); BitOutputArchive( const BitOutputArchive& ) = delete; @@ -344,7 +350,9 @@ class BitOutputArchive { auto initOutFileStream( const fs::path& outArchive, bool updatingArchive ) const -> CMyComPtr< IOutStream >; - BitOutputArchive( const BitAbstractArchiveCreator& creator, const fs::path& inArc ); + BitOutputArchive( const BitAbstractArchiveCreator& creator, + const fs::path& inArc, + ArchiveStartOffset archiveStart ); void compressToFile( const fs::path& outFile, UpdateCallback* updateCallback ); diff --git a/src/bitarchiveeditor.cpp b/src/bitarchiveeditor.cpp index 2bbe1f67..ae0efe93 100644 --- a/src/bitarchiveeditor.cpp +++ b/src/bitarchiveeditor.cpp @@ -28,7 +28,7 @@ BitArchiveEditor::BitArchiveEditor( const Bit7zLibrary& lib, const tstring& inFile, const BitInOutFormat& format, const tstring& password ) - : BitArchiveWriter( lib, inFile, format, password ) { + : BitArchiveWriter( lib, inFile, ArchiveStartOffset::FileStart, format, password ) { if ( inputArchive() != nullptr ) { return; // Input file was correctly read by base class BitOutputArchive constructor } @@ -198,7 +198,7 @@ void BitArchiveEditor::applyChanges() { auto archivePath = inputArchive()->archivePath(); compressTo( archivePath ); mEditedItems.clear(); - setInputArchive( std::make_unique< BitInputArchive >( *this, archivePath ) ); + setInputArchive( std::make_unique< BitInputArchive >( *this, archivePath, ArchiveStartOffset::FileStart ) ); } auto BitArchiveEditor::findItem( const tstring& itemPath ) -> uint32_t { diff --git a/src/bitarchivereader.cpp b/src/bitarchivereader.cpp index 3c318712..cffd4a1d 100644 --- a/src/bitarchivereader.cpp +++ b/src/bitarchivereader.cpp @@ -18,6 +18,13 @@ using namespace bit7z; +BitArchiveReader::BitArchiveReader( const Bit7zLibrary& lib, + const tstring& inArchive, + ArchiveStartOffset archiveStart, + const BitInFormat& format, + const tstring& password ) + : BitAbstractArchiveOpener( lib, format, password ), BitInputArchive( *this, inArchive, archiveStart ) {} + BitArchiveReader::BitArchiveReader( const Bit7zLibrary& lib, const tstring& inArchive, const BitInFormat& format, @@ -25,11 +32,25 @@ BitArchiveReader::BitArchiveReader( const Bit7zLibrary& lib, : BitAbstractArchiveOpener( lib, format, password ), BitInputArchive( *this, inArchive ) {} BitArchiveReader::BitArchiveReader( const Bit7zLibrary& lib, - const std::vector< byte_t >& inArchive, + const buffer_t& inArchive, + ArchiveStartOffset archiveStart, + const BitInFormat& format, + const tstring& password ) + : BitAbstractArchiveOpener( lib, format, password ), BitInputArchive( *this, inArchive, archiveStart ) {} + +BitArchiveReader::BitArchiveReader( const Bit7zLibrary& lib, + const buffer_t& inArchive, const BitInFormat& format, const tstring& password ) : BitAbstractArchiveOpener( lib, format, password ), BitInputArchive( *this, inArchive ) {} +BitArchiveReader::BitArchiveReader( const Bit7zLibrary& lib, + std::istream& inArchive, + ArchiveStartOffset archiveStart, + const BitInFormat& format, + const tstring& password ) + : BitAbstractArchiveOpener( lib, format, password ), BitInputArchive( *this, inArchive, archiveStart ) {} + BitArchiveReader::BitArchiveReader( const Bit7zLibrary& lib, std::istream& inArchive, const BitInFormat& format, diff --git a/src/bitarchivewriter.cpp b/src/bitarchivewriter.cpp index e91e20fa..66e74185 100644 --- a/src/bitarchivewriter.cpp +++ b/src/bitarchivewriter.cpp @@ -18,6 +18,14 @@ namespace bit7z { BitArchiveWriter::BitArchiveWriter( const Bit7zLibrary& lib, const BitInOutFormat& format ) : BitAbstractArchiveCreator( lib, format ), BitOutputArchive( *this, tstring{} ) {} +BitArchiveWriter::BitArchiveWriter( const Bit7zLibrary& lib, + const tstring& inArchive, + ArchiveStartOffset startOffset, + const BitInOutFormat& format, + const tstring& password ) + : BitAbstractArchiveCreator( lib, format, password, UpdateMode::Append ), + BitOutputArchive( *this, inArchive, startOffset ) {} + BitArchiveWriter::BitArchiveWriter( const Bit7zLibrary& lib, const tstring& inArchive, const BitInOutFormat& format, @@ -26,12 +34,28 @@ BitArchiveWriter::BitArchiveWriter( const Bit7zLibrary& lib, BitOutputArchive( *this, inArchive ) {} BitArchiveWriter::BitArchiveWriter( const Bit7zLibrary& lib, - const std::vector< byte_t >& inArchive, + const buffer_t& inArchive, + ArchiveStartOffset startOffset, + const BitInOutFormat& format, + const tstring& password ) + : BitAbstractArchiveCreator( lib, format, password, UpdateMode::Append ), + BitOutputArchive( *this, inArchive, startOffset ) {} + +BitArchiveWriter::BitArchiveWriter( const Bit7zLibrary& lib, + const buffer_t& inArchive, const BitInOutFormat& format, const tstring& password ) : BitAbstractArchiveCreator( lib, format, password, UpdateMode::Append ), BitOutputArchive( *this, inArchive ) {} +BitArchiveWriter::BitArchiveWriter( const Bit7zLibrary& lib, + std::istream& inArchive, + ArchiveStartOffset startOffset, + const BitInOutFormat& format, + const tstring& password ) + : BitAbstractArchiveCreator( lib, format, password, UpdateMode::Append ), + BitOutputArchive( *this, inArchive, startOffset ) {} + BitArchiveWriter::BitArchiveWriter( const Bit7zLibrary& lib, std::istream& inArchive, const BitInOutFormat& format, diff --git a/src/bitinputarchive.cpp b/src/bitinputarchive.cpp index a73f1217..9aacff49 100644 --- a/src/bitinputarchive.cpp +++ b/src/bitinputarchive.cpp @@ -59,7 +59,9 @@ void extract_arc( IInArchive* inArchive, } } -auto BitInputArchive::openArchiveStream( const fs::path& name, IInStream* inStream ) -> IInArchive* { +auto BitInputArchive::openArchiveStream( const fs::path& name, + IInStream* inStream, + ArchiveStartOffset startOffset ) -> IInArchive* { #ifdef BIT7Z_AUTO_FORMAT bool detectedBySignature = false; if ( *mDetectedFormat == BitFormat::Auto ) { @@ -81,7 +83,13 @@ auto BitInputArchive::openArchiveStream( const fs::path& name, IInStream* inStre #ifndef BIT7Z_AUTO_FORMAT const #endif - HRESULT res = inArchive->Open( inStream, nullptr, openCallback ); + HRESULT res = [&]() -> HRESULT { + if ( startOffset == ArchiveStartOffset::FileStart ) { + const UInt64 maxCheckStartPosition = 0; + return inArchive->Open( inStream, &maxCheckStartPosition, openCallback ); + } + return inArchive->Open( inStream, nullptr, openCallback ); + }(); #ifdef BIT7Z_AUTO_FORMAT if ( res != S_OK && mArchiveHandler.format() == BitFormat::Auto && !detectedBySignature ) { @@ -119,10 +127,14 @@ inline auto detect_format( const BitInFormat& format, const fs::path& arcPath ) #endif } -BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, const tstring& inFile ) - : BitInputArchive( handler, tstring_to_path( inFile ) ) {} +BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, + const tstring& inFile, + ArchiveStartOffset startOffset ) + : BitInputArchive( handler, tstring_to_path( inFile ), startOffset ) {} -BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, const fs::path& arcPath ) +BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, + const fs::path& arcPath, + ArchiveStartOffset startOffset ) : mDetectedFormat{ detect_format( handler.format(), arcPath ) }, mArchiveHandler{ handler }, mArchivePath{ path_to_tstring( arcPath ) } { @@ -132,21 +144,25 @@ BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, cons } else { fileStream = bit7z::make_com< CFileInStream, IInStream >( arcPath ); } - mInArchive = openArchiveStream( arcPath, fileStream ); + mInArchive = openArchiveStream( arcPath, fileStream, startOffset ); } -BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, const std::vector< byte_t >& inBuffer ) +BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, + const buffer_t& inBuffer, + ArchiveStartOffset startOffset ) : mDetectedFormat{ &handler.format() }, // if auto, detect the format from content, otherwise try the passed format. mArchiveHandler{ handler } { auto bufStream = bit7z::make_com< CBufferInStream, IInStream >( inBuffer ); - mInArchive = openArchiveStream( fs::path{}, bufStream ); + mInArchive = openArchiveStream( fs::path{}, bufStream, startOffset ); } -BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, std::istream& inStream ) +BitInputArchive::BitInputArchive( const BitAbstractArchiveHandler& handler, + std::istream& inStream, + ArchiveStartOffset startOffset ) : mDetectedFormat{ &handler.format() }, // if auto, detect the format from content, otherwise try the passed format. mArchiveHandler{ handler } { auto stdStream = bit7z::make_com< CStdInStream, IInStream >( inStream ); - mInArchive = openArchiveStream( fs::path{}, stdStream ); + mInArchive = openArchiveStream( fs::path{}, stdStream, startOffset ); } auto BitInputArchive::archiveProperty( BitProperty property ) const -> BitPropVariant { diff --git a/src/bitoutputarchive.cpp b/src/bitoutputarchive.cpp index 0872ecda..babcc37e 100644 --- a/src/bitoutputarchive.cpp +++ b/src/bitoutputarchive.cpp @@ -26,10 +26,14 @@ namespace bit7z { BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator ) : mArchiveCreator{ creator }, mInputArchiveItemsCount{ 0 } {} -BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, const tstring& inFile ) - : BitOutputArchive( creator, tstring_to_path( inFile ) ) {} +BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, + const tstring& inFile, + ArchiveStartOffset startOffset ) + : BitOutputArchive( creator, tstring_to_path( inFile ), startOffset ) {} -BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, const fs::path& inArc ) +BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, + const fs::path& inArc, + ArchiveStartOffset archiveStart ) : mArchiveCreator{ creator }, mInputArchiveItemsCount{ 0 } { if ( mArchiveCreator.overwriteMode() != OverwriteMode::None ) { return; @@ -55,23 +59,26 @@ BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, co make_error_code( BitError::FormatFeatureNotSupported ) ); } - mInputArchive = std::make_unique< BitInputArchive >( creator, inArc ); + mInputArchive = std::make_unique< BitInputArchive >( creator, inArc, archiveStart ); mInputArchiveItemsCount = mInputArchive->itemsCount(); } BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, - const std::vector< bit7z::byte_t >& inBuffer ) + const buffer_t& inBuffer, + ArchiveStartOffset startOffset ) : mArchiveCreator{ creator }, mInputArchiveItemsCount{ 0 } { if ( !inBuffer.empty() ) { - mInputArchive = std::make_unique< BitInputArchive >( creator, inBuffer ); + mInputArchive = std::make_unique< BitInputArchive >( creator, inBuffer, startOffset ); mInputArchiveItemsCount = mInputArchive->itemsCount(); } } -BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, std::istream& inStream ) +BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, + std::istream& inStream, + ArchiveStartOffset startOffset ) : mArchiveCreator{ creator }, mInputArchiveItemsCount{ 0 } { if ( inStream.good() ) { - mInputArchive = std::make_unique< BitInputArchive >( creator, inStream ); + mInputArchive = std::make_unique< BitInputArchive >( creator, inStream, startOffset ); mInputArchiveItemsCount = mInputArchive->itemsCount(); } } From 7d640d53f56b445aa7f7ed0a362967338f0ec7ae Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 3 Nov 2024 10:12:52 +0100 Subject: [PATCH 05/31] Add tests for archive start offset --- tests/CMakeLists.txt | 2 +- tests/src/test_bitarchivereader.cpp | 183 +++++++++++++++++++++++++++- 2 files changed, 183 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7956ebc8..bdf87f19 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,7 +53,7 @@ if( BIT7Z_TESTS_FILESYSTEM ) include( FetchContent ) FetchContent_Declare( bit7z-test-data GIT_REPOSITORY https://github.com/rikyoz/bit7z-test-data.git - GIT_TAG abc1adc273dd8dd17f55969838811f98872d77b8 + GIT_TAG 5e8143dfefac51cf3c67e72d733f64a3c1e44174 GIT_SHALLOW ON SOURCE_DIR ${BIT7Z_TESTS_DATA_DIR} ) FetchContent_MakeAvailable( bit7z-test-data ) diff --git a/tests/src/test_bitarchivereader.cpp b/tests/src/test_bitarchivereader.cpp index c3fdbdb3..ea89c02d 100644 --- a/tests/src/test_bitarchivereader.cpp +++ b/tests/src/test_bitarchivereader.cpp @@ -980,4 +980,185 @@ TEST_CASE( "BitArchiveReader: Format detection of archives", "[bitarchivereader] } } -#endif \ No newline at end of file +#endif + + +// NOLINTNEXTLINE(*-err58-cpp) +TEMPLATE_TEST_CASE( "BitInputArchive: Reading the archive from the start of the input file", + "[bitinputarchive]", tstring, buffer_t, stream_t ) { + const TestDirectory testDir{ fs::path{ test_archives_dir } / "extraction" / "single_file" }; + +#ifdef BIT7Z_BUILD_FOR_P7ZIP + const auto testArchive = GENERATE( as< TestInputFormat >(), + TestInputFormat{ "7z", BitFormat::SevenZip }, + TestInputFormat{ "bz2", BitFormat::BZip2 }, + TestInputFormat{ "gz", BitFormat::GZip }, + TestInputFormat{ "iso", BitFormat::Iso }, + TestInputFormat{ "lzh", BitFormat::Lzh }, + TestInputFormat{ "lzma", BitFormat::Lzma }, + TestInputFormat{ "tar", BitFormat::Tar }, + TestInputFormat{ "wim", BitFormat::Wim }, + TestInputFormat{ "xz", BitFormat::Xz }, + TestInputFormat{ "zip", BitFormat::Zip } ); +#else + const auto testArchive = GENERATE( as< TestInputFormat >(), + TestInputFormat{ "7z", BitFormat::SevenZip }, + TestInputFormat{ "bz2", BitFormat::BZip2 }, + TestInputFormat{ "gz", BitFormat::GZip }, + TestInputFormat{ "iso", BitFormat::Iso }, + TestInputFormat{ "lzh", BitFormat::Lzh }, + TestInputFormat{ "lzma", BitFormat::Lzma }, + TestInputFormat{ "rar4.rar", BitFormat::Rar }, + TestInputFormat{ "rar5.rar", BitFormat::Rar5 }, + TestInputFormat{ "tar", BitFormat::Tar }, + TestInputFormat{ "wim", BitFormat::Wim }, + TestInputFormat{ "xz", BitFormat::Xz }, + TestInputFormat{ "zip", BitFormat::Zip } ); +#endif + + DYNAMIC_SECTION( "Archive format: " << testArchive.extension ) { + const fs::path arcFileName = fs::path{ clouds.name }.concat( "." + testArchive.extension ); + + TestType inputArchive{}; + getInputArchive( arcFileName, inputArchive ); + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + REQUIRE_NOTHROW( BitArchiveReader( lib, inputArchive, ArchiveStartOffset::FileStart, testArchive.format ) ); + } +} + +// NOLINTNEXTLINE(*-err58-cpp) +TEMPLATE_TEST_CASE( "BitInputArchive: Scanning a file for the archive start", + "[bitinputarchive]", tstring, buffer_t, stream_t ) { + const TestDirectory testDir{ fs::path{ test_archives_dir } / "extraction" / "nested" }; + + const fs::path arcFileName = "multiple_nested2.tar"; + + TestType inputArchive{}; + getInputArchive( arcFileName, inputArchive ); + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + +#ifdef BIT7Z_AUTO_FORMAT + SECTION( "Detecting the format from the file extension (extension is correct)" ) { + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::None ); + REQUIRE_NOTHROW( reader.detectedFormat() == BitFormat::Tar ); + } +#endif + + SECTION( "Opening the archive with the Zip format succeeds, " + "as 7-Zip will scan the input Tar archive and find the nested Zip archive" ) { + REQUIRE_NOTHROW( BitArchiveReader( lib, inputArchive, ArchiveStartOffset::None, BitFormat::Zip ) ); + } + + SECTION( "Opening the archive with the 7z format succeeds, " + "as 7-Zip will scan the input Tar archive and find the nested 7z archive" ) { + REQUIRE_NOTHROW( BitArchiveReader( lib, inputArchive, ArchiveStartOffset::None, BitFormat::SevenZip ) ); + } + + SECTION( "The BZip2 format doesn't support scanning the input file for the archive start," + "so the opening must fail even though the Tar archive contains a BZip2 file") { + REQUIRE_THROWS( BitArchiveReader( lib, inputArchive, ArchiveStartOffset::None, BitFormat::BZip2 ) ); + } +} + +// NOLINTNEXTLINE(*-err58-cpp) +TEMPLATE_TEST_CASE( "BitInputArchive: Checking only the file start for the archive start", + "[bitinputarchive]", tstring, buffer_t, stream_t ) { + const TestDirectory testDir{ fs::path{ test_archives_dir } / "extraction" / "nested" }; + + const fs::path arcFileName = "multiple_nested2.tar"; + + TestType inputArchive{}; + getInputArchive( arcFileName, inputArchive ); + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + +#ifdef BIT7Z_AUTO_FORMAT + SECTION( "Detecting the format from the file extension (extension is correct)" ) { + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::FileStart ); + REQUIRE_NOTHROW( reader.detectedFormat() == BitFormat::Tar ); + } +#endif + + SECTION( "Opening the Tar file as a Zip archive fails, as 7-Zip will check the format only at the file start" ) { + REQUIRE_THROWS( BitArchiveReader( lib, inputArchive, ArchiveStartOffset::FileStart, BitFormat::Zip ) ); + } + + SECTION( "Opening the Tar file as a 7z archive fails, as 7-Zip will check the format only at the file start" ) { + REQUIRE_THROWS( BitArchiveReader( lib, inputArchive, ArchiveStartOffset::FileStart, BitFormat::SevenZip ) ); + } + + SECTION( "Opening the Tar file as a BZip2 archive fails, as 7-Zip will check the format only at the file start" ) { + REQUIRE_THROWS( BitArchiveReader( lib, inputArchive, ArchiveStartOffset::FileStart, BitFormat::BZip2 ) ); + } +} + +// NOLINTNEXTLINE(*-err58-cpp) +TEMPLATE_TEST_CASE( "BitInputArchive: Reading a nested archive with wrong extension", + "[bitinputarchive]", tstring, buffer_t, stream_t ) { + const TestDirectory testDir{ fs::path{ test_archives_dir } / "detection" }; + + const fs::path arcFileName = "nested_wrong_extension.zip"; // 7z file with zip extension + + TestType inputArchive{}; + getInputArchive( arcFileName, inputArchive ); + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + SECTION( "Checking archive start at input file start" ){ +#ifdef BIT7Z_AUTO_FORMAT + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::FileStart ); + REQUIRE( reader.detectedFormat() == BitFormat::SevenZip ); + REQUIRE_NOTHROW( reader.test() ); +#else + REQUIRE_THROWS( BitArchiveReader( lib, inputArchive, ArchiveStartOffset::FileStart, BitFormat::Zip ) ); +#endif + } + + SECTION( "Checking archive start by scanning through the input file" ){ +#ifdef BIT7Z_AUTO_FORMAT + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::None ); + if ( reader.archivePath().empty() ) { + REQUIRE( reader.detectedFormat() == BitFormat::SevenZip ); + REQUIRE_NOTHROW( reader.test() ); + } else { + REQUIRE( reader.detectedFormat() == BitFormat::Zip ); + REQUIRE_THROWS( reader.test() ); + } +#else + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::None, BitFormat::Zip ); + REQUIRE_THROWS( reader.test() ); +#endif + } +} + +// NOLINTNEXTLINE(*-err58-cpp) +TEMPLATE_TEST_CASE( "BitInputArchive: Reading a nested zip archive", + "[bitinputarchive]", tstring, buffer_t, stream_t ) { + const TestDirectory testDir{ fs::path{ test_archives_dir } / "extraction" / "nested" }; + + const fs::path arcFileName = "nested_zip.zip"; + + TestType inputArchive{}; + getInputArchive( arcFileName, inputArchive ); + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + SECTION( "Checking archive start at input file start" ){ +#ifdef BIT7Z_AUTO_FORMAT + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::FileStart ); + REQUIRE( reader.detectedFormat() == BitFormat::Zip ); +#else + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::FileStart, BitFormat::Zip ); +#endif + REQUIRE_NOTHROW( reader.test() ); + REQUIRE( reader.contains( italy.name ) ); + } + + SECTION( "Checking archive start by scanning through the input file" ){ +#ifdef BIT7Z_AUTO_FORMAT + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::None ); + REQUIRE( reader.detectedFormat() == BitFormat::Zip ); +#else + const BitArchiveReader reader( lib, inputArchive, ArchiveStartOffset::None, BitFormat::Zip ); +#endif + REQUIRE_NOTHROW( reader.test() ); + REQUIRE( reader.contains( italy.name ) ); + } +} \ No newline at end of file From 1739c615c9cf1a9efaac646e9ee858fd1826f649 Mon Sep 17 00:00:00 2001 From: Oz Date: Sat, 28 Sep 2024 21:39:16 +0200 Subject: [PATCH 06/31] Fix missing change from v3 in README Close issue #245 --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 992f27f3..7b4d3162 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,10 @@ The newest bit7z v4 introduced some significant breaking changes to the library' + The old `BitCompressor` class is now called `BitFileCompressor`. + Now `BitCompressor` is just the name of a template class for all the compression classes. + The `ProgressCallback` now must return a `bool` value indicating whether the current operation can continue (`true`) or not (`false`). ++ The `BitException` class now inherits from `std::system_error` rather than `std::runtime_error`. + + + The method `BitException::getErrorCode()` was renamed `BitException::hresultCode()`. + + The project structure changed: + Public API headers moved from `include/` to the `include/bit7z/` folder, so `#include` directives now need to prepend `bit7z/` to the included header name (e.g., `#include `). + Even though it is a bit verbose, it is a typical structure for C and C++ libraries, and it makes explicit which third-party library a header file belongs to. From a0e483171c41d4387b9088aac524cfeea32e405c Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 6 Oct 2024 11:07:48 +0200 Subject: [PATCH 07/31] Fix long path support for UNC paths Close issue #240 --- src/internal/fsutil.cpp | 10 +++++++++- tests/src/test_fsutil.cpp | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/internal/fsutil.cpp b/src/internal/fsutil.cpp index ffbd9f3a..10be36f5 100644 --- a/src/internal/fsutil.cpp +++ b/src/internal/fsutil.cpp @@ -302,7 +302,9 @@ auto fsutil::get_file_attributes_ex( const fs::path& filePath, #if defined( _WIN32 ) && defined( BIT7Z_AUTO_PREFIX_LONG_PATHS ) +namespace { constexpr auto kLongPathPrefix = BIT7Z_NATIVE_STRING( R"(\\?\)" ); +} // namespace auto fsutil::should_format_long_path( const fs::path& path ) -> bool { constexpr auto kMaxDosFilenameSize = 12; @@ -311,7 +313,7 @@ auto fsutil::should_format_long_path( const fs::path& path ) -> bool { return false; } const auto& pathStr = path.native(); - if ( pathStr.size() < ( MAX_PATH - kMaxDosFilenameSize ) ) { + if ( pathStr.size() < static_cast( MAX_PATH - kMaxDosFilenameSize ) ) { return false; } return !starts_with( pathStr, kLongPathPrefix ); @@ -319,6 +321,12 @@ auto fsutil::should_format_long_path( const fs::path& path ) -> bool { auto fsutil::format_long_path( const fs::path& path ) -> fs::path { fs::path longPath = kLongPathPrefix; + // Note: we call this function after checking if we should format the given path as a long path. + // This means that if the path starts with the \\ prefix, + // it is a UNC path and not a long path prefixed with \\?\. + if ( starts_with( path.native(), BIT7Z_NATIVE_STRING( R"(\\)" ) ) ) { + longPath += L"UNC\\"; + } longPath += path; return longPath; } diff --git a/tests/src/test_fsutil.cpp b/tests/src/test_fsutil.cpp index ffaa88a5..8a4d81ba 100644 --- a/tests/src/test_fsutil.cpp +++ b/tests/src/test_fsutil.cpp @@ -237,6 +237,40 @@ TEST_CASE( "fsutil: In-archive path computation", "[fsutil][in_archive_path]" ) #endif +#if defined( _WIN32 ) && defined( BIT7Z_AUTO_PREFIX_LONG_PATHS ) +TEST_CASE( "fsutil: Format long Windows paths", "[fsutil][format_long_path]" ) { + const std::wstring kLongPathPrefix = BIT7Z_NATIVE_STRING( R"(\\?\)" ); + + constexpr auto short_path = L"short_path\\file.txt"; + REQUIRE_FALSE( should_format_long_path( short_path ) ); + + constexpr auto very_long_path = LR"(C:\very\long\dummy\path\)" + LR"(ABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyz\0123456789\)" + LR"(Lorem ipsum dolor sit amet\consectetur adipiscing elit\)" + LR"(Mauris ac leo dui\Morbi non elit lacus\)" + LR"(Ut ullamcorper sapien eget commodo eleifend\Curabitur varius magna sit\)" + LR"(Hello_World.txt)"; + REQUIRE( should_format_long_path( very_long_path ) ); + REQUIRE( format_long_path( very_long_path ) == ( kLongPathPrefix + very_long_path ) ); + + + const auto prefixed_very_long_path = std::wstring{ LR"(\\?\)" } + very_long_path; + REQUIRE_FALSE( should_format_long_path( prefixed_very_long_path ) ); + + constexpr auto very_long_unc_path = LR"(\\very\long\dummy\UNC\path\)" + LR"(ABCDEFGHIJKLMNOPQRSTUVWXYZ\abcdefghijklmnopqrstuvwxyz\0123456789\)" + LR"(Lorem ipsum dolor sit amet\consectetur adipiscing elit\)" + LR"(Mauris ac leo dui\Morbi non elit lacus\)" + LR"(Ut ullamcorper sapien eget commodo eleifend\Curabitur varius magna sit\)" + LR"(Hello_World.txt)"; + REQUIRE( should_format_long_path( very_long_unc_path ) ); + REQUIRE( format_long_path( very_long_unc_path ) == ( kLongPathPrefix + L"UNC\\" + very_long_unc_path ) ); + + const auto prefixed_very_long_unc_path = std::wstring{ LR"(\\?\UNC\)" } + very_long_unc_path; + REQUIRE_FALSE( should_format_long_path( prefixed_very_long_unc_path ) ); +} +#endif + #if defined( _WIN32 ) && defined( BIT7Z_PATH_SANITIZATION ) TEST_CASE( "fsutil: Sanitizing Windows paths", "[fsutil][sanitize_path]" ) { REQUIRE( sanitize_path( L"hello world.txt" ) == L"hello world.txt" ); From ecd6c3014c83876f4d3210ac5e554c3a3ac8e76f Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 6 Oct 2024 15:52:18 +0200 Subject: [PATCH 08/31] Improve error handling in OpenCallback --- src/internal/opencallback.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/internal/opencallback.cpp b/src/internal/opencallback.cpp index 427aa865..ce1be868 100644 --- a/src/internal/opencallback.cpp +++ b/src/internal/opencallback.cpp @@ -60,8 +60,9 @@ STDMETHODIMP OpenCallback::GetStream( const wchar_t* name, IInStream** inStream if ( name != nullptr ) { streamPath = streamPath.parent_path(); streamPath.append( name ); - const auto streamStatus = fs::status( streamPath ); - if ( !fs::exists( streamStatus ) || fs::is_directory( streamStatus ) ) { // avoid exceptions using status + std::error_code error; + const auto streamStatus = fs::status( streamPath, error ); + if ( error || !fs::exists( streamStatus ) || fs::is_directory( streamStatus ) ) { return S_FALSE; } } From 9921e0cd145dfc25059c268645d299032ff7e9e0 Mon Sep 17 00:00:00 2001 From: Oz Date: Sat, 19 Oct 2024 16:39:41 +0200 Subject: [PATCH 09/31] Explicitly use wide variant of Windows API functions in fsutil --- src/internal/fsutil.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/internal/fsutil.cpp b/src/internal/fsutil.cpp index 10be36f5..8df736e1 100644 --- a/src/internal/fsutil.cpp +++ b/src/internal/fsutil.cpp @@ -238,13 +238,13 @@ auto fsutil::set_file_time( const fs::path& filePath, } bool res = false; - HANDLE hFile = ::CreateFile( filePath.c_str(), - GENERIC_READ | FILE_WRITE_ATTRIBUTES, - FILE_SHARE_READ, - nullptr, - OPEN_EXISTING, - 0, - nullptr ); + HANDLE hFile = ::CreateFileW( filePath.c_str(), + GENERIC_READ | FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + 0, + nullptr ); if ( hFile != INVALID_HANDLE_VALUE ) { // NOLINT(cppcoreguidelines-pro-type-cstyle-cast,performance-no-int-to-ptr) res = ::SetFileTime( hFile, &creation, &access, &modified ) != FALSE; CloseHandle( hFile ); @@ -273,7 +273,7 @@ auto fsutil::get_file_attributes_ex( const fs::path& filePath, #ifdef _WIN32 (void)symlinkPolicy; - return ::GetFileAttributesEx( filePath.c_str(), GetFileExInfoStandard, &fileMetadata ) != FALSE; + return ::GetFileAttributesExW( filePath.c_str(), GetFileExInfoStandard, &fileMetadata ) != FALSE; #else stat_t statInfo{}; const auto statRes = symlinkPolicy == SymlinkPolicy::Follow ? From 71b4388532b4272f4bf50d9e7f2f05c4b01e6e2e Mon Sep 17 00:00:00 2001 From: Oz Date: Sat, 19 Oct 2024 16:40:43 +0200 Subject: [PATCH 10/31] Fix and improve the README --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7b4d3162..51cd0a30 100644 --- a/README.md +++ b/README.md @@ -255,14 +255,16 @@ You can also clone/download this repository and build the library yourself (plea + **Operating System:** Windows, Linux, macOS, Android[^1]. + **Architecture:** x86, x86_64, arm, arm64. + **Language Standard:** C++11 (for using the library), C++14 (for building the library). -+ **Compiler:** MSVC 2015 or later[^2], MinGW v6.4 or later, GCC v4.9 or later, Clang 3.6 or later. -+ **Shared Library:** a 7-zip `.dll` library on Windows, a 7-zip/p7zip `.so` library on Unix[^3]. ++ **Compiler:** MSVC 2015 or later[^2], MinGW v6.4 or later[^3], GCC v4.9 or later, Clang 3.6 or later. ++ **Shared Library:** a 7-zip `.dll` library on Windows, a 7-zip/p7zip `.so` library on Unix[^4]. [^1]: On Windows, you should link your program _also_ with _oleaut32_ (e.g., `-lbit7z -loleaut32`).
On Linux and macOS, you should link your program _also_ with _dl_ (e.g., `-lbit7z -ldl`).
If you are using the library via CMake, these dependencies will be linked automatically to your project. [^2]: MSVC 2010 was supported until v2.x, MSVC 2012/2013 until v3.x. -[^3]: bit7z doesn't ship with the 7-zip shared libraries. You can build them from the source code available at [7-zip.org](http://www.7-zip.org/). +[^3]: When using MinGW, you should link your program also with `libuuid` (e.g. `-lbit7z -loleaut32 -luuid`). + +[^4]: bit7z doesn't ship with the 7-zip shared libraries. You can build them from the source code available at [7-zip.org](http://www.7-zip.org/). ## ⚙️ Building and Using bit7z From bd1c89f6e0660fd720ae2f9f521e40f1bf55a580 Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 3 Nov 2024 10:22:04 +0100 Subject: [PATCH 11/31] Increase maximum dictionary size limit for LZMA Close issue #256 --- src/bitabstractarchivecreator.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bitabstractarchivecreator.cpp b/src/bitabstractarchivecreator.cpp index 19f31c56..15182b8f 100644 --- a/src/bitabstractarchivecreator.cpp +++ b/src/bitabstractarchivecreator.cpp @@ -41,9 +41,9 @@ auto is_valid_compression_method( const BitInOutFormat& format, BitCompressionMe } auto is_valid_dictionary_size( BitCompressionMethod method, uint32_t dictionarySize ) noexcept -> bool { - constexpr auto kMaxLzmaDictionarySize = 1536 * ( 1LL << 20 ); // less than 1536 MiB - constexpr auto kMaxPpmdDictionarySize = ( 1LL << 30 ); // less than 1 GiB, i.e., 2^30 bytes - constexpr auto kMaxBzip2DictionarySize = 900 * ( 1LL << 10 ); // less than 900 KiB + constexpr auto kMaxLzmaDictionarySize = 3840 * ( 1ull << 20ull ); // less than 3840 MiB + constexpr auto kMaxPpmdDictionarySize = ( 1ull << 30ull ); // less than 1 GiB, i.e., 2^30 bytes + constexpr auto kMaxBzip2DictionarySize = 900 * ( 1ull << 10ull ); // less than 900 KiB switch ( method ) { case BitCompressionMethod::Lzma: From 32af0f5c2a0d00e5b364b2f2438df6820bb2af5b Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 3 Nov 2024 10:27:30 +0100 Subject: [PATCH 12/31] Add support for using format properties in BitInputArchive Close issue #248 --- include/bit7z/bitinputarchive.hpp | 21 +++++++++++++++++++++ include/bit7z/bittypes.hpp | 4 ++++ src/bitinputarchive.cpp | 18 ++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/include/bit7z/bitinputarchive.hpp b/include/bit7z/bitinputarchive.hpp index 9e630465..32bcfbc5 100644 --- a/include/bit7z/bitinputarchive.hpp +++ b/include/bit7z/bitinputarchive.hpp @@ -152,6 +152,27 @@ class BitInputArchive { */ BIT7Z_NODISCARD auto handler() const noexcept -> const BitAbstractArchiveHandler&; + /** + * @brief Use the given format property to read the archive. + * + * @param name the name of the property. + * @param property the property value. + */ + void useFormatProperty( const wchar_t* name, const BitPropVariant& property ) const; + + /** + * @brief Use the given format property to read the archive. + * + * @tparam T the type of the property. + * @param name the name of the property. + * @param value the property value. + */ + template< typename T, + typename = typename std::enable_if< is_explicitly_convertible< T, BitPropVariant >::value >::type > + void useFormatProperty( const wchar_t* name, T&& value ) const { // NOLINT(*-avoid-c-arrays) + useFormatProperty( name, BitPropVariant{ std::forward< T >( value ) } ); + } + BIT7Z_DEPRECATED_MSG("Since v4.0; please, use the extractTo method.") inline void extract( const tstring& outDir, const std::vector< uint32_t >& indices = {} ) const { extractTo( outDir, indices ); diff --git a/include/bit7z/bittypes.hpp b/include/bit7z/bittypes.hpp index d0ebfbfa..cbefc9cc 100644 --- a/include/bit7z/bittypes.hpp +++ b/include/bit7z/bittypes.hpp @@ -120,6 +120,10 @@ auto to_tstring( const native_string& str ) -> tstring; auto to_tstring( const native_string& str ) -> const tstring&; #endif +template< typename From, typename To > +using is_explicitly_convertible = std::integral_constant< bool, std::is_constructible< To, From >::value && + !std::is_convertible< From, To >::value >; + } // namespace bit7z #endif // BITTYPES_HPP diff --git a/src/bitinputarchive.cpp b/src/bitinputarchive.cpp index 9aacff49..54b1347c 100644 --- a/src/bitinputarchive.cpp +++ b/src/bitinputarchive.cpp @@ -237,6 +237,24 @@ auto BitInputArchive::handler() const noexcept -> const BitAbstractArchiveHandle return mArchiveHandler; } +void BitInputArchive::useFormatProperty( const wchar_t* name, const BitPropVariant& property ) const { + CMyComPtr< ISetProperties > setProperties; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + HRESULT res = mInArchive->QueryInterface( ::IID_ISetProperties, reinterpret_cast< void** >( &setProperties ) ); + if ( res != S_OK ) { + throw BitException( "ISetProperties unsupported", make_hresult_code( res ) ); + } + + const auto propertyNames = { name }; + const auto propertyValues = { property }; + res = setProperties->SetProperties( propertyNames.begin(), + propertyValues.begin(), + static_cast< std:: uint32_t >( propertyNames.size() ) ); + if ( res != S_OK ) { + throw BitException( "Cannot use the archive format property", make_hresult_code( res ) ); + } +} + void BitInputArchive::extractTo( const tstring& outDir ) const { auto callback = bit7z::make_com< FileExtractCallback, ExtractCallback >( *this, outDir ); extract_arc( mInArchive, {}, callback ); From 4669e81f21d6f29f052855fc46e43193cd2ce872 Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 3 Nov 2024 10:31:39 +0100 Subject: [PATCH 13/31] Add tests for reading zip archives using a different codepage encoding --- tests/CMakeLists.txt | 2 +- tests/src/test_bitarchivereader.cpp | 27 ++++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index bdf87f19..60da280d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -53,7 +53,7 @@ if( BIT7Z_TESTS_FILESYSTEM ) include( FetchContent ) FetchContent_Declare( bit7z-test-data GIT_REPOSITORY https://github.com/rikyoz/bit7z-test-data.git - GIT_TAG 5e8143dfefac51cf3c67e72d733f64a3c1e44174 + GIT_TAG 077e407b1c07b7443626b5902eeb4819388bf656 GIT_SHALLOW ON SOURCE_DIR ${BIT7Z_TESTS_DATA_DIR} ) FetchContent_MakeAvailable( bit7z-test-data ) diff --git a/tests/src/test_bitarchivereader.cpp b/tests/src/test_bitarchivereader.cpp index ea89c02d..0c0c9726 100644 --- a/tests/src/test_bitarchivereader.cpp +++ b/tests/src/test_bitarchivereader.cpp @@ -1161,4 +1161,29 @@ TEMPLATE_TEST_CASE( "BitInputArchive: Reading a nested zip archive", REQUIRE_NOTHROW( reader.test() ); REQUIRE( reader.contains( italy.name ) ); } -} \ No newline at end of file +} + +#ifdef _WIN32 +TEMPLATE_TEST_CASE( "BitInputArchive: Reading a zip archive using a different encoding", + "[bitinputarchive]", tstring, buffer_t, stream_t ) { + const TestDirectory testDir{ fs::path{ test_archives_dir } / "metadata" / "unicode" }; + + const fs::path arcFileName = "codepage.zip"; + + TestType inputArchive{}; + getInputArchive( arcFileName, inputArchive ); + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + const BitArchiveReader reader{ lib, inputArchive, BitFormat::Zip }; + REQUIRE( reader.itemsCount() == 1 ); + + constexpr auto expectedItemName = BIT7Z_NATIVE_STRING( "ユニコード.pdf" ); + + // The archive uses the Shift-JS encoding (Codepage 932) for the file names. + // If we do not set the codepage to be used, 7-Zip will report a wrongly-encoded string for the name. + REQUIRE_FALSE( reader.itemAt( 0 ).nativePath() == expectedItemName ); + + // Setting the correct codepage will make 7-Zip correctly encode the string. + reader.useFormatProperty( L"cp", 932u ); + REQUIRE( reader.itemAt( 0 ).nativePath() == expectedItemName ); +} +#endif \ No newline at end of file From 8da189b905b6bba7d4c48b3fee6a56316bb2cd04 Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 3 Nov 2024 10:32:56 +0100 Subject: [PATCH 14/31] [Test] Fix building tests using MinGW --- tests/src/test_cbufferinstream.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/test_cbufferinstream.cpp b/tests/src/test_cbufferinstream.cpp index 8f43b958..e5159dac 100644 --- a/tests/src/test_cbufferinstream.cpp +++ b/tests/src/test_cbufferinstream.cpp @@ -17,6 +17,7 @@ #include +#include #include using bit7z::byte_t; From 9db31f286cfa20781b4db4e0f2650ddcacc0b81c Mon Sep 17 00:00:00 2001 From: Oz Date: Sat, 9 Nov 2024 21:34:44 +0100 Subject: [PATCH 15/31] Update and improve the README --- README.md | 107 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 51cd0a30..652bcfd8 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,27 @@

bit7z

-

A C++ static library offering a clean and simple interface to the 7-zip shared libraries

+

A C++ static library offering a clean and simple interface to the 7-zip shared libraries.

+
+ GitHub releaseC++14WindowsLinuxmacOSBSDx86, x64, arm, arm64donatedocsBuild status +
+ MSVC 2015+GCC 4.9+Clang 3.6+CodeFactor GradeLicense +
+

Supported FeaturesGetting StartedDownloadRequirements • - Building & Using • + Installation • + ConfigurationDonateLicense

+ -
- GitHub releaseC++14WindowsLinuxmacOSBSDx86, x64, arm, arm64donatedocsBuild status -
- MSVC 2015+GCC 4.9+Clang 3.6+CodeFactor GradeLicense -
## ⚡️ Introduction @@ -45,12 +48,12 @@ It supports compression and extraction to and from the filesystem or the memory, + **Operation callbacks** for obtaining real-time information about ongoing operations. + **Canceling** or **pausing** the current operation. -### Notes - -The presence or not of some of the above features depends on the particular shared library used along with bit7z.
-For example, 7z.dll should support all these features, 7za.dll should work only with the 7z file format, and 7zxa.dll can only extract 7z files. For more information about the 7-zip DLLs, please check this [wiki page](https://github.com/rikyoz/bit7z/wiki/7z-DLLs). - -In the end, some other features (e.g., _automatic format detection_ and _selective extraction using regular expressions_) are disabled by default, and macro definitions must be used during compilation to have them available ([wiki](https://github.com/rikyoz/bit7z/wiki/Building-the-library)). +> [!NOTE] +> +> The presence or not of some of the above features depends on the particular shared library used along with bit7z.
+> For example, 7z.dll should support all these features, 7za.dll should work only with the 7z file format, and 7zxa.dll can only extract 7z files. For more information about the 7-zip DLLs, please check this [wiki page](https://github.com/rikyoz/bit7z/wiki/7z-DLLs). +> +> In the end, some other features (e.g., _automatic format detection_ and _selective extraction using regular expressions_) are disabled by default, and macro definitions must be used during compilation to have them available ([wiki](https://github.com/rikyoz/bit7z/wiki/Building-the-library)). ## 🔥 Getting Started (Library Usage) @@ -256,7 +259,7 @@ You can also clone/download this repository and build the library yourself (plea + **Architecture:** x86, x86_64, arm, arm64. + **Language Standard:** C++11 (for using the library), C++14 (for building the library). + **Compiler:** MSVC 2015 or later[^2], MinGW v6.4 or later[^3], GCC v4.9 or later, Clang 3.6 or later. -+ **Shared Library:** a 7-zip `.dll` library on Windows, a 7-zip/p7zip `.so` library on Unix[^4]. ++ **Shared Library:** a 7-zip `.dll` library on Windows, a 7-zip/p7zip[^4] `.so` library on Unix[^5]. [^1]: On Windows, you should link your program _also_ with _oleaut32_ (e.g., `-lbit7z -loleaut32`).
On Linux and macOS, you should link your program _also_ with _dl_ (e.g., `-lbit7z -ldl`).
If you are using the library via CMake, these dependencies will be linked automatically to your project. @@ -264,11 +267,56 @@ You can also clone/download this repository and build the library yourself (plea [^3]: When using MinGW, you should link your program also with `libuuid` (e.g. `-lbit7z -loleaut32 -luuid`). -[^4]: bit7z doesn't ship with the 7-zip shared libraries. You can build them from the source code available at [7-zip.org](http://www.7-zip.org/). +[^4]: bit7z doesn't support RAR archives when using the p7zip `.so` libraries. + +[^5]: bit7z doesn't ship with the 7-zip shared libraries. You can build them from the source code available at [7-zip.org](http://www.7-zip.org/). + +## 🔗 Installation + +Bit7z can be used as a dependency in several alternative ways: + +### Using [CPM.cmake](https://github.com/cpm-cmake/CPM.cmake) + +```cmake +CPMAddPackage("gh:rikyoz/bit7z@") # Replace with the version of bit7z you want to use. +# To enable/disable build options, use set(BIT7Z_