Home | History | Annotate | Download | only in Common
      1 // Extract.cpp
      2 
      3 #include "StdAfx.h"
      4 
      5 #include <stdio.h>
      6 
      7 #include "Windows/FileDir.h"
      8 #include "Windows/PropVariant.h"
      9 #include "Windows/PropVariantConversions.h"
     10 
     11 #include "../Common/ExtractingFilePath.h"
     12 
     13 #include "Extract.h"
     14 #include "SetProperties.h"
     15 
     16 using namespace NWindows;
     17 
     18 static HRESULT DecompressArchive(
     19     const CArc &arc,
     20     UInt64 packSize,
     21     const NWildcard::CCensorNode &wildcardCensor,
     22     const CExtractOptions &options,
     23     IExtractCallbackUI *callback,
     24     CArchiveExtractCallback *extractCallbackSpec,
     25     UString &errorMessage,
     26     UInt64 &stdInProcessed)
     27 {
     28   stdInProcessed = 0;
     29   IInArchive *archive = arc.Archive;
     30   CRecordVector<UInt32> realIndices;
     31   if (!options.StdInMode)
     32   {
     33     UInt32 numItems;
     34     RINOK(archive->GetNumberOfItems(&numItems));
     35 
     36     for (UInt32 i = 0; i < numItems; i++)
     37     {
     38       UString filePath;
     39       RINOK(arc.GetItemPath(i, filePath));
     40       bool isFolder;
     41       RINOK(IsArchiveItemFolder(archive, i, isFolder));
     42       if (!wildcardCensor.CheckPath(filePath, !isFolder))
     43         continue;
     44       realIndices.Add(i);
     45     }
     46     if (realIndices.Size() == 0)
     47     {
     48       callback->ThereAreNoFiles();
     49       return S_OK;
     50     }
     51   }
     52 
     53   UStringVector removePathParts;
     54 
     55   UString outDir = options.OutputDir;
     56   outDir.Replace(L"*", GetCorrectFsPath(arc.DefaultName));
     57   #ifdef _WIN32
     58   // GetCorrectFullFsPath doesn't like "..".
     59   // outDir.TrimRight();
     60   // outDir = GetCorrectFullFsPath(outDir);
     61   #endif
     62 
     63   if (!outDir.IsEmpty())
     64     if (!NFile::NDirectory::CreateComplexDirectory(outDir))
     65     {
     66       HRESULT res = ::GetLastError();
     67       if (res == S_OK)
     68         res = E_FAIL;
     69       errorMessage = ((UString)L"Can not create output directory ") + outDir;
     70       return res;
     71     }
     72 
     73   extractCallbackSpec->Init(
     74       options.StdInMode ? &wildcardCensor : NULL,
     75       &arc,
     76       callback,
     77       options.StdOutMode, options.TestMode, options.CalcCrc,
     78       outDir,
     79       removePathParts,
     80       packSize);
     81 
     82   #if !defined(_7ZIP_ST) && !defined(_SFX)
     83   RINOK(SetProperties(archive, options.Properties));
     84   #endif
     85 
     86   HRESULT result;
     87   Int32 testMode = (options.TestMode && !options.CalcCrc) ? 1: 0;
     88   if (options.StdInMode)
     89   {
     90     result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, extractCallbackSpec);
     91     NCOM::CPropVariant prop;
     92     if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)
     93       if (prop.vt == VT_UI8 || prop.vt == VT_UI4)
     94         stdInProcessed = ConvertPropVariantToUInt64(prop);
     95   }
     96   else
     97     result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, extractCallbackSpec);
     98 
     99   return callback->ExtractResult(result);
    100 }
    101 
    102 HRESULT DecompressArchives(
    103     CCodecs *codecs, const CIntVector &formatIndices,
    104     UStringVector &arcPaths, UStringVector &arcPathsFull,
    105     const NWildcard::CCensorNode &wildcardCensor,
    106     const CExtractOptions &options,
    107     IOpenCallbackUI *openCallback,
    108     IExtractCallbackUI *extractCallback,
    109     UString &errorMessage,
    110     CDecompressStat &stat)
    111 {
    112   stat.Clear();
    113   int i;
    114   UInt64 totalPackSize = 0;
    115   CRecordVector<UInt64> archiveSizes;
    116 
    117   int numArcs = options.StdInMode ? 1 : arcPaths.Size();
    118 
    119   for (i = 0; i < numArcs; i++)
    120   {
    121     NFile::NFind::CFileInfoW fi;
    122     fi.Size = 0;
    123     if (!options.StdInMode)
    124     {
    125       const UString &arcPath = arcPaths[i];
    126       if (!fi.Find(arcPath))
    127         throw "there is no such archive";
    128       if (fi.IsDir())
    129         throw "can't decompress folder";
    130     }
    131     archiveSizes.Add(fi.Size);
    132     totalPackSize += fi.Size;
    133   }
    134   CArchiveExtractCallback *extractCallbackSpec = new CArchiveExtractCallback;
    135   CMyComPtr<IArchiveExtractCallback> ec(extractCallbackSpec);
    136   bool multi = (numArcs > 1);
    137   extractCallbackSpec->InitForMulti(multi, options.PathMode, options.OverwriteMode);
    138   if (multi)
    139   {
    140     RINOK(extractCallback->SetTotal(totalPackSize));
    141   }
    142   for (i = 0; i < numArcs; i++)
    143   {
    144     const UString &arcPath = arcPaths[i];
    145     NFile::NFind::CFileInfoW fi;
    146     if (options.StdInMode)
    147     {
    148       fi.Size = 0;
    149       fi.Attrib = 0;
    150     }
    151     else
    152     {
    153       if (!fi.Find(arcPath) || fi.IsDir())
    154         throw "there is no such archive";
    155     }
    156 
    157     #ifndef _NO_CRYPTO
    158     openCallback->Open_ClearPasswordWasAskedFlag();
    159     #endif
    160 
    161     RINOK(extractCallback->BeforeOpen(arcPath));
    162     CArchiveLink archiveLink;
    163 
    164     CIntVector formatIndices2 = formatIndices;
    165     #ifndef _SFX
    166     if (formatIndices.IsEmpty())
    167     {
    168       int pos = arcPath.ReverseFind(L'.');
    169       if (pos >= 0)
    170       {
    171         UString s = arcPath.Mid(pos + 1);
    172         int index = codecs->FindFormatForExtension(s);
    173         if (index >= 0 && s == L"001")
    174         {
    175           s = arcPath.Left(pos);
    176           pos = s.ReverseFind(L'.');
    177           if (pos >= 0)
    178           {
    179             int index2 = codecs->FindFormatForExtension(s.Mid(pos + 1));
    180             if (index2 >= 0 && s.CompareNoCase(L"rar") != 0)
    181             {
    182               formatIndices2.Add(index2);
    183               formatIndices2.Add(index);
    184             }
    185           }
    186         }
    187       }
    188     }
    189     #endif
    190     HRESULT result = archiveLink.Open2(codecs, formatIndices2, options.StdInMode, NULL, arcPath, openCallback);
    191     if (result == E_ABORT)
    192       return result;
    193 
    194     bool crypted = false;
    195     #ifndef _NO_CRYPTO
    196     crypted = openCallback->Open_WasPasswordAsked();
    197     #endif
    198 
    199     RINOK(extractCallback->OpenResult(arcPath, result, crypted));
    200     if (result != S_OK)
    201       continue;
    202 
    203     if (!options.StdInMode)
    204     for (int v = 0; v < archiveLink.VolumePaths.Size(); v++)
    205     {
    206       int index = arcPathsFull.FindInSorted(archiveLink.VolumePaths[v]);
    207       if (index >= 0 && index > i)
    208       {
    209         arcPaths.Delete(index);
    210         arcPathsFull.Delete(index);
    211         totalPackSize -= archiveSizes[index];
    212         archiveSizes.Delete(index);
    213         numArcs = arcPaths.Size();
    214       }
    215     }
    216     if (archiveLink.VolumePaths.Size() != 0)
    217     {
    218       totalPackSize += archiveLink.VolumesSize;
    219       RINOK(extractCallback->SetTotal(totalPackSize));
    220     }
    221 
    222     #ifndef _NO_CRYPTO
    223     UString password;
    224     RINOK(openCallback->Open_GetPasswordIfAny(password));
    225     if (!password.IsEmpty())
    226     {
    227       RINOK(extractCallback->SetPassword(password));
    228     }
    229     #endif
    230 
    231     for (int v = 0; v < archiveLink.Arcs.Size(); v++)
    232     {
    233       const UString &s = archiveLink.Arcs[v].ErrorMessage;
    234       if (!s.IsEmpty())
    235       {
    236         RINOK(extractCallback->MessageError(s));
    237       }
    238     }
    239 
    240     CArc &arc = archiveLink.Arcs.Back();
    241     arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);
    242     arc.MTime = fi.MTime;
    243 
    244     UInt64 packProcessed;
    245     RINOK(DecompressArchive(arc,
    246         fi.Size + archiveLink.VolumesSize,
    247         wildcardCensor, options, extractCallback, extractCallbackSpec, errorMessage, packProcessed));
    248     if (!options.StdInMode)
    249       packProcessed = fi.Size + archiveLink.VolumesSize;
    250     extractCallbackSpec->LocalProgressSpec->InSize += packProcessed;
    251     extractCallbackSpec->LocalProgressSpec->OutSize = extractCallbackSpec->UnpackSize;
    252     if (!errorMessage.IsEmpty())
    253       return E_FAIL;
    254   }
    255   stat.NumFolders = extractCallbackSpec->NumFolders;
    256   stat.NumFiles = extractCallbackSpec->NumFiles;
    257   stat.UnpackSize = extractCallbackSpec->UnpackSize;
    258   stat.CrcSum = extractCallbackSpec->CrcSum;
    259 
    260   stat.NumArchives = arcPaths.Size();
    261   stat.PackSize = extractCallbackSpec->LocalProgressSpec->InSize;
    262   return S_OK;
    263 }
    264