Source code

Revision control

Copy as Markdown

Other Tools

// Extract.cpp↩
#include "StdAfx.h"
#include "../../../../C/Sort.h"
#include "../../../Common/StringConvert.h"
#include "../../../Windows/FileDir.h"
#include "../../../Windows/PropVariant.h"
#include "../../../Windows/PropVariantConv.h"
#include "../Common/ExtractingFilePath.h"
#include "Extract.h"
#include "SetProperties.h"
using namespace NWindows;↩
using namespace NFile;↩
using namespace NDir;↩
static HRESULT DecompressArchive(↩
CCodecs *codecs,↩
const CArchiveLink &arcLink,↩
UInt64 packSize,↩
const NWildcard::CCensorNode &wildcardCensor,↩
const CExtractOptions &options,↩
bool calcCrc,↩
IExtractCallbackUI *callback,↩
CArchiveExtractCallback *ecs,↩
UString &errorMessage,↩
UInt64 &stdInProcessed)↩
{↩
const CArc &arc = arcLink.Arcs.Back();↩
stdInProcessed = 0;↩
IInArchive *archive = arc.Archive;↩
CRecordVector<UInt32> realIndices;↩
UStringVector removePathParts;↩
FString outDir = options.OutputDir;↩
UString replaceName = arc.DefaultName;↩
if (arcLink.Arcs.Size() > 1)↩
{↩
// Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1".↩
// So it extracts different archives to one folder.↩
// We will use top level archive name↩
const CArc &arc0 = arcLink.Arcs[0];↩
if (StringsAreEqualNoCase_Ascii(codecs->Formats[arc0.FormatIndex].Name, "pe"))↩
replaceName = arc0.DefaultName;↩
}↩
outDir.Replace(FString("*"), us2fs(Get_Correct_FsFile_Name(replaceName)));↩
bool elimIsPossible = false;↩
UString elimPrefix; // only pure name without dir delimiter↩
FString outDirReduced = outDir;↩
if (options.ElimDup.Val && options.PathMode != NExtract::NPathMode::kAbsPaths)↩
{↩
UString dirPrefix;↩
SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix);↩
if (!elimPrefix.IsEmpty())↩
{↩
if (IsPathSepar(elimPrefix.Back()))↩
elimPrefix.DeleteBack();↩
if (!elimPrefix.IsEmpty())↩
{↩
outDirReduced = us2fs(dirPrefix);↩
elimIsPossible = true;↩
}↩
}↩
}↩
bool allFilesAreAllowed = wildcardCensor.AreAllAllowed();↩
if (!options.StdInMode)↩
{↩
UInt32 numItems;↩
RINOK(archive->GetNumberOfItems(&numItems));↩
CReadArcItem item;↩
for (UInt32 i = 0; i < numItems; i++)↩
{↩
if (elimIsPossible || !allFilesAreAllowed)↩
{↩
RINOK(arc.GetItem(i, item));↩
}↩
else
{↩
#ifdef SUPPORT_ALT_STREAMS↩
item.IsAltStream = false;↩
if (!options.NtOptions.AltStreams.Val && arc.Ask_AltStream)↩
{↩
RINOK(Archive_IsItem_AltStream(arc.Archive, i, item.IsAltStream));↩
}↩
#endif
}↩
#ifdef SUPPORT_ALT_STREAMS↩
if (!options.NtOptions.AltStreams.Val && item.IsAltStream)↩
continue;↩
#endif
if (elimIsPossible)↩
{↩
const UString &s =↩
#ifdef SUPPORT_ALT_STREAMS↩
item.MainPath;↩
#else
item.Path;↩
#endif
if (!IsPath1PrefixedByPath2(s, elimPrefix))↩
elimIsPossible = false;↩
else
{↩
wchar_t c = s[elimPrefix.Len()];↩
if (c == 0)↩
{↩
if (!item.MainIsDir)↩
elimIsPossible = false;↩
}↩
else if (!IsPathSepar(c))↩
elimIsPossible = false;↩
}↩
}↩
if (!allFilesAreAllowed)↩
{↩
if (!CensorNode_CheckPath(wildcardCensor, item))↩
continue;↩
}↩
realIndices.Add(i);↩
}↩
if (realIndices.Size() == 0)↩
{↩
callback->ThereAreNoFiles();↩
return callback->ExtractResult(S_OK);↩
}↩
}↩
if (elimIsPossible)↩
{↩
removePathParts.Add(elimPrefix);↩
// outDir = outDirReduced;↩
}↩
#ifdef _WIN32↩
// GetCorrectFullFsPath doesn't like "..".↩
// outDir.TrimRight();↩
// outDir = GetCorrectFullFsPath(outDir);↩
#endif
if (outDir.IsEmpty())↩
outDir = "." STRING_PATH_SEPARATOR;↩
/*↩
#ifdef _WIN32↩
else if (NName::IsAltPathPrefix(outDir)) {}↩
#endif↩
*/
else if (!CreateComplexDir(outDir))↩
{↩
HRESULT res = ::GetLastError();↩
if (res == S_OK)↩
res = E_FAIL;↩
errorMessage = "Can not create output directory: ";↩
errorMessage += fs2us(outDir);↩
return res;↩
}↩
ecs->Init(↩
options.NtOptions,↩
options.StdInMode ? &wildcardCensor : NULL,↩
&arc,↩
callback,↩
options.StdOutMode, options.TestMode,↩
outDir,↩
removePathParts, false,↩
packSize);↩
#ifdef SUPPORT_LINKS↩
if (!options.StdInMode &&↩
!options.TestMode &&↩
options.NtOptions.HardLinks.Val)↩
{↩
RINOK(ecs->PrepareHardLinks(&realIndices));↩
}↩
#endif
HRESULT result;↩
Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0;↩
CArchiveExtractCallback_Closer ecsCloser(ecs);↩
if (options.StdInMode)↩
{↩
result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs);↩
NCOM::CPropVariant prop;↩
if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)↩
ConvertPropVariantToUInt64(prop, stdInProcessed);↩
}↩
else
result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs);↩
HRESULT res2 = ecsCloser.Close();↩
if (result == S_OK)↩
result = res2;↩
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 */
int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name)↩
{↩
unsigned left = 0, right = fileName.Size();↩
while (left != right)↩
{↩
unsigned mid = (left + right) / 2;↩
const UString &midValue = fileName[mid];↩
int compare = CompareFileNames(name, midValue);↩
if (compare == 0)↩
return mid;↩
if (compare < 0)↩
right = mid;↩
else
left = mid + 1;↩
}↩
return -1;↩
}↩
HRESULT Extract(↩
CCodecs *codecs,↩
const CObjectVector<COpenType> &types,↩
const CIntVector &excludedFormats,↩
UStringVector &arcPaths, UStringVector &arcPathsFull,↩
const NWildcard::CCensorNode &wildcardCensor,↩
const CExtractOptions &options,↩
IOpenCallbackUI *openCallback,↩
IExtractCallbackUI *extractCallback,↩
#ifndef _SFX↩
IHashCalc *hash,↩
#endif
UString &errorMessage,↩
CDecompressStat &st)↩
{↩
st.Clear();↩
UInt64 totalPackSize = 0;↩
CRecordVector<UInt64> arcSizes;↩
unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size();↩
unsigned i;↩
for (i = 0; i < numArcs; i++)↩
{↩
NFind::CFileInfo fi;↩
fi.Size = 0;↩
if (!options.StdInMode)↩
{↩
const FString &arcPath = us2fs(arcPaths[i]);↩
if (!fi.Find(arcPath))↩
throw "there is no such archive";↩
if (fi.IsDir())↩
throw "can't decompress folder";↩
}↩
arcSizes.Add(fi.Size);↩
totalPackSize += fi.Size;↩
}↩
CBoolArr skipArcs(numArcs);↩
for (i = 0; i < numArcs; i++)↩
skipArcs[i] = false;↩
CArchiveExtractCallback *ecs = new CArchiveExtractCallback;↩
CMyComPtr<IArchiveExtractCallback> ec(ecs);↩
bool multi = (numArcs > 1);↩
ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode,↩
false // keepEmptyDirParts↩
);↩
#ifndef _SFX↩
ecs->SetHashMethods(hash);↩
#endif
if (multi)↩
{↩
RINOK(extractCallback->SetTotal(totalPackSize));↩
}↩
UInt64 totalPackProcessed = 0;↩
bool thereAreNotOpenArcs = false;↩
for (i = 0; i < numArcs; i++)↩
{↩
if (skipArcs[i])↩
continue;↩
const UString &arcPath = arcPaths[i];↩
NFind::CFileInfo fi;↩
if (options.StdInMode)↩
{↩
fi.Size = 0;↩
fi.Attrib = 0;↩
}↩
else
{↩
if (!fi.Find(us2fs(arcPath)) || fi.IsDir())↩
throw "there is no such archive";↩
}↩
/*↩
#ifndef _NO_CRYPTO↩
openCallback->Open_Clear_PasswordWasAsked_Flag();↩
#endif↩
*/
RINOK(extractCallback->BeforeOpen(arcPath, options.TestMode));↩
CArchiveLink arcLink;↩
CObjectVector<COpenType> types2 = types;↩
/*↩
#ifndef _SFX↩
if (types.IsEmpty())↩
{↩
int pos = arcPath.ReverseFind(L'.');↩
if (pos >= 0)↩
{↩
UString s = arcPath.Ptr(pos + 1);↩
int index = codecs->FindFormatForExtension(s);↩
if (index >= 0 && s == L"001")↩
{↩
s = arcPath.Left(pos);↩
pos = s.ReverseFind(L'.');↩
if (pos >= 0)↩
{↩
int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1));↩
if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0↩
{↩
types2.Add(index2);↩
types2.Add(index);↩
}↩
}↩
}↩
}↩
}↩
#endif↩
*/
COpenOptions op;↩
#ifndef _SFX↩
op.props = &options.Properties;↩
#endif
op.codecs = codecs;↩
op.types = &types2;↩
op.excludedFormats = &excludedFormats;↩
op.stdInMode = options.StdInMode;↩
op.stream = NULL;↩
op.filePath = arcPath;↩
HRESULT result = arcLink.Open_Strict(op, openCallback);↩
if (result == E_ABORT)↩
return result;↩
// arcLink.Set_ErrorsText();↩
RINOK(extractCallback->OpenResult(codecs, arcLink, arcPath, result));↩
if (result != S_OK)↩
{↩
thereAreNotOpenArcs = true;↩
if (!options.StdInMode)↩
{↩
NFind::CFileInfo fi2;↩
if (fi2.Find(us2fs(arcPath)))↩
if (!fi2.IsDir())↩
totalPackProcessed += fi2.Size;↩
}↩
continue;↩
}↩
if (!options.StdInMode)↩
{↩
// numVolumes += arcLink.VolumePaths.Size();↩
// arcLink.VolumesSize;↩
// totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes);↩
// numArcs = arcPaths.Size();↩
if (arcLink.VolumePaths.Size() != 0)↩
{↩
Int64 correctionSize = arcLink.VolumesSize;↩
FOR_VECTOR (v, arcLink.VolumePaths)↩
{↩
int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]);↩
if (index >= 0)↩
{↩
if ((unsigned)index > i)↩
{↩
skipArcs[(unsigned)index] = true;↩
correctionSize -= arcSizes[(unsigned)index];↩
}↩
}↩
}↩
if (correctionSize != 0)↩
{↩
Int64 newPackSize = (Int64)totalPackSize + correctionSize;↩
if (newPackSize < 0)↩
newPackSize = 0;↩
totalPackSize = newPackSize;↩
RINOK(extractCallback->SetTotal(totalPackSize));↩
}↩
}↩
}↩
/*↩
// Now openCallback and extractCallback use same object. So we don't need to send password.↩
#ifndef _NO_CRYPTO↩
bool passwordIsDefined;↩
UString password;↩
RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password));↩
if (passwordIsDefined)↩
{↩
RINOK(extractCallback->SetPassword(password));↩
}↩
#endif↩
*/
CArc &arc = arcLink.Arcs.Back();↩
arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);↩
arc.MTime = fi.MTime;↩
UInt64 packProcessed;↩
bool calcCrc =↩
#ifndef _SFX↩
(hash != NULL);↩
#else
false;↩
#endif
RINOK(DecompressArchive(↩
codecs,↩
arcLink,↩
fi.Size + arcLink.VolumesSize,↩
wildcardCensor,↩
options,↩
calcCrc,↩
extractCallback, ecs, errorMessage, packProcessed));↩
if (!options.StdInMode)↩
packProcessed = fi.Size + arcLink.VolumesSize;↩
totalPackProcessed += packProcessed;↩
ecs->LocalProgressSpec->InSize += packProcessed;↩
ecs->LocalProgressSpec->OutSize = ecs->UnpackSize;↩
if (!errorMessage.IsEmpty())↩
return E_FAIL;↩
}↩
if (multi || thereAreNotOpenArcs)↩
{↩
RINOK(extractCallback->SetTotal(totalPackSize));↩
RINOK(extractCallback->SetCompleted(&totalPackProcessed));↩
}↩
st.NumFolders = ecs->NumFolders;↩
st.NumFiles = ecs->NumFiles;↩
st.NumAltStreams = ecs->NumAltStreams;↩
st.UnpackSize = ecs->UnpackSize;↩
st.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize;↩
st.NumArchives = arcPaths.Size();↩
st.PackSize = ecs->LocalProgressSpec->InSize;↩
return S_OK;↩
}↩