1 // Extract.cpp
2
3 #include "StdAfx.h"
4
5 #include "../../../../C/Sort.h"
6
7 #include "../../../Common/StringConvert.h"
8
9 #include "../../../Windows/FileDir.h"
10 #include "../../../Windows/PropVariant.h"
11 #include "../../../Windows/PropVariantConv.h"
12
13 #include "../Common/ExtractingFilePath.h"
14
15 #include "Extract.h"
16 #include "SetProperties.h"
17
18 using namespace NWindows;
19 using namespace NFile;
20 using namespace NDir;
21
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)22 static HRESULT DecompressArchive(
23 CCodecs *codecs,
24 const CArchiveLink &arcLink,
25 UInt64 packSize,
26 const NWildcard::CCensorNode &wildcardCensor,
27 const CExtractOptions &options,
28 bool calcCrc,
29 IExtractCallbackUI *callback,
30 CArchiveExtractCallback *ecs,
31 UString &errorMessage,
32 UInt64 &stdInProcessed)
33 {
34 const CArc &arc = arcLink.Arcs.Back();
35 stdInProcessed = 0;
36 IInArchive *archive = arc.Archive;
37 CRecordVector<UInt32> realIndices;
38
39 UStringVector removePathParts;
40
41 FString outDir = options.OutputDir;
42 UString replaceName = arc.DefaultName;
43
44 if (arcLink.Arcs.Size() > 1)
45 {
46 // Most "pe" archives have same name of archive subfile "[0]" or ".rsrc_1".
47 // So it extracts different archives to one folder.
48 // We will use top level archive name
49 const CArc &arc0 = arcLink.Arcs[0];
50 if (StringsAreEqualNoCase_Ascii(codecs->Formats[arc0.FormatIndex].Name, "pe"))
51 replaceName = arc0.DefaultName;
52 }
53
54 outDir.Replace(FSTRING_ANY_MASK, us2fs(GetCorrectFsPath(replaceName)));
55
56 bool elimIsPossible = false;
57 UString elimPrefix; // only pure name without dir delimiter
58 FString outDirReduced = outDir;
59
60 if (options.ElimDup.Val)
61 {
62 UString dirPrefix;
63 SplitPathToParts_Smart(fs2us(outDir), dirPrefix, elimPrefix);
64 if (!elimPrefix.IsEmpty())
65 {
66 if (IsCharDirLimiter(elimPrefix.Back()))
67 elimPrefix.DeleteBack();
68 if (!elimPrefix.IsEmpty())
69 {
70 outDirReduced = us2fs(dirPrefix);
71 elimIsPossible = true;
72 }
73 }
74 }
75
76 if (!options.StdInMode)
77 {
78 UInt32 numItems;
79 RINOK(archive->GetNumberOfItems(&numItems));
80
81 UString filePath;
82
83 for (UInt32 i = 0; i < numItems; i++)
84 {
85 RINOK(arc.GetItemPath(i, filePath));
86
87 if (elimIsPossible && options.ElimDup.Val)
88 {
89 if (!IsPath1PrefixedByPath2(filePath, elimPrefix))
90 elimIsPossible = false;
91 else
92 {
93 wchar_t c = filePath[elimPrefix.Len()];
94 if (c != 0 && !IsCharDirLimiter(c))
95 elimIsPossible = false;
96 }
97 }
98
99 bool isFolder;
100 RINOK(Archive_IsItem_Folder(archive, i, isFolder));
101 bool isAltStream;
102 RINOK(Archive_IsItem_AltStream(archive, i, isAltStream));
103 if (!options.NtOptions.AltStreams.Val && isAltStream)
104 continue;
105 if (!wildcardCensor.CheckPath(isAltStream, filePath, !isFolder))
106 continue;
107 realIndices.Add(i);
108 }
109
110 if (realIndices.Size() == 0)
111 {
112 callback->ThereAreNoFiles();
113 return callback->ExtractResult(S_OK);
114 }
115 }
116
117 if (elimIsPossible)
118 outDir = outDirReduced;
119
120 #ifdef _WIN32
121 // GetCorrectFullFsPath doesn't like "..".
122 // outDir.TrimRight();
123 // outDir = GetCorrectFullFsPath(outDir);
124 #endif
125
126 if (outDir.IsEmpty())
127 outDir = FString(FTEXT(".")) + FString(FSTRING_PATH_SEPARATOR);
128 else
129 if (!CreateComplexDir(outDir))
130 {
131 HRESULT res = ::GetLastError();
132 if (res == S_OK)
133 res = E_FAIL;
134 errorMessage = ((UString)L"Can not create output directory ") + fs2us(outDir);
135 return res;
136 }
137
138 ecs->Init(
139 options.NtOptions,
140 options.StdInMode ? &wildcardCensor : NULL,
141 &arc,
142 callback,
143 options.StdOutMode, options.TestMode,
144 outDir,
145 removePathParts,
146 packSize);
147
148
149 #ifdef SUPPORT_LINKS
150
151 if (!options.StdInMode &&
152 !options.TestMode &&
153 options.NtOptions.HardLinks.Val)
154 {
155 RINOK(ecs->PrepareHardLinks(&realIndices));
156 }
157
158 #endif
159
160
161 HRESULT result;
162 Int32 testMode = (options.TestMode && !calcCrc) ? 1: 0;
163 if (options.StdInMode)
164 {
165 result = archive->Extract(NULL, (UInt32)(Int32)-1, testMode, ecs);
166 NCOM::CPropVariant prop;
167 if (archive->GetArchiveProperty(kpidPhySize, &prop) == S_OK)
168 ConvertPropVariantToUInt64(prop, stdInProcessed);
169 }
170 else
171 result = archive->Extract(&realIndices.Front(), realIndices.Size(), testMode, ecs);
172 if (result == S_OK && !options.StdInMode)
173 result = ecs->SetDirsTimes();
174 return callback->ExtractResult(result);
175 }
176
177 /* v9.31: BUG was fixed:
178 Sorted list for file paths was sorted with case insensitive compare function.
179 But FindInSorted function did binary search via case sensitive compare function */
180
Find_FileName_InSortedVector(const UStringVector & fileName,const UString & name)181 int Find_FileName_InSortedVector(const UStringVector &fileName, const UString &name)
182 {
183 unsigned left = 0, right = fileName.Size();
184 while (left != right)
185 {
186 unsigned mid = (left + right) / 2;
187 const UString &midValue = fileName[mid];
188 int compare = CompareFileNames(name, midValue);
189 if (compare == 0)
190 return mid;
191 if (compare < 0)
192 right = mid;
193 else
194 left = mid + 1;
195 }
196 return -1;
197 }
198
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,IHashCalc * hash,UString & errorMessage,CDecompressStat & stat)199 HRESULT Extract(
200 CCodecs *codecs,
201 const CObjectVector<COpenType> &types,
202 const CIntVector &excludedFormats,
203 UStringVector &arcPaths, UStringVector &arcPathsFull,
204 const NWildcard::CCensorNode &wildcardCensor,
205 const CExtractOptions &options,
206 IOpenCallbackUI *openCallback,
207 IExtractCallbackUI *extractCallback,
208 #ifndef _SFX
209 IHashCalc *hash,
210 #endif
211 UString &errorMessage,
212 CDecompressStat &stat)
213 {
214 stat.Clear();
215 UInt64 totalPackSize = 0;
216 CRecordVector<UInt64> arcSizes;
217
218 unsigned numArcs = options.StdInMode ? 1 : arcPaths.Size();
219
220 unsigned i;
221 for (i = 0; i < numArcs; i++)
222 {
223 NFind::CFileInfo fi;
224 fi.Size = 0;
225 if (!options.StdInMode)
226 {
227 const FString &arcPath = us2fs(arcPaths[i]);
228 if (!fi.Find(arcPath))
229 throw "there is no such archive";
230 if (fi.IsDir())
231 throw "can't decompress folder";
232 }
233 arcSizes.Add(fi.Size);
234 totalPackSize += fi.Size;
235 }
236
237 CBoolArr skipArcs(numArcs);
238 for (i = 0; i < numArcs; i++)
239 skipArcs[i] = false;
240
241 CArchiveExtractCallback *ecs = new CArchiveExtractCallback;
242 CMyComPtr<IArchiveExtractCallback> ec(ecs);
243 bool multi = (numArcs > 1);
244 ecs->InitForMulti(multi, options.PathMode, options.OverwriteMode);
245 #ifndef _SFX
246 ecs->SetHashMethods(hash);
247 #endif
248
249 if (multi)
250 {
251 RINOK(extractCallback->SetTotal(totalPackSize));
252 }
253
254 UInt64 totalPackProcessed = 0;
255 bool thereAreNotOpenArcs = false;
256
257 for (i = 0; i < numArcs; i++)
258 {
259 if (skipArcs[i])
260 continue;
261
262 const UString &arcPath = arcPaths[i];
263 NFind::CFileInfo fi;
264 if (options.StdInMode)
265 {
266 fi.Size = 0;
267 fi.Attrib = 0;
268 }
269 else
270 {
271 if (!fi.Find(us2fs(arcPath)) || fi.IsDir())
272 throw "there is no such archive";
273 }
274
275 #ifndef _NO_CRYPTO
276 openCallback->Open_ClearPasswordWasAskedFlag();
277 #endif
278
279 RINOK(extractCallback->BeforeOpen(arcPath));
280 CArchiveLink arcLink;
281
282 CObjectVector<COpenType> types2 = types;
283 /*
284 #ifndef _SFX
285 if (types.IsEmpty())
286 {
287 int pos = arcPath.ReverseFind(L'.');
288 if (pos >= 0)
289 {
290 UString s = arcPath.Ptr(pos + 1);
291 int index = codecs->FindFormatForExtension(s);
292 if (index >= 0 && s == L"001")
293 {
294 s = arcPath.Left(pos);
295 pos = s.ReverseFind(L'.');
296 if (pos >= 0)
297 {
298 int index2 = codecs->FindFormatForExtension(s.Ptr(pos + 1));
299 if (index2 >= 0) // && s.CompareNoCase(L"rar") != 0
300 {
301 types2.Add(index2);
302 types2.Add(index);
303 }
304 }
305 }
306 }
307 }
308 #endif
309 */
310
311 COpenOptions op;
312 #ifndef _SFX
313 op.props = &options.Properties;
314 #endif
315 op.codecs = codecs;
316 op.types = &types2;
317 op.excludedFormats = &excludedFormats;
318 op.stdInMode = options.StdInMode;
319 op.stream = NULL;
320 op.filePath = arcPath;
321 HRESULT result = arcLink.Open2(op, openCallback);
322
323 if (result == E_ABORT)
324 return result;
325
326 bool crypted = false;
327 #ifndef _NO_CRYPTO
328 crypted = openCallback->Open_WasPasswordAsked();
329 #endif
330
331 if (arcLink.NonOpen_ErrorInfo.ErrorFormatIndex >= 0)
332 result = S_FALSE;
333
334 // arcLink.Set_ErrorsText();
335 RINOK(extractCallback->OpenResult(arcPath, result, crypted));
336
337
338 {
339 FOR_VECTOR (r, arcLink.Arcs)
340 {
341 const CArc &arc = arcLink.Arcs[r];
342 const CArcErrorInfo &er = arc.ErrorInfo;
343 if (er.IsThereErrorOrWarning())
344 {
345 RINOK(extractCallback->SetError(r, arc.Path,
346 er.GetErrorFlags(), er.ErrorMessage,
347 er.GetWarningFlags(), er.WarningMessage));
348 }
349 }
350 }
351
352 if (result != S_OK)
353 {
354 thereAreNotOpenArcs = true;
355 if (!options.StdInMode)
356 {
357 NFind::CFileInfo fi;
358 if (fi.Find(us2fs(arcPath)))
359 if (!fi.IsDir())
360 totalPackProcessed += fi.Size;
361 }
362 continue;
363 }
364
365 if (!options.StdInMode)
366 {
367 // numVolumes += arcLink.VolumePaths.Size();
368 // arcLink.VolumesSize;
369
370 // totalPackSize -= DeleteUsedFileNamesFromList(arcLink, i + 1, arcPaths, arcPathsFull, &arcSizes);
371 // numArcs = arcPaths.Size();
372 if (arcLink.VolumePaths.Size() != 0)
373 {
374 Int64 correctionSize = arcLink.VolumesSize;
375 FOR_VECTOR (v, arcLink.VolumePaths)
376 {
377 int index = Find_FileName_InSortedVector(arcPathsFull, arcLink.VolumePaths[v]);
378 if (index >= 0)
379 {
380 if ((unsigned)index > i)
381 {
382 skipArcs[index] = true;
383 correctionSize -= arcSizes[index];
384 }
385 }
386 }
387 if (correctionSize != 0)
388 {
389 Int64 newPackSize = (Int64)totalPackSize + correctionSize;
390 if (newPackSize < 0)
391 newPackSize = 0;
392 totalPackSize = newPackSize;
393 RINOK(extractCallback->SetTotal(totalPackSize));
394 }
395 }
396 }
397
398 #ifndef _NO_CRYPTO
399 bool passwordIsDefined;
400 UString password;
401 RINOK(openCallback->Open_GetPasswordIfAny(passwordIsDefined, password));
402 if (passwordIsDefined)
403 {
404 RINOK(extractCallback->SetPassword(password));
405 }
406 #endif
407
408 FOR_VECTOR (k, arcLink.Arcs)
409 {
410 const CArc &arc = arcLink.Arcs[k];
411 const CArcErrorInfo &er = arc.ErrorInfo;
412
413 if (er.ErrorFormatIndex >= 0)
414 {
415 RINOK(extractCallback->OpenTypeWarning(arc.Path,
416 codecs->GetFormatNamePtr(arc.FormatIndex),
417 codecs->GetFormatNamePtr(er.ErrorFormatIndex)))
418 /*
419 UString s = L"Can not open the file as [" + codecs->Formats[arc.ErrorFormatIndex].Name + L"] archive\n";
420 s += L"The file is open as [" + codecs->Formats[arc.FormatIndex].Name + L"] archive";
421 RINOK(extractCallback->MessageError(s));
422 */
423 }
424 {
425 const UString &s = er.ErrorMessage;
426 if (!s.IsEmpty())
427 {
428 RINOK(extractCallback->MessageError(s));
429 }
430 }
431 }
432
433 CArc &arc = arcLink.Arcs.Back();
434 arc.MTimeDefined = (!options.StdInMode && !fi.IsDevice);
435 arc.MTime = fi.MTime;
436
437 UInt64 packProcessed;
438 bool calcCrc =
439 #ifndef _SFX
440 (hash != NULL);
441 #else
442 false;
443 #endif
444
445 RINOK(DecompressArchive(
446 codecs,
447 arcLink,
448 fi.Size + arcLink.VolumesSize,
449 wildcardCensor,
450 options,
451 calcCrc,
452 extractCallback, ecs, errorMessage, packProcessed));
453 if (!options.StdInMode)
454 packProcessed = fi.Size + arcLink.VolumesSize;
455 totalPackProcessed += packProcessed;
456 ecs->LocalProgressSpec->InSize += packProcessed;
457 ecs->LocalProgressSpec->OutSize = ecs->UnpackSize;
458 if (!errorMessage.IsEmpty())
459 return E_FAIL;
460 }
461
462 if (multi || thereAreNotOpenArcs)
463 {
464 RINOK(extractCallback->SetTotal(totalPackSize));
465 RINOK(extractCallback->SetCompleted(&totalPackProcessed));
466 }
467 stat.NumFolders = ecs->NumFolders;
468 stat.NumFiles = ecs->NumFiles;
469 stat.NumAltStreams = ecs->NumAltStreams;
470 stat.UnpackSize = ecs->UnpackSize;
471 stat.AltStreams_UnpackSize = ecs->AltStreams_UnpackSize;
472 stat.NumArchives = arcPaths.Size();
473 stat.PackSize = ecs->LocalProgressSpec->InSize;
474 return S_OK;
475 }
476