From 2f4adde213048c980f800c71edac194c9f8a8a33 Mon Sep 17 00:00:00 2001 From: softworkz Date: Fri, 17 Apr 2026 07:25:45 +0200 Subject: [PATCH] Chained Archive Extraction This implements single-pass extraction for archives like .tar.gz, .tar.xz, .tgz etc. and adds a new switch "-sce" for command-line use --- CPP/7zip/7zip_gcc.mak | 2 + CPP/7zip/Archive/Tar/TarHandler.cpp | 11 +- CPP/7zip/Archive/Tar/TarIn.cpp | 4 + CPP/7zip/Bundles/Alone/makefile.gcc | 1 + CPP/7zip/Bundles/Alone7z/makefile.gcc | 1 + CPP/7zip/Bundles/Fm/makefile | 1 + CPP/7zip/Bundles/SFXCon/makefile | 1 + CPP/7zip/Bundles/SFXCon/makefile.gcc | 1 + CPP/7zip/Bundles/SFXWin/makefile | 1 + CPP/7zip/Common/StreamBinder.h | 1 + CPP/7zip/UI/Common/ArchiveCommandLine.cpp | 5 + CPP/7zip/UI/Common/ArchiveExtractCallback.cpp | 11 +- CPP/7zip/UI/Common/ArchiveExtractCallback.h | 6 + CPP/7zip/UI/Common/ChainedExtract.cpp | 294 ++++++++++++++++++ CPP/7zip/UI/Common/ChainedExtract.h | 22 ++ CPP/7zip/UI/Common/CompressCall.cpp | 5 +- CPP/7zip/UI/Common/CompressCall.h | 2 +- CPP/7zip/UI/Common/CompressCall2.cpp | 3 +- CPP/7zip/UI/Common/Extract.cpp | 73 +++-- CPP/7zip/UI/Common/Extract.h | 16 + CPP/7zip/UI/Common/LoadCodecs.h | 10 + CPP/7zip/UI/Common/OpenArchive.cpp | 3 +- CPP/7zip/UI/Console/Console.mak | 1 + CPP/7zip/UI/Console/Main.cpp | 1 + CPP/7zip/UI/Console/makefile | 2 + CPP/7zip/UI/FileManager/Panel.cpp | 1 + CPP/7zip/UI/GUI/GUI.cpp | 6 + CPP/7zip/UI/GUI/GUI.dsp | 12 + CPP/7zip/UI/GUI/makefile | 3 + 29 files changed, 470 insertions(+), 30 deletions(-) create mode 100644 CPP/7zip/UI/Common/ChainedExtract.cpp create mode 100644 CPP/7zip/UI/Common/ChainedExtract.h diff --git a/CPP/7zip/7zip_gcc.mak b/CPP/7zip/7zip_gcc.mak index a78c0fa..d571f68 100644 --- a/CPP/7zip/7zip_gcc.mak +++ b/CPP/7zip/7zip_gcc.mak @@ -896,6 +896,8 @@ $O/DefaultName.o: ../../UI/Common/DefaultName.cpp $(CXX) $(CXXFLAGS) $< $O/EnumDirItems.o: ../../UI/Common/EnumDirItems.cpp $(CXX) $(CXXFLAGS) $< +$O/ChainedExtract.o: ../../UI/Common/ChainedExtract.cpp + $(CXX) $(CXXFLAGS) $< $O/Extract.o: ../../UI/Common/Extract.cpp $(CXX) $(CXXFLAGS) $< $O/ExtractingFilePath.o: ../../UI/Common/ExtractingFilePath.cpp diff --git a/CPP/7zip/Archive/Tar/TarHandler.cpp b/CPP/7zip/Archive/Tar/TarHandler.cpp index 5761ea3..93a8d18 100644 --- a/CPP/7zip/Archive/Tar/TarHandler.cpp +++ b/CPP/7zip/Archive/Tar/TarHandler.cpp @@ -730,15 +730,18 @@ Z7_COM7F_IMF(CHandler::Extract(const UInt32 *indices, UInt32 numItems, stream = _stream; const bool allFilesMode = (numItems == (UInt32)(Int32)-1); - if (allFilesMode) + if (allFilesMode && !seqMode) numItems = _items.Size(); if (_stream && numItems == 0) return S_OK; UInt64 totalSize = 0; UInt32 i; - for (i = 0; i < numItems; i++) - totalSize += _items[allFilesMode ? i : indices[i]].Get_UnpackSize(); - RINOK(extractCallback->SetTotal(totalSize)) + if (!(seqMode && allFilesMode)) + { + for (i = 0; i < numItems; i++) + totalSize += _items[allFilesMode ? i : indices[i]].Get_UnpackSize(); + RINOK(extractCallback->SetTotal(totalSize)) + } UInt64 totalPackSize; totalSize = totalPackSize = 0; diff --git a/CPP/7zip/Archive/Tar/TarIn.cpp b/CPP/7zip/Archive/Tar/TarIn.cpp index e702b68..092dd04 100644 --- a/CPP/7zip/Archive/Tar/TarIn.cpp +++ b/CPP/7zip/Archive/Tar/TarIn.cpp @@ -186,6 +186,7 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) filled = false; bool thereAreEmptyRecords = false; + unsigned numEmptyRecords = 0; for (;;) { size_t processedSize = NFileHeader::kRecordSize; @@ -219,6 +220,9 @@ HRESULT CArchive::GetNextItemReal(CItemEx &item) break; item.HeaderSize += NFileHeader::kRecordSize; thereAreEmptyRecords = true; + numEmptyRecords++; + if (!InStream && numEmptyRecords >= 2) + return S_OK; RINOK(Progress(item, 0)) } if (thereAreEmptyRecords) diff --git a/CPP/7zip/Bundles/Alone/makefile.gcc b/CPP/7zip/Bundles/Alone/makefile.gcc index 621c0ce..1cbeb9b 100644 --- a/CPP/7zip/Bundles/Alone/makefile.gcc +++ b/CPP/7zip/Bundles/Alone/makefile.gcc @@ -101,6 +101,7 @@ UI_COMMON_OBJS = \ $O/Bench.o \ $O/DefaultName.o \ $O/EnumDirItems.o \ + $O/ChainedExtract.o \ $O/Extract.o \ $O/ExtractingFilePath.o \ $O/HashCalc.o \ diff --git a/CPP/7zip/Bundles/Alone7z/makefile.gcc b/CPP/7zip/Bundles/Alone7z/makefile.gcc index a31475e..5d50380 100644 --- a/CPP/7zip/Bundles/Alone7z/makefile.gcc +++ b/CPP/7zip/Bundles/Alone7z/makefile.gcc @@ -99,6 +99,7 @@ UI_COMMON_OBJS = \ $O/Bench.o \ $O/DefaultName.o \ $O/EnumDirItems.o \ + $O/ChainedExtract.o \ $O/Extract.o \ $O/ExtractingFilePath.o \ $O/HashCalc.o \ diff --git a/CPP/7zip/Bundles/Fm/makefile b/CPP/7zip/Bundles/Fm/makefile index de74f76..8cf619b 100644 --- a/CPP/7zip/Bundles/Fm/makefile +++ b/CPP/7zip/Bundles/Fm/makefile @@ -48,6 +48,7 @@ UI_COMMON_OBJS = \ $O\CompressCall2.obj \ $O\DefaultName.obj \ $O\EnumDirItems.obj \ + $O\ChainedExtract.obj \ $O\Extract.obj \ $O\ExtractingFilePath.obj \ $O\HashCalc.obj \ diff --git a/CPP/7zip/Bundles/SFXCon/makefile b/CPP/7zip/Bundles/SFXCon/makefile index a72e3f8..c319f68 100644 --- a/CPP/7zip/Bundles/SFXCon/makefile +++ b/CPP/7zip/Bundles/SFXCon/makefile @@ -67,6 +67,7 @@ UI_COMMON_OBJS = \ $O\ArchiveExtractCallback.obj \ $O\ArchiveOpenCallback.obj \ $O\DefaultName.obj \ + $O\ChainedExtract.obj \ $O\Extract.obj \ $O\ExtractingFilePath.obj \ $O\LoadCodecs.obj \ diff --git a/CPP/7zip/Bundles/SFXCon/makefile.gcc b/CPP/7zip/Bundles/SFXCon/makefile.gcc index 87d78a9..7fe3366 100644 --- a/CPP/7zip/Bundles/SFXCon/makefile.gcc +++ b/CPP/7zip/Bundles/SFXCon/makefile.gcc @@ -129,6 +129,7 @@ UI_COMMON_OBJS = \ $O/ArchiveExtractCallback.o \ $O/ArchiveOpenCallback.o \ $O/DefaultName.o \ + $O/ChainedExtract.o \ $O/Extract.o \ $O/ExtractingFilePath.o \ $O/LoadCodecs.o \ diff --git a/CPP/7zip/Bundles/SFXWin/makefile b/CPP/7zip/Bundles/SFXWin/makefile index 806bd07..73e8b0c 100644 --- a/CPP/7zip/Bundles/SFXWin/makefile +++ b/CPP/7zip/Bundles/SFXWin/makefile @@ -76,6 +76,7 @@ UI_COMMON_OBJS = \ $O\ArchiveExtractCallback.obj \ $O\ArchiveOpenCallback.obj \ $O\DefaultName.obj \ + $O\ChainedExtract.obj \ $O\Extract.obj \ $O\ExtractingFilePath.obj \ $O\LoadCodecs.obj \ diff --git a/CPP/7zip/Common/StreamBinder.h b/CPP/7zip/Common/StreamBinder.h index c0a7079..45d468b 100644 --- a/CPP/7zip/Common/StreamBinder.h +++ b/CPP/7zip/Common/StreamBinder.h @@ -3,6 +3,7 @@ #ifndef ZIP7_INC_STREAM_BINDER_H #define ZIP7_INC_STREAM_BINDER_H +#include "../../Common/MyCom.h" #include "../../Windows/Synchronization.h" #include "../IStream.h" diff --git a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp index 73974e6..817c833 100644 --- a/CPP/7zip/UI/Common/ArchiveCommandLine.cpp +++ b/CPP/7zip/UI/Common/ArchiveCommandLine.cpp @@ -187,6 +187,7 @@ enum Enum kUseSlashMark, kDisableWildcardParsing, kElimDup, + kChainedExtract, kFullPathMode, kHardLinks, @@ -338,6 +339,7 @@ static const CSwitchForm kSwitchForms[] = { "spm", SWFRM_STRING_SINGL(0) }, { "spd", SWFRM_SIMPLE }, { "spe", SWFRM_MINUS }, + { "sce", SWFRM_MINUS }, { "spf", SWFRM_STRING_SINGL(0) }, { "snh", SWFRM_MINUS }, @@ -1358,6 +1360,9 @@ void CArcCmdLineParser::Parse2(CArcCmdLineOptions &options) options.ExtractOptions.ElimDup.Def = true; options.ExtractOptions.ElimDup.Val = !parser[NKey::kElimDup].WithMinus; } + + if (parser[NKey::kChainedExtract].ThereIs) + options.ExtractOptions.EnableChainedExtract = !parser[NKey::kChainedExtract].WithMinus; NWildcard::ECensorPathMode censorPathMode = NWildcard::k_RelatPath; bool fullPathMode = parser[NKey::kFullPathMode].ThereIs; diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp index 6631629..b7a26de 100644 --- a/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp +++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.cpp @@ -301,7 +301,8 @@ CArchiveExtractCallback::CArchiveExtractCallback(): // Write_MTime(true), Is_elimPrefix_Mode(false), _arc(NULL), - _multiArchives(false) + _multiArchives(false), + _disableProgress(false) { #ifdef Z7_USE_SECURITY_CODE _saclEnabled = InitLocalPrivileges(); @@ -311,6 +312,7 @@ CArchiveExtractCallback::CArchiveExtractCallback(): void CArchiveExtractCallback::InitBeforeNewArchive() { + _disableProgress = false; #if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX) ZoneBuf.Free(); #endif @@ -403,6 +405,8 @@ Z7_COM7F_IMF(CArchiveExtractCallback::SetTotal(UInt64 size)) COM_TRY_BEGIN _progressTotal = size; // _progressTotal_Defined = true; + if (_disableProgress) + return S_OK; if (!_multiArchives && _extractCallback2) return _extractCallback2->SetTotal(size); return S_OK; @@ -438,6 +442,9 @@ Z7_COM7F_IMF(CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)) if (!_extractCallback2) return S_OK; + if (_disableProgress) + return S_OK; + UInt64 packCur; if (_multiArchives) { @@ -455,6 +462,8 @@ Z7_COM7F_IMF(CArchiveExtractCallback::SetCompleted(const UInt64 *completeValue)) Z7_COM7F_IMF(CArchiveExtractCallback::SetRatioInfo(const UInt64 *inSize, const UInt64 *outSize)) { COM_TRY_BEGIN + if (_disableProgress) + return S_OK; return LocalProgressSpec.Interface()->SetRatioInfo(inSize, outSize); COM_TRY_END } diff --git a/CPP/7zip/UI/Common/ArchiveExtractCallback.h b/CPP/7zip/UI/Common/ArchiveExtractCallback.h index 3c62763..ab6b1e3 100644 --- a/CPP/7zip/UI/Common/ArchiveExtractCallback.h +++ b/CPP/7zip/UI/Common/ArchiveExtractCallback.h @@ -391,6 +391,7 @@ private: bool _some_pathParts_wereRemoved; bool _multiArchives; + bool _disableProgress; bool _keepAndReplaceEmptyDirPrefixes; // replace them to "_"; #if defined(_WIN32) && !defined(UNDER_CE) && !defined(Z7_SFX) bool _saclEnabled; @@ -529,6 +530,11 @@ public: void InitBeforeNewArchive(); + void DisableProgress() + { + _disableProgress = true; + } + void Init( const CExtractNtOptions &ntOptions, const NWildcard::CCensorNode *wildcardCensor, diff --git a/CPP/7zip/UI/Common/ChainedExtract.cpp b/CPP/7zip/UI/Common/ChainedExtract.cpp new file mode 100644 index 0000000..986ee6d --- /dev/null +++ b/CPP/7zip/UI/Common/ChainedExtract.cpp @@ -0,0 +1,294 @@ +// ChainedExtract.cpp + +#include "StdAfx.h" + +#include "../../Common/StreamBinder.h" +#include "../../Common/VirtThread.h" + +#include "ChainedExtract.h" + + +static void SetExtractOpResMessage(Int32 opRes, UString &message) +{ + message.Empty(); + + switch (opRes) + { + case NArchive::NExtract::NOperationResult::kUnsupportedMethod: + message = "Unsupported Method"; + break; + case NArchive::NExtract::NOperationResult::kDataError: + message = "Data Error"; + break; + case NArchive::NExtract::NOperationResult::kCRCError: + message = "CRC Failed"; + break; + case NArchive::NExtract::NOperationResult::kUnavailable: + message = "Unavailable data"; + break; + case NArchive::NExtract::NOperationResult::kUnexpectedEnd: + message = "Unexpected end of data"; + break; + case NArchive::NExtract::NOperationResult::kDataAfterEnd: + message = "There are some data after the end of the payload data"; + break; + case NArchive::NExtract::NOperationResult::kIsNotArc: + message = "Is not archive"; + break; + case NArchive::NExtract::NOperationResult::kHeadersError: + message = "Headers Error"; + break; + case NArchive::NExtract::NOperationResult::kWrongPassword: + message = "Wrong password"; + break; + } +} + + +static HRESULT CheckOuterStreamResult(Int32 opRes, UString &errorMessage) +{ + if (opRes == NArchive::NExtract::NOperationResult::kOK) + return S_OK; + + SetExtractOpResMessage(opRes, errorMessage); + if (errorMessage.IsEmpty()) + errorMessage = "Error"; + errorMessage = UString("Outer stream: ") + errorMessage; + return E_FAIL; +} + + +static HRESULT DrainChainedStream(ISequentialInStream *stream) +{ + Byte buf[1 << 15]; + + for (;;) + { + UInt32 processed = 0; + RINOK(stream->Read(buf, (UInt32)sizeof(buf), &processed)) + if (processed == 0) + return S_OK; + } +} + + +Z7_CLASS_IMP_COM_1( + CChainedStreamExtractCallback + , IArchiveExtractCallback +) + Z7_IFACE_COM7_IMP(IProgress) +public: + CMyComPtr Progress; + CMyComPtr Stream; + Int32 OperationResult; + bool MultiMode; + UInt64 CompletedBeforeArc; + + void Init(IFolderArchiveExtractCallback *progress, ISequentialOutStream *stream, + bool multiMode, UInt64 completedBeforeArc) + { + Progress = progress; + Stream = stream; + OperationResult = NArchive::NExtract::NOperationResult::kOK; + MultiMode = multiMode; + CompletedBeforeArc = completedBeforeArc; + } +}; + + +Z7_COM7F_IMF(CChainedStreamExtractCallback::SetTotal(UInt64 size)) +{ + if (MultiMode) + return S_OK; + return Progress ? Progress->SetTotal(size) : S_OK; +} + + +Z7_COM7F_IMF(CChainedStreamExtractCallback::SetCompleted(const UInt64 *completeValue)) +{ + if (MultiMode) + { + if (!Progress || !completeValue) + return S_OK; + const UInt64 completed = CompletedBeforeArc + *completeValue; + return Progress->SetCompleted(&completed); + } + return Progress ? Progress->SetCompleted(completeValue) : S_OK; +} + + +Z7_COM7F_IMF(CChainedStreamExtractCallback::GetStream( + UInt32 index, ISequentialOutStream **outStream, Int32 /* askExtractMode */)) +{ + *outStream = NULL; + if (index != 0 || !Stream) + return E_FAIL; + *outStream = Stream.Detach(); + return S_OK; +} + + +Z7_COM7F_IMF(CChainedStreamExtractCallback::PrepareOperation(Int32 /* askExtractMode */)) +{ + return S_OK; +} + + +Z7_COM7F_IMF(CChainedStreamExtractCallback::SetOperationResult(Int32 opRes)) +{ + OperationResult = opRes; + return S_OK; +} + + +struct CChainedStreamExtractThread Z7_final: + public CVirtThread +{ + const CArc *Arc; + CMyComPtr Callback; + HRESULT Result; + + CChainedStreamExtractThread(): + Arc(NULL), + Result(S_OK) + {} + + void Execute() Z7_override + { + Result = Arc->Archive->Extract(NULL, (UInt32)(Int32)-1, 0, Callback); + } +}; + + +static int FindChainedFormatIndex( + const CCodecs *codecs, + const CArc &arc, + const CExtractOptions &options) +{ + if (!options.EnableChainedExtract) + return -1; + if (options.StdInMode || options.StdOutMode) + return -1; + if (arc.FormatIndex < 0) + return -1; + const CArcInfoEx &ai = codecs->Formats[(unsigned)arc.FormatIndex]; + const UString wrappedExt = ai.GetWrappedExt(); + if (wrappedExt.IsEmpty()) + return -1; + UInt32 numItems = 0; + if (arc.Archive->GetNumberOfItems(&numItems) != S_OK || numItems != 1) + return -1; + UString ext(wrappedExt); + if (ext.IsPrefixedBy(L".")) + ext.DeleteFrontal(1); + + const int dotPos = arc.DefaultName.ReverseFind_Dot(); + if (dotPos < 0) + return -1; + if (!ext.IsEqualTo_NoCase(arc.DefaultName.Ptr((unsigned)(dotPos + 1)))) + return -1; + FOR_VECTOR (i, codecs->Formats) + if (codecs->Formats[i].FindExtension(ext) >= 0) + return (int)i; + return -1; +} + + +HRESULT TryChainedExtract( + CCodecs *codecs, + const CArchiveLink &arcLink, + UInt64 packSize, + UInt64 completedBeforeArc, + bool multi, + const NWildcard::CCensorNode &wildcardCensor, + const CExtractOptions &options, + bool calcCrc, + IExtractCallbackUI *callback, + IFolderArchiveExtractCallback *callbackFAE, + CArchiveExtractCallback *ecs, + UString &errorMessage) +{ + const CArc &arc = arcLink.Arcs.Back(); + const int innerFormatIndex = FindChainedFormatIndex(codecs, arc, options); + if (innerFormatIndex < 0) + return S_FALSE; + + CStreamBinder binder; + RINOK(binder.Create_ReInit()) + + CMyComPtr binderInStream; + CMyComPtr binderOutStream; + binder.CreateStreams2(binderInStream, binderOutStream); + + CMyComPtr2_Create outerExtractCallback; + outerExtractCallback->Init(callbackFAE, binderOutStream, multi, completedBeforeArc); + binderOutStream.Release(); + + CChainedStreamExtractThread outerExtractThread; + outerExtractThread.Arc = &arc; + outerExtractThread.Callback = outerExtractCallback; + RINOK(HRESULT_FROM_WIN32(outerExtractThread.Create())) + RINOK(HRESULT_FROM_WIN32(outerExtractThread.Start())) + + HRESULT result = S_OK; + + { + CArchiveLink innerArcLink; + CObjectVector innerTypes; + COpenType innerType; + innerType.FormatIndex = innerFormatIndex; + innerTypes.Add(innerType); + CIntVector excludedFormats; + + COpenOptions op; + #ifndef Z7_SFX + op.props = &options.Properties; + #endif + op.codecs = codecs; + op.types = &innerTypes; + op.excludedFormats = &excludedFormats; + op.seqStream = binderInStream; + op.filePath = arc.DefaultName; + + result = innerArcLink.Open(op); + if (result == S_OK) + { + UInt64 innerProcessed = 0; + ecs->DisableProgress(); + result = DecompressArchive( + codecs, + innerArcLink, + packSize, + wildcardCensor, + options, + calcCrc, + callback, + callbackFAE, + ecs, + errorMessage, + innerProcessed, + true); + } + innerArcLink.Release(); + } + + if (result == S_OK) + { + const HRESULT drainResult = DrainChainedStream(binderInStream); + if (drainResult != S_OK) + result = drainResult; + } + + binderInStream.Release(); + + RINOK(HRESULT_FROM_WIN32(outerExtractThread.WaitExecuteFinish())) + + const HRESULT outerResult = outerExtractThread.Result; + + if (outerResult != S_OK && outerResult != k_My_HRESULT_WritingWasCut) + return outerResult; + if (result != S_OK) + return result; + + return CheckOuterStreamResult(outerExtractCallback->OperationResult, errorMessage); +} diff --git a/CPP/7zip/UI/Common/ChainedExtract.h b/CPP/7zip/UI/Common/ChainedExtract.h new file mode 100644 index 0000000..d5be670 --- /dev/null +++ b/CPP/7zip/UI/Common/ChainedExtract.h @@ -0,0 +1,22 @@ +// ChainedExtract.h + +#ifndef ZIP7_INC_CHAINED_EXTRACT_H +#define ZIP7_INC_CHAINED_EXTRACT_H + +#include "Extract.h" + +HRESULT TryChainedExtract( + CCodecs *codecs, + const CArchiveLink &arcLink, + UInt64 packSize, + UInt64 completedBeforeArc, + bool multi, + const NWildcard::CCensorNode &wildcardCensor, + const CExtractOptions &options, + bool calcCrc, + IExtractCallbackUI *callback, + IFolderArchiveExtractCallback *callbackFAE, + CArchiveExtractCallback *ecs, + UString &errorMessage); + +#endif diff --git a/CPP/7zip/UI/Common/CompressCall.cpp b/CPP/7zip/UI/Common/CompressCall.cpp index 1ded1b3..c872aae 100644 --- a/CPP/7zip/UI/Common/CompressCall.cpp +++ b/CPP/7zip/UI/Common/CompressCall.cpp @@ -44,6 +44,7 @@ using namespace NWindows; #define kArcIncludeSwitches " -an -ai" ISWITCH_NO_WILDCARD_POSTFIX #define kHashIncludeSwitches kIncludeSwitch #define kStopSwitchParsing " --" +#define kChainedExtractSwitch " -sce" extern HWND g_HWND; @@ -252,7 +253,7 @@ static void ExtractGroupCommand(const UStringVector &arcPaths, UString ¶ms, ErrorMessageHRESULT(result); } -void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup, UInt32 writeZone) +void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup, UInt32 writeZone, bool enableChainedExtract) { MY_TRY_BEGIN UString params ('x'); @@ -268,6 +269,8 @@ void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bo params += " -snz"; params.Add_UInt32(writeZone); } + if (enableChainedExtract) + params += kChainedExtractSwitch; if (showDialog) params += kShowDialogSwitch; ExtractGroupCommand(arcPaths, params, false); diff --git a/CPP/7zip/UI/Common/CompressCall.h b/CPP/7zip/UI/Common/CompressCall.h index f2da163..0be3075 100644 --- a/CPP/7zip/UI/Common/CompressCall.h +++ b/CPP/7zip/UI/Common/CompressCall.h @@ -15,7 +15,7 @@ HRESULT CompressFiles( const UStringVector &names, bool email, bool showDialog, bool waitFinish); -void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup, UInt32 writeZone); +void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, bool showDialog, bool elimDup, UInt32 writeZone, bool enableChainedExtract); void TestArchives(const UStringVector &arcPaths, bool hashMode = false); void CalcChecksum(const UStringVector &paths, diff --git a/CPP/7zip/UI/Common/CompressCall2.cpp b/CPP/7zip/UI/Common/CompressCall2.cpp index fef0877..89d6fd5 100644 --- a/CPP/7zip/UI/Common/CompressCall2.cpp +++ b/CPP/7zip/UI/Common/CompressCall2.cpp @@ -224,11 +224,12 @@ static HRESULT ExtractGroupCommand(const UStringVector &arcPaths, } void ExtractArchives(const UStringVector &arcPaths, const UString &outFolder, - bool showDialog, bool elimDup, UInt32 writeZone) + bool showDialog, bool elimDup, UInt32 writeZone, bool enableChainedExtract) { CExtractOptions eo; eo.OutputDir = us2fs(outFolder); eo.TestMode = false; + eo.EnableChainedExtract = enableChainedExtract; eo.ElimDup.Val = elimDup; eo.ElimDup.Def = elimDup; if (writeZone != (UInt32)(Int32)-1) diff --git a/CPP/7zip/UI/Common/Extract.cpp b/CPP/7zip/UI/Common/Extract.cpp index 0301976..b759298 100644 --- a/CPP/7zip/UI/Common/Extract.cpp +++ b/CPP/7zip/UI/Common/Extract.cpp @@ -13,6 +13,7 @@ #include "../Common/ExtractingFilePath.h" #include "../Common/HashCalc.h" +#include "ChainedExtract.h" #include "Extract.h" #include "SetProperties.h" @@ -33,7 +34,7 @@ static void SetErrorMessage(const char *message, } -static HRESULT DecompressArchive( +HRESULT DecompressArchive( CCodecs *codecs, const CArchiveLink &arcLink, UInt64 packSize, @@ -44,12 +45,14 @@ static HRESULT DecompressArchive( IFolderArchiveExtractCallback *callbackFAE, CArchiveExtractCallback *ecs, UString &errorMessage, - UInt64 &stdInProcessed) + UInt64 &stdInProcessed, + bool chainedMode) { const CArc &arc = arcLink.Arcs.Back(); stdInProcessed = 0; IInArchive *archive = arc.Archive; CRecordVector realIndices; + const bool singlePassMode = (options.StdInMode || chainedMode); UStringVector removePathParts; @@ -90,7 +93,7 @@ static HRESULT DecompressArchive( const bool allFilesAreAllowed = wildcardCensor.AreAllAllowed(); - if (!options.StdInMode) + if (!singlePassMode) { UInt32 numItems; RINOK(archive->GetNumberOfItems(&numItems)) @@ -163,7 +166,7 @@ static HRESULT DecompressArchive( } } - if (elimIsPossible) + if (elimIsPossible && !singlePassMode) { removePathParts.Add(elimPrefix); // outDir = outDirReduced; @@ -191,7 +194,7 @@ static HRESULT DecompressArchive( ecs->Init( options.NtOptions, - options.StdInMode ? &wildcardCensor : NULL, + singlePassMode ? &wildcardCensor : NULL, &arc, callbackFAE, options.StdOutMode, options.TestMode, @@ -199,12 +202,12 @@ static HRESULT DecompressArchive( removePathParts, false, packSize); - ecs->Is_elimPrefix_Mode = elimIsPossible; + ecs->Is_elimPrefix_Mode = (elimIsPossible && !singlePassMode); #ifdef SUPPORT_LINKS - if (!options.StdInMode && + if (!singlePassMode && !options.TestMode && options.NtOptions.HardLinks.Val) { @@ -219,12 +222,15 @@ static HRESULT DecompressArchive( CArchiveExtractCallback_Closer ecsCloser(ecs); - if (options.StdInMode) + if (singlePassMode) { result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs); - NCOM::CPropVariant prop; - if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK) - ConvertPropVariantToUInt64(prop, stdInProcessed); + if (options.StdInMode) + { + NCOM::CPropVariant prop; + if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK) + ConvertPropVariantToUInt64(prop, stdInProcessed); + } } else { @@ -242,6 +248,7 @@ static HRESULT DecompressArchive( return callback->ExtractResult(result); } + /* v9.31: BUG was fixed: Sorted list for file paths was sorted with case insensitive compare function. But FindInSorted function did binary search via case sensitive compare function */ @@ -539,15 +546,41 @@ HRESULT Extract( false; #endif - RINOK(DecompressArchive( - codecs, - arcLink, - fi.Size + arcLink.VolumesSize, - wildcardCensor, - options, - calcCrc, - extractCallback, faeCallback, ecs, - errorMessage, packProcessed)) + HRESULT chainRes = S_FALSE; + + chainRes = TryChainedExtract( + codecs, + arcLink, + fi.Size + arcLink.VolumesSize, + totalPackProcessed, + multi, + wildcardCensor, + options, + calcCrc, + extractCallback, + faeCallback, + ecs, + errorMessage); + + if (chainRes != S_FALSE) + { + RINOK(chainRes) + packProcessed = fi.Size + arcLink.VolumesSize; + } + else + RINOK(DecompressArchive( + codecs, + arcLink, + fi.Size + arcLink.VolumesSize, + wildcardCensor, + options, + calcCrc, + extractCallback, + faeCallback, + ecs, + errorMessage, + packProcessed, + false)) if (!options.StdInMode) packProcessed = fi.Size + arcLink.VolumesSize; diff --git a/CPP/7zip/UI/Common/Extract.h b/CPP/7zip/UI/Common/Extract.h index defaa27..9fc7677 100644 --- a/CPP/7zip/UI/Common/Extract.h +++ b/CPP/7zip/UI/Common/Extract.h @@ -17,6 +17,7 @@ struct CExtractOptionsBase { CBoolPair ElimDup; + bool EnableChainedExtract; bool ExcludeDirItems; bool ExcludeFileItems; @@ -33,6 +34,7 @@ struct CExtractOptionsBase UString HashDir; CExtractOptionsBase(): + EnableChainedExtract(false), ExcludeDirItems(false), ExcludeFileItems(false), PathMode_Force(false), @@ -87,6 +89,20 @@ struct CDecompressStat } }; +HRESULT DecompressArchive( + CCodecs *codecs, + const CArchiveLink &arcLink, + UInt64 packSize, + const NWildcard::CCensorNode &wildcardCensor, + const CExtractOptions &options, + bool calcCrc, + IExtractCallbackUI *callback, + IFolderArchiveExtractCallback *callbackFAE, + CArchiveExtractCallback *ecs, + UString &errorMessage, + UInt64 &stdInProcessed, + bool chainedMode); + HRESULT Extract( // DECL_EXTERNAL_CODECS_LOC_VARS CCodecs *codecs, diff --git a/CPP/7zip/UI/Common/LoadCodecs.h b/CPP/7zip/UI/Common/LoadCodecs.h index a81bb5c..3a6a08c 100644 --- a/CPP/7zip/UI/Common/LoadCodecs.h +++ b/CPP/7zip/UI/Common/LoadCodecs.h @@ -184,6 +184,16 @@ struct CArcInfoEx return UString(); return Exts[0].Ext; } + + UString GetWrappedExt() const + { + if (Flags_KeepName()) + FOR_VECTOR (i, Exts) + if (!Exts[i].AddExt.IsEmpty()) + return Exts[i].AddExt; + return UString(); + } + int FindExtension(const UString &ext) const; bool Is_7z() const { return Name.IsEqualTo_Ascii_NoCase("7z"); } diff --git a/CPP/7zip/UI/Common/OpenArchive.cpp b/CPP/7zip/UI/Common/OpenArchive.cpp index c26d4c0..dd6384a 100644 --- a/CPP/7zip/UI/Common/OpenArchive.cpp +++ b/CPP/7zip/UI/Common/OpenArchive.cpp @@ -3094,11 +3094,10 @@ HRESULT CArc::OpenStreamOrFile(COpenOptions &op) #endif op.seqStream = seqStream; } - else if (!op.stream) + else if (!op.stream && !op.seqStream) { fileStreamSpec = new CInFileStream; fileStream = fileStreamSpec; - Path = filePath; if (!fileStreamSpec->Open(us2fs(Path))) return GetLastError_noZero_HRESULT(); op.stream = fileStream; diff --git a/CPP/7zip/UI/Console/Console.mak b/CPP/7zip/UI/Console/Console.mak index 4effea6..fe5f931 100644 --- a/CPP/7zip/UI/Console/Console.mak +++ b/CPP/7zip/UI/Console/Console.mak @@ -25,6 +25,7 @@ UI_COMMON_OBJS = \ $O\Bench.obj \ $O\DefaultName.obj \ $O\EnumDirItems.obj \ + $O\ChainedExtract.obj \ $O\Extract.obj \ $O\ExtractingFilePath.obj \ $O\HashCalc.obj \ diff --git a/CPP/7zip/UI/Console/Main.cpp b/CPP/7zip/UI/Console/Main.cpp index 90e00a4..c4499a2 100644 --- a/CPP/7zip/UI/Console/Main.cpp +++ b/CPP/7zip/UI/Console/Main.cpp @@ -165,6 +165,7 @@ static const char * const kHelpString = " -p{Password} : set Password\n" #endif " -r[-|0] : Recurse subdirectories for name search\n" + " -sce : chained extract archives with tar wrapper in a single operation\n" " -sa{a|e|s} : set Archive name mode\n" " -scc{UTF-8|WIN|DOS} : set charset for console input/output\n" " -scs{UTF-8|UTF-16LE|UTF-16BE|WIN|DOS|{id}} : set charset for list files\n" diff --git a/CPP/7zip/UI/Console/makefile b/CPP/7zip/UI/Console/makefile index d449b38..cfa2cd4 100644 --- a/CPP/7zip/UI/Console/makefile +++ b/CPP/7zip/UI/Console/makefile @@ -45,9 +45,11 @@ WIN_OBJS = \ $O\MultiOutStream.obj \ $O\ProgressUtils.obj \ $O\PropId.obj \ + $O\StreamBinder.obj \ $O\StreamObjects.obj \ $O\StreamUtils.obj \ $O\UniqBlocks.obj \ + $O\VirtThread.obj \ AR_COMMON_OBJS = \ $O\ItemNameUtils.obj \ diff --git a/CPP/7zip/UI/FileManager/Panel.cpp b/CPP/7zip/UI/FileManager/Panel.cpp index 84bd88c..9d72934 100644 --- a/CPP/7zip/UI/FileManager/Panel.cpp +++ b/CPP/7zip/UI/FileManager/Panel.cpp @@ -1035,6 +1035,7 @@ void CPanel::ExtractArchives() , true // showDialog , false // elimDup , ci.WriteZone + , false // enableChainedExtract ); } diff --git a/CPP/7zip/UI/GUI/GUI.cpp b/CPP/7zip/UI/GUI/GUI.cpp index 6bb6693..1a13f63 100644 --- a/CPP/7zip/UI/GUI/GUI.cpp +++ b/CPP/7zip/UI/GUI/GUI.cpp @@ -282,6 +282,12 @@ static int Main2() } #endif + if (options.StdInMode) + { + ArchivePathsSorted.Add(options.ArcName_for_StdInMode); + ArchivePathsFullSorted.Add(options.ArcName_for_StdInMode); + } + else { CDirItemsStat st; HRESULT hresultMain = EnumerateDirItemsAndSort( diff --git a/CPP/7zip/UI/GUI/GUI.dsp b/CPP/7zip/UI/GUI/GUI.dsp index 1742f26..4d7e20d 100644 --- a/CPP/7zip/UI/GUI/GUI.dsp +++ b/CPP/7zip/UI/GUI/GUI.dsp @@ -693,6 +693,10 @@ SOURCE=..\..\Common\PropId.cpp # End Source File # Begin Source File +SOURCE=..\..\Common\StreamBinder.cpp +# End Source File +# Begin Source File + SOURCE=..\..\Common\StreamObjects.cpp # End Source File # Begin Source File @@ -715,6 +719,14 @@ SOURCE=..\..\Common\UniqBlocks.cpp SOURCE=..\..\Common\UniqBlocks.h # End Source File +# Begin Source File + +SOURCE=..\..\Common\VirtThread.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\Common\VirtThread.h +# End Source File # End Group # Begin Group "Compress" diff --git a/CPP/7zip/UI/GUI/makefile b/CPP/7zip/UI/GUI/makefile index b879a5d..7298612 100644 --- a/CPP/7zip/UI/GUI/makefile +++ b/CPP/7zip/UI/GUI/makefile @@ -76,9 +76,11 @@ WIN_CTRL_OBJS = \ $O\MultiOutStream.obj \ $O\ProgressUtils.obj \ $O\PropId.obj \ + $O\StreamBinder.obj \ $O\StreamObjects.obj \ $O\StreamUtils.obj \ $O\UniqBlocks.obj \ + $O\VirtThread.obj \ UI_COMMON_OBJS = \ $O\ArchiveCommandLine.obj \ @@ -87,6 +89,7 @@ UI_COMMON_OBJS = \ $O\Bench.obj \ $O\DefaultName.obj \ $O\EnumDirItems.obj \ + $O\ChainedExtract.obj \ $O\Extract.obj \ $O\ExtractingFilePath.obj \ $O\HashCalc.obj \